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