From 45956dfc958e6625d847d568754acd6161685f8d Mon Sep 17 00:00:00 2001 From: Nicolas Berthier Date: Wed, 6 Nov 2024 11:44:36 +0100 Subject: [PATCH] Try and fix loading of `libjvm` for older JREs, and JNI code --- cobc/codegen.c | 103 ++++++++++++++++---------------- configure.ac | 24 +++++--- libcob/call.c | 14 ++++- libcob/coblocal.h | 2 - libcob/java.c | 39 ++++++------ tests/testsuite.src/run_java.at | 27 ++------- tests/testsuite.src/syn_misc.at | 16 +++++ 7 files changed, 118 insertions(+), 107 deletions(-) diff --git a/cobc/codegen.c b/cobc/codegen.c index 643f4378e..910e40e01 100644 --- a/cobc/codegen.c +++ b/cobc/codegen.c @@ -397,7 +397,7 @@ lookup_source (const char *p) } static void -lookup_java_call(const char *p) +lookup_java_call (const char *p) { struct call_list *clp; @@ -407,7 +407,7 @@ lookup_java_call(const char *p) } } clp = cobc_parse_malloc (sizeof (struct call_list)); - clp->call_name = p; + clp->call_name = cobc_parse_strdup (p); clp->next = call_java_cache; call_java_cache = clp; } @@ -7091,73 +7091,70 @@ output_field_constant (cb_tree x, int n, const char *flagname) } static void -output_exception_handling(struct cb_call *p) +output_exception_handling (struct cb_call *p) { - if (p->stmt1) { - output_line("cob_glob_ptr->cob_stmt_exception = 1;"); - output_line("COB_RESET_EXCEPTION(0);"); - } else { - output_line("cob_glob_ptr->cob_stmt_exception = 0;"); - } + if (p->stmt1) { + output_line ("cob_glob_ptr->cob_stmt_exception = 1;"); + output_line ("COB_RESET_EXCEPTION(0);"); + } else { + output_line ("cob_glob_ptr->cob_stmt_exception = 0;"); + } - output_line("if ((cob_glob_ptr->cob_exception_code & 0xff00) != 0) "); - output_block_open(); + output_line ("if ((cob_glob_ptr->cob_exception_code & 0xff00) != 0) "); + output_block_open (); - if (p->stmt1) { - output_stmt(p->stmt1); - } else if (p->stmt2) { - output_stmt(p->stmt2); + if (p->stmt1) { + output_stmt (p->stmt1); + } else if (p->stmt2) { + output_stmt (p->stmt2); } - output_block_close(); - output_line("COB_RESET_EXCEPTION(0);"); + output_block_close (); + output_line ("COB_RESET_EXCEPTION(0);"); } static void -output_java_call(struct cb_call *p) +output_java_call (struct cb_call *p) { - if (p->args != NULL || p->call_returning != NULL) { - CB_PENDING("Java method call with parameters or return values"); - COBC_ABORT(); - } + char *full_name, *class_and_method_name, *last_dot; + const char *class_name, *method_name; + char mangled[COB_NORMAL_BUFF]; - char* full_name = (char*)CB_LITERAL(p->name)->data; /* Assume java.prefix (enforced in `parser.y`, rule `call_body`) */ - char* class_and_method_name = full_name + 5; - char *last_dot; - char *method_name; - const char *class_name; - char* mangled; + /* TODO: move that back into cb_check_conformance (*) */ + if (p->args != NULL || p->call_returning != NULL) { + CB_PENDING("Java method call with parameters or return values"); + COBC_ABORT(); + } - // Directly duplicate the class_and_method_name - mangled = strdup(class_and_method_name); - if (!mangled) { - cobc_err_msg(_("Memory allocation failed for mangled name")); - COBC_ABORT(); - } + full_name = (char*)CB_LITERAL(p->name)->data; - last_dot = strrchr (mangled, '.'); - *last_dot = '_'; - lookup_java_call(mangled); + /* Assume "Java." prefix (enforced in `parser.y`, rule `call_body`) */ + class_and_method_name = full_name + 5; - last_dot = strrchr(class_and_method_name, '.'); + strncpy (mangled, class_and_method_name, COB_NORMAL_MAX); + last_dot = strrchr (mangled, '.'); + *last_dot = '_'; + lookup_java_call (mangled); - if (last_dot == NULL) { - cobc_err_msg(_("malformed call '%s' to a Java method"), class_and_method_name); - cobc_free(mangled); - return; - } + last_dot = strrchr (class_and_method_name, '.'); + /* TODO: same as (*) above */ + if (last_dot == NULL) { + cobc_err_msg(_("malformed call '%s' to a Java method"), class_and_method_name); + cobc_free(mangled); + return; + } - *last_dot = '\0'; - method_name = last_dot + 1; - class_name = class_and_method_name; + *last_dot = '\0'; + method_name = last_dot + 1; + class_name = class_and_method_name; - output_line("if (call_java_%s == NULL)", mangled); - output_block_open(); + output_line ("if (call_java_%s == NULL)", mangled); + output_block_open (); + output_line ("call_java_%s = cob_resolve_java (\"%s\", \"%s\", \"()V\");", + mangled, class_name, method_name); + output_line ("cob_call_java (call_java_%s);", mangled); + output_block_close (); - output_line("call_java_%s = cob_resolve_java(\"%s\", \"%s\", \"()V\");", - mangled, class_name, method_name); - output_line("cob_call_java(call_java_%s);", mangled); - output_block_close(); - output_exception_handling(p); + output_exception_handling (p); } static void diff --git a/configure.ac b/configure.ac index 8191a5e96..190d5e8d2 100644 --- a/configure.ac +++ b/configure.ac @@ -152,6 +152,8 @@ dnl done via AC_CHECK_FUNCS: AH_TEMPLATE([HAVE_RAISE], [Has raise function]) AH_TEMPLATE([HAVE_FINITE_IEEEFP_H], [Declaration of finite function in ieeefp.h instead of math.h]) +AH_TEMPLATE([COB_JAVA_ARCH], [Java platform architecture]) + dnl preparation for cross-compilation AC_ARG_PROGRAM @@ -951,6 +953,11 @@ AS_IF([test "x$with_java" != "xno"], [ AC_MSG_NOTICE([Given Java home: ${JAVA_HOME}]) fi + dnl Note: One more hack needed to properly locate libjvm with + dnl early versions of openjdk (1.8 at least). + JAVA_ARCH="$( $JAVA -XshowSettings:properties -version 2>&1 >/dev/null | \ + $SED -e '/^[ ]*os.arch/!d' -e 's/.*=[ ]*//' )" + dnl Note: AX_PROG_JAVAC might find a `javac` binary that does dnl not match the version of `$JAVA` found above, so we set dnl its path manually. @@ -962,15 +969,17 @@ AS_IF([test "x$with_java" != "xno"], [ for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do JNI_CPPFLAGS="$JNI_CPPFLAGS -I$JNI_INCLUDE_DIR" done - for _dir in "${JAVA_HOME}/jre/lib" "${JAVA_HOME}/lib"; do + for _dir in "${JAVA_HOME}/jre/lib" \ + "${JAVA_HOME}/jre/lib/${JAVA_ARCH}" \ + "${JAVA_HOME}/lib"; do if test -d "$_dir"; then JNI_LIBS="$JNI_LIBS -L$_dir" - fi - if test -d "$_dir/server"; then - JNI_LIBS="$JNI_LIBS -L$_dir/server" - fi - if test -d "$_dir/client"; then - JNI_LIBS="$JNI_LIBS -L$_dir/client" + if test -d "$_dir/server"; then + JNI_LIBS="$JNI_LIBS -L$_dir/server" + fi + if test -d "$_dir/client"; then + JNI_LIBS="$JNI_LIBS -L$_dir/client" + fi fi done curr_LIBS="$LIBS" @@ -988,6 +997,7 @@ AS_IF([test "x$with_java" != "xno"], [ ], [ AC_MSG_RESULT([yes]) AC_DEFINE([WITH_JNI], [1]) + AC_DEFINE_UNQUOTED([COB_JAVA_ARCH], ["$JAVA_ARCH"]) JNI_LDFLAGS="$JNI_LIBS" JNI_LIBS="-ljvm" cob_has_jni=yes diff --git a/libcob/call.c b/libcob/call.c index 9b3dc391e..f38e24339 100644 --- a/libcob/call.c +++ b/libcob/call.c @@ -129,7 +129,6 @@ lt_dlerror (void) static lt_dlhandle jvm_handle = NULL; #else /* Using libltdl, no need to preload. */ -# define JVM_PRELOAD 0 #endif #include "sysdefines.h" @@ -1855,7 +1854,7 @@ cob_exit_call (void) } base_dynload_ptr = NULL; -#if JVM_PRELOAD +#ifdef JVM_PRELOAD if (jvm_handle) { lt_dlclose (jvm_handle); jvm_handle = NULL; @@ -1993,6 +1992,8 @@ cob_init_call (cob_global *lptr, cob_settings* sptr, const int check_mainhandle) /* Java API handling */ +#ifdef WITH_JNI + /* "Standard" path suffixes to the dynamically loadable JVM library, from "typical" JAVA_HOME. */ const char* const path_to_jvm[] = { @@ -2003,7 +2004,11 @@ const char* const path_to_jvm[] = { #else # define JVM_FILE "libjvm." COB_MODULE_EXT "/lib/server", + "/jre/lib/server", + "/jre/lib/" COB_JAVA_ARCH "/server", "/lib/client", + "/jre/lib/client", + "/jre/lib/" COB_JAVA_ARCH "/client", #endif NULL, }; @@ -2040,13 +2045,14 @@ init_jvm_search_dirs (void) { break; #else /* Append to search path. */ + int success; # warning On some systems, JAVA_HOME-based lookup via `libltdl` does not work if (snprintf (jvm_path, (size_t)COB_FILE_MAX, "%s%s", java_home, path_suffix) == 0) { continue; } DEBUG_LOG ("call", ("appending '%s' to load path: ", jvm_path)); - int success = lt_dladdsearchdir (jvm_path); + success = lt_dladdsearchdir (jvm_path); DEBUG_LOG ("call", ("%s\n", success == 0 ? "success" : "failed")); #endif } @@ -2092,6 +2098,8 @@ cob_init_java (void) { return 0; } +#endif /* WITH_JNI */ + cob_java_handle* cob_resolve_java (const char *class_name, const char *method_name, diff --git a/libcob/coblocal.h b/libcob/coblocal.h index d3f5351d8..71be6fcd8 100644 --- a/libcob/coblocal.h +++ b/libcob/coblocal.h @@ -480,8 +480,6 @@ COB_HIDDEN void cob_runtime_warning_ss (const char *, const char *); COB_EXPIMP int cob_ncase_cmp (char *, const char *, unsigned ); COB_EXPIMP char * cob_str_case_str (char *, const char *); -COB_EXPIMP int cob_jni_init (cob_java_api *api); - /* static inline of smaller helpers */ static COB_INLINE int diff --git a/libcob/java.c b/libcob/java.c index 34e70101a..026d440cd 100644 --- a/libcob/java.c +++ b/libcob/java.c @@ -25,8 +25,8 @@ /* Force symbol exports */ #define COB_LIB_EXPIMP +#include "config.h" #include "common.h" -#include "libcob.h" #include "coblocal.h" /* Declarations */ @@ -38,32 +38,29 @@ typedef struct __cob_java_static_method { jmethodID mid; } cob_java_handle; +/* Only exported symbol: */ +int cob_jni_init (cob_java_api *api); + static int /* non-zero means there's an error */ jvm_load (void) { /* JDK/JRE 6 VM initialization arguments */ - JavaVMInitArgs args; - JavaVMOption* options = { 0, }; - const char *classpath; - size_t option_len; - char option_buffer[COB_NORMAL_BUFF]; + JavaVMInitArgs args; + JavaVMOption options[1]; + const char *classpath; + char cp_buffer[COB_MEDIUM_BUFF]; args.version = JNI_VERSION_1_6; - classpath = getenv ("CLASSPATH"); - if (classpath == NULL) { - classpath = ""; - } - args.nOptions = 1; - option_len = strlen("-Djava.class.path=") + strlen(classpath) + 1; - if (option_len > sizeof(option_buffer)) { - return -1; - } - strcpy(option_buffer, "-Djava.class.path="); - strcat(option_buffer, classpath); - options[0].optionString = option_buffer; args.options = options; - args.ignoreUnrecognized = 1; - /* loading and initializing a Java VM, returning as JNI interface */ - return JNI_CreateJavaVM(&jvm, (void**)&env, &args); + args.nOptions = 0; + args.ignoreUnrecognized = JNI_FALSE; + + if ((classpath = getenv ("CLASSPATH")) != NULL) { + snprintf (cp_buffer, COB_MEDIUM_MAX, + "-Djava.class.path=%s", classpath); + options[args.nOptions++].optionString = cp_buffer; + } + + return JNI_CreateJavaVM (&jvm, (void**)&env, &args); } static diff --git a/tests/testsuite.src/run_java.at b/tests/testsuite.src/run_java.at index 8c9a76031..194382550 100644 --- a/tests/testsuite.src/run_java.at +++ b/tests/testsuite.src/run_java.at @@ -32,6 +32,7 @@ public class Test { } } ]) + AT_DATA([prog.cob], [ IDENTIFICATION DIVISION. PROGRAM-ID. prog. @@ -47,25 +48,7 @@ AT_CHECK([$JAVAC Test.java], [0], [], []) AT_CHECK([$COMPILE prog.cob], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [Hello world! -]) -AT_CLEANUP - - -AT_SETUP([CALL Java with malformed method name]) -AT_KEYWORDS([extensions jni malformed]) - -AT_SKIP_IF([test "$COB_HAS_JNI" = "no"]) - -AT_DATA([prog.cob], [ - IDENTIFICATION DIVISION. - PROGRAM-ID. prog. - PROCEDURE DIVISION. - CALL "Java.InvalidName" - STOP RUN. -]) - -AT_CHECK([$COMPILE prog.cob], [1], [], -[prog.cob:5: error: malformed Java method name 'Java.InvalidName', expected format 'Java.ClassName.methodName' +Java call worked ]) AT_CLEANUP @@ -114,6 +97,7 @@ AT_CHECK([$COBCRUN_DIRECT ./prog], [1], [], ]) AT_CLEANUP + AT_SETUP([CALL Java static void (void) (missing class with ON EXCEPTION)]) AT_KEYWORDS([extensions jni exception]) @@ -133,11 +117,13 @@ AT_CHECK([$COBCRUN_DIRECT ./prog], [0], [], [java call not successful ]) AT_CLEANUP + AT_SETUP([CALL Java static void (void) in a package]) AT_KEYWORDS([extensions jni package]) AT_SKIP_IF([test "$COB_HAS_JNI" = "no"]) +AT_CHECK([mkdir -p testpackage]) AT_DATA([testpackage/Test.java], [ package testpackage; public class Test { @@ -158,7 +144,6 @@ AT_DATA([prog.cob], [ STOP RUN. ]) -AT_CHECK([mkdir -p testpackage]) AT_CHECK([$JAVAC testpackage/Test.java], [0], [], []) AT_CHECK([$COMPILE prog.cob], [0], [], []) AT_CHECK([$COBCRUN_DIRECT ./prog], [0], @@ -166,4 +151,4 @@ AT_CHECK([$COBCRUN_DIRECT ./prog], [0], Java call to package class worked ]) -AT_CLEANUP \ No newline at end of file +AT_CLEANUP diff --git a/tests/testsuite.src/syn_misc.at b/tests/testsuite.src/syn_misc.at index 7ab38951d..60c9db68b 100644 --- a/tests/testsuite.src/syn_misc.at +++ b/tests/testsuite.src/syn_misc.at @@ -9531,3 +9531,19 @@ pgm1.cob:18: error: start of statement in Area A AT_CLEANUP + +AT_SETUP([CALL Java with malformed method name]) +AT_KEYWORDS([extensions jni malformed]) + +AT_DATA([prog.cob], [ + IDENTIFICATION DIVISION. + PROGRAM-ID. prog. + PROCEDURE DIVISION. + CALL "Java.InvalidName" + STOP RUN. +]) + +AT_CHECK([$COMPILE prog.cob], [1], [], +[prog.cob:5: error: malformed Java method name 'Java.InvalidName', expected format 'Java.ClassName.methodName' +]) +AT_CLEANUP