Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial JNI configuration #157

Open
wants to merge 20 commits into
base: java-interop
Choose a base branch
from
Open
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Try and fix loading of libjvm for older JREs, and JNI code
  • Loading branch information
nberth committed Nov 12, 2024
commit 56aac26a574830c90e9a88544321c9ec83df3e52
87 changes: 39 additions & 48 deletions cobc/codegen.c
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file and most others in cobc miss a ChangeLog entry

Original file line number Diff line number Diff line change
@@ -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,65 +7091,56 @@ 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 = (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 *method_name;
const char *class_name;
char* mangled;
char *class_and_method_name, *last_dot, *c;
const char *class_name, *method_name;
char mangled[COB_NORMAL_BUFF];

// 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();
}
/* Assume "Java." prefix (enforced in `parser.y`, rule `call_body`) */
class_and_method_name = (char*)CB_LITERAL(p->name)->data + 5;

last_dot = strrchr (mangled, '.');
*last_dot = '_';
lookup_java_call(mangled);
strncpy (mangled, class_and_method_name, COB_NORMAL_MAX);
for (c = mangled; *c; c++) {
if (*c == '.') *c = '_';
}
lookup_java_call (mangled);

char* last_dot = strrchr(class_and_method_name, '.');
*last_dot = '\0';
method_name = last_dot + 1;
class_name = class_and_method_name;
last_dot = strrchr (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);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do an exception check afterwards, as done for normal calls; also add the ON EXCEPTION/ NOT ON EXCEPTION codegen; possibly by moving those parts out of output_call() and executing them also in the 'CB_CONV_JAVA` part.


static void
10 changes: 7 additions & 3 deletions cobc/typeck.c
Original file line number Diff line number Diff line change
@@ -3921,7 +3921,7 @@ check_argument_conformance (struct cb_program *program, cb_tree argument_tripple

void
cb_check_conformance (cb_tree prog_ref, cb_tree using_list,
cb_tree returning)
cb_tree returning)
{
struct cb_program *program = NULL;
cb_tree l;
@@ -3950,8 +3950,12 @@ cb_check_conformance (cb_tree prog_ref, cb_tree using_list,
if (last_dot == NULL) {
cobc_err_msg(_("Malformed Java method name '%s'"), class_and_method_name);
return;
}
}
}
if (using_list != NULL || returning != NULL) {
CB_PENDING ("Java method call with parameters or return values");
COBC_ABORT ();
}
}

/*
Check each parameter is conformant: has right type, has right
24 changes: 17 additions & 7 deletions configure.ac
Original file line number Diff line number Diff line change
@@ -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])
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this define should be outside (in the "else" part of the code below, when all checks worked)

AC_DEFINE_UNQUOTED([COB_JAVA_ARCH], ["$JAVA_ARCH"])
JNI_LDFLAGS="$JNI_LIBS"
JNI_LIBS="-ljvm"
cob_has_jni=yes
14 changes: 11 additions & 3 deletions libcob/call.c
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NBRTH: this delay-load part is missing in the changedoc

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure! There are still many missing ChangeLog entries; I am just rushing to try and find out where any remaining "hard-to-do" parts about the dynamic loading are. Based on our tests with @xevor11, things appear to depend heavily on the Java version. Like for now the "Hello World!" test works on my setup but exists silently on his machine (or else exhibits a segfault within JNI_CreateJavaVM when in a gdb session…).

Original file line number Diff line number Diff line change
@@ -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 */
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nberth Shouldn't most of this be placed into a #ifdef WITH_JNI as well?


#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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nberth this line should get extra comments, but I feel it may be better to put that in the documentation instead of running in a compile warning - especially as some people compile with -Werror, which possibly breaks here (haven't checked)

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,
2 changes: 0 additions & 2 deletions libcob/coblocal.h
Original file line number Diff line number Diff line change
@@ -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
39 changes: 18 additions & 21 deletions libcob/java.c
Original file line number Diff line number Diff line change
@@ -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
Loading