-
Notifications
You must be signed in to change notification settings - Fork 24
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
base: java-interop
Are you sure you want to change the base?
Conversation
cobc/codegen.c
Outdated
} | ||
output ("cob_resolve_java ();"); | ||
output_newline (); | ||
output_block_close (); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right here you need to generate a call to cob_call_java(…)
with the appropriate handle name call_java_%s
. Arguments will come in a subsequent PR.
cobc/ppparse.output
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file should not be committed.
po/[email protected]
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everything under po
should not be committed either.
8df9f29
to
9164ac8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the real next change that should be done is an actual test for calling a static java method that has no parameters, to verify that java.c indeed works
cobc/codegen.c
Outdated
@@ -146,6 +152,7 @@ static struct literal_list *literal_cache = NULL; | |||
static struct field_list *field_cache = NULL; | |||
static struct field_list *local_field_cache = NULL; | |||
static struct call_list *call_cache = NULL; | |||
static struct java_call_list *call_java_cache = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor formatting: use a tab before the *
cobc/codegen.c
Outdated
lookup_java_call(const char *p) { | ||
struct java_call_list *clp; | ||
|
||
for (clp = call_java_cache; clp; clp = clp->next) { | ||
if (strcmp(p, clp->call_name) == 0) { | ||
return; | ||
} | ||
} | ||
clp = cobc_parse_malloc(sizeof(struct java_call_list)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor formatting: a space before the (
, see lookup_call below (and sorry for not providing an automated formatting rule via gnu indent yet)
cobc/codegen.c
Outdated
static void | ||
lookup_call (const char *p) | ||
{ | ||
struct call_list *clp; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
minor formatting: please keep this nl here, reduces the diff :-)
cobc/codegen.c
Outdated
if( | ||
#ifdef HAVE_JNI | ||
strncmp("Java.", s, 6) == 0 | ||
#else | ||
0 | ||
#endif | ||
) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is possible that the compiler isn't build with JNI, but the runtime is - and we don't call into JNI here either, so please use the code unconditional; in any case this whole block should be moved, either to the start of lookup_func_call
, or (if there aren't too much places) in the caller with having this code be placed completely in a different function.
Mind the formatting = space before (
in that.
cobc/codegen.c
Outdated
#endif | ||
) { | ||
lookup_java_call(s + 6); | ||
output_line ("if (call_java_%s == NULL || cob_glob_ptr->cob_physical_cancel)", name_str); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to CANCEL
a java class (and if yes to really unload it without jni teardown)?
I guess not - and in this case the check for physical cancel should be dropped.
cobc/codegen.c
Outdated
output_block_open(); | ||
output_prefix(); | ||
output ("call_java_%s = ", name_str); | ||
char *first_dot = strchr(s + 6, '.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be able to compile that with C89 (still a requirement for 4.x, may be dropped later), move this line to the start of the block.
configure.ac
Outdated
AC_ARG_ENABLE([java], | ||
[AC_HELP_STRING([--disable-java], | ||
[disable Java Interoperability])]) | ||
AS_IF([test X$enable_java != Xno], [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ARC_ARG_ENABLE
is more about optional features (like the debug log) and less about external packages where AC_ARG_WITH
is preferable (and would also match what configure has for example for libxml2).
See https://www.gnu.org/savannah-checkouts/gnu/autoconf/manual/autoconf-2.72/html_node/External-Software.html and adjust (you may postpone that part until you have actual running tests, though).
In any case please move the JNI block directly before the libxml2 part.
libcob/common.h
Outdated
// #ifdef HAVE_JNI | ||
// #include "jni.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these commented line and the one above should be dropped
I hope my commit to java.c did not break anything... (I just felt that the explanations would take much longer and those are more related to libcob in general than the changes of this project itself). And yes, once there is a running simple test, we should update the branch from gcos4 which will then also include the CI. for further commits. |
Update: Fixed warnings |
cobc/codegen.c
Outdated
char *method_name; | ||
const char *class_name; | ||
|
||
lookup_java_call(p + 5); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here you need to do some mangling: at the moment maybe substitute .
s into some other character like _
.
cobc/codegen.c
Outdated
struct java_call_list *clp; | ||
|
||
for (clp = call_java_cache; clp; clp = clp->next) { | ||
if (strcmp (p, clp->call_name) == 0) { | ||
return; | ||
} | ||
} | ||
clp = cobc_parse_malloc (sizeof(struct java_call_list)); | ||
clp->call_name = p; | ||
clp->next = call_java_cache; | ||
call_java_cache = clp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please indent on the left with tab
cobc/codegen.c
Outdated
for (clp = func_call_cache; clp; clp = clp->next) { | ||
if (strcmp (p, clp->call_name) == 0) { | ||
return; | ||
} | ||
} | ||
clp = cobc_parse_malloc (sizeof (struct call_list)); | ||
clp->call_name = p; | ||
clp->next = func_call_cache; | ||
func_call_cache = clp; | ||
for (clp = func_call_cache; clp; clp = clp->next) { | ||
if (strcmp(p, clp->call_name) == 0) { | ||
return; | ||
} | ||
} | ||
clp = cobc_parse_malloc(sizeof(struct call_list)); | ||
clp->call_name = p; | ||
clp->next = func_call_cache; | ||
func_call_cache = clp; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation got wrong, please clean up, which will also lead to a smaller diff
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requesting mostly formatting changes (and the variable name pointed out by Nicolas) for now - we really would need a testcase that showcase a call to a static, preferably parameterless, jre internal function works fine.
The one "real" change is to move the check for the java call into the parser - see review comments for the details.
And even if it doesn't work it should still be added to a new testcase at the end of run_extensions.at
cobc/codegen.c
Outdated
if(strncasecmp("Java.", s, 5) == 0) { | ||
output_java_call(s + 5); | ||
return; | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
drop that else and the closing brace as you have a return
already and don't need the additional scope (and create smaller diffs that way)
ChangeLog
Outdated
* ax_prog_java.m4: Added macro for jni check | ||
* ax_jni_include_dir.m4: Added macro for jni check | ||
* configure.ac: added support for Java interoperability through JNI |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
space indent here, leave an empty line before and after the name, please
cobc/codegen.c
Outdated
@@ -7531,6 +7582,10 @@ output_call (struct cb_call *p) | |||
/* Dynamic link */ | |||
if (name_is_literal_or_prototype) { | |||
s = get_program_id_str (p->name); | |||
if(strncasecmp("Java.", s, 5) == 0) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comparision should go into parser.y
Lines 12529 to 12530 in 00f6832
if (CB_LITERAL_P ($3)) { | |
cb_check_conformance ($3, $7, $8); |
I suggest that after we found out if this is a java call or not, we set the call_convention
to a new value matching a define JAVA_CALL. This has the benefit that we can then also check the parameter conformance later (codegen is too later for that) and output a warning if the current environment does not support Java interop.
In this function here we can then directly at the start or even in the caller get to output_call_java()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xevor11 To help with that, I've just rebased java-interop
on top of master
(which is trunk
on the upstream). Now you can rebase your jni-config
branch on top of that so you'll have access to the piece of code that's linked above.
After that you can define CB_CONV_JAVA
in tree.h
, and then do the check for the "Java."
prefix in parser.y
(I think right before the call to cb_check_conformance
, but I'm not sure what that latter function does exactly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes right before that function, which checks that something is according to a known prototype - in the case of Java that should check that only "java interop compatible" arguments are passed.
Addressing this specific system call:
I wrote a simple method call:
however internally it must be create the VM and retrieve the methods from the JNI Function Table? |
There is another PR #174 which includes the non-void method handling as well |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The overall state is quite good!
Biggest issues:
- from glancing at the code I think
CALL...[NOT] ON EXCEPTION
is broken (adjust the tests as requested and we'll see) - the build system need some changes
- @nberth has a bit to do on the delay-load part (incl. ChangeLog entries)
- if possible I'd like to see the parameter check already, but we can leave this for later as well
DEPENDENCIES.md
Outdated
The JDK is distributed under various open-source licenses depending on the vendor and version. Common licenses include the GNU General Public License (GPL) and the Oracle Binary Code License Agreement. | ||
|
||
To enable JNI support, ensure that the JDK is installed on your system, and set the appropriate environment variables (e.g., JAVA_HOME) to point to the JDK installation directory. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please word-wrap as for SCREEN SECTION
NEWS
Outdated
@@ -20,7 +20,7 @@ NEWS - user visible changes -*- outline -*- | |||
|
|||
|
|||
* New GnuCOBOL features | |||
|
|||
** Initial support for Java interoperability through JNI (new optional dependency JDK) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please adjust as for "JSON GENERATE" (just check in the same file searching for "json"):
- what is available now
- what is needed for support (I guess this will only be a dependency for runtime support, e. g. all checks in cobc work identical without that)
- add the related configure option / flags
- word-wrap your new content to col80
ChangeLog
Outdated
* ax_prog_java.m4: Added macro for jni check | ||
* ax_jni_include_dir.m4: Added macro for jni check |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- combine both m4 entries to one entry (comma separate filename, include their "m4/" prefix)
- make the configure entry more specific, like the 2020-07-19 one
- add something like
NEWS, DEPENDENCIES: updated for JNI
There was a problem hiding this comment.
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
libcob/java.c
Outdated
if ((*env)->ExceptionCheck(env)) { | ||
(*env)->ExceptionDescribe(env); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of ExceptionDescribe which puts the message unconditionally to stderr and implicit frees the exception, the message should be extracted and setup into last error - see call.c for the later;
no idea if there's a cleaner way (please give it a short check), but ChatGPT said we need to use JNI to get the details, giving the following example (which at least needs to be converted to C89 compat [vars+comments]:
// Find the Throwable class
jclass throwableClass = (*env)->FindClass(env, "java/lang/Throwable");
// Get the getMessage() method from Throwable class
jmethodID getMessageMethod = (*env)->GetMethodID(env, throwableClass, "getMessage", "()Ljava/lang/String;");
if (getMessageMethod != NULL) {
// Call getMessage() on the exception object
jstring message = (jstring)(*env)->CallObjectMethod(env, exception, getMessageMethod);
// Convert the Java String (jstring) to a C string (char*)
const char *messageChars = (*env)->GetStringUTFChars(env, message, NULL);
printf("Exception message: %s\n", messageChars);
(*env)->ReleaseStringUTFChars(env, message, messageChars); // Clean up
}
// Find java.io.StringWriter and java.io.PrintWriter classes
jclass stringWriterClass = (*env)->FindClass(env, "java/io/StringWriter");
jclass printWriterClass = (*env)->FindClass(env, "java/io/PrintWriter");
// Create a new StringWriter object
jobject stringWriter = (*env)->NewObject(env, stringWriterClass, (*env)->GetMethodID(env, stringWriterClass, "<init>", "()V"));
// Create a new PrintWriter object that wraps the StringWriter
jobject printWriter = (*env)->NewObject(env, printWriterClass,
(*env)->GetMethodID(env, printWriterClass, "<init>", "(Ljava/io/Writer;)V"),
stringWriter);
// Call exception.printStackTrace(printWriter) to write the stack trace to the StringWriter
jclass throwableClass = (*env)->GetObjectClass
instead of the convenience function ExceptionCheck
use ExceptionOccurred
which returns either NULL
or the jthrowable
we need for the code above
libcob/call.c
Outdated
#else | ||
static int first_java = 1; | ||
|
||
COB_UNUSED (method_handle); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of the unused: check if it is empty (before the #if WITH_JNI
) and set the relevant exceptions then; scoping the complete#else
in a separate block {}
output_line("cob_call_java(call_java_%s);\n", mangled); | ||
output_newline(); | ||
output_block_close(); | ||
} |
There was a problem hiding this comment.
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.
tests/testsuite.src/run_java.at
Outdated
AT_SETUP([CALL Java static void (void) (missing class)]) | ||
AT_KEYWORDS([extensions jni]) | ||
|
||
AT_SKIP_IF([test "$COB_HAS_JNI" = "no"]) | ||
|
||
AT_DATA([prog.cob], [ | ||
IDENTIFICATION DIVISION. | ||
PROGRAM-ID. prog. | ||
PROCEDURE DIVISION. | ||
CALL "Java.Test.isAMissingClass" | ||
STOP RUN. | ||
]) | ||
|
||
AT_CHECK([$COMPILE prog.cob], [0], [], []) | ||
AT_CHECK([$COBCRUN_DIRECT ./prog], [1], [], | ||
[libcob: prog.cob:5: error: Java class 'Test' not found | ||
]) | ||
AT_CLEANUP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate this test with ON EXCEPTION
and DISPLAY "java call not successful"
in there; this duplicated test also doesn't need to be skipped if we don't have JNI as it should run in the same exception
PROGRAM-ID. prog. | ||
PROCEDURE DIVISION. | ||
CALL "Java.Test.printHelloWorld" | ||
STOP RUN. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add NOT ON EXCEPTION DISPLAY "Java call worked"
to this call
I've got word from the original author of the java related macros used and that's (part of) his answer:
I'm likely to adjust the part configure for that (but that likely takes some days until I get to this), @nberth can you please check the review related to the delay-load part and @xevor11 could you please check on the rest? |
45956df
to
5aaa4a9
Compare
cobc/codegen.c
Outdated
const char *class_name, *method_name; | ||
char mangled[COB_NORMAL_BUFF]; | ||
|
||
/* TODO: move that back into cb_check_conformance (*) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xevor11 I think you have started moving this check and the one below back into cb_check_conformance
; so I'll stop pushing changes to this branch to let you advance with that.
Cleaned up java.c, added the requested changes from PR#150, ready for feedback and comments