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 Handling of Non-Void Java Method Calls in GnuCOBOL #174

Open
wants to merge 7 commits into
base: java-interop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
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
8 changes: 7 additions & 1 deletion ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@

2024-07-12 Vedant Tewari <[email protected]>

2023-02-25 Ron Norman <[email protected]>
* 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

2023-02-25 Ron Norman <[email protected]>

* configure.ac: Add check for sys/time.h

Expand Down
15 changes: 15 additions & 0 deletions DEPENDENCIES
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@ The following libraries ARE required WHEN :

JSON-C is distributed under Expat License.

5) JNI (Java Native Interface) support is used

BOTH runtime AND development components required.

Java Development Kit (JDK) 8 or later

https://openjdk.org/

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.

See HACKING if you wish to hack the GnuCOBOL source or build directly
from version control as this includes the list of additional tools
Expand Down
10 changes: 10 additions & 0 deletions DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,13 @@ Support for GENERATE JSON is provided by *one* of the following:

JSON-C is distributed under Expat License.

JNI Support
------------

Support for JNI (Java Native Interface) is provided by:

* [Java Development Kit (JDK)](https://openjdk.java.net/) 8 or later.

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.
2 changes: 1 addition & 1 deletion NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ NEWS - user visible changes -*- outline -*-


* New GnuCOBOL features

** Initial support for Java interoperability through JNI (new optional dependency JDK)
** file handling: added backends for ODBC (so far PostgrSQL, MySQL, SQLite,
MSSQL) and OCI, along with new directory COB_SCHEMA_DIR containing the
necessary internal schema files to match the file definition to the
Expand Down
4 changes: 4 additions & 0 deletions cobc/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@

2024-08-14 Nicolas Berthier <[email protected]>

* cobc.c (cobc_print_info): added note for Java interoperability

2024-08-04 David Declerck <[email protected]>

Adjustments to merge 2022-12-21:
Expand Down
6 changes: 6 additions & 0 deletions cobc/cobc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2620,6 +2620,12 @@ cobc_print_info (void)

cobc_var_print (_("JSON library"), _(WITH_JSON), 0);

#ifdef WITH_JNI
cobc_var_print (_("Java interoperability"), _("enabled"), 0);
#else
cobc_var_print (_("Java interoperability"), _("disabled"), 0);
#endif

#ifdef COB_DEBUG_LOG
cobc_var_print ("DEBUG_LOG", _("enabled"), 0);
#endif
Expand Down
155 changes: 154 additions & 1 deletion cobc/codegen.c
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
Copyright (C) 2003-2024 Free Software Foundation, Inc.
Written by Keisuke Nishida, Roger While, Ron Norman, Simon Sobisch,
Edward Hart
Edward Hart, Vedant Tewari

This file is part of GnuCOBOL.

Expand Down Expand Up @@ -147,6 +147,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 call_list *call_java_cache = NULL;
static struct call_list *func_call_cache = NULL;
static struct static_call_list *static_call_cache = NULL;
static struct base_list *base_cache = NULL;
Expand Down Expand Up @@ -395,6 +396,22 @@ lookup_source (const char *p)
return source_id++;
}

static void
lookup_java_call(const char *p)
{
struct 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 call_list));
clp->call_name = p;
clp->next = call_java_cache;
call_java_cache = clp;
}

static void
lookup_call (const char *p)
{
Expand Down Expand Up @@ -1978,6 +1995,11 @@ output_call_cache (void)
output_local ("static cob_call_union\tcall_%s;\n",
call->call_name);
}
call_java_cache = call_list_reverse (call_java_cache);
for (call = call_java_cache; call; call = call->next) {
output_local ("static cob_java_handle*\tcall_java_%s;\n",
call->call_name);
}
func_call_cache = call_list_reverse (func_call_cache);
for (call = func_call_cache; call; call = call->next) {
output_local ("static cob_call_union\tfunc_%s;\n",
Expand Down Expand Up @@ -7068,6 +7090,132 @@ output_field_constant (cb_tree x, int n, const char *flagname)
output_newline ();
}

static void
output_java_call (struct cb_call *p)
{
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 method_signature[256] = "(";
Copy link
Contributor

Choose a reason for hiding this comment

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

For internal buffer sizes you have COB_*_BUFF definitions in libcob/common.h that you can use here as well. Typical signature length could exceed 256 without much effort I think (with long-enough Java package names). I'd go for COB_NORMAL_BUFF (2048) for now.

char* mangled;
struct cb_tree *ptr;

mangled = strdup(class_and_method_name);
for (size_t i = 0; i < strlen(mangled) + 1; i++) {
mangled[i] = (mangled[i] == '.') ? '_' : mangled[i];
}

last_dot = strrchr(class_and_method_name, '.');
if (last_dot == NULL) {
cobc_err_msg (_("malformed call '%s' to a Java method"), class_and_method_name);
COBC_ABORT ();
}

*last_dot = '\0';
method_name = last_dot + 1;
class_name = class_and_method_name;

for(ptr = p->args; ptr != NULL; ptr = ptr->next) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Note: indentation is strange here. I think you need to adjust that in your settings for this project.

switch(CB_TREE_TAG(ptr)) {
case CB_TAG_INTEGER:
strcat(method_signature, "I");
break;
case CB_TAG_LONG:
strcat(method_signature, "J");
break;
case CB_TAG_FLOAT:
strcat(method_signature, "F");
break;
case CB_TAG_DOUBLE:
strcat(method_signature, "D");
break;
case CB_TAG_BOOLEAN:
strcat(method_signature, "Z");
break;
case CB_TAG_BYTE:
strcat(method_signature, "B");
break;
case CB_TAG_SHORT:
strcat(method_signature, "S");
break;
case CB_TAG_CHAR:
strcat(method_signature, "C");
break;
case CB_TAG_STRING:
strcat(method_signature, "Ljava/lang/String;");
break;
case CB_TAG_OBJECT:
strcat(method_signature, "Ljava/lang/Object;");
break;
case CB_TAG_LITERAL:
if(CB_TREE_CATEGORY(ptr) == CB_CATEGORY_NUMERIC) {
strcat(method_signature, "I");
} else if(CB_TREE_CATEGORY(ptr) == CB_CATEGORY_ALPHANUMERIC) {
strcat(method_signature, "Ljava/lang/String;");
}
break;
case CB_TAG_ARRAY:
switch(CB_TREE_TAG(CB_TREE(ptr)->next)) {
case CB_TAG_INTEGER:
strcat(method_signature, "[I");
break;
case CB_TAG_LONG:
strcat(method_signature, "[J");
break;
case CB_TAG_FLOAT:
strcat(method_signature, "[F");
break;
case CB_TAG_DOUBLE:
strcat(method_signature, "[D");
break;
case CB_TAG_BOOLEAN:
strcat(method_signature, "[Z");
break;
case CB_TAG_BYTE:
strcat(method_signature, "[B");
break;
case CB_TAG_SHORT:
strcat(method_signature, "[S");
break;
case CB_TAG_CHAR:
strcat(method_signature, "[C");
break;
case CB_TAG_STRING:
strcat(method_signature, "[Ljava/lang/String;");
break;
case CB_TAG_OBJECT:
strcat(method_signature, "[Ljava/lang/Object;");
break;
default:
cobc_err_msg(_("Unsupported array type in Java method call"));
COBC_ABORT();
}
break;
default:
cobc_err_msg(_("Unsupported argument type in Java method call"));
COBC_ABORT();
}
}

strcat(method_signature, ")V");

lookup_java_call(mangled);
output_line("if (call_java_%s == NULL)", mangled);
output_block_open();

output_prefix();
output_line("call_java_%s = ", mangled);
output("cob_resolve_java(\"%s\", \"%s\", \"()V\");", class_name, method_name, method_signature);
Copy link
Contributor

Choose a reason for hiding this comment

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

Here you should probable replace ()V with %s so the method signature is included.

output_newline ();
output_prefix ();
output_line("cob_call_java(call_java_%s);\n", mangled);
output_newline();
output_block_close();
cobc_free(mangled);
Copy link
Contributor

Choose a reason for hiding this comment

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

This will likely cause a use-after-free since mangled is referenced in the list created by lookup_java_call.

}

static void
output_call (struct cb_call *p)
{
Expand Down Expand Up @@ -7097,6 +7245,11 @@ output_call (struct cb_call *p)
}
system_call = NULL;

if (p->convention & CB_CONV_JAVA) {
output_java_call(p);
return;
}

#ifdef _WIN32
if (p->convention & CB_CONV_STDCALL) {
convention = "_std";
Expand Down
5 changes: 5 additions & 0 deletions cobc/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -12253,6 +12253,11 @@ call_body:

/* Check parameter conformance, if we can work out what is being called. */
if (CB_LITERAL_P ($3)) {
/* Check for "Java." prefix and set call convention */
char* s = (char *)CB_LITERAL ($3)->data;
if (strncasecmp("Java.", s, 5) == 0) {
call_conv = CB_CONV_JAVA;
}
cb_check_conformance ($3, $7, $8);
} else if (CB_REFERENCE_P ($3)) {
cb_tree ref = cb_ref ($3);
Expand Down
1 change: 1 addition & 0 deletions cobc/tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ enum cb_tag {
#define CB_CONV_THUNK_16 (1 << 5)
#define CB_CONV_STDCALL (1 << 6)
#define CB_CONV_COBOL (1 << 15)
#define CB_CONV_JAVA (1 << 16)
#define CB_CONV_C (0)
#define CB_CONV_PASCAL (CB_CONV_L_TO_R | CB_CONV_CALLEE_STACK)

Expand Down
Loading