GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
ov-java.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2007-2021 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 
27 //! @file ov-java.cc
28 //!
29 //! Provides Octave's Java interface.
30 
31 #if defined (HAVE_CONFIG_H)
32 # include "config.h"
33 #endif
34 
35 #if defined (HAVE_WINDOWS_H)
36 # include <windows.h>
37 #endif
38 
39 #include <algorithm>
40 #include <fstream>
41 #include <map>
42 #include <string>
43 #include <vector>
44 
45 #include <clocale>
46 
47 #include "Cell.h"
48 #include "builtin-defun-decls.h"
49 #include "cmd-edit.h"
50 #include "defaults.h"
51 #include "defun.h"
52 #include "error.h"
53 #include "errwarn.h"
54 #include "file-ops.h"
55 #include "file-stat.h"
56 #include "fpucw-wrappers.h"
57 #include "interpreter.h"
58 #include "interpreter-private.h"
59 #include "load-path.h"
60 #include "lo-sysdep.h"
61 #include "oct-env.h"
62 #include "oct-process.h"
63 #include "oct-shlib.h"
64 #include "ov-java.h"
65 #include "parse.h"
66 #include "variables.h"
67 
68 #if defined (HAVE_JAVA)
69 #include <jni.h>
70 #endif
71 
72 #if defined (HAVE_JAVA)
73 
74 // FIXME: Should these values be configurable at run time?
75 
76 #if defined (OCTAVE_USE_WINDOWS_API)
77 # define LIBJVM_FILE_NAME "jvm.dll"
78 #elif defined (__APPLE__)
79 # define LIBJVM_FILE_NAME "libjvm.dylib"
80 # define JAVA_HOME_CMD "/usr/libexec/java_home"
81 #else
82 # define LIBJVM_FILE_NAME "libjvm.so"
83 #endif
84 
85 #define TO_JOBJECT(obj) reinterpret_cast<jobject> (obj)
86 #define TO_JCLASS(obj) reinterpret_cast<jclass> (obj)
87 
88 #define TO_JNIENV(env) reinterpret_cast<JNIEnv *> (env)
89 
90 typedef jint (JNICALL *JNI_CreateJavaVM_t) (JavaVM **pvm, JNIEnv **penv,
91  void *args);
92 
93 typedef jint (JNICALL *JNI_GetCreatedJavaVMs_t) (JavaVM **pvm, jsize bufLen,
94  jsize *nVMs);
95 
96 template <typename T>
98 {
99 public:
100 
101  java_local_ref (JNIEnv *_env)
102  : jobj (nullptr), detached (false), env (_env) { }
103 
104  java_local_ref (JNIEnv *_env, T obj)
105  : jobj (obj), detached (false), env (_env) { }
106 
107  ~java_local_ref (void) { release (); }
108 
109  T& operator = (T obj)
110  {
111  release ();
112 
113  jobj = obj;
114  detached = false;
115 
116  return jobj;
117  }
118 
119  operator bool () const { return (jobj != 0); }
120  operator T () { return jobj; }
121 
122  void detach (void) { detached = true; }
123 
124 private:
125 
126  void release (void)
127  {
128  if (env && jobj && ! detached)
129  env->DeleteLocalRef (jobj);
130 
131  jobj = nullptr;
132  }
133 
135  : jobj (0), detached (false), env (0)
136  { }
137 
138 protected:
139 
140  T jobj;
141  bool detached;
142  JNIEnv *env;
143 };
144 
153 
154 static std::string
155 jstring_to_string (JNIEnv *jni_env, jstring s);
156 
157 static std::string
158 jstring_to_string (JNIEnv *jni_env, jobject obj);
159 
160 static octave_value
161 box (JNIEnv *jni_env, void *jobj, void *jcls_arg = nullptr);
162 
163 static octave_value
164 box_more (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg = nullptr);
165 
166 static bool
167 unbox (JNIEnv *jni_env, const octave_value& val, jobject_ref& jobj,
168  jclass_ref& jcls);
169 
170 static bool
171 unbox (JNIEnv *jni_env, const octave_value_list& args,
172  jobjectArray_ref& jobjs, jobjectArray_ref& jclss);
173 
174 extern "C"
175 {
176  JNIEXPORT jboolean JNICALL
177  Java_org_octave_Octave_call (JNIEnv *, jclass, jstring, jobjectArray,
178  jobjectArray);
179 
180  JNIEXPORT void JNICALL
182 
183  JNIEXPORT void JNICALL
184  Java_org_octave_Octave_doInvoke (JNIEnv *, jclass, jint, jobjectArray);
185 
186  JNIEXPORT void JNICALL
187  Java_org_octave_Octave_doEvalString (JNIEnv *, jclass, jstring);
188 
189  JNIEXPORT jboolean JNICALL
191 }
192 
193 //! The pointer to a java virtual machine either created in the current thread
194 //! or attached this thread to it.
195 
196 static JavaVM *jvm = nullptr;
197 
198 //! Whether the current thread is attached to the jvm given by #jvm.
199 //! This is @c false also if no jvm exists, i.e. if #jvm is @c nullptr.
200 //! @see #initialize_jvm()
201 //! @see #terminate_jvm()
202 
203 static bool jvm_attached = false;
204 
205 //! Need to keep hold of the shared library handle until exit.
206 //! @see #initialize_jvm()
207 //! @see #terminate_jvm()
208 
210 
211 static std::map<int,octave_value> listener_map;
212 static std::map<int,octave_value> octave_ref_map;
213 static int octave_java_refcount = 0;
214 
215 //! The thread id of the currently executing thread or @c -1 if this is
216 //! unknown.
217 //! @see #initialize_java()
218 
219 static long octave_thread_ID = -1;
220 
223 bool Vdebug_java = false;
224 
225 namespace octave
226 {
227  class JVMArgs
228  {
229  public:
230 
231  JVMArgs (void)
232  {
233  vm_args.version = JNI_VERSION_1_6;
234  vm_args.nOptions = 0;
235  vm_args.options = nullptr;
236  vm_args.ignoreUnrecognized = false;
237  }
238 
239  ~JVMArgs (void)
240  {
241  clean ();
242  }
243 
244  JavaVMInitArgs * to_args ()
245  {
246  update ();
247  return &vm_args;
248  }
249 
250  void add (const std::string& opt)
251  {
252  java_opts.push_back (opt);
253  }
254 
255  void read_java_opts (const std::string& filename)
256  {
257  std::ifstream js = sys::ifstream (filename.c_str ());
258 
259  if (! js.bad () && ! js.fail ())
260  {
261  std::string line;
262 
263  while (! js.eof () && ! js.fail ())
264  {
265  std::getline (js, line);
266 
267  if (line.find ('-') == 0)
268  java_opts.push_back (line);
269  else if (line.length () > 0 && Vdebug_java)
270  warning ("invalid JVM option, skipping: %s", line.c_str ());
271  }
272  }
273  }
274 
275  private:
276 
277  void clean (void)
278  {
279  if (vm_args.options != nullptr)
280  {
281  for (int i = 0; i < vm_args.nOptions; i++)
282  delete [] vm_args.options[i].optionString;
283 
284  delete [] vm_args.options;
285 
286  vm_args.options = nullptr;
287  vm_args.nOptions = 0;
288  }
289  }
290 
291  void update (void)
292  {
293  clean ();
294 
295  if (java_opts.size () > 0)
296  {
297  int index = 0;
298 
299  vm_args.nOptions = java_opts.size ();
300  vm_args.options = new JavaVMOption [vm_args.nOptions];
301 
302  for (const auto& opt : java_opts)
303  {
304  if (Vdebug_java)
305  octave_stdout << opt << std::endl;
306  vm_args.options[index++].optionString = strsave (opt.c_str ());
307  }
308 
309  java_opts.clear ();
310  }
311  }
312 
313  private:
314 
315  JavaVMInitArgs vm_args;
316 
317  std::list<std::string> java_opts;
318  };
319 }
320 
321 
322 //! The java initialization directory is given by the environment variable
323 //! @c OCTAVE_JAVA_DIR if defined; otherwise it is the directory of Octave's
324 //! m-files defining Java functions.
325 //!
326 //! The Java initialization directory is the directory where resides:
327 //!
328 //! - @c octave.jar, defining the java classes implementing octave's java
329 //! interface,
330 //! - @c javaclasspath.txt, defining the installation defined portion of the
331 //! (static) classpath,
332 //! - @c java.opts, defining the configurable options of the java virtual
333 //! machine.
334 //!
335 //! Note that the (static) java classpath of the java virtual machine starts
336 //! with @c octave.jar, and that the static java classpath ends with what
337 //! is read from @c javaclasspath.txt located in the initial java directory.
338 //! Moreover, the java virtual machine is created essentially with
339 //! the options given by @c java.opts.
340 
341 static std::string
343 {
344  static std::string java_dir;
345 
346  if (java_dir.empty ())
347  {
348  java_dir = octave::sys::env::getenv ("OCTAVE_JAVA_DIR");
349 
350  if (java_dir.empty ())
351  java_dir = (octave::config::fcn_file_dir ()
352  + octave::sys::file_ops::dir_sep_str () + "java");
353  }
354 
355  return java_dir;
356 }
357 
358 //! Return the classpath in the given file @c filepath as a string.
359 //!
360 //! In the classpath file, each line which is neither empty nor a comment, is
361 //! interpreted as a segment of a path. Comment lines are those starting with
362 //! a @c # or with a @c % in the very first column.
363 //!
364 //! @param filepath The path to the file (usually @c classpath.txt) containing
365 //! a portion of the classpath.
366 //!
367 //! @returns A string consisting of the lines of @c filepath which are neither
368 //! comments nor empty without trailing whitespace separated by
369 //! 'octave::directory_path::path_sep_str()'. The returned string also
370 //! starts with that path separator.
371 
372 static std::string
373 read_classpath_txt (const std::string& filepath)
374 {
375  std::string classpath;
376 
377  std::ifstream fs = octave::sys::ifstream (filepath.c_str ());
378 
379  if (! fs.bad () && ! fs.fail ())
380  {
381  std::string line;
382 
383  while (! fs.eof () && ! fs.fail ())
384  {
385  std::getline (fs, line);
386  if (line.length () > 0 && line[0] != '#' && line[0] != '%')
387  {
388  // prepend separator character
389  classpath.append (octave::directory_path::path_sep_str ());
390 
391  // append content of line without whitespace
392  int last = line.find_last_not_of (" \t\f\v\r\n");
393 
394  classpath.append (octave::sys::file_ops::tilde_expand (line.substr (0, last+1)));
395  }
396  }
397  }
398 
399  return (classpath);
400 }
401 
402 
403 //! Return the initial classpath.
404 //!
405 //! The initial classpath starts with a pointer to @c octave.jar which is
406 //! located in the initial java directory given by #java_init_dir().
407 //!
408 //! @attention This is nowhere documented and also the script
409 //! @c javaclasspath.m drops this. On the other hand, this is vital because
410 //! @c octave.jar contains the java core classes of octave's java interface.
411 //!
412 //! The rest of the classpath is read sequentially from files
413 //! @c javaclasspath.txt located in either:
414 //!
415 //! - the current directory,
416 //! - the user's home directory,
417 //! - the initial java directory returned by #initial_java_dir()
418 //!
419 //! @returns The initial classpath.
420 
421 static std::string
423 {
424  std::string java_dir = initial_java_dir ();
425 
426  std::string retval = java_dir;
427 
428  // Find octave.jar file.
429  if (! retval.empty ())
430  {
431  std::string sep = octave::sys::file_ops::dir_sep_str ();
432 
433  std::string jar_file = java_dir + sep + "octave.jar";
434 
435  octave::sys::file_stat jar_exists (jar_file);
436 
437  if (jar_exists)
438  {
439  // Initialize static classpath to octave.jar.
440  retval = jar_file;
441 
442  // The base classpath has been set.
443  // Try to find an optional file specifying classpaths in 3 places.
444  // 1) Current directory
445  // 2) User's home directory
446  // 3) Octave installation directory where octave.jar resides
447 
448  std::string cwd = octave::sys::env::get_current_directory ();
449  std::string home_dir = octave::sys::env::get_home_directory ();
450 
451  // The filename is "javaclasspath.txt", but historically has been
452  // "classpath.txt" so both are supported.
453  std::vector<std::string> cp_list = {"javaclasspath.txt",
454  "classpath.txt"};
455 
456  for (std::string filename : cp_list)
457  {
458  std::string cp_file = filename;
459  octave::sys::file_stat cp_exists;
460 
461  // Try to find classpath file in the current directory.
462 
463  cp_exists = octave::sys::file_stat (cp_file);
464  if (cp_exists)
465  {
466  // File found. Add its contents to the static classpath.
467  std::string classpath = read_classpath_txt (cp_file);
468  retval.append (classpath);
469  }
470 
471  // Try to find classpath file in the user's home directory.
472 
473  if (cwd != home_dir)
474  {
475  cp_file = '~' + sep + filename;
476  cp_file = octave::sys::file_ops::tilde_expand (cp_file);
477  cp_exists = octave::sys::file_stat (cp_file);
478  if (cp_exists)
479  {
480  // File found. Add its contents to the static classpath.
481  std::string classpath = read_classpath_txt (cp_file);
482  retval.append (classpath);
483  }
484  }
485 
486  // Try to find classpath file in the Octave install directory.
487 
488  if (cwd != java_dir)
489  {
490  cp_file = java_dir + sep + filename;
491  cp_exists = octave::sys::file_stat (cp_file);
492  if (cp_exists)
493  {
494  // File found. Add its contents to the static classpath.
495  std::string classpath = read_classpath_txt (cp_file);
496  retval.append (classpath);
497  }
498  }
499  }
500  }
501  else
502  error ("octave.jar does not exist: %s", jar_file.c_str ());
503  }
504  else
505  error ("initial java dir is empty");
506 
507  return retval;
508 }
509 
510 static std::string
511 get_jvm_lib_path_in_subdir (std::string java_home_path)
512 {
513  // This assumes that whatever architectures are installed are appropriate for
514  // this machine
515 #if defined (OCTAVE_USE_WINDOWS_API)
516  std::string subdirs[] = {"bin/client", "bin/server"};
517 #else
518  std::string subdirs[] = {"jre/lib/server", "jre/lib",
519  "lib/client", "lib/server",
520  "jre/lib/amd64/client", "jre/lib/amd64/server",
521  "jre/lib/i386/client", "jre/lib/i386/server"
522  };
523 #endif
524 
525  for (size_t i = 0; i < sizeof (subdirs) / sizeof (subdirs[0]); i++)
526  {
527  std::string candidate = java_home_path + "/" + subdirs[i]
528  + "/" LIBJVM_FILE_NAME;
529  if (octave::sys::file_stat (candidate))
530  return candidate;
531  }
532  return "";
533 }
534 
535 #if defined (OCTAVE_USE_WINDOWS_API)
536 
537 namespace octave
538 {
539  // Declare function defined in sysdep.cc
540  extern LONG
541  get_regkey_value (HKEY h_rootkey, const std::string subkey,
542  const std::string name, octave_value& value);
543 }
544 
545 static std::string
546 get_jvm_lib_path_from_registry ()
547 {
548  // In Windows, find the location of the JRE from the registry
549  std::string key, jversion, value;
550 
551  // First search for JRE >= 15
552  key = R"(software\javasoft\jdk)";
553 
554  jversion = octave::sys::env::getenv ("JAVA_VERSION");
555  bool maybe_version_15_or_newer = true;
556  octave_value regval;
557  LONG retval;
558  if (jversion.empty ())
559  {
560  value = "CurrentVersion";
561  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
562  regval);
563 
564  if (retval != ERROR_SUCCESS)
565  {
566  // Search for JRE < 15
567  maybe_version_15_or_newer = false;
568  key = R"(software\javasoft\jre)";
569  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
570  regval);
571 
572  if (retval != ERROR_SUCCESS)
573  {
574  // Search for JRE < 9
575  key = R"(software\javasoft\java runtime environment)";
576  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key,
577  value, regval);
578  }
579  }
580 
581  if (retval != ERROR_SUCCESS)
582  error ("unable to find Java Runtime Environment: %s::%s",
583  key.c_str (), value.c_str ());
584 
585  jversion = regval.xstring_value ("initialize_jvm: registry value "
586  R"("%s" at "%s" must be a string)",
587  value.c_str (), key.c_str ());
588  }
589 
590  std::string jvm_lib_path;
591  if (maybe_version_15_or_newer)
592  {
593  // Look for value used by JRE >= 15
594  key = key + '\\' + jversion;
595  value = "JavaHome";
596  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
597  regval);
598 
599  if (retval != ERROR_SUCCESS)
600  error ("unable to find Java Runtime Environment: %s::%s",
601  key.c_str (), value.c_str ());
602 
603  jvm_lib_path
604  = regval.xstring_value (R"(initialize_jvm: registry value "%s" at )"
605  R"("%s" must be a string)",
606  value.c_str (), key.c_str ())
607  + R"(\bin\server\jvm.dll)";
608 
609  if (! jvm_lib_path.empty ())
610  return jvm_lib_path;
611 
612  }
613 
614  // Search for JRE < 15
615  key = R"(software\javasoft\jre\)" + jversion;
616  value = "RuntimeLib";
617  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
618  regval);
619 
620  if (retval != ERROR_SUCCESS)
621  {
622  // Search for JRE < 9
623  key = R"(software\javasoft\java runtime environment\)" + jversion;
624  retval = octave::get_regkey_value (HKEY_LOCAL_MACHINE, key, value,
625  regval);
626  }
627 
628  if (retval != ERROR_SUCCESS)
629  error ("unable to find Java Runtime Environment: %s::%s",
630  key.c_str (), value.c_str ());
631 
632  jvm_lib_path
633  = regval.xstring_value (R"(initialize_jvm: registry value "%s" at )"
634  R"("%s" must be a string)",
635  value.c_str (), key.c_str ());
636 
637  if (jvm_lib_path.empty ())
638  error ("unable to find Java Runtime Environment: %s::%s",
639  key.c_str (), value.c_str ());
640 
641  return jvm_lib_path;
642 }
643 #endif
644 
645 
646 //! Initialize the java virtual machine (jvm) and field #jvm if necessary.
647 //!
648 //! If the jvm exists and is initialized, #jvm points to it, i.e. is not 0
649 //! and there is nothing to do.
650 //!
651 //! If #jvm is 0 and if at least one jvm exists, attach the current thread to
652 //! it by setting #jvm_attached. Otherwise, create a #jvm with some hard-
653 //! coded options:
654 //!
655 //! - '-Djava.class.path=classpath', where @c classpath is given by
656 //! #initial_class_path().
657 //! - '-Xrs'
658 //!
659 //! Further options are read from the file @c java.opts in the directory given
660 //! by #java_init_dir().
661 //!
662 //! Note that #initial_class_path() determines the initial classpath. This
663 //! is the static classpath which cannot be changed. Elements of the dynamic
664 //! classpath can be added and removed using the m-file scripts
665 //! @c javaaddpath.m and @c javarmpath.m.
666 //!
667 //! @see #terminate_jvm()
668 
669 static void
671 {
672  // Most of the time JVM already exists and has been initialized.
673  // Also it seems, as if jvm is set, the jvm is already attached.
674  // This does not fit terminate_jvm.
675  if (jvm)
676  return;
677 
678  JNIEnv *current_env;
679  const char *static_locale = setlocale (LC_ALL, nullptr);
680  std::string locale;
681  if (static_locale)
682  locale = std::string (static_locale);
683 
684  octave::dynamic_library lib ("");
685  std::string jvm_lib_path;
686 
687  // Check whether the Java VM library is already loaded or linked in.
688  JNI_CreateJavaVM_t create_vm = reinterpret_cast<JNI_CreateJavaVM_t>
689  (lib.search ("JNI_CreateJavaVM"));
690  JNI_GetCreatedJavaVMs_t get_vm = reinterpret_cast<JNI_GetCreatedJavaVMs_t>
691  (lib.search ("JNI_GetCreatedJavaVMs"));
692 
693  if (create_vm && get_vm)
694  jvm_lib_path = "linked in or loaded libraries";
695  else
696  {
697  // JAVA_HOME environment variable takes precedence
698  std::string java_home_env = octave::sys::env::getenv ("JAVA_HOME");
699  if (! java_home_env.empty ())
700  {
701  jvm_lib_path = get_jvm_lib_path_in_subdir (java_home_env);
702 
703  // If JAVA_HOME does not look like a Java directory, use it anyway
704  // to fail with a useful error message indicating the directory
705  if (jvm_lib_path.empty ())
706  jvm_lib_path = java_home_env + "/" LIBJVM_FILE_NAME;
707  }
708 
709 # if defined (__APPLE__)
710  // Use standard /usr/libexec/java_home if available.
711  if (jvm_lib_path.empty ())
712  {
713  octave::sys::file_stat libexec_java_home_exists (JAVA_HOME_CMD);
714  if (libexec_java_home_exists)
715  {
716  // FIXME: Should this command be fully configurable at run
717  // time? Or is it OK for the options to be fixed here?
718 
719  std::string java_home_cmd = std::string (JAVA_HOME_CMD)
720  + " --failfast --version 1.6+ 2>/dev/null";
721 
723  = octave::run_command_and_return_output (java_home_cmd);
724 
725  if (rslt.exit_status () == 0)
726  {
727  std::string output = rslt.stdout_output ();
728  std::string found_path = output.substr (0, output.length() - 1);
729  std::string jvm_lib_found = get_jvm_lib_path_in_subdir (found_path);
730  if (!jvm_lib_found.empty ())
731  jvm_lib_path = jvm_lib_found;
732  }
733  }
734  }
735 # endif
736 
737  if (jvm_lib_path.empty ())
738  {
739 #if defined (OCTAVE_USE_WINDOWS_API)
740  jvm_lib_path = get_jvm_lib_path_from_registry ();
741 #else
742  // Fall back to JAVA_LDPATH, determined by the build system
743  jvm_lib_path = std::string (JAVA_LDPATH) + "/" LIBJVM_FILE_NAME;
744 #endif
745  }
746 
747  lib = octave::dynamic_library (jvm_lib_path);
748 
749  if (! lib)
750  error ("unable to load Java Runtime Environment from %s",
751  jvm_lib_path.c_str ());
752 
753  create_vm = reinterpret_cast<JNI_CreateJavaVM_t>
754  (lib.search ("JNI_CreateJavaVM"));
755  get_vm = reinterpret_cast<JNI_GetCreatedJavaVMs_t>
756  (lib.search ("JNI_GetCreatedJavaVMs"));
757 
758  if (! create_vm)
759  error ("unable to find JNI_CreateJavaVM in %s", jvm_lib_path.c_str ());
760 
761  if (! get_vm)
762  error ("unable to find JNI_GetCreatedJavaVMs in %s",
763  jvm_lib_path.c_str ());
764  }
765 
766  //! The number of created jvm's.
767  jsize nVMs = 0;
768 
769  if (get_vm (&jvm, 1, &nVMs) == 0 && nVMs > 0)
770  {
771  // At least one JVM exists, try to attach the current thread to it.
772 
773  switch (jvm->GetEnv (reinterpret_cast<void **> (&current_env),
774  JNI_VERSION_1_6))
775  {
776  case JNI_EDETACHED:
777  // Attach the current thread
778  JavaVMAttachArgs vm_args;
779  vm_args.version = JNI_VERSION_1_6;
780  vm_args.name = const_cast<char *> ("octave");
781  vm_args.group = nullptr;
782  if (jvm->AttachCurrentThread (reinterpret_cast<void **> (&current_env),
783  &vm_args) < 0)
784  error ("JVM internal error, unable to attach octave to existing JVM");
785  break;
786 
787  case JNI_EVERSION:
788  error ("JVM internal error, the required JNI version is not supported");
789  break;
790 
791  case JNI_OK:
792  // Don't do anything, the current thread is already attached to JVM
793  break;
794  }
795 
796  jvm_attached = true;
797  }
798  else
799  {
800  // No JVM exists, create one
801 
802  octave::JVMArgs vm_args;
803 
804  // Hard-coded options for the jvm.
805  vm_args.add ("-Djava.class.path=" + initial_class_path ());
806 #if defined (HAVE_BROKEN_PTHREAD_STACKSIZE)
807  vm_args.add ("-Djdk.lang.processReaperUseDefaultStackSize=true");
808 #endif
809  vm_args.add ("-Xrs");
810 
811  // Additional options given by file java.opts.
812  vm_args.read_java_opts (initial_java_dir () +
814  "java.opts");
815 
816  if (create_vm (&jvm, &current_env, vm_args.to_args ()) != JNI_OK)
817  error ("unable to start Java VM in %s", jvm_lib_path.c_str ());
818  }
819 
820  jvm_lib = lib;
821 
822  setlocale (LC_ALL, locale.c_str ());
823 }
824 
825 //! Terminate the current jvm, if there is any.
826 //!
827 //! Otherwise, detach the jvm if this thread is attached to it and unload it
828 //! if this thread created it itself.
829 //!
830 //! @see #initialize_jvm()
831 
832 static void
834 {
835  // There is nothing to do if jvm is not set (= nullptr).
836  if (jvm)
837  {
838  // FIXME: Seems that if jvm_attached is always true if jvm is not null.
839  if (jvm_attached)
840  jvm->DetachCurrentThread ();
841  else
842  jvm->DestroyJavaVM ();
843 
844  jvm = nullptr;
845  jvm_attached = false;
846 
847  if (jvm_lib)
848  jvm_lib.close ();
849 
851  }
852 }
853 
854 //! Converts a Java string object to std::string.
855 //!{
856 static std::string
857 jstring_to_string (JNIEnv *jni_env, jstring s)
858 {
859  std::string retval;
860 
861  if (jni_env)
862  {
863  const char *cstr = jni_env->GetStringUTFChars (s, nullptr);
864  retval = cstr;
865  jni_env->ReleaseStringUTFChars (s, cstr);
866  }
867 
868  return retval;
869 }
870 
871 static std::string
872 jstring_to_string (JNIEnv *jni_env, jobject obj)
873 {
874  std::string retval;
875 
876  if (jni_env && obj)
877  {
878  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));
879  if (cls)
880  {
881  if (jni_env->IsInstanceOf (obj, cls))
882  retval = jstring_to_string (jni_env,
883  reinterpret_cast<jstring> (obj));
884  }
885  }
886 
887  return retval;
888 }
889 //!}
890 
891 //! Returns a reference to the jni (java native interface) environment of the
892 //! Java virtual machine #jvm.
893 //!
894 //! @returns A reference to jni, if #jvm is present, otherwise @c nullptr.
895 
896 static inline JNIEnv *
898 {
899  JNIEnv *env = nullptr;
900 
901  if (jvm)
902  jvm->GetEnv (reinterpret_cast<void **> (&env), JNI_VERSION_1_6);
903 
904  return env;
905 }
906 
907 #endif
908 
909 bool
911 {
912 #if defined (HAVE_JAVA)
913 
914  JNIEnv *current_env = thread_jni_env ();
915 
916  if (current_env && java_object)
917  {
918  jclass_ref cls (current_env, current_env->FindClass ("java/lang/String"));
919  return current_env->IsInstanceOf (TO_JOBJECT (java_object), cls);
920  }
921 
922  return false;
923 
924 #else
925 
926  // This shouldn't happen because construction of octave_java objects is
927  // supposed to be impossible if Java is not available.
928 
929  panic_impossible ();
930 
931 #endif
932 }
933 
934 bool
935 octave_java::is_instance_of (const std::string& cls_name) const
936 {
937 #if defined (HAVE_JAVA)
938 
939  JNIEnv *current_env = thread_jni_env ();
940 
941  std::string cls_cpp = cls_name;
942  std::replace (cls_cpp.begin (), cls_cpp.end (), '.', '/');
943 
944  if (current_env && java_object)
945  {
946  jclass_ref cls (current_env, current_env->FindClass (cls_cpp.c_str ()));
947  if (current_env->ExceptionCheck ())
948  current_env->ExceptionClear ();
949  else
950  return current_env->IsInstanceOf (TO_JOBJECT (java_object), cls);
951  }
952  return false;
953 
954 #else
955 
956  octave_unused_parameter (cls_name);
957 
958  // This shouldn't happen because construction of octave_java objects is
959  // supposed to be impossible if Java is not available.
960 
961  panic_impossible ();
962 
963 #endif
964 }
965 
966 #if defined (HAVE_JAVA)
967 
968 static octave_value
969 check_exception (JNIEnv *jni_env)
970 {
972 
973  jthrowable_ref ex (jni_env, jni_env->ExceptionOccurred ());
974 
975  if (ex)
976  {
977  if (Vdebug_java)
978  jni_env->ExceptionDescribe ();
979 
980  jni_env->ExceptionClear ();
981 
982  jclass_ref jcls (jni_env, jni_env->GetObjectClass (ex));
983  jmethodID mID = jni_env->GetMethodID (jcls, "toString",
984  "()Ljava/lang/String;");
985  jstring_ref js (jni_env,
986  reinterpret_cast<jstring> (jni_env->CallObjectMethod (ex,
987  mID)));
988  std::string msg = jstring_to_string (jni_env, js);
989 
990  error ("[java] %s", msg.c_str ());
991  }
992  else
993  retval = Matrix ();
994 
995  return retval;
996 }
997 
998 static jclass
999 find_octave_class (JNIEnv *jni_env, const char *name)
1000 {
1001  static std::string class_loader;
1002  static jclass uiClass = nullptr;
1003 
1004  jclass jcls = jni_env->FindClass (name);
1005 
1006  if (jcls == nullptr)
1007  {
1008  jni_env->ExceptionClear ();
1009 
1010  if (! uiClass)
1011  {
1012  if (class_loader.empty ())
1013  {
1014  jclass_ref syscls (jni_env,
1015  jni_env->FindClass ("java/lang/System"));
1016  jmethodID mID = jni_env->GetStaticMethodID
1017  (syscls,
1018  "getProperty",
1019  "(Ljava/lang/String;)Ljava/lang/String;");
1020  jstring_ref js (jni_env,
1021  jni_env->NewStringUTF ("octave.class.loader"));
1022  js = reinterpret_cast<jstring> (jni_env->CallStaticObjectMethod
1023  (syscls, mID, jstring (js)));
1024  class_loader = jstring_to_string (jni_env, jstring (js));
1025  std::replace (class_loader.begin (), class_loader.end (),
1026  '.', '/');
1027  }
1028 
1029  jclass_ref uicls (jni_env,
1030  jni_env->FindClass (class_loader.c_str ()));
1031 
1032  if (! uicls)
1033  {
1034  jni_env->ExceptionClear ();
1035 
1036  // Try the netbeans way
1037  std::replace (class_loader.begin (), class_loader.end (),
1038  '/', '.');
1039  jclass_ref jcls2 (jni_env,
1040  jni_env->FindClass ("org/openide/util/Lookup"));
1041  jmethodID mID = jni_env->GetStaticMethodID
1042  (jcls2, "getDefault", "()Lorg/openide/util/Lookup;");
1043  jobject_ref lObj (jni_env,
1044  jni_env->CallStaticObjectMethod (jcls2, mID));
1045  mID = jni_env->GetMethodID (jcls2, "lookup",
1046  "(Ljava/lang/Class;)Ljava/lang/Object;");
1047  jclass_ref cLoaderCls (jni_env,
1048  jni_env->FindClass ("java/lang/ClassLoader"));
1049  jobject_ref cLoader (jni_env,
1050  jni_env->CallObjectMethod
1051  (lObj, mID, jclass (cLoaderCls)));
1052  mID = jni_env->GetMethodID (cLoaderCls, "loadClass",
1053  "(Ljava/lang/String;)Ljava/lang/Class;");
1054  jstring_ref js (jni_env,
1055  jni_env->NewStringUTF (class_loader.c_str ()));
1056  uicls = reinterpret_cast<jclass>
1057  (jni_env->CallObjectMethod (cLoader, mID, jstring (js)));
1058  }
1059 
1060  if (uicls)
1061  uiClass = reinterpret_cast<jclass>
1062  (jni_env->NewGlobalRef (jclass (uicls)));
1063  }
1064 
1065  if (uiClass)
1066  {
1067  jmethodID mID = jni_env->GetStaticMethodID
1068  (uiClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
1069  jstring_ref js (jni_env, jni_env->NewStringUTF (name));
1070  jcls = reinterpret_cast<jclass>
1071  (jni_env->CallStaticObjectMethod (uiClass, mID, jstring (js)));
1072  }
1073  }
1074 
1075  return jcls;
1076 }
1077 
1078 static dim_vector
1079 compute_array_dimensions (JNIEnv *jni_env, jobject obj)
1080 {
1081  jobjectArray_ref jobj (jni_env, reinterpret_cast<jobjectArray> (obj));
1082  jclass_ref jcls (jni_env, jni_env->GetObjectClass (obj));
1083  jclass_ref ccls (jni_env, jni_env->GetObjectClass (jcls));
1084  jmethodID isArray_ID = jni_env->GetMethodID (ccls, "isArray", "()Z");
1085  jmethodID getComponentType_ID = jni_env->GetMethodID (ccls,
1086  "getComponentType",
1087  "()Ljava/lang/Class;");
1088 
1089  dim_vector dv (1, 1);
1090  int idx = 0;
1091 
1092  jobj.detach ();
1093  while (jcls && jni_env->CallBooleanMethod (jcls, isArray_ID))
1094  {
1095  int len = (jobj ? jni_env->GetArrayLength (jobj) : 0);
1096  if (idx >= dv.ndims ())
1097  dv.resize (idx+1);
1098  dv(idx) = len;
1099  jcls = reinterpret_cast<jclass>
1100  (jni_env->CallObjectMethod (jcls, getComponentType_ID));
1101  jobj = len > 0
1102  ? reinterpret_cast<jobjectArray> (jni_env->GetObjectArrayElement (jobj,
1103  0))
1104  : nullptr;
1105  idx++;
1106  }
1107 
1109 
1110  return dv;
1111 }
1112 
1113 static jobject
1114 make_java_index (JNIEnv *jni_env, const octave_value_list& idx)
1115 {
1116  jclass_ref ocls (jni_env, jni_env->FindClass ("[I"));
1117  jobjectArray retval = jni_env->NewObjectArray (idx.length (), ocls, nullptr);
1118  // Here retval has the same length as idx
1119 
1120  // Fill in entries of idx into retval
1121  for (int i = 0; i < idx.length (); i++)
1122  try
1123  {
1124  idx_vector v = idx(i).index_vector ();
1125 
1126  jintArray_ref i_array (jni_env, jni_env->NewIntArray (v.length ()));
1127  jint *buf = jni_env->GetIntArrayElements (i_array, nullptr);
1128  // Here, buf points to the beginning of i_array
1129 
1130  // Copy v to buf.
1131  for (int k = 0; k < v.length (); k++)
1132  buf[k] = v(k);
1133 
1134  // Set retval[i] = i_array
1135  jni_env->ReleaseIntArrayElements (i_array, buf, 0);
1136  jni_env->SetObjectArrayElement (retval, i, i_array);
1137 
1138  check_exception (jni_env);
1139  }
1140  catch (octave::index_exception& e)
1141  {
1142  // Rethrow to allow more info to be reported later.
1143  e.set_pos_if_unset (idx.length (), i + 1);
1144  throw;
1145  }
1146 
1147  return retval;
1148 }
1149 
1150 static octave_value
1151 get_array_elements (JNIEnv *jni_env, jobject jobj,
1152  const octave_value_list& idx)
1153 {
1155  jobject_ref resObj (jni_env);
1156  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));
1157 
1158  jclass_ref helperClass (jni_env,
1159  find_octave_class (jni_env,
1160  "org/octave/ClassHelper"));
1161  jmethodID mID = jni_env
1162  ->GetStaticMethodID (helperClass, "arraySubsref",
1163  "(Ljava/lang/Object;[[I)Ljava/lang/Object;");
1164  resObj = jni_env->CallStaticObjectMethod
1165  (helperClass, mID, jobj, jobject (java_idx));
1166 
1167  if (resObj)
1168  retval = box (jni_env, resObj);
1169  else
1170  retval = check_exception (jni_env);
1171 
1173 
1174  return retval;
1175 }
1176 
1177 static octave_value
1178 set_array_elements (JNIEnv *jni_env, jobject jobj,
1179  const octave_value_list& idx, const octave_value& rhs)
1180 {
1182 
1183  jclass_ref rhsCls (jni_env);
1184  jobject_ref resObj (jni_env);
1185  jobject_ref rhsObj (jni_env);
1186  jobject_ref java_idx (jni_env, make_java_index (jni_env, idx));
1187 
1188  if (unbox (jni_env, rhs, rhsObj, rhsCls))
1189  {
1190  jclass_ref helperClass (jni_env,
1191  find_octave_class (jni_env,
1192  "org/octave/ClassHelper"));
1193  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "arraySubsasgn",
1194  "(Ljava/lang/Object;[[ILjava/lang/Object;)" "Ljava/lang/Object;");
1195  resObj = jni_env->CallStaticObjectMethod
1196  (helperClass, mID, jobj, jobject (java_idx), jobject (rhsObj));
1197  }
1198 
1199  if (resObj)
1200  retval = box (jni_env, resObj);
1201  else
1202  retval = check_exception (jni_env);
1203 
1205 
1206  return retval;
1207 }
1208 
1209 static string_vector
1210 get_invoke_list (JNIEnv *jni_env, void *jobj_arg)
1211 {
1212  jobject jobj = TO_JOBJECT (jobj_arg);
1213 
1214  std::list<std::string> name_list;
1215 
1216  if (jni_env)
1217  {
1218  jclass_ref cls (jni_env, jni_env->GetObjectClass (jobj));
1219  jclass_ref ccls (jni_env, jni_env->GetObjectClass (cls));
1220  jmethodID getMethods_ID = jni_env->GetMethodID
1221  (ccls, "getMethods", "()[Ljava/lang/reflect/Method;");
1222  jmethodID getFields_ID = jni_env->GetMethodID
1223  (ccls, "getFields", "()[Ljava/lang/reflect/Field;");
1224  jobjectArray_ref mList (jni_env,
1225  reinterpret_cast<jobjectArray>
1226  (jni_env->CallObjectMethod (cls, getMethods_ID)));
1227  jobjectArray_ref fList (jni_env,
1228  reinterpret_cast<jobjectArray>
1229  (jni_env->CallObjectMethod (cls, getFields_ID)));
1230  int mLen = jni_env->GetArrayLength (mList);
1231  int fLen = jni_env->GetArrayLength (fList);
1232  jclass_ref mCls (jni_env,
1233  jni_env->FindClass ("java/lang/reflect/Method"));
1234  jclass_ref fCls (jni_env,
1235  jni_env->FindClass ("java/lang/reflect/Field"));
1236  jmethodID m_getName_ID = jni_env->GetMethodID (mCls, "getName",
1237  "()Ljava/lang/String;");
1238  jmethodID f_getName_ID = jni_env->GetMethodID (fCls, "getName",
1239  "()Ljava/lang/String;");
1240 
1241  for (int i = 0; i < mLen; i++)
1242  {
1243  jobject_ref meth (jni_env, jni_env->GetObjectArrayElement (mList, i));
1244  jstring_ref methName (jni_env, reinterpret_cast<jstring>
1245  (jni_env->CallObjectMethod (meth,
1246  m_getName_ID)));
1247  name_list.push_back (jstring_to_string (jni_env, methName));
1248  }
1249 
1250  for (int i = 0; i < fLen; i++)
1251  {
1252  jobject_ref field (jni_env,
1253  jni_env->GetObjectArrayElement (fList, i));
1254  jstring_ref fieldName (jni_env,
1255  reinterpret_cast<jstring>
1256  (jni_env->CallObjectMethod
1257  (field, f_getName_ID)));
1258  name_list.push_back (jstring_to_string (jni_env, fieldName));
1259  }
1260 
1262  }
1263 
1264  string_vector v (name_list);
1265 
1266  return v.sort (true);
1267 }
1268 
1269 static octave_value
1270 convert_to_string (JNIEnv *jni_env, jobject java_object, bool force, char type)
1271 {
1273 
1274  if (jni_env && java_object)
1275  {
1276  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/String"));
1277 
1278  if (jni_env->IsInstanceOf (java_object, cls))
1279  retval = octave_value (jstring_to_string (jni_env, java_object), type);
1280  else if (force)
1281  {
1282  cls = jni_env->FindClass ("[Ljava/lang/String;");
1283 
1284  if (jni_env->IsInstanceOf (java_object, cls))
1285  {
1286  jobjectArray array = reinterpret_cast<jobjectArray> (java_object);
1287  int len = jni_env->GetArrayLength (array);
1288  Cell c (len, 1);
1289 
1290  for (int i = 0; i < len; i++)
1291  {
1292  jstring_ref js (jni_env,
1293  reinterpret_cast<jstring>
1294  (jni_env->GetObjectArrayElement (array, i)));
1295 
1296  if (js)
1297  c(i) = octave_value (jstring_to_string (jni_env, js), type);
1298  else
1299  c(i) = check_exception (jni_env);
1300  }
1301 
1302  retval = octave_value (c);
1303  }
1304  else
1305  {
1306  cls = jni_env->FindClass ("java/lang/Object");
1307  jmethodID mID = jni_env->GetMethodID (cls, "toString",
1308  "()Ljava/lang/String;");
1309  jstring_ref js (jni_env,
1310  reinterpret_cast<jstring>
1311  (jni_env->CallObjectMethod (java_object,
1312  mID)));
1313 
1314  if (js)
1315  retval = octave_value (jstring_to_string (jni_env, js), type);
1316  else
1317  retval = check_exception (jni_env);
1318  }
1319  }
1320  else
1321  error ("unable to convert Java object to string");
1322 
1324  }
1325 
1326  return retval;
1327 }
1328 
1329 #define TO_JAVA(obj) dynamic_cast<octave_java *> ((obj).internal_rep ())
1330 
1331 //! Return whether @c jobj shall be automatically converted to an Octave
1332 //! numeric value.
1333 //!
1334 //! If @c jobj is an instance of any of the numeric wrapper classes @c Byte,
1335 //! @c Integer, @c Long, @c Short, @c Float, or @c Double, then it will be
1336 //! converted using the @c java.lang.Number.doubleValue() method.
1337 //!
1338 //! @param jobj Java object being returned to Octave
1339 //! @return @c true if @c jobj shall be converted into a numeric value
1340 //! automatically, @c false otherwise
1341 static bool
1342 is_auto_convertible_number (JNIEnv *jni_env, jobject jobj)
1343 {
1344  jclass_ref cls (jni_env);
1345  cls = jni_env->FindClass ("java/lang/Double");
1346  if (jni_env->IsInstanceOf (jobj, cls))
1347  return true;
1348  cls = jni_env->FindClass ("java/lang/Float");
1349  if (jni_env->IsInstanceOf (jobj, cls))
1350  return true;
1351  cls = jni_env->FindClass ("java/lang/Byte");
1352  if (jni_env->IsInstanceOf (jobj, cls))
1353  return true;
1354  cls = jni_env->FindClass ("java/lang/Short");
1355  if (jni_env->IsInstanceOf (jobj, cls))
1356  return true;
1357  cls = jni_env->FindClass ("java/lang/Integer");
1358  if (jni_env->IsInstanceOf (jobj, cls))
1359  return true;
1360  cls = jni_env->FindClass ("java/lang/Long");
1361  if (jni_env->IsInstanceOf (jobj, cls))
1362  return true;
1363 
1364  return false;
1365 }
1366 
1367 //! Convert the Java object pointed to by @c jobj_arg with class @c jcls_arg
1368 //! to an Octave value.
1369 //!
1370 //! @param jni_env JNI environment pointer.
1371 //! @param jobj_arg Pointer to a Java object.
1372 //! @param jcls_arg Optional pointer to the Java class of @c jobj_arg.
1373 //!
1374 //! @return
1375 //! @arg numeric value as a @c double if @c jobj_arg is of type @c Byte,
1376 //! @c Short, @c Integer, @c Long, @c Float or @c Double
1377 //! @arg logical value if @c jobj_arg is of type @c Boolean
1378 //! @arg string value if @c jobj_arg is of type @c Character or @c String
1379 //! @arg Octave array of numeric, logical, or char type if @c jobj_arg is
1380 //! a Java array of primitive types
1381 //! @arg Octave matrix if @c jobj_arg is of type @c org.octave.Matrix and
1382 //! #Vjava_matrix_autoconversion is enabled
1383 //! @arg Octave object if @c jobj_arg is of type
1384 //! @c org.octave.OctaveReference
1385 //! @arg @c octave_java object wrapping the Java object otherwise.
1386 
1387 static octave_value
1388 box (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
1389 {
1391 
1392  jobject jobj = TO_JOBJECT (jobj_arg);
1393  jclass jcls = TO_JCLASS (jcls_arg);
1394 
1395  jclass_ref cls (jni_env);
1396 
1397  if (! jobj)
1398  retval = Matrix ();
1399 
1400  while (retval.is_undefined ())
1401  {
1402  // Convert a scalar of any numeric class wrapping a primitive class
1403  // (byte, short, integer, long, float, double) to a double value.
1404  // Test whether java.lang.Number before testing for each type.
1405  cls = jni_env->FindClass ("java/lang/Number");
1406  if (jni_env->IsInstanceOf (jobj, cls)
1407  && is_auto_convertible_number (jni_env, jobj))
1408  {
1409  jmethodID m = jni_env->GetMethodID (cls, "doubleValue", "()D");
1410  retval = jni_env->CallDoubleMethod (jobj, m);
1411  break;
1412  }
1413 
1414  cls = jni_env->FindClass ("java/lang/Boolean");
1415  if (jni_env->IsInstanceOf (jobj, cls))
1416  {
1417  jmethodID m = jni_env->GetMethodID (cls, "booleanValue", "()Z");
1418  retval = (jni_env->CallBooleanMethod (jobj, m) ? true : false);
1419  break;
1420  }
1421 
1422  cls = jni_env->FindClass ("java/lang/String");
1423  if (jni_env->IsInstanceOf (jobj, cls))
1424  {
1425  retval = jstring_to_string (jni_env, jobj);
1426  break;
1427  }
1428 
1429  cls = jni_env->FindClass ("java/lang/Character");
1430  if (jni_env->IsInstanceOf (jobj, cls))
1431  {
1432  jmethodID m = jni_env->GetMethodID (cls, "charValue", "()C");
1433  retval = jni_env->CallCharMethod (jobj, m);
1434  retval = retval.convert_to_str (false, true);
1435  break;
1436  }
1437 
1438 #define BOX_PRIMITIVE_ARRAY(JAVA_TYPE, JAVA_ID, JAVA_TYPE_CAP, OCTAVE_ID) \
1439  cls = jni_env->FindClass (JAVA_ID); \
1440  if (jni_env->IsInstanceOf (jobj, cls)) \
1441  { \
1442  const JAVA_TYPE ## Array jarr = reinterpret_cast<JAVA_TYPE ## Array> (jobj); \
1443  const jsize len = jni_env->GetArrayLength (jarr); \
1444  OCTAVE_ID ## NDArray d (dim_vector (len, 1)); \
1445  JAVA_TYPE *buffer = reinterpret_cast<JAVA_TYPE *> (d.fortran_vec ()); \
1446  jni_env->Get ## JAVA_TYPE_CAP ## ArrayRegion (jarr, 0, len, buffer); \
1447  retval = d; \
1448  break; \
1449  }
1450 
1451  BOX_PRIMITIVE_ARRAY (jdouble, "[D", Double, )
1452  BOX_PRIMITIVE_ARRAY (jboolean, "[Z", Boolean, bool)
1453  BOX_PRIMITIVE_ARRAY (jfloat, "[F", Float, Float)
1454  BOX_PRIMITIVE_ARRAY (jchar, "[C", Char, char)
1455  BOX_PRIMITIVE_ARRAY (jbyte, "[B", Byte, int8)
1456  BOX_PRIMITIVE_ARRAY (jshort, "[S", Short, int16)
1457  BOX_PRIMITIVE_ARRAY (jint, "[I", Int, int32)
1458  BOX_PRIMITIVE_ARRAY (jlong, "[J", Long, int64)
1459 
1460 #undef BOX_PRIMITIVE_ARRAY
1461 
1463  {
1464  cls = find_octave_class (jni_env, "org/octave/Matrix");
1465 
1466  if (jni_env->IsInstanceOf (jobj, cls))
1467  {
1468  jmethodID mID = jni_env->GetMethodID (cls, "getDims", "()[I");
1469  jintArray_ref iv (jni_env,
1470  reinterpret_cast<jintArray>
1471  (jni_env->CallObjectMethod (jobj, mID)));
1472  jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv),
1473  nullptr);
1474  dim_vector dims;
1475  dims.resize (jni_env->GetArrayLength (jintArray (iv)));
1476 
1477  for (int i = 0; i < dims.ndims (); i++)
1478  dims(i) = iv_data[i];
1479 
1480  jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);
1481  mID = jni_env->GetMethodID (cls, "getClassName",
1482  "()Ljava/lang/String;");
1483  jstring_ref js (jni_env,
1484  reinterpret_cast<jstring>
1485  (jni_env->CallObjectMethod (jobj, mID)));
1486 
1487  std::string s = jstring_to_string (jni_env, js);
1488 
1489  if (s == "double")
1490  {
1491  NDArray m (dims);
1492  mID = jni_env->GetMethodID (cls, "toDouble", "()[D");
1493  jdoubleArray_ref dv (jni_env,
1494  reinterpret_cast<jdoubleArray>
1495  (jni_env->CallObjectMethod (jobj,
1496  mID)));
1497  jni_env->GetDoubleArrayRegion (dv, 0, m.numel (),
1498  m.fortran_vec ());
1499  retval = m;
1500  break;
1501  }
1502  else if (s == "byte")
1503  {
1505  {
1506  uint8NDArray m (dims);
1507  mID = jni_env->GetMethodID (cls, "toByte", "()[B");
1508  jbyteArray_ref dv (jni_env,
1509  reinterpret_cast<jbyteArray>
1510  (jni_env->CallObjectMethod (jobj,
1511  mID)));
1512  jni_env->GetByteArrayRegion (dv, 0, m.numel (),
1513  reinterpret_cast<jbyte *>
1514  (m.fortran_vec ()));
1515  retval = m;
1516  break;
1517  }
1518  else
1519  {
1520  int8NDArray m (dims);
1521  mID = jni_env->GetMethodID (cls, "toByte", "()[B");
1522  jbyteArray_ref dv (jni_env,
1523  reinterpret_cast<jbyteArray>
1524  (jni_env->CallObjectMethod (jobj,
1525  mID)));
1526  jni_env->GetByteArrayRegion (dv, 0, m.numel (),
1527  reinterpret_cast<jbyte *>
1528  (m.fortran_vec ()));
1529  retval = m;
1530  break;
1531  }
1532  }
1533  else if (s == "integer")
1534  {
1536  {
1537  uint32NDArray m (dims);
1538  mID = jni_env->GetMethodID (cls, "toInt", "()[I");
1539  jintArray_ref dv (jni_env,
1540  reinterpret_cast<jintArray>
1541  (jni_env->CallObjectMethod (jobj,
1542  mID)));
1543  jni_env->GetIntArrayRegion (dv, 0, m.numel (),
1544  reinterpret_cast<jint *>
1545  (m.fortran_vec ()));
1546  retval = m;
1547  break;
1548  }
1549  else
1550  {
1551  int32NDArray m (dims);
1552  mID = jni_env->GetMethodID (cls, "toInt", "()[I");
1553  jintArray_ref dv (jni_env,
1554  reinterpret_cast<jintArray>
1555  (jni_env->CallObjectMethod (jobj,
1556  mID)));
1557  jni_env->GetIntArrayRegion (dv, 0, m.numel (),
1558  reinterpret_cast<jint *>
1559  (m.fortran_vec ()));
1560  retval = m;
1561  break;
1562  }
1563  }
1564  }
1565  }
1566 
1567  cls = find_octave_class (jni_env, "org/octave/OctaveReference");
1568  if (jni_env->IsInstanceOf (jobj, cls))
1569  {
1570  jmethodID mID = jni_env->GetMethodID (cls, "getID", "()I");
1571  int ID = jni_env->CallIntMethod (jobj, mID);
1572  auto it = octave_ref_map.find (ID);
1573 
1574  if (it != octave_ref_map.end ())
1575  retval = it->second;
1576  break;
1577  }
1578 
1579  // No suitable class found. Return a generic octave_java object.
1580  retval = octave_value (new octave_java (jobj, jcls));
1581  break;
1582  }
1583 
1584  return retval;
1585 }
1586 
1587 static octave_value
1588 box_more (JNIEnv *jni_env, void *jobj_arg, void *jcls_arg)
1589 {
1590  jobject jobj = TO_JOBJECT (jobj_arg);
1591  jclass jcls = TO_JCLASS (jcls_arg);
1592 
1593  octave_value retval = box (jni_env, jobj, jcls);
1594 
1595  if (retval.isjava ())
1596  {
1597  retval = octave_value ();
1598 
1599  jclass_ref cls (jni_env);
1600 
1601  if (retval.is_undefined ())
1602  {
1603  cls = jni_env->FindClass ("[D");
1604 
1605  if (jni_env->IsInstanceOf (jobj, cls))
1606  {
1607  jdoubleArray jarr = reinterpret_cast<jdoubleArray> (jobj);
1608  int len = jni_env->GetArrayLength (jarr);
1609 
1610  if (len > 0)
1611  {
1612  Matrix m (1, len);
1613  jni_env->GetDoubleArrayRegion (jarr, 0, len,
1614  m.fortran_vec ());
1615  retval = m;
1616  }
1617  else
1618  retval = Matrix ();
1619  }
1620  }
1621 
1622  if (retval.is_undefined ())
1623  {
1624  cls = jni_env->FindClass ("[[D");
1625 
1626  if (jni_env->IsInstanceOf (jobj, cls))
1627  {
1628  jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
1629  int rows = jni_env->GetArrayLength (jarr);
1630  int cols = 0;
1631 
1632  if (rows > 0)
1633  {
1634  Matrix m;
1635 
1636  for (int r = 0; r < rows; r++)
1637  {
1638  jdoubleArray_ref row (jni_env,
1639  reinterpret_cast<jdoubleArray>
1640  (jni_env->GetObjectArrayElement
1641  (jarr, r)));
1642 
1643  if (m.isempty ())
1644  {
1645  cols = jni_env->GetArrayLength (row);
1646  m.resize (cols, rows);
1647  }
1648  jni_env->GetDoubleArrayRegion
1649  (row, 0, cols, m.fortran_vec () + r * cols);
1650  }
1651  retval = m.transpose ();
1652  }
1653  else
1654  retval = Matrix ();
1655  }
1656  }
1657 
1658  if (retval.is_undefined ())
1659  {
1660  cls = jni_env->FindClass ("[Ljava/lang/String;");
1661 
1662  if (jni_env->IsInstanceOf (jobj, cls))
1663  {
1664  jobjectArray jarr = reinterpret_cast<jobjectArray> (jobj);
1665  int len = jni_env->GetArrayLength (jarr);
1666  Cell m (len, 1);
1667 
1668  for (int i = 0; i < len; i++)
1669  {
1670  jstring_ref js (jni_env,
1671  reinterpret_cast<jstring>
1672  (jni_env->GetObjectArrayElement (jarr, i)));
1673  m(i) = jstring_to_string (jni_env, js);
1674  }
1675 
1676  retval = m;
1677  }
1678  }
1679  }
1680 
1681  if (retval.is_undefined ())
1682  retval = octave_value (new octave_java (jobj, jcls));
1683 
1685 
1686  return retval;
1687 }
1688 
1689 static bool
1690 unbox (JNIEnv *jni_env, const octave_value& val, jobject_ref& jobj,
1691  jclass_ref& jcls)
1692 {
1693  bool found = true;
1694 
1695  if (val.isjava ())
1696  {
1697  octave_java *ovj = TO_JAVA (val);
1698  jobj = TO_JOBJECT (ovj->to_java ());
1699  jobj.detach ();
1700  jcls = jni_env->GetObjectClass (jobj);
1701  }
1702  else if (val.is_string ())
1703  {
1704  std::string s = val.string_value ();
1705 
1706  jobj = jni_env->NewStringUTF (s.c_str ());
1707  jcls = jni_env->GetObjectClass (jobj);
1708  }
1709  else if (val.iscellstr ())
1710  {
1711  const Array<std::string> str_arr = val.cellstr_value ();
1712  const octave_idx_type n = str_arr.numel ();
1713 
1714  jclass_ref scls (jni_env, jni_env->FindClass ("java/lang/String"));
1715  jobjectArray array = jni_env->NewObjectArray (n, scls, nullptr);
1716 
1717  for (octave_idx_type i = 0; i < n; i++)
1718  {
1719  jstring_ref jstr (jni_env, jni_env->NewStringUTF
1720  (str_arr(i).c_str ()));
1721  jni_env->SetObjectArrayElement (array, i, jstr);
1722  }
1723 
1724  jobj = array;
1725  jcls = jni_env->GetObjectClass (jobj);
1726  }
1727  else if (val.numel () > 1 && val.dims ().isvector ())
1728  {
1729  // FIXME: Is there any way to avoid code duplication here without
1730  // using a macro?
1731 
1732 #define UNBOX_PRIMITIVE_ARRAY(METHOD_T, OCTAVE_T, JAVA_T, JAVA_T_CAP) \
1733  do \
1734  { \
1735  const OCTAVE_T ## NDArray v = val.METHOD_T ## array_value (); \
1736  JAVA_T ## Array jarr = jni_env->New ## JAVA_T_CAP ## Array (v.numel ()); \
1737  const JAVA_T *jv = reinterpret_cast<const JAVA_T *> (v.data ()); \
1738  jni_env->Set ## JAVA_T_CAP ## ArrayRegion (jarr, 0, v.numel (), jv); \
1739  jobj = reinterpret_cast<jobject> (jarr); \
1740  jcls = jni_env->GetObjectClass (jobj); \
1741  } \
1742  while (0)
1743 
1744  // Note that we do NOT handle char here because they are unboxed
1745  // into a String[], not into a char array
1746 
1747  if (val.is_double_type ())
1748  UNBOX_PRIMITIVE_ARRAY ( , , jdouble, Double);
1749  else if (val.islogical ())
1750  UNBOX_PRIMITIVE_ARRAY (bool_, bool, jboolean, Boolean);
1751  else if (val.isfloat ())
1752  UNBOX_PRIMITIVE_ARRAY (float_, Float, jfloat, Float);
1753  else if (val.is_int8_type ())
1754  UNBOX_PRIMITIVE_ARRAY (int8_, int8, jbyte, Byte);
1755  else if (val.is_uint8_type ())
1756  UNBOX_PRIMITIVE_ARRAY (uint8_, uint8, jbyte, Byte);
1757  else if (val.is_int16_type ())
1758  UNBOX_PRIMITIVE_ARRAY (int16_, int16, jshort, Short);
1759  else if (val.is_uint16_type ())
1760  UNBOX_PRIMITIVE_ARRAY (uint16_, uint16, jshort, Short);
1761  else if (val.is_int32_type ())
1762  UNBOX_PRIMITIVE_ARRAY (int32_, int32, jint, Int);
1763  else if (val.is_uint32_type ())
1764  UNBOX_PRIMITIVE_ARRAY (uint32_, uint32, jint, Int);
1765  else if (val.is_int64_type ())
1766  UNBOX_PRIMITIVE_ARRAY (int64_, int64, jlong, Long);
1767  else if (val.is_uint64_type ())
1768  UNBOX_PRIMITIVE_ARRAY (uint64_, uint64, jlong, Long);
1769 
1770 #undef UNBOX_PRIMITIVE_ARRAY
1771  }
1772  else if (val.is_real_scalar () || val.is_bool_scalar ())
1773  {
1774  // FIXME: Is there any way to avoid code duplication here without
1775  // using a macro?
1776 
1777 #define UNBOX_PRIMITIVE_SCALAR(OCTAVE_T, METHOD_T, JAVA_T, JAVA_CON) \
1778  do \
1779  { \
1780  const OCTAVE_T ov = val.METHOD_T ## _value (); \
1781  jclass_ref dcls (jni_env, jni_env->FindClass (JAVA_T)); \
1782  const jfieldID fid = jni_env->GetStaticFieldID (dcls, "TYPE", "Ljava/lang/Class;"); \
1783  const jmethodID mid = jni_env->GetMethodID (dcls, "<init>", JAVA_CON); \
1784  jcls = reinterpret_cast<jclass> (jni_env->GetStaticObjectField (dcls, fid)); \
1785  jobj = jni_env->NewObject (dcls, mid, ov); \
1786  } \
1787  while (0)
1788 
1789  if (val.is_double_type ())
1790  UNBOX_PRIMITIVE_SCALAR (double, double, "java/lang/Double", "(D)V");
1791  else if (val.islogical ())
1792  UNBOX_PRIMITIVE_SCALAR (bool, bool, "java/lang/Boolean", "(Z)V");
1793  else if (val.isfloat ())
1794  UNBOX_PRIMITIVE_SCALAR (float, float, "java/lang/Float", "(F)V");
1795  else if (val.is_int8_type ())
1796  UNBOX_PRIMITIVE_SCALAR (int8_t, int8_scalar, "java/lang/Byte", "(B)V");
1797  else if (val.is_uint8_type ())
1798  UNBOX_PRIMITIVE_SCALAR (uint8_t, uint8_scalar, "java/lang/Byte", "(B)V");
1799  else if (val.is_int16_type ())
1800  UNBOX_PRIMITIVE_SCALAR (int16_t, int16_scalar, "java/lang/Short", "(S)V");
1801  else if (val.is_uint16_type ())
1802  UNBOX_PRIMITIVE_SCALAR (uint16_t, uint16_scalar, "java/lang/Short", "(S)V");
1803  else if (val.is_int32_type ())
1804  UNBOX_PRIMITIVE_SCALAR (int32_t, int32_scalar, "java/lang/Integer", "(I)V");
1805  else if (val.is_uint32_type ())
1806  UNBOX_PRIMITIVE_SCALAR (uint32_t, uint32_scalar, "java/lang/Integer", "(I)V");
1807  else if (val.is_int64_type ())
1808  UNBOX_PRIMITIVE_SCALAR (int64_t, int64_scalar, "java/lang/Long", "(J)V");
1809  else if (val.is_uint64_type ())
1810  UNBOX_PRIMITIVE_SCALAR (uint64_t, uint64_scalar, "java/lang/Long", "(J)V");
1811 
1812 #undef UNBOX_PRIMITIVE_SCALAR
1813  }
1814  else if (val.isempty ())
1815  {
1816  jobj = nullptr;
1817  jcls = nullptr;
1818  //jcls = jni_env->FindClass ("java/lang/Object");
1819  }
1820  else if (! Vjava_matrix_autoconversion
1821  && ((val.is_real_matrix ()
1822  && (val.rows () == 1 || val.columns () == 1))
1823  || val.is_range ()))
1824  {
1825  Matrix m = val.matrix_value ();
1826  jdoubleArray dv = jni_env->NewDoubleArray (m.numel ());
1827  jni_env->SetDoubleArrayRegion (dv, 0, m.numel (), m.fortran_vec ());
1828  jobj = dv;
1829  jcls = jni_env->GetObjectClass (jobj);
1830  }
1832  && (val.is_matrix_type () || val.is_range ())
1833  && val.isreal ())
1834  {
1835  jclass_ref mcls (jni_env, find_octave_class (jni_env,
1836  "org/octave/Matrix"));
1837  dim_vector dims = val.dims ();
1838  jintArray_ref iv (jni_env, jni_env->NewIntArray (dims.ndims ()));
1839  jint *iv_data = jni_env->GetIntArrayElements (jintArray (iv), nullptr);
1840 
1841  for (int i = 0; i < dims.ndims (); i++)
1842  iv_data[i] = dims(i);
1843 
1844  jni_env->ReleaseIntArrayElements (jintArray (iv), iv_data, 0);
1845 
1846  if (val.is_double_type ())
1847  {
1848  NDArray m = val.array_value ();
1849  jdoubleArray_ref dv (jni_env, jni_env->NewDoubleArray (m.numel ()));
1850  jni_env->SetDoubleArrayRegion (jdoubleArray (dv), 0, m.numel (),
1851  m.fortran_vec ());
1852  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([D[I)V");
1853  jobj = jni_env->NewObject (jclass (mcls), mID, jdoubleArray (dv),
1854  jintArray (iv));
1855  jcls = jni_env->GetObjectClass (jobj);
1856  }
1857  else if (val.is_int8_type ())
1858  {
1859  int8NDArray m = val.int8_array_value ();
1860  jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
1861  jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
1862  reinterpret_cast<jbyte *>
1863  (m.fortran_vec ()));
1864  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
1865  jobj = jni_env->NewObject
1866  (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
1867  jcls = jni_env->GetObjectClass (jobj);
1868  }
1869  else if (val.is_uint8_type ())
1870  {
1871  uint8NDArray m = val.uint8_array_value ();
1872  jbyteArray_ref bv (jni_env, jni_env->NewByteArray (m.numel ()));
1873  jni_env->SetByteArrayRegion (jbyteArray (bv), 0, m.numel (),
1874  reinterpret_cast<jbyte *>
1875  (m.fortran_vec ()));
1876  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([B[I)V");
1877  jobj = jni_env->NewObject
1878  (jclass (mcls), mID, jbyteArray (bv), jintArray (iv));
1879  jcls = jni_env->GetObjectClass (jobj);
1880  }
1881  else if (val.is_int32_type ())
1882  {
1883  int32NDArray m = val.int32_array_value ();
1884  jintArray_ref v (jni_env, jni_env->NewIntArray (m.numel ()));
1885  jni_env->SetIntArrayRegion (jintArray (v), 0, m.numel (),
1886  reinterpret_cast<jint *>
1887  (m.fortran_vec ()));
1888  jmethodID mID = jni_env->GetMethodID (mcls, "<init>", "([I[I)V");
1889  jobj = jni_env->NewObject
1890  (jclass (mcls), mID, jintArray (v), jintArray (iv));
1891  jcls = jni_env->GetObjectClass (jobj);
1892  }
1893  else
1894  {
1895  error ("cannot convert matrix of type '%s'",
1896  val.class_name ().c_str ());
1897  }
1898  }
1899  else
1900  {
1901  jclass rcls = find_octave_class (jni_env, "org/octave/OctaveReference");
1902  jmethodID mID = jni_env->GetMethodID (rcls, "<init>", "(I)V");
1903  int ID = octave_java_refcount++;
1904 
1905  jobj = jni_env->NewObject (rcls, mID, ID);
1906  jcls = rcls;
1907  octave_ref_map[ID] = val;
1908  }
1909 
1910  return found;
1911 }
1912 
1913 static bool
1914 unbox (JNIEnv *jni_env, const octave_value_list& args,
1915  jobjectArray_ref& jobjs, jobjectArray_ref& jclss)
1916 {
1917  bool found = true;
1918 
1919  jclass_ref ocls (jni_env, jni_env->FindClass ("java/lang/Object"));
1920  jclass_ref ccls (jni_env, jni_env->FindClass ("java/lang/Class"));
1921 
1922  if (! jobjs)
1923  jobjs = jni_env->NewObjectArray (args.length (), ocls, nullptr);
1924 
1925  if (! jclss)
1926  jclss = jni_env->NewObjectArray (args.length (), ccls, nullptr);
1927 
1928  for (int i = 0; i < args.length (); i++)
1929  {
1930  jobject_ref jobj (jni_env);
1931  jclass_ref jcls (jni_env);
1932 
1933  found = unbox (jni_env, args(i), jobj, jcls);
1934  if (! found)
1935  break;
1936 
1937  jni_env->SetObjectArrayElement (jobjs, i, jobj);
1938  jni_env->SetObjectArrayElement (jclss, i, jcls);
1939  }
1940 
1941  return found;
1942 }
1943 
1944 //! Returns the id of the current thread.
1945 //!
1946 //! @param jni_env The current environment or @c nullptr.
1947 //!
1948 //! @returns The id of the current thread or -1 otherwise. The latter happens
1949 //! if @c jni_env is @c nullptr, for example.
1950 
1951 static long
1952 get_current_thread_ID (JNIEnv *jni_env)
1953 {
1954  if (jni_env)
1955  {
1956  // Call Java method static Thread java.lang.Thread.currentThread().
1957  jclass_ref cls (jni_env, jni_env->FindClass ("java/lang/Thread"));
1958  jmethodID mID = jni_env->GetStaticMethodID (cls, "currentThread",
1959  "()Ljava/lang/Thread;");
1960  jobject_ref jthread (jni_env, jni_env->CallStaticObjectMethod (cls, mID));
1961 
1962  if (jthread)
1963  {
1964  // Call Java method long java.lang.Thread.getId().
1965  jclass_ref jth_cls (jni_env, jni_env->GetObjectClass (jthread));
1966  mID = jni_env->GetMethodID (jth_cls, "getId", "()J");
1967  long result = jni_env->CallLongMethod (jthread, mID);
1968  return result;
1969  }
1970  }
1971 
1972  return -1;
1973 }
1974 
1975 //! Run the java method @c org.octave.Octave.checkPendingAction().
1976 //!
1977 //! @returns 0 in any case for good reason.
1978 
1979 static int
1980 java_event_hook (void)
1981 {
1982  JNIEnv *current_env = thread_jni_env ();
1983 
1984  if (current_env)
1985  {
1986  // Invoke static void org.octave.Octave.checkPendingAction().
1987  jclass_ref cls (current_env, find_octave_class (current_env,
1988  "org/octave/Octave"));
1989  jmethodID mID = current_env->GetStaticMethodID
1990  (cls, "checkPendingAction", "()V");
1991  current_env->CallStaticVoidMethod (cls, mID);
1992 
1994  }
1995 
1996  return 0;
1997 }
1998 
1999 //! Initialize java including the virtual machine (jvm) if necessary.
2000 //!
2001 //! Initializes the fields #jvm, #jvm_attached, #jvm_lib, and
2002 //! #octave_thread_ID. To ensure that java is initialized, this method is
2003 //! used as part of octave functions @c javaObject, @c javaMethod,
2004 //! @c __java_get__, @c __java_set__, and @c __java2mat__.
2005 
2006 static void
2007 initialize_java (void)
2008 {
2009  if (! jvm)
2010  {
2011  try
2012  {
2013  initialize_jvm ();
2014 
2015  JNIEnv *current_env = thread_jni_env ();
2016 
2017  octave::command_editor::add_event_hook (java_event_hook);
2018 
2019  octave_thread_ID = get_current_thread_ID (current_env);
2020  }
2021  catch (std::string msg)
2022  {
2023  error ("%s", msg.c_str ());
2024  }
2025 
2027  }
2028 }
2029 
2030 JNIEXPORT jboolean JNICALL
2031 Java_org_octave_Octave_call (JNIEnv *env, jclass, jstring funcName,
2032  jobjectArray argin, jobjectArray argout)
2033 {
2034  std::string fname = jstring_to_string (env, funcName);
2035 
2036  int nargout = env->GetArrayLength (argout);
2037  int nargin = env->GetArrayLength (argin);
2038 
2039  octave_value_list varargin, varargout;
2040 
2041  for (int i = 0; i < nargin; i++)
2042  varargin(i) = box (env, env->GetObjectArrayElement (argin, i), nullptr);
2043 
2044  varargout = octave::feval (fname, varargin, nargout);
2045 
2046  jobjectArray_ref out_objs (env, argout), out_clss (env);
2047  out_objs.detach ();
2048  return unbox (env, varargout, out_objs, out_clss);
2049 }
2050 
2051 JNIEXPORT void JNICALL
2052 Java_org_octave_OctaveReference_doFinalize (JNIEnv *, jclass, jint ID)
2053 {
2054  octave_ref_map.erase (ID);
2055 }
2056 
2057 JNIEXPORT void JNICALL
2058 Java_org_octave_Octave_doInvoke (JNIEnv *env, jclass, jint ID,
2059  jobjectArray args)
2060 {
2061  auto it = octave_ref_map.find (ID);
2062 
2063  if (it != octave_ref_map.end ())
2064  {
2065  octave_value val = it->second;
2066  int len = env->GetArrayLength (args);
2067  octave_value_list oct_args;
2068 
2069  for (int i = 0; i < len; i++)
2070  {
2071  jobject_ref jobj (env, env->GetObjectArrayElement (args, i));
2072  oct_args(i) = box (env, jobj, nullptr);
2073  }
2074 
2075  if (val.is_function_handle ())
2076  {
2077  octave_function *fcn = val.function_value ();
2078  octave::feval (fcn, oct_args);
2079  }
2080  else if (val.iscell () && val.length () > 0
2081  && (val.rows () == 1 || val.columns () == 1)
2082  && val.cell_value()(0).is_function_handle ())
2083  {
2084  Cell c = val.cell_value ();
2085  octave_function *fcn = c(0).function_value ();
2086 
2087  for (int i=1; i<c.numel (); i++)
2088  oct_args(len+i-1) = c(i);
2089 
2090  octave::feval (fcn, oct_args);
2091  }
2092  else
2093  error ("trying to invoke non-invocable object");
2094  }
2095 }
2096 
2097 JNIEXPORT void JNICALL
2098 Java_org_octave_Octave_doEvalString (JNIEnv *env, jclass, jstring cmd)
2099 {
2100  octave::interpreter& interp
2101  = octave::__get_interpreter__ ("Java_org_octave_Octave_doEvalString");
2102 
2103  std::string s = jstring_to_string (env, cmd);
2104  int pstatus;
2105  interp.eval_string (s, false, pstatus, 0);
2106 }
2107 
2108 JNIEXPORT jboolean JNICALL
2110 {
2111  return (get_current_thread_ID (env) != octave_thread_ID);
2112 }
2113 
2114 #endif
2115 
2116 //! Ctor.
2117 
2119  : octave_base_value (), java_object (nullptr), java_class (nullptr)
2120 {
2121 #if ! defined (HAVE_JAVA)
2122 
2123  err_disabled_feature ("Java Objects", "Java");
2124 
2125 #endif
2126 }
2127 
2128 octave_java::octave_java (const voidptr& jobj, void *jcls)
2129  : octave_base_value (), java_object (nullptr), java_class (nullptr)
2130 {
2131 #if defined (HAVE_JAVA)
2132 
2133  init (jobj, jcls);
2134 
2135 #else
2136 
2137  octave_unused_parameter (jobj);
2138  octave_unused_parameter (jcls);
2139 
2140  err_disabled_feature ("Java Objects", "Java");
2141 
2142 #endif
2143 }
2144 
2145 int octave_java::t_id (-1);
2146 
2147 const std::string octave_java::t_name ("octave_java");
2148 
2149 void
2151 {
2152 #if defined (HAVE_JAVA)
2153 
2154  t_id = ti.register_type (octave_java::t_name, "<unknown>",
2155  octave_value (new octave_java ()));
2156 
2157 #else
2158 
2159  octave_unused_parameter (ti);
2160 
2161 #endif
2162 }
2163 
2164 dim_vector
2165 octave_java::dims (void) const
2166 {
2167 #if defined (HAVE_JAVA)
2168 
2169  JNIEnv *current_env = thread_jni_env ();
2170 
2171  if (current_env && java_object)
2172  return compute_array_dimensions (current_env, TO_JOBJECT (java_object));
2173  else
2174  return dim_vector (1, 1);
2175 
2176 #else
2177 
2178  // This shouldn't happen because construction of octave_java objects is
2179  // supposed to be impossible if Java is not available.
2180 
2181  panic_impossible ();
2182 
2183 #endif
2184 }
2185 
2187 octave_java::subsref (const std::string& type,
2188  const std::list<octave_value_list>& idx, int nargout)
2189 {
2190 #if defined (HAVE_JAVA)
2191 
2193  int skip = 1;
2194 
2195  JNIEnv *current_env = thread_jni_env ();
2196 
2197  switch (type[0])
2198  {
2199  case '.':
2200  if (type.length () > 1 && type[1] == '(')
2201  {
2203  count++;
2204  ovl(1) = octave_value (this);
2205  ovl(0) = (idx.front ())(0);
2206  auto it = idx.begin ();
2207  ovl.append (*++it);
2208  retval = FjavaMethod (ovl, 1);
2209  skip++;
2210  }
2211  else
2212  {
2214  count++;
2215  ovl(0) = octave_value (this);
2216  ovl(1) = (idx.front ())(0);
2217  retval = F__java_get__ (ovl, 1);
2218  }
2219  break;
2220 
2221  case '(':
2222  if (current_env)
2224  (current_env, TO_JOBJECT (to_java ()), idx.front ());
2225  break;
2226 
2227  default:
2228  error ("subsref: Java object cannot be indexed with %c", type[0]);
2229  break;
2230  }
2231 
2232  if (idx.size () > 1 && type.length () > 1)
2233  retval = retval(0).next_subsref (nargout, type, idx, skip);
2234 
2235  return retval;
2236 
2237 #else
2238 
2239  octave_unused_parameter (type);
2240  octave_unused_parameter (idx);
2241  octave_unused_parameter (nargout);
2242 
2243  // This shouldn't happen because construction of octave_java objects is
2244  // supposed to be impossible if Java is not available.
2245 
2246  panic_impossible ();
2247 
2248 #endif
2249 }
2250 
2252 octave_java::subsasgn (const std::string& type,
2253  const std::list<octave_value_list>& idx,
2254  const octave_value& rhs)
2255 {
2256 #if defined (HAVE_JAVA)
2257 
2259 
2260  JNIEnv *current_env = thread_jni_env ();
2261 
2262  switch (type[0])
2263  {
2264  case '.':
2265  if (type.length () == 1)
2266  {
2267  // field assignment
2269  count++;
2270  ovl(0) = octave_value (this);
2271  ovl(1) = (idx.front ())(0);
2272  ovl(2) = rhs;
2273  F__java_set__ (ovl);
2274 
2275  count++;
2276  retval = octave_value (this);
2277  }
2278  else if (type.length () > 2 && type[1] == '(')
2279  {
2280  std::list<octave_value_list> new_idx;
2281  auto it = idx.begin ();
2282  new_idx.push_back (*it++);
2283  new_idx.push_back (*it++);
2284  octave_value_list u = subsref (type.substr (0, 2), new_idx, 1);
2285 
2286  std::list<octave_value_list> next_idx (idx);
2287  next_idx.erase (next_idx.begin ());
2288  next_idx.erase (next_idx.begin ());
2289  u(0).subsasgn (type.substr (2), next_idx, rhs);
2290 
2291  count++;
2292  retval = octave_value (this);
2293  }
2294  else if (type[1] == '.')
2295  {
2296  octave_value_list u = subsref (type.substr (0, 1), idx, 1);
2297 
2298  std::list<octave_value_list> next_idx (idx);
2299  next_idx.erase (next_idx.begin ());
2300  u(0).subsasgn (type.substr (1), next_idx, rhs);
2301 
2302  count++;
2303  retval = octave_value (this);
2304  }
2305  else
2306  error ("invalid indexing/assignment on Java object");
2307  break;
2308 
2309  case '(':
2310  if (current_env)
2311  {
2312  set_array_elements (current_env, TO_JOBJECT (to_java ()),
2313  idx.front (), rhs);
2314 
2315  count++;
2316  retval = octave_value (this);
2317  }
2318  break;
2319 
2320  default:
2321  error ("Java object cannot be indexed with %c", type[0]);
2322  break;
2323  }
2324 
2325  return retval;
2326 
2327 #else
2328 
2329  octave_unused_parameter (type);
2330  octave_unused_parameter (idx);
2331  octave_unused_parameter (rhs);
2332 
2333  // This shouldn't happen because construction of octave_java objects is
2334  // supposed to be impossible if Java is not available.
2335 
2336  panic_impossible ();
2337 
2338 #endif
2339 }
2340 
2342 octave_java::map_keys (void) const
2343 {
2344 #if defined (HAVE_JAVA)
2345 
2346  JNIEnv *current_env = thread_jni_env ();
2347 
2348  if (current_env)
2349  return get_invoke_list (current_env, to_java ());
2350  else
2351  return string_vector ();
2352 
2353 #else
2354 
2355  // This shouldn't happen because construction of octave_java objects is
2356  // supposed to be impossible if Java is not available.
2357 
2358  panic_impossible ();
2359 
2360 #endif
2361 }
2362 
2364 octave_java::convert_to_str_internal (bool, bool force, char type) const
2365 {
2366 #if defined (HAVE_JAVA)
2367 
2368  JNIEnv *current_env = thread_jni_env ();
2369 
2370  if (current_env)
2371  return convert_to_string (current_env, TO_JOBJECT (to_java ()), force, type);
2372  else
2373  return octave_value ("");
2374 
2375 #else
2376 
2377  octave_unused_parameter (force);
2378  octave_unused_parameter (type);
2379 
2380  // This shouldn't happen because construction of octave_java objects is
2381  // supposed to be impossible if Java is not available.
2382 
2383  panic_impossible ();
2384 
2385 #endif
2386 }
2387 
2388 void
2389 octave_java::print (std::ostream& os, bool)
2390 {
2391  print_raw (os);
2392  newline (os);
2393 }
2394 
2395 void
2396 octave_java::print_raw (std::ostream& os, bool) const
2397 {
2398  os << "<Java object: " << java_classname << '>';
2399 }
2400 
2401 // FIXME: Need routines to actually save/load java objects through Serialize.
2402 // See bug #42112.
2403 
2404 bool
2405 octave_java::save_ascii (std::ostream& /* os */)
2406 {
2407  warning ("save: unable to save java objects, skipping");
2408 
2409  return true;
2410 }
2411 
2412 bool
2413 octave_java::load_ascii (std::istream& /* is */)
2414 {
2415  // Silently skip over java object that was not saved
2416  return true;
2417 }
2418 
2419 bool
2420 octave_java::save_binary (std::ostream& /* os */, bool /* save_as_floats */)
2421 {
2422  warning ("save: unable to save java objects, skipping");
2423 
2424  return true;
2425 }
2426 
2427 bool
2428 octave_java::load_binary (std::istream& /* is */, bool /* swap*/,
2429  octave::mach_info::float_format /* fmt */)
2430 {
2431  // Silently skip over java object that was not saved
2432  return true;
2433 }
2434 
2435 bool
2436 octave_java::save_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */,
2437  bool /* save_as_floats */)
2438 {
2439  warning ("save: unable to save java objects, skipping");
2440 
2441  return true;
2442 }
2443 
2444 bool
2445 octave_java::load_hdf5 (octave_hdf5_id /* loc_id */, const char * /* name */)
2446 {
2447  // Silently skip object that was not saved
2448  return true;
2449 }
2450 
2451 octave_value
2452 octave_java::do_javaMethod (void *jni_env_arg, const std::string& name,
2453  const octave_value_list& args)
2454 {
2455 #if defined (HAVE_JAVA)
2456 
2457  octave_value retval;
2458 
2459  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2460 
2461  if (jni_env)
2462  {
2463  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2464  if (unbox (jni_env, args, arg_objs, arg_types))
2465  {
2466  jclass_ref helperClass (jni_env, find_octave_class (jni_env, "org/octave/ClassHelper"));
2467  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "invokeMethod",
2468  "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2469  jstring_ref methName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2470  jobjectArray_ref resObj (jni_env, reinterpret_cast<jobjectArray> (jni_env->CallStaticObjectMethod (helperClass, mID,
2471  to_java (), jstring (methName), jobjectArray (arg_objs), jobjectArray (arg_types))));
2472  if (resObj)
2473  retval = box (jni_env, resObj);
2474  else
2475  retval = check_exception (jni_env);
2476  }
2477 
2478  octave_set_default_fpucw ();
2479  }
2480 
2481  return retval;
2482 
2483 #else
2484 
2485  octave_unused_parameter (jni_env_arg);
2486  octave_unused_parameter (name);
2487  octave_unused_parameter (args);
2488 
2489  // This shouldn't happen because construction of octave_java objects is
2490  // supposed to be impossible if Java is not available.
2491 
2492  panic_impossible ();
2493 
2494 #endif
2495 }
2496 
2498 octave_java::do_javaMethod (const std::string& name,
2499  const octave_value_list& args)
2500 {
2501 #if defined (HAVE_JAVA)
2502 
2503  return do_javaMethod (thread_jni_env (), name, args);
2504 
2505 #else
2506 
2507  octave_unused_parameter (name);
2508  octave_unused_parameter (args);
2509 
2510  // This shouldn't happen because construction of octave_java
2511  // objects is supposed to be impossible if Java is not available.
2512 
2513  panic_impossible ();
2514 
2515 #endif
2516 }
2517 
2519 octave_java::do_javaMethod (void *jni_env_arg,
2520  const std::string& class_name,
2521  const std::string& name,
2522  const octave_value_list& args)
2523 {
2524 #if defined (HAVE_JAVA)
2525 
2527 
2528  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2529 
2530  if (jni_env)
2531  {
2532  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2533  if (unbox (jni_env, args, arg_objs, arg_types))
2534  {
2535  jclass_ref helperClass (jni_env,
2536  find_octave_class (jni_env,
2537  "org/octave/ClassHelper"));
2538  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2539  "invokeStaticMethod",
2540  "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2541  jstring_ref methName (jni_env,
2542  jni_env->NewStringUTF (name.c_str ()));
2543  jstring_ref clsName (jni_env,
2544  jni_env->NewStringUTF (class_name.c_str ()));
2545  jobject_ref resObj (jni_env,
2546  jni_env->CallStaticObjectMethod (helperClass,
2547  mID,
2548  jstring (clsName),
2549  jstring (methName),
2550  jobjectArray (arg_objs),
2551  jobjectArray (arg_types)));
2552  if (resObj)
2553  retval = box (jni_env, resObj);
2554  else
2555  retval = check_exception (jni_env);
2556  }
2557 
2559  }
2560 
2561  return retval;
2562 
2563 #else
2564 
2565  octave_unused_parameter (jni_env_arg);
2566  octave_unused_parameter (class_name);
2567  octave_unused_parameter (name);
2568  octave_unused_parameter (args);
2569 
2570  // This shouldn't happen because construction of octave_java
2571  // objects is supposed to be impossible if Java is not available.
2572 
2573  panic_impossible ();
2574 
2575 #endif
2576 }
2577 
2579 octave_java::do_javaMethod (const std::string& class_name,
2580  const std::string& name,
2581  const octave_value_list& args)
2582 {
2583 #if defined (HAVE_JAVA)
2584 
2585  return do_javaMethod (thread_jni_env (), class_name, name, args);
2586 
2587 #else
2588 
2589  octave_unused_parameter (class_name);
2590  octave_unused_parameter (name);
2591  octave_unused_parameter (args);
2592 
2593  // This shouldn't happen because construction of octave_java
2594  // objects is supposed to be impossible if Java is not available.
2595 
2596  panic_impossible ();
2597 
2598 #endif
2599 }
2600 
2602 octave_java::do_javaObject (void *jni_env_arg, const std::string& name,
2603  const octave_value_list& args)
2604 {
2605 #if defined (HAVE_JAVA)
2606 
2608 
2609  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2610 
2611  if (jni_env)
2612  {
2613  jobjectArray_ref arg_objs (jni_env), arg_types (jni_env);
2614 
2615  if (unbox (jni_env, args, arg_objs, arg_types))
2616  {
2617  jclass_ref helperClass (jni_env,
2618  find_octave_class (jni_env,
2619  "org/octave/ClassHelper"));
2620  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2621  "invokeConstructor",
2622  "(Ljava/lang/String;[Ljava/lang/Object;[Ljava/lang/Class;)Ljava/lang/Object;");
2623  jstring_ref clsName (jni_env,
2624  jni_env->NewStringUTF (name.c_str ()));
2625  jobject_ref resObj (jni_env,
2626  jni_env->CallStaticObjectMethod (helperClass,
2627  mID,
2628  jstring (clsName),
2629  jobjectArray (arg_objs),
2630  jobjectArray (arg_types)));
2631 
2632  if (resObj)
2633  retval = octave_value (new octave_java (resObj, nullptr));
2634  else
2635  check_exception (jni_env);
2636  }
2637 
2639  }
2640 
2641  return retval;
2642 
2643 #else
2644 
2645  octave_unused_parameter (jni_env_arg);
2646  octave_unused_parameter (name);
2647  octave_unused_parameter (args);
2648 
2649  // This shouldn't happen because construction of octave_java
2650  // objects is supposed to be impossible if Java is not available.
2651 
2652  panic_impossible ();
2653 
2654 #endif
2655 }
2656 
2658 octave_java::do_javaObject (const std::string& name,
2659  const octave_value_list& args)
2660 {
2661 #if defined (HAVE_JAVA)
2662 
2663  return do_javaObject (thread_jni_env (), name, args);
2664 
2665 #else
2666 
2667  octave_unused_parameter (name);
2668  octave_unused_parameter (args);
2669 
2670  // This shouldn't happen because construction of octave_java
2671  // objects is supposed to be impossible if Java is not available.
2672 
2673  panic_impossible ();
2674 
2675 #endif
2676 }
2677 
2679 octave_java::do_java_get (void *jni_env_arg, const std::string& name)
2680 {
2681 #if defined (HAVE_JAVA)
2682 
2684 
2685  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2686 
2687  if (jni_env)
2688  {
2689  jclass_ref helperClass (jni_env,
2690  find_octave_class (jni_env,
2691  "org/octave/ClassHelper"));
2692  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "getField",
2693  "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
2694  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2695  jobject_ref resObj (jni_env,
2696  jni_env->CallStaticObjectMethod (helperClass,
2697  mID,
2698  to_java (),
2699  jstring (fName)));
2700 
2701  if (resObj)
2702  retval = box (jni_env, resObj);
2703  else
2704  retval = check_exception (jni_env);
2705 
2707  }
2708 
2709  return retval;
2710 
2711 #else
2712 
2713  octave_unused_parameter (jni_env_arg);
2714  octave_unused_parameter (name);
2715 
2716  // This shouldn't happen because construction of octave_java
2717  // objects is supposed to be impossible if Java is not available.
2718 
2719  panic_impossible ();
2720 
2721 #endif
2722 }
2723 
2725 octave_java::do_java_get (const std::string& name)
2726 {
2727 #if defined (HAVE_JAVA)
2728 
2729  return do_java_get (thread_jni_env (), name);
2730 
2731 #else
2732 
2733  octave_unused_parameter (name);
2734 
2735  // This shouldn't happen because construction of octave_java
2736  // objects is supposed to be impossible if Java is not available.
2737 
2738  panic_impossible ();
2739 
2740 #endif
2741 }
2742 
2744 octave_java::do_java_get (void *jni_env_arg, const std::string& class_name,
2745  const std::string& name)
2746 {
2747 #if defined (HAVE_JAVA)
2748 
2750 
2751  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2752 
2753  if (jni_env)
2754  {
2755  jclass_ref helperClass (jni_env,
2756  find_octave_class (jni_env,
2757  "org/octave/ClassHelper"));
2758  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2759  "getStaticField",
2760  "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;");
2761  jstring_ref cName (jni_env, jni_env->NewStringUTF (class_name.c_str ()));
2762  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2763  jobject_ref resObj (jni_env,
2764  jni_env->CallStaticObjectMethod (helperClass, mID,
2765  jstring (cName),
2766  jstring (fName)));
2767  if (resObj)
2768  retval = box (jni_env, resObj);
2769  else
2770  retval = check_exception (jni_env);
2771 
2773  }
2774 
2775  return retval;
2776 
2777 #else
2778 
2779  octave_unused_parameter (jni_env_arg);
2780  octave_unused_parameter (class_name);
2781  octave_unused_parameter (name);
2782 
2783  // This shouldn't happen because construction of octave_java
2784  // objects is supposed to be impossible if Java is not available.
2785 
2786  panic_impossible ();
2787 
2788 #endif
2789 }
2790 
2792 octave_java::do_java_get (const std::string& class_name,
2793  const std::string& name)
2794 {
2795 #if defined (HAVE_JAVA)
2796 
2797  return do_java_get (thread_jni_env (), class_name, name);
2798 
2799 #else
2800 
2801  octave_unused_parameter (class_name);
2802  octave_unused_parameter (name);
2803 
2804  // This shouldn't happen because construction of octave_java
2805  // objects is supposed to be impossible if Java is not available.
2806 
2807  panic_impossible ();
2808 
2809 #endif
2810 }
2811 
2813 octave_java::do_java_set (void *jni_env_arg, const std::string& name,
2814  const octave_value& val)
2815 {
2816 #if defined (HAVE_JAVA)
2817 
2819 
2820  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2821 
2822  if (jni_env)
2823  {
2824  jobject_ref jobj (jni_env);
2825  jclass_ref jcls (jni_env);
2826 
2827  if (unbox (jni_env, val, jobj, jcls))
2828  {
2829  jclass_ref helperClass (jni_env,
2830  find_octave_class (jni_env,
2831  "org/octave/ClassHelper"));
2832  jmethodID mID = jni_env->GetStaticMethodID (helperClass, "setField",
2833  "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)V");
2834  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2835  jni_env->CallStaticObjectMethod (helperClass, mID, to_java (),
2836  jstring (fName), jobject (jobj));
2837  check_exception (jni_env);
2838  }
2839 
2841  }
2842 
2843  return retval;
2844 
2845 #else
2846 
2847  octave_unused_parameter (jni_env_arg);
2848  octave_unused_parameter (name);
2849  octave_unused_parameter (val);
2850 
2851  // This shouldn't happen because construction of octave_java
2852  // objects is supposed to be impossible if Java is not available.
2853 
2854  panic_impossible ();
2855 
2856 #endif
2857 }
2858 
2860 octave_java::do_java_set (const std::string& name, const octave_value& val)
2861 {
2862 #if defined (HAVE_JAVA)
2863 
2864  return do_java_set (thread_jni_env (), name, val);
2865 
2866 #else
2867 
2868  octave_unused_parameter (name);
2869  octave_unused_parameter (val);
2870 
2871  // This shouldn't happen because construction of octave_java
2872  // objects is supposed to be impossible if Java is not available.
2873 
2874  panic_impossible ();
2875 
2876 #endif
2877 }
2878 
2880 octave_java::do_java_set (void *jni_env_arg, const std::string& class_name,
2881  const std::string& name, const octave_value& val)
2882 {
2883 #if defined (HAVE_JAVA)
2884 
2886 
2887  JNIEnv *jni_env = TO_JNIENV (jni_env_arg);
2888 
2889  if (jni_env)
2890  {
2891  jobject_ref jobj (jni_env);
2892  jclass_ref jcls (jni_env);
2893 
2894  if (unbox (jni_env, val, jobj, jcls))
2895  {
2896  jclass_ref helperClass (jni_env,
2897  find_octave_class (jni_env,
2898  "org/octave/ClassHelper"));
2899  jmethodID mID = jni_env->GetStaticMethodID (helperClass,
2900  "setStaticField",
2901  "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V");
2902  jstring_ref cName (jni_env,
2903  jni_env->NewStringUTF (class_name.c_str ()));
2904  jstring_ref fName (jni_env, jni_env->NewStringUTF (name.c_str ()));
2905  jni_env->CallStaticObjectMethod (helperClass, mID, jstring (cName),
2906  jstring (fName), jobject (jobj));
2907  check_exception (jni_env);
2908  }
2909 
2911  }
2912 
2913  return retval;
2914 
2915 #else
2916 
2917  octave_unused_parameter (jni_env_arg);
2918  octave_unused_parameter (class_name);
2919  octave_unused_parameter (name);
2920  octave_unused_parameter (val);
2921 
2922  // This shouldn't happen because construction of octave_java
2923  // objects is supposed to be impossible if Java is not available.
2924 
2925  panic_impossible ();
2926 
2927 #endif
2928 }
2929 
2931 octave_java::do_java_set (const std::string& class_name,
2932  const std::string& name,
2933  const octave_value& val)
2934 {
2935 #if defined (HAVE_JAVA)
2936 
2937  return do_java_set (thread_jni_env (), class_name, name, val);
2938 
2939 #else
2940 
2941  octave_unused_parameter (class_name);
2942  octave_unused_parameter (name);
2943  octave_unused_parameter (val);
2944 
2945  // This shouldn't happen because construction of octave_java
2946  // objects is supposed to be impossible if Java is not available.
2947 
2948  panic_impossible ();
2949 
2950 #endif
2951 }
2952 
2953 void
2954 octave_java::init (void *jobj_arg, void *jcls_arg)
2955 {
2956 #if defined (HAVE_JAVA)
2957 
2958  jobject jobj = TO_JOBJECT (jobj_arg);
2959  jclass jcls = TO_JCLASS (jcls_arg);
2960 
2961  JNIEnv *current_env = thread_jni_env ();
2962 
2963  if (current_env)
2964  {
2965  if (jobj)
2966  java_object = current_env->NewGlobalRef (jobj);
2967 
2968  if (jcls)
2969  java_class = current_env->NewGlobalRef (jcls);
2970  else if (java_object)
2971  {
2972  jclass_ref ocls (current_env,
2973  current_env->GetObjectClass(TO_JOBJECT (java_object)));
2974  java_class = current_env->NewGlobalRef (jclass (ocls));
2975  }
2976 
2977  if (java_class)
2978  {
2979  jclass_ref clsCls (current_env,
2980  current_env->GetObjectClass (TO_JCLASS (java_class)));
2981  jmethodID mID = current_env->GetMethodID (clsCls,
2982  "getCanonicalName",
2983  "()Ljava/lang/String;");
2984  jobject_ref resObj (current_env,
2985  current_env->CallObjectMethod (TO_JCLASS (java_class), mID));
2986  java_classname = jstring_to_string (current_env, resObj);
2987  }
2988  }
2989 
2990 #else
2991 
2992  octave_unused_parameter (jobj_arg);
2993  octave_unused_parameter (jcls_arg);
2994 
2995  // This shouldn't happen because construction of octave_java
2996  // objects is supposed to be impossible if Java is not available.
2997 
2998  panic_impossible ();
2999 
3000 #endif
3001 }
3002 
3003 void
3004 octave_java::release (void)
3005 {
3006 #if defined (HAVE_JAVA)
3007 
3008  JNIEnv *current_env = thread_jni_env ();
3009 
3010  if (current_env)
3011  {
3012  if (java_object)
3013  current_env->DeleteGlobalRef (TO_JOBJECT (java_object));
3014 
3015  if (java_class)
3016  current_env->DeleteGlobalRef (TO_JCLASS (java_class));
3017 
3018  java_object = nullptr;
3019  java_class = nullptr;
3020  }
3021 
3022 #else
3023 
3024  // This shouldn't happen because construction of octave_java objects is
3025  // supposed to be impossible if Java is not available.
3026 
3027  panic_impossible ();
3028 
3029 #endif
3030 }
3031 
3032 // DEFUN blocks below must be outside of HAVE_JAVA block so that documentation
3033 // strings are always available, even when functions are not.
3034 
3035 DEFUN (__java_init__, , ,
3036  doc: /* -*- texinfo -*-
3037 @deftypefn {} {} __java_init__ ()
3038 Internal function used @strong{only} when debugging Java interface.
3039 
3040 Function will directly call initialize_java to create an instance of a JVM.
3041 @end deftypefn */)
3042 {
3043 #if defined (HAVE_JAVA)
3044 
3045  octave_value retval = 0;
3046 
3047  initialize_java ();
3048 
3049  retval = 1;
3050 
3051  return retval;
3052 
3053 #else
3054 
3055  err_disabled_feature ("__java_init__", "Java");
3056 
3057 #endif
3058 }
3059 
3060 DEFUN (__java_exit__, , ,
3061  doc: /* -*- texinfo -*-
3062 @deftypefn {} {} __java_exit__ ()
3063 Internal function used @strong{only} when debugging Java interface.
3064 
3065 Function will directly call terminate_jvm to destroy the current JVM
3066 instance.
3067 @end deftypefn */)
3068 {
3069 #if defined (HAVE_JAVA)
3070 
3071  terminate_jvm ();
3072 
3073  return ovl ();
3074 
3075 #else
3076 
3077  err_disabled_feature ("__java_exit__", "Java");
3078 
3079 #endif
3080 }
3081 
3082 DEFUN (javaObject, args, ,
3083  doc: /* -*- texinfo -*-
3084 @deftypefn {} {@var{jobj} =} javaObject (@var{classname})
3085 @deftypefnx {} {@var{jobj} =} javaObject (@var{classname}, @var{arg1}, @dots{})
3086 Create a Java object of class @var{classsname}, by calling the class
3087 constructor with the arguments @var{arg1}, @dots{}
3088 
3089 The first example below creates an uninitialized object, while the second
3090 example supplies an initial argument to the constructor.
3091 
3092 @example
3093 @group
3094 x = javaObject ("java.lang.StringBuffer")
3095 x = javaObject ("java.lang.StringBuffer", "Initial string")
3096 @end group
3097 @end example
3098 
3099 @seealso{javaMethod, javaArray}
3100 @end deftypefn */)
3101 {
3102 #if defined (HAVE_JAVA)
3103 
3104  if (args.length () == 0)
3105  print_usage ();
3106 
3107  std::string classname = args(0).xstring_value ("javaObject: CLASSNAME must be a string");
3108 
3109  initialize_java ();
3110 
3111  JNIEnv *current_env = thread_jni_env ();
3112 
3113  octave_value_list tmp;
3114  for (int i=1; i<args.length (); i++)
3115  tmp(i-1) = args(i);
3116 
3117  return ovl (octave_java::do_javaObject (current_env, classname, tmp));
3118 
3119 #else
3120 
3121  octave_unused_parameter (args);
3122 
3123  err_disabled_feature ("javaObject", "Java");
3124 
3125 #endif
3126 }
3127 
3128 /*
3129 ## The tests below merely check if javaObject works at all. Whether
3130 ## it works properly, i.e., creates the right values, is a matter of
3131 ## Java itself. Create a Short and check if it really is a short, i.e.,
3132 ## whether it overflows.
3133 %!testif HAVE_JAVA; usejava ("jvm")
3134 %! assert (javaObject ("java.lang.Short", 40000).doubleValue < 0);
3135 */
3136 
3137 DEFUN (javaMethod, args, ,
3138  doc: /* -*- texinfo -*-
3139 @deftypefn {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj})
3140 @deftypefnx {} {@var{ret} =} javaMethod (@var{methodname}, @var{obj}, @var{arg1}, @dots{})
3141 Invoke the method @var{methodname} on the Java object @var{obj} with the
3142 arguments @var{arg1}, @dots{}.
3143 
3144 For static methods, @var{obj} can be a string representing the fully
3145 qualified name of the corresponding class.
3146 
3147 When @var{obj} is a regular Java object, structure-like indexing can be
3148 used as a shortcut syntax. For instance, the two following statements are
3149 equivalent
3150 
3151 @example
3152 @group
3153  ret = javaMethod ("method1", x, 1.0, "a string")
3154  ret = x.method1 (1.0, "a string")
3155 @end group
3156 @end example
3157 
3158 @code{javaMethod} returns the result of the method invocation.
3159 
3160 @seealso{methods, javaObject}
3161 @end deftypefn */)
3162 {
3163 #if defined (HAVE_JAVA)
3164 
3165  if (args.length () < 2)
3166  print_usage ();
3167 
3168  std::string methodname = args(0).xstring_value ("javaMethod: METHODNAME must be a string");
3169 
3170  initialize_java ();
3171 
3172  JNIEnv *current_env = thread_jni_env ();
3173 
3175 
3176  octave_value_list tmp;
3177  for (int i=2; i<args.length (); i++)
3178  tmp(i-2) = args(i);
3179 
3180  if (args(1).isjava ())
3181  {
3182  octave_java *jobj = TO_JAVA (args(1));
3183  retval = jobj->do_javaMethod (current_env, methodname, tmp);
3184  }
3185  else if (args(1).is_string ())
3186  {
3187  std::string cls = args(1).string_value ();
3188  retval = octave_java::do_javaMethod (current_env, cls, methodname, tmp);
3189  }
3190  else
3191  error ("javaMethod: OBJ must be a Java object or a string");
3192 
3193  return retval;
3194 
3195 #else
3196 
3197  octave_unused_parameter (args);
3198 
3199  err_disabled_feature ("javaMethod", "Java");
3200 
3201 #endif
3202 }
3203 
3204 /*
3205 %!testif HAVE_JAVA; usejava ("jvm")
3206 %! jver = javaMethod ("getProperty", "java.lang.System", "java.version");
3207 %! jver = strsplit (jver, ".");
3208 %! if (numel (jver) > 1)
3209 %! assert (isfinite (str2double (jver{1})));
3210 %! assert (isfinite (str2double (jver{2})));
3211 %! else
3212 %! assert (isfinite (str2double (jver{1})));
3213 %! endif
3214 */
3215 
3216 DEFUN (__java_get__, args, ,
3217  doc: /* -*- texinfo -*-
3218 @deftypefn {} {@var{val} =} __java_get__ (@var{obj}, @var{name})
3219 Get the value of the field @var{name} of the Java object @var{obj}.
3220 
3221 For static fields, @var{obj} can be a string representing the fully
3222 qualified name of the corresponding class.
3223 
3224 When @var{obj} is a regular Java object, structure-like indexing can be used
3225 as a shortcut syntax. For instance, the two following statements are
3226 equivalent
3227 
3228 @example
3229 @group
3230  __java_get__ (x, "field1")
3231  x.field1
3232 @end group
3233 @end example
3234 
3235 @seealso{__java_set__, javaMethod, javaObject}
3236 @end deftypefn */)
3237 {
3238 #if defined (HAVE_JAVA)
3239 
3240  if (args.length () != 2)
3241  print_usage ();
3242 
3243  std::string name = args(1).xstring_value ("__java_get__: NAME must be a string");
3244 
3245  initialize_java ();
3246 
3247  JNIEnv *current_env = thread_jni_env ();
3248 
3250 
3251  if (args(0).isjava ())
3252  {
3253  octave_java *jobj = TO_JAVA (args(0));
3254  retval = jobj->do_java_get (current_env, name);
3255  }
3256  else if (args(0).is_string ())
3257  {
3258  std::string cls = args(0).string_value ();
3259  retval = octave_java::do_java_get (current_env, cls, name);
3260  }
3261  else
3262  error ("__java_get__: OBJ must be a Java object or a string");
3263 
3264  return retval;
3265 
3266 #else
3267 
3268  octave_unused_parameter (args);
3269 
3270  err_disabled_feature ("__java_get__", "Java");
3271 
3272 #endif
3273 }
3274 
3275 DEFUN (__java_set__, args, ,
3276  doc: /* -*- texinfo -*-
3277 @deftypefn {} {@var{obj} =} __java_set__ (@var{obj}, @var{name}, @var{val})
3278 Set the value of the field @var{name} of the Java object @var{obj} to
3279 @var{val}.
3280 
3281 For static fields, @var{obj} can be a string representing the fully
3282 qualified named of the corresponding Java class.
3283 
3284 When @var{obj} is a regular Java object, structure-like indexing can be
3285 used as a shortcut syntax. For instance, the two following statements are
3286 equivalent
3287 
3288 @example
3289 @group
3290  __java_set__ (x, "field1", val)
3291  x.field1 = val
3292 @end group
3293 @end example
3294 
3295 @seealso{__java_get__, javaMethod, javaObject}
3296 @end deftypefn */)
3297 {
3298 #if defined (HAVE_JAVA)
3299 
3300  if (args.length () != 3)
3301  print_usage ();
3302 
3303  std::string name = args(1).xstring_value ("__java_set__: NAME must be a string");
3304 
3305  initialize_java ();
3306 
3307  JNIEnv *current_env = thread_jni_env ();
3308 
3310 
3311  if (args(0).isjava ())
3312  {
3313  octave_java *jobj = TO_JAVA (args(0));
3314  retval = jobj->do_java_set (current_env, name, args(2));
3315  }
3316  else if (args(0).is_string ())
3317  {
3318  std::string cls = args(0).string_value ();
3319  retval = octave_java::do_java_set (current_env, cls, name, args(2));
3320  }
3321  else
3322  error ("__java_set__: OBJ must be a Java object or a string");
3323 
3324  return retval;
3325 
3326 #else
3327 
3328  octave_unused_parameter (args);
3329 
3330  err_disabled_feature ("__java_set__", "Java");
3331 
3332 #endif
3333 }
3334 
3335 DEFUN (__java2mat__, args, ,
3336  doc: /* -*- texinfo -*-
3337 @deftypefn {} {} __java2mat__ (@var{javaobj})
3338 Undocumented internal function.
3339 @end deftypefn */)
3340 {
3341 #if defined (HAVE_JAVA)
3342 
3343  if (args.length () != 1)
3344  print_usage ();
3345 
3346  initialize_java ();
3347 
3348  JNIEnv *current_env = thread_jni_env ();
3349 
3351 
3352  if (args(0).isjava ())
3353  {
3354  octave_java *jobj = TO_JAVA (args(0));
3355  retval = ovl (box_more (current_env, jobj->to_java (), nullptr));
3356  }
3357  else
3358  retval = ovl (args(0));
3359 
3360  return retval;
3361 
3362 #else
3363 
3364  octave_unused_parameter (args);
3365 
3366  err_disabled_feature ("__java2mat__", "Java");
3367 
3368 #endif
3369 }
3370 
3371 DEFUN (java_matrix_autoconversion, args, nargout,
3372  doc: /* -*- texinfo -*-
3373 @deftypefn {} {@var{val} =} java_matrix_autoconversion ()
3374 @deftypefnx {} {@var{old_val} =} java_matrix_autoconversion (@var{new_val})
3375 @deftypefnx {} {} java_matrix_autoconversion (@var{new_val}, "local")
3376 Query or set the internal variable that controls whether Java arrays are
3377 automatically converted to Octave matrices.
3378 
3379 The default value is false.
3380 
3381 When called from inside a function with the @qcode{"local"} option, the
3382 variable is changed locally for the function and any subroutines it calls.
3383 The original variable value is restored when exiting the function.
3384 @seealso{java_unsigned_autoconversion, debug_java}
3385 @end deftypefn */)
3386 {
3387 #if defined (HAVE_JAVA)
3388 
3389  return SET_INTERNAL_VARIABLE (java_matrix_autoconversion);
3390 
3391 #else
3392 
3393  octave_unused_parameter (args);
3394  octave_unused_parameter (nargout);
3395 
3396  err_disabled_feature ("java_matrix_autoconversion", "Java");
3397 
3398 #endif
3399 }
3400 
3401 DEFUN (java_unsigned_autoconversion, args, nargout,
3402  doc: /* -*- texinfo -*-
3403 @deftypefn {} {@var{val} =} java_unsigned_autoconversion ()
3404 @deftypefnx {} {@var{old_val} =} java_unsigned_autoconversion (@var{new_val})
3405 @deftypefnx {} {} java_unsigned_autoconversion (@var{new_val}, "local")
3406 Query or set the internal variable that controls how integer classes are
3407 converted when @code{java_matrix_autoconversion} is enabled.
3408 
3409 When enabled, Java arrays of class Byte or Integer are converted to matrices
3410 of class uint8 or uint32 respectively. The default value is true.
3411 
3412 When called from inside a function with the @qcode{"local"} option, the
3413 variable is changed locally for the function and any subroutines it calls.
3414 The original variable value is restored when exiting the function.
3415 @seealso{java_matrix_autoconversion, debug_java}
3416 @end deftypefn */)
3417 {
3418 #if defined (HAVE_JAVA)
3419 
3420  return SET_INTERNAL_VARIABLE (java_unsigned_autoconversion);
3421 
3422 #else
3423 
3424  octave_unused_parameter (args);
3425  octave_unused_parameter (nargout);
3426 
3427  err_disabled_feature ("java_unsigned_autoconversion", "Java");
3428 
3429 #endif
3430 }
3431 
3432 DEFUN (debug_java, args, nargout,
3433  doc: /* -*- texinfo -*-
3434 @deftypefn {} {@var{val} =} debug_java ()
3435 @deftypefnx {} {@var{old_val} =} debug_java (@var{new_val})
3436 @deftypefnx {} {} debug_java (@var{new_val}, "local")
3437 Query or set the internal variable that determines whether extra debugging
3438 information regarding the initialization of the JVM and any Java exceptions
3439 is printed.
3440 
3441 When called from inside a function with the @qcode{"local"} option, the
3442 variable is changed locally for the function and any subroutines it calls.
3443 The original variable value is restored when exiting the function.
3444 @seealso{java_matrix_autoconversion, java_unsigned_autoconversion}
3445 @end deftypefn */)
3446 {
3447 #if defined (HAVE_JAVA)
3448 
3449  return SET_INTERNAL_VARIABLE (debug_java);
3450 
3451 #else
3452 
3453  octave_unused_parameter (args);
3454  octave_unused_parameter (nargout);
3455 
3456  err_disabled_feature ("debug_java", "Java");
3457 
3458 #endif
3459 }
3460 
3461 // Outside of #if defined (HAVE_JAVA) because it is desirable to be able
3462 // to test for the presence of a Java object without having Java
3463 // installed.
3464 
3465 DEFUN (isjava, args, ,
3466  doc: /* -*- texinfo -*-
3467 @deftypefn {} {} isjava (@var{x})
3468 Return true if @var{x} is a Java object.
3469 @seealso{class, typeinfo, isa, javaObject}
3470 @end deftypefn */)
3471 {
3472  if (args.length () != 1)
3473  print_usage ();
3474 
3475  return ovl (args(0).isjava ());
3476 }
3477 
3478 /*
3479 ## Check automatic conversion of java primitive arrays into octave types.
3480 %!testif HAVE_JAVA; usejava ("jvm")
3481 %! assert (javaObject ("java.lang.String", "hello").getBytes (),
3482 %! int8 ([104 101 108 108 111]'));
3483 
3484 ## Check automatic conversion of octave types into java primitive arrays.
3485 ## Note that uint8 is casted to int8.
3486 %!testif HAVE_JAVA; usejava ("jvm")
3487 %! assert (javaMethod ("binarySearch", "java.util.Arrays", [90 100 255], 255), 2);
3488 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 255]), uint8 (255)) < 0);
3489 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 128]), uint8 (128)) < 0);
3490 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint8 ([90 100 127]), uint8 (127)), 2);
3491 %! assert (javaMethod ("binarySearch", "java.util.Arrays", uint16 ([90 100 128]), uint16 (128)), 2);
3492 
3493 ## Check we can create objects that wrap java literals
3494 %!testif HAVE_JAVA; usejava ("jvm") <*38821>
3495 %! assert (class (javaObject ("java.lang.Byte", uint8 (1))), "java.lang.Byte");
3496 %! assert (class (javaObject ("java.lang.Byte", int8 (1))), "java.lang.Byte");
3497 %! assert (class (javaObject ("java.lang.Short", uint16 (1))), "java.lang.Short");
3498 %! assert (class (javaObject ("java.lang.Short", int16 (1))), "java.lang.Short");
3499 %! assert (class (javaObject ("java.lang.Integer", uint32 (1))), "java.lang.Integer");
3500 %! assert (class (javaObject ("java.lang.Integer", int32 (1))), "java.lang.Integer");
3501 %! assert (class (javaObject ("java.lang.Long", uint64 (1))), "java.lang.Long");
3502 %! assert (class (javaObject ("java.lang.Long", int64 (1))), "java.lang.Long");
3503 
3504 ## More checks of java numeric and boolean class instances
3505 %!testif HAVE_JAVA; usejava ("jvm")
3506 %! n = javaObject ("java.lang.Double", 1.35);
3507 %! assert (n.compareTo (1.0), 1);
3508 %! assert (n.compareTo (1.35), 0);
3509 %! assert (n.compareTo (10), -1);
3510 %! assert (n.isInfinite (), false);
3511 
3512 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3513 %! n = javaObject ("java.lang.Float", 1.35);
3514 %! assert (n.compareTo (1.0), 1);
3515 %! assert (n.compareTo (1.35), 0);
3516 %! assert (n.compareTo (10), -1);
3517 %! assert (n.doubleValue (), 1.35, 1e7);
3518 
3519 %!testif HAVE_JAVA; usejava ("jvm")
3520 %! n = javaObject ("java.lang.Long", (int64 (1)));
3521 %! assert (n.compareTo (int64 (0)), 1);
3522 %! assert (n.compareTo (int64 (1)), 0);
3523 %! assert (n.compareTo (int64 (2)), -1);
3524 %! assert (n.toString (), "1");
3525 
3526 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3527 %! n = javaObject ("java.lang.Integer", 1.35);
3528 %! assert (n.compareTo (0), 1);
3529 %! assert (n.compareTo (1), 0);
3530 %! assert (n.compareTo (2), -1);
3531 
3532 %!testif HAVE_JAVA; usejava ("jvm") <51804>
3533 %! n = javaObject ("java.lang.Short", 1.35);
3534 %! assert (n.compareTo (0), 1);
3535 %! assert (n.compareTo (1), 0);
3536 %! assert (n.compareTo (2), -1);
3537 
3538 %!testif HAVE_JAVA; usejava ("jvm")
3539 %! n = javaObject ("java.lang.Byte", int8 (17));
3540 %! assert (n.compareTo (int8 (20)), -3);
3541 %! assert (n.compareTo (int8 (10)), 7);
3542 %! assert (n.compareTo (int8 (17)), 0);
3543 
3544 %!testif HAVE_JAVA; usejava ("jvm")
3545 %! b = javaObject ("java.lang.Boolean", true);
3546 %! assert (b.compareTo (true), 0);
3547 %! assert (b.compareTo (false), 1);
3548 %! b = javaObject ("java.lang.Boolean", false);
3549 %! assert (b.compareTo (true), -1);
3550 %! assert (b.compareTo (false), 0);
3551 
3552 ## Test for automatic conversion of specific numeric classes
3553 %!testif HAVE_JAVA; usejava ("jvm") <*48013>
3554 %! assert (javaMethod ("valueOf", "java.lang.Byte", int8 (1)), 1)
3555 %! assert (javaMethod ("valueOf", "java.lang.Short", int16 (1)), 1)
3556 %! assert (javaMethod ("valueOf", "java.lang.Integer", int32 (1)), 1)
3557 %! assert (javaMethod ("valueOf", "java.lang.Long", int64 (1)), 1)
3558 %! assert (javaMethod ("valueOf", "java.lang.Float", single (1)), 1)
3559 %! assert (javaMethod ("valueOf", "java.lang.Double", double (1)), 1)
3560 %! assert (class (javaMethod ("valueOf", "java.math.BigDecimal", double (1))), "java.math.BigDecimal")
3561 %! assert (class (javaMethod ("valueOf", "java.math.BigInteger", int64 (1))), "java.math.BigInteger")
3562 
3563 ## Automatic conversion from string cell array into String[]
3564 %!testif HAVE_JAVA; usejava ("jvm") <*45290>
3565 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "aaa"), 0);
3566 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "zzz"), 3);
3567 %! assert (javaMethod ("binarySearch", "java.util.Arrays", {"aaa", "bbb", "ccc", "zzz"}, "hhh") < 0);
3568 
3569 ## Test that Octave index syntax allows Java object method calls with args
3570 %!testif HAVE_JAVA; usejava ("jvm") <*51152>
3571 %! s = javaObject ("java.lang.String", "Octave");
3572 %! assert (s.length (), 6)
3573 %! assert (s.charAt (0), "O")
3574 %! assert (s.charAt (5), "e")
3575 %! assert (s.matches ("^Octave$"))
3576 %! assert (s.startsWith ("Oct"))
3577 %! ## same tests with Java object as part of another indexing expression
3578 %! a(1).s = s;
3579 %! assert (! a(1).s.isEmpty ())
3580 %! assert (a(1).s.length (), 6)
3581 %! assert (a(1).s.charAt (0), "O")
3582 %! assert (a(1).s.charAt (5), "e")
3583 %! assert (a(1).s.matches ("^Octave$"))
3584 %! assert (a(1).s.startsWith ("Oct"))
3585 
3586 ## Check for basic usability of the java awt library
3587 ## Skip the test on OS X where we currently have Java 9 and attempting
3588 ## to use awt causes Octave to exit with a message about Java not being
3589 ## installed (it is) instead of returning false.
3590 %!testif HAVE_JAVA; ! ismac () && usejava ("jvm") && usejava ("awt") && have_window_system ()
3591 %! frame = javaObject ("java.awt.Frame");
3592 %! frame.setResizable (true);
3593 %! assert (frame.isResizable ());
3594 */
#define C(a, b)
Definition: Faddeeva.cc:246
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
Definition: Cell.h:43
Definition: dMatrix.h:42
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
void resize(int n, int fill_value=0)
Definition: dim-vector.h:349
bool isvector(void) const
Definition: dim-vector.h:461
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
octave_idx_type length(octave_idx_type n=0) const
Definition: idx-vector.h:558
java_local_ref(JNIEnv *_env, T obj)
Definition: ov-java.cc:104
JNIEnv * env
Definition: ov-java.cc:142
void release(void)
Definition: ov-java.cc:126
java_local_ref(void)
Definition: ov-java.cc:134
~java_local_ref(void)
Definition: ov-java.cc:107
void detach(void)
Definition: ov-java.cc:122
java_local_ref(JNIEnv *_env)
Definition: ov-java.cc:101
T & operator=(T obj)
Definition: ov-java.cc:109
~JVMArgs(void)
Definition: ov-java.cc:239
void add(const std::string &opt)
Definition: ov-java.cc:250
JavaVMInitArgs vm_args
Definition: ov-java.cc:315
JavaVMInitArgs * to_args()
Definition: ov-java.cc:244
void read_java_opts(const std::string &filename)
Definition: ov-java.cc:255
std::list< std::string > java_opts
Definition: ov-java.cc:317
void update(void)
Definition: ov-java.cc:291
void clean(void)
Definition: ov-java.cc:277
static void add_event_hook(event_hook_fcn f)
Definition: cmd-edit.cc:1552
static std::string path_sep_str(void)
Definition: pathsearch.cc:127
std::list< std::string > close(void)
Definition: oct-shlib.h:164
void * search(const std::string &nm, const name_mangler &mangler=name_mangler()) const
Definition: oct-shlib.h:175
void set_pos_if_unset(octave_idx_type nd_arg, octave_idx_type dim_arg)
octave_value_list eval_string(const std::string &eval_str, bool silent, int &parse_status, int nargout)
std::string stdout_output(void) const
Definition: oct-process.h:64
static std::string getenv(const std::string &name)
Definition: oct-env.cc:271
static std::string get_home_directory(void)
Definition: oct-env.cc:147
static std::string get_current_directory(void)
Definition: oct-env.cc:140
int register_type(const std::string &, const std::string &, const octave_value &, bool abort_on_duplicate=false)
Definition: ov-typeinfo.cc:88
octave::refcount< octave_idx_type > count
Definition: ov-base.h:860
void newline(std::ostream &os) const
Definition: ov-base.cc:1321
static void register_type(void)
Definition: ov-base.cc:99
friend class octave_value
Definition: ov-base.h:228
static const std::string t_name
Definition: ov-java.h:194
virtual octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov-base.cc:201
static int t_id
Definition: ov-java.h:193
octave_value do_java_set(void *jni_env, const std::string &name, const octave_value &val)
void * java_class
Definition: ov-java.h:176
void print(std::ostream &os, bool pr_as_read_syntax=false)
void release(void)
octave_java(void)
void * java_object
Definition: ov-java.h:174
void * to_java(void) const
Definition: ov-java.h:57
octave_value do_javaMethod(void *jni_env, const std::string &name, const octave_value_list &args)
octave_value convert_to_str_internal(bool pad, bool force, char type) const
octave_value do_java_get(void *jni_env, const std::string &name)
void print_raw(std::ostream &os, bool pr_as_read_syntax=false) const
bool is_instance_of(const std::string &) const
Definition: ov-java.cc:935
bool is_java_string(void) const
Definition: ov-java.cc:910
std::string java_classname
Definition: ov-java.h:178
octave_value subsasgn(const std::string &type, const std::list< octave_value_list > &idx, const octave_value &rhs)
void init(void *jobj, void *jcls)
static octave_value do_javaObject(void *jni_env, const std::string &name, const octave_value_list &args)
std::string class_name(void) const
Definition: ov-java.h:184
dim_vector dims(void) const
string_vector map_keys(void) const
octave_value_list & append(const octave_value &val)
Definition: ovl.cc:98
octave_idx_type length(void) const
Definition: ovl.h:113
octave_idx_type length(void) const
int32NDArray int32_array_value(void) const
Definition: ov.h:909
bool iscellstr(void) const
Definition: ov.h:563
bool iscell(void) const
Definition: ov.h:560
bool isreal(void) const
Definition: ov.h:691
bool is_uint16_type(void) const
Definition: ov.h:674
bool is_bool_scalar(void) const
Definition: ov.h:578
bool is_int8_type(void) const
Definition: ov.h:659
octave_idx_type rows(void) const
Definition: ov.h:504
octave_idx_type numel(void) const
Definition: ov.h:518
bool is_string(void) const
Definition: ov.h:593
bool is_double_type(void) const
Definition: ov.h:648
Cell cell_value(void) const
bool is_function_handle(void) const
Definition: ov.h:721
std::string class_name(void) const
Definition: ov.h:1256
bool is_uint32_type(void) const
Definition: ov.h:677
octave_idx_type columns(void) const
Definition: ov.h:506
int8NDArray int8_array_value(void) const
Definition: ov.h:903
bool is_int64_type(void) const
Definition: ov.h:668
octave_function * function_value(bool silent=false) const
uint8NDArray uint8_array_value(void) const
Definition: ov.h:915
std::string string_value(bool force=false) const
Definition: ov.h:927
bool isjava(void) const
Definition: ov.h:623
bool is_matrix_type(void) const
Definition: ov.h:700
bool is_int32_type(void) const
Definition: ov.h:665
bool is_uint64_type(void) const
Definition: ov.h:680
bool is_int16_type(void) const
Definition: ov.h:662
bool is_range(void) const
Definition: ov.h:602
bool isempty(void) const
Definition: ov.h:557
NDArray array_value(bool frc_str_conv=false) const
Definition: ov.h:812
bool is_real_matrix(void) const
Definition: ov.h:569
std::string xstring_value(const char *fmt,...) const
bool is_real_scalar(void) const
Definition: ov.h:566
Array< std::string > cellstr_value(void) const
Definition: ov.h:935
bool is_uint8_type(void) const
Definition: ov.h:671
Matrix matrix_value(bool frc_str_conv=false) const
Definition: ov.h:806
bool islogical(void) const
Definition: ov.h:688
bool isfloat(void) const
Definition: ov.h:654
dim_vector dims(void) const
Definition: ov.h:500
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1050
void error(const char *fmt,...)
Definition: error.cc:968
#define panic_impossible()
Definition: error.h:380
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
void octave_set_default_fpucw(void)
QString name
F77_RET_T const F77_INT F77_CMPLX const F77_INT F77_CMPLX * B
F77_RET_T const F77_INT const F77_INT const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE const F77_INT F77_DBLE * Z
static char * strsave(const char *s)
Definition: main.in.cc:195
T octave_idx_type m
Definition: mx-inlines.cc:773
octave_idx_type n
Definition: mx-inlines.cc:753
T * r
Definition: mx-inlines.cc:773
std::string fcn_file_dir(void)
Definition: defaults.cc:315
std::string dir_sep_str(void)
Definition: file-ops.cc:243
std::string tilde_expand(const std::string &name)
Definition: file-ops.cc:286
std::ifstream ifstream(const std::string &filename, const std::ios::openmode mode)
Definition: lo-sysdep.cc:381
interpreter & __get_interpreter__(const std::string &who)
octave_value_list feval(const char *name, const octave_value_list &args, int nargout)
Evaluate an Octave function (built-in or interpreted) and return the list of result values.
Definition: oct-parse.cc:9580
OCTINTERP_API process_execution_result run_command_and_return_output(const std::string &cmd_str)
Definition: oct-process.cc:54
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))
static octave_value get_array_elements(JNIEnv *jni_env, jobject jobj, const octave_value_list &idx)
Definition: ov-java.cc:1151
#define LIBJVM_FILE_NAME
Definition: ov-java.cc:82
static octave_value check_exception(JNIEnv *jni_env)
Definition: ov-java.cc:969
#define BOX_PRIMITIVE_ARRAY(JAVA_TYPE, JAVA_ID, JAVA_TYPE_CAP, OCTAVE_ID)
static std::map< int, octave_value > listener_map
Definition: ov-java.cc:211
static long octave_thread_ID
The thread id of the currently executing thread or -1 if this is unknown.
Definition: ov-java.cc:219
#define TO_JCLASS(obj)
Definition: ov-java.cc:86
static std::string read_classpath_txt(const std::string &filepath)
Return the classpath in the given file filepath as a string.
Definition: ov-java.cc:373
JNIEXPORT jboolean JNICALL Java_org_octave_Octave_call(JNIEnv *, jclass, jstring, jobjectArray, jobjectArray)
bool Vjava_unsigned_autoconversion
Definition: ov-java.cc:222
static std::string get_jvm_lib_path_in_subdir(std::string java_home_path)
Definition: ov-java.cc:511
java_local_ref< jthrowable > jthrowable_ref
Definition: ov-java.cc:152
#define UNBOX_PRIMITIVE_SCALAR(OCTAVE_T, METHOD_T, JAVA_T, JAVA_CON)
static bool jvm_attached
Whether the current thread is attached to the jvm given by jvm.
Definition: ov-java.cc:203
JNIEXPORT jboolean JNICALL Java_org_octave_Octave_needThreadedInvokation(JNIEnv *, jclass)
static jobject make_java_index(JNIEnv *jni_env, const octave_value_list &idx)
Definition: ov-java.cc:1114
#define TO_JNIENV(env)
Definition: ov-java.cc:88
static int octave_java_refcount
Definition: ov-java.cc:213
static std::string initial_java_dir(void)
The java initialization directory is given by the environment variable OCTAVE_JAVA_DIR if defined; ot...
Definition: ov-java.cc:342
static dim_vector compute_array_dimensions(JNIEnv *jni_env, jobject obj)
Definition: ov-java.cc:1079
java_local_ref< jdoubleArray > jdoubleArray_ref
Definition: ov-java.cc:151
java_local_ref< jobjectArray > jobjectArray_ref
Definition: ov-java.cc:148
java_local_ref< jclass > jclass_ref
Definition: ov-java.cc:146
jint(JNICALL * JNI_CreateJavaVM_t)(JavaVM **pvm, JNIEnv **penv, void *args)
Definition: ov-java.cc:90
static octave::dynamic_library jvm_lib
Need to keep hold of the shared library handle until exit.
Definition: ov-java.cc:209
java_local_ref< jbyteArray > jbyteArray_ref
Definition: ov-java.cc:150
static std::map< int, octave_value > octave_ref_map
Definition: ov-java.cc:212
static void initialize_jvm(void)
Initialize the java virtual machine (jvm) and field jvm if necessary.
Definition: ov-java.cc:670
static jclass find_octave_class(JNIEnv *jni_env, const char *name)
Definition: ov-java.cc:999
JNIEXPORT void JNICALL Java_org_octave_Octave_doInvoke(JNIEnv *, jclass, jint, jobjectArray)
static bool unbox(JNIEnv *jni_env, const octave_value &val, jobject_ref &jobj, jclass_ref &jcls)
JNIEXPORT void JNICALL Java_org_octave_Octave_doEvalString(JNIEnv *, jclass, jstring)
java_local_ref< jobject > jobject_ref
Definition: ov-java.cc:145
bool Vjava_matrix_autoconversion
Definition: ov-java.cc:221
static octave_value box(JNIEnv *jni_env, void *jobj, void *jcls_arg=nullptr)
#define UNBOX_PRIMITIVE_ARRAY(METHOD_T, OCTAVE_T, JAVA_T, JAVA_T_CAP)
#define TO_JAVA(obj)
JNIEXPORT void JNICALL Java_org_octave_OctaveReference_doFinalize(JNIEnv *, jclass, jint)
static JNIEnv * thread_jni_env(void)
}
Definition: ov-java.cc:897
bool Vdebug_java
Definition: ov-java.cc:223
java_local_ref< jstring > jstring_ref
Definition: ov-java.cc:147
static void terminate_jvm(void)
Terminate the current jvm, if there is any.
Definition: ov-java.cc:833
static std::string jstring_to_string(JNIEnv *jni_env, jstring s)
Converts a Java string object to std::string.
Definition: ov-java.cc:857
#define TO_JOBJECT(obj)
Definition: ov-java.cc:85
static JavaVM * jvm
The pointer to a java virtual machine either created in the current thread or attached this thread to...
Definition: ov-java.cc:196
java_local_ref< jintArray > jintArray_ref
Definition: ov-java.cc:149
static octave_value box_more(JNIEnv *jni_env, void *jobj_arg, void *jcls_arg=nullptr)
static std::string initial_class_path(void)
Return the initial classpath.
Definition: ov-java.cc:422
jint(JNICALL * JNI_GetCreatedJavaVMs_t)(JavaVM **pvm, jsize bufLen, jsize *nVMs)
Definition: ov-java.cc:93
void * voidptr
Definition: ov-java.h:39
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211
#define octave_stdout
Definition: pager.h:313
#define SET_INTERNAL_VARIABLE(NM)
Definition: variables.h:103
F77_RET_T len
Definition: xerbla.cc:61