Patch: JNI stubs for gcj

Tom Tromey tromey@cygnus.com
Thu Apr 20 12:08:00 GMT 2000


This patch gives gcj the ability to generate stubs for native
functions when -fjni is given.  The idea here is that the user might
have a .class file and a .so that implements the class' native methods
using JNI.  With this patch, the user can compile the .class file to
native code, and gcj will automatically generate stubs for the native
methods which will, at runtime, make the proper JNI calls.

This patch works fine on all my JNI test programs (well, except one,
but that is a runtime problem and not a compiler problem).

This patch also makes a couple minor tweaks:

* Puts java-tree.def into C mode
* It turns out we were declaring _Jv_InitClass with the wrong number
  of arguments.  I fixed that.

Alex, this patch is more important than the other patches I've sent.

2000-04-19  Tom Tromey  <tromey@cygnus.com>

	* class.c (add_method_1): Set both DECL_EXTERNAL and METHOD_NATIVE
	on native function.
	* jcf-parse.c (parse_class_file): Call build_jni_stub for native
	JNI methods.
	* expr.c (build_jni_stub): New function.
	* lang-specs.h: -fjni and -femit-class-file are incompatible.
	* parse.c: Rebuilt.
	* parse.y (java_complete_expand_methods): Expand a native method
	and call build_jni_stub if -fjni given.
	* lang-options.h: Document -fjni.
	* lang.c (flag_jni): New global.
	(lang_f_options): Added `jni' entry.
	* java-tree.h (soft_lookupjnimethod_node,
	soft_getjnienvnewframe_node, soft_jnipopsystemframe_node):
	Declare.
	(flag_jni): Declare.
	(build_jni_stub): Declare.
	(struct lang_decl): Added `native' flag.
	(METHOD_NATIVE): Redefined to use `native' field of lang specific
	structure.
	* decl.c (soft_lookupjnimethod_node, soft_getjnienvnewframe_node,
	soft_jnipopsystemframe_node): New globals.
	(init_decl_processing): Set them.  _Jv_InitClass only takes one
	argument.

	* java-tree.def: Put into `C' mode.

Tom

Index: class.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/class.c,v
retrieving revision 1.65
diff -u -r1.65 class.c
--- class.c	2000/04/06 01:01:11	1.65
+++ class.c	2000/04/20 19:04:12
@@ -644,7 +644,11 @@
   if (access_flags & ACC_PROTECTED) METHOD_PROTECTED (fndecl) = 1;
   if (access_flags & ACC_PRIVATE)
     METHOD_PRIVATE (fndecl) = DECL_INLINE (fndecl) = 1;
-  if (access_flags & ACC_NATIVE) METHOD_NATIVE (fndecl) = 1;
+  if (access_flags & ACC_NATIVE)
+    {
+      METHOD_NATIVE (fndecl) = 1;
+      DECL_EXTERNAL (fndecl) = 1;
+    }
   if (access_flags & ACC_STATIC) 
     METHOD_STATIC (fndecl) = DECL_INLINE (fndecl) = 1;
   if (access_flags & ACC_FINAL) 
Index: decl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/decl.c,v
retrieving revision 1.60
diff -u -r1.60 decl.c
--- decl.c	2000/04/04 20:40:19	1.60
+++ decl.c	2000/04/20 19:04:16
@@ -380,6 +380,9 @@
 tree soft_monitorenter_node;
 tree soft_monitorexit_node;
 tree soft_lookupinterfacemethod_node;
+tree soft_lookupjnimethod_node;
+tree soft_getjnienvnewframe_node;
+tree soft_jnipopsystemframe_node;
 tree soft_fmod_node;
 tree soft_exceptioninfo_call_node;
 tree soft_idiv_node;
@@ -752,12 +755,13 @@
 					build_function_type (ptr_type_node, t),
 					0, NOT_BUILT_IN, NULL_PTR);
   DECL_IS_MALLOC (alloc_object_node) = 1;
+
+  t = tree_cons (NULL_TREE, ptr_type_node, endlink);
   soft_initclass_node = builtin_function ("_Jv_InitClass",
 					  build_function_type (void_type_node,
 							       t),
 					  0, NOT_BUILT_IN,
 					  NULL_PTR);
-  t = tree_cons (NULL_TREE, ptr_type_node, endlink);
   throw_node[0] = builtin_function ("_Jv_Throw",
 				    build_function_type (ptr_type_node, t),
 				    0, NOT_BUILT_IN, NULL_PTR);
@@ -838,6 +842,24 @@
     = builtin_function ("_Jv_LookupInterfaceMethodIdx",
 			build_function_type (ptr_type_node, t),
 			0, NOT_BUILT_IN, NULL_PTR);
+
+  t = tree_cons (NULL_TREE, object_ptr_type_node,
+		 tree_cons (NULL_TREE, ptr_type_node,
+			    tree_cons (NULL_TREE, ptr_type_node, endlink)));
+  soft_lookupjnimethod_node
+    = builtin_function ("_Jv_LookupJNIMethod",
+			build_function_type (ptr_type_node, t),
+			0, NOT_BUILT_IN, NULL_PTR);
+  t = tree_cons (NULL_TREE, ptr_type_node, endlink);
+  soft_getjnienvnewframe_node
+    = builtin_function ("_Jv_GetJNIEnvNewFrame",
+			build_function_type (ptr_type_node, t),
+			0, NOT_BUILT_IN, NULL_PTR);
+  soft_jnipopsystemframe_node
+    = builtin_function ("_Jv_JNI_PopSystemFrame",
+			build_function_type (ptr_type_node, t),
+			0, NOT_BUILT_IN, NULL_PTR);
+
   t = tree_cons (NULL_TREE, double_type_node,
 		 tree_cons (NULL_TREE, double_type_node, endlink));
   soft_fmod_node
@@ -1715,7 +1737,7 @@
 
   if (METHOD_SYNCHRONIZED (fndecl) && ! flag_emit_class_files)
     {
-      /* Warp function body with a monitorenter plus monitorexit cleanup. */
+      /* Wrap function body with a monitorenter plus monitorexit cleanup. */
       tree enter, exit, lock;
       if (METHOD_STATIC (fndecl))
 	lock = build_class_ref (DECL_CONTEXT (fndecl));
Index: expr.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/expr.c,v
retrieving revision 1.70
diff -u -r1.70 expr.c
--- expr.c	2000/03/30 23:41:57	1.70
+++ expr.c	2000/04/20 19:04:23
@@ -1810,6 +1810,165 @@
     }
 }
 
+/* Create a stub which will be put into the vtable but which will call
+   a JNI function.  */
+
+tree
+build_jni_stub (method)
+     tree method;
+{
+  tree jnifunc, call, args, body, lookup_arg, method_sig, arg_types;
+  tree jni_func_type, tem;
+  tree env_var, res_var = NULL_TREE, block;
+  tree method_args, res_type;
+
+  tree klass = DECL_CONTEXT (method);
+  int from_class = ! CLASS_FROM_SOURCE_P (klass);
+  klass = build_class_ref (klass);
+
+  if (! METHOD_NATIVE (method) || ! flag_jni)
+    abort ();
+
+  DECL_ARTIFICIAL (method) = 1;
+  DECL_EXTERNAL (method) = 0;
+
+  env_var = build_decl (VAR_DECL, get_identifier ("env"), ptr_type_node);
+  if (TREE_TYPE (TREE_TYPE (method)) != void_type_node)
+    {
+      res_var = build_decl (VAR_DECL, get_identifier ("res"),
+			    TREE_TYPE (TREE_TYPE (method)));
+      TREE_CHAIN (env_var) = res_var;
+    }
+
+  /* One strange way that the front ends are different is that they
+     store arguments differently.  */
+  if (from_class)
+    method_args = DECL_ARGUMENTS (method);
+  else
+    method_args = BLOCK_EXPR_DECLS (DECL_FUNCTION_BODY (method));
+  block = build_block (env_var, NULL_TREE, NULL_TREE,
+		       method_args, NULL_TREE);
+  TREE_SIDE_EFFECTS (block) = 1;
+  /* When compiling from source we don't set the type of the block,
+     because that will prevent patch_return from ever being run.  */
+  if (from_class)
+    TREE_TYPE (block) = TREE_TYPE (TREE_TYPE (method));
+
+  /* Compute the local `env' by calling _Jv_GetJNIEnvNewFrame.  */
+  body = build (MODIFY_EXPR, ptr_type_node, env_var,
+		build (CALL_EXPR, ptr_type_node,
+		       build_address_of (soft_getjnienvnewframe_node),
+		       build_tree_list (NULL_TREE, klass),
+		       NULL_TREE));
+  CAN_COMPLETE_NORMALLY (body) = 1;
+
+  /* All the arguments to this method become arguments to the
+     underlying JNI function.  If we had to wrap object arguments in a
+     special way, we would do that here.  */
+  args = NULL_TREE;
+  for (tem = method_args; tem != NULL_TREE; tem = TREE_CHAIN (tem))
+    args = tree_cons (NULL_TREE, tem, args);
+  args = nreverse (args);
+  arg_types = TYPE_ARG_TYPES (TREE_TYPE (method));
+
+  /* For a static method the second argument is the class.  For a
+     non-static method the second argument is `this'; that is already
+     available in the argument list.  */
+  if (METHOD_STATIC (method))
+    {
+      args = tree_cons (NULL_TREE, klass, args);
+      arg_types = tree_cons (NULL_TREE, object_ptr_type_node, arg_types);
+    }
+
+  /* The JNIEnv structure is the first argument to the JNI function.  */
+  args = tree_cons (NULL_TREE, env_var, args);
+  arg_types = tree_cons (NULL_TREE, ptr_type_node, arg_types);
+
+  /* We call _Jv_LookupJNIMethod to find the actual underlying
+     function pointer.  _Jv_LookupJNIMethod will throw the appropriate
+     exception if this function is not found at runtime.  */
+  method_sig = build_java_signature (TREE_TYPE (method));
+  lookup_arg =
+    build_tree_list (NULL_TREE,
+		     build_utf8_ref (unmangle_classname
+				     (IDENTIFIER_POINTER (method_sig),
+				      IDENTIFIER_LENGTH (method_sig))));
+  tem = DECL_NAME (method);
+  lookup_arg
+    = tree_cons (NULL_TREE, klass,
+		 tree_cons (NULL_TREE, build_utf8_ref (tem), lookup_arg));
+
+  jni_func_type
+    = build_pointer_type (build_function_type (TREE_TYPE (TREE_TYPE (method)),
+					       arg_types));
+
+  jnifunc = build (CALL_EXPR, ptr_type_node,
+		   build_address_of (soft_lookupjnimethod_node),
+		   lookup_arg, NULL_TREE);
+
+  /* Now we make the actual JNI call via the resulting function
+     pointer.    */
+  call = build (CALL_EXPR, TREE_TYPE (TREE_TYPE (method)),
+		build1 (NOP_EXPR, jni_func_type, jnifunc),
+		args, NULL_TREE);
+
+  /* If the JNI call returned a result, capture it here.  If we had to
+     unwrap JNI object results, we would do that here.  */
+  if (res_var != NULL_TREE)
+    call = build (MODIFY_EXPR, TREE_TYPE (TREE_TYPE (method)),
+		  res_var, call);
+
+  TREE_SIDE_EFFECTS (call) = 1;
+  CAN_COMPLETE_NORMALLY (call) = 1;
+
+  body = build (COMPOUND_EXPR, void_type_node, body, call);
+  TREE_SIDE_EFFECTS (body) = 1;
+
+  /* Now free the environment we allocated.  */
+  call = build (CALL_EXPR, ptr_type_node,
+		build_address_of (soft_jnipopsystemframe_node),
+		build_tree_list (NULL_TREE, env_var),
+		NULL_TREE);
+  TREE_SIDE_EFFECTS (call) = 1;
+  CAN_COMPLETE_NORMALLY (call) = 1;
+  body = build (COMPOUND_EXPR, void_type_node, body, call);
+  TREE_SIDE_EFFECTS (body) = 1;
+
+  /* Finally, do the return.  When compiling from source we rely on
+     patch_return to patch the return value -- because DECL_RESULT is
+     not set at the time this function is called.  */
+  if (from_class)
+    {
+      res_type = void_type_node;
+      if (res_var != NULL_TREE)
+	{
+	  tree drt;
+	  if (! DECL_RESULT (method))
+	    abort ();
+	  /* Make sure we copy the result variable to the actual
+	     result.  We use the type of the DECL_RESULT because it
+	     might be different from the return type of the function:
+	     it might be promoted.  */
+	  drt = TREE_TYPE (DECL_RESULT (method));
+	  if (drt != TREE_TYPE (res_var))
+	    res_var = build1 (CONVERT_EXPR, drt, res_var);
+	  res_var = build (MODIFY_EXPR, drt, DECL_RESULT (method), res_var);
+	  TREE_SIDE_EFFECTS (res_var) = 1;
+	}
+    }
+  else
+    {
+      /* This is necessary to get patch_return to run.  */
+      res_type = NULL_TREE;
+    }
+  body = build (COMPOUND_EXPR, void_type_node, body,
+		build1 (RETURN_EXPR, res_type, res_var));
+  TREE_SIDE_EFFECTS (body) = 1;
+
+  BLOCK_EXPR_BODY (block) = body;
+  return block;
+}
+
 
 /* Expand an operation to extract from or store into a field.
    IS_STATIC is 1 iff the field is static.
Index: java-tree.def
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/java-tree.def,v
retrieving revision 1.13
diff -u -r1.13 java-tree.def
--- java-tree.def	2000/03/23 07:01:23	1.13
+++ java-tree.def	2000/04/20 19:04:23
@@ -63,7 +63,7 @@
 DEFTREECODE (SYNCHRONIZED_EXPR, "synchronized", 'e', 2)
 
 /* Throw statement.
-   Operand 0 is the throw expresion.  */
+   Operand 0 is the throw expression.  */
 DEFTREECODE (THROW_EXPR, "throw", '1', 1)
 
 /* Conditional operator.
@@ -93,3 +93,8 @@
    is used for context detection, so that special rules can be
    enforced. */
 DEFTREECODE (INSTANCE_INITIALIZERS_EXPR, "instance_initializers_expr", '1', 1)
+/*
+Local variables:
+mode:c
+End:
+*/
Index: java-tree.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/java-tree.h,v
retrieving revision 1.64
diff -u -r1.64 java-tree.h
--- java-tree.h	2000/04/05 23:57:18	1.64
+++ java-tree.h	2000/04/20 19:04:28
@@ -135,6 +135,11 @@
 
 extern int flag_emit_class_files;
 
+/* When non zero, assume all native functions are implemented with
+   JNI, not CNI.  */
+
+extern int flag_jni;
+
 /* When non zero, we emit xref strings. Values of the flag for xref
    backends are defined in xref.h.  */
 
@@ -295,6 +300,9 @@
 extern tree soft_monitorenter_node;
 extern tree soft_monitorexit_node;
 extern tree soft_lookupinterfacemethod_node;
+extern tree soft_lookupjnimethod_node;
+extern tree soft_getjnienvnewframe_node;
+extern tree soft_jnipopsystemframe_node;
 extern tree soft_fmod_node;
 extern tree soft_exceptioninfo_call_node;
 extern tree soft_idiv_node;
@@ -513,6 +521,9 @@
   tree inner_access;		/* The identifier of the access method
 				   used for invocation from inner classes */
   int nap;			/* Number of artificial parameters */
+
+  int native : 1;		/* Nonzero if this is a native
+				   method.  */
 };
 
 /* init_test_table hash table entry structure.  */
@@ -648,6 +659,7 @@
 extern tree build_class_init PARAMS ((tree, tree));
 extern tree build_invokevirtual PARAMS ((tree, tree));
 extern tree build_invokeinterface PARAMS ((tree, tree));
+extern tree build_jni_stub PARAMS ((tree));
 extern tree invoke_build_dtable PARAMS ((int, tree));
 extern tree build_field_ref PARAMS ((tree, tree, tree));
 extern void pushdecl_force_head PARAMS ((tree));
@@ -772,7 +784,7 @@
 #define METHOD_STATIC(DECL) DECL_LANG_FLAG_2 (DECL)
 #define METHOD_FINAL(DECL) DECL_LANG_FLAG_3 (DECL)
 #define METHOD_SYNCHRONIZED(DECL) DECL_LANG_FLAG_4 (DECL)
-#define METHOD_NATIVE(DECL) DECL_EXTERNAL(DECL)
+#define METHOD_NATIVE(DECL) (DECL_LANG_SPECIFIC(DECL)->native)
 #define METHOD_ABSTRACT(DECL) DECL_LANG_FLAG_5 (DECL)
 #define METHOD_TRANSIENT(DECL) DECL_LANG_FLAG_6 (DECL)
 
Index: jcf-parse.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/jcf-parse.c,v
retrieving revision 1.45
diff -u -r1.45 jcf-parse.c
--- jcf-parse.c	2000/03/28 08:33:45	1.45
+++ jcf-parse.c	2000/04/20 19:04:30
@@ -684,8 +684,23 @@
     {
       JCF *jcf = current_jcf;
 
-      if (METHOD_NATIVE (method) || METHOD_ABSTRACT (method))
+      if (METHOD_ABSTRACT (method))
 	continue;
+
+      if (METHOD_NATIVE (method))
+	{
+	  tree block;
+
+	  if (! flag_jni)
+	    continue;
+	  DECL_MAX_LOCALS (method)
+	    = list_length (TYPE_ARG_TYPES (TREE_TYPE (method)));
+	  start_java_method (method);
+	  give_name_to_locals (jcf);
+	  expand_expr_stmt (build_jni_stub (method));
+	  end_java_method ();
+	  continue;
+	}
 
       if (DECL_CODE_OFFSET (method) == 0)
 	{
Index: lang-options.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/lang-options.h,v
retrieving revision 1.13
diff -u -r1.13 lang-options.h
--- lang-options.h	2000/03/16 18:32:45	1.13
+++ lang-options.h	2000/04/20 19:04:30
@@ -35,6 +35,7 @@
   { "-femit-class-files", "Dump class files to <name>.class" },
   { "-fuse-boehm-gc", "Generate code for Boehm GC" },
   { "-fhash-synchronization", "Don't put synchronization structure in each object" },
+  { "-fjni", "Assume native functions are implemented using JNI" },
 #if ! USE_CPPLIB
   { "-MD", "Print dependencies to FILE.d" },
   { "-MMD", "Print dependencies to FILE.d" },
Index: lang-specs.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/lang-specs.h,v
retrieving revision 1.10
diff -u -r1.10 lang-specs.h
--- lang-specs.h	1999/09/22 20:30:30	1.10
+++ lang-specs.h	2000/04/20 19:04:30
@@ -1,5 +1,5 @@
 /* Definitions for specs for the GNU compiler for the Java(TM) language.
-   Copyright (C) 1996, 1998, 1999 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1998, 1999, 2000 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -36,6 +36,7 @@
 		    %{f*} %{+e*} %{aux-info*} %{Qn:-fno-ident}\
                     %{I*}\
 		    %{MD} %{MMD} %{M} %{MM}\
+                    %{fjni:%{femit-class-file:%e-fjni and -femit-class-file are incompatible}}\
 		    %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
 		    %{S:%W{o*}%{!o*:-o %b.s}}%{!S:-o %{|!pipe:%g.s}} |\n\
             %{!S:as %a %Y\
Index: lang.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/lang.c,v
retrieving revision 1.40
diff -u -r1.40 lang.c
--- lang.c	2000/03/16 18:32:45	1.40
+++ lang.c	2000/04/20 19:04:31
@@ -117,6 +117,10 @@
    object to its synchronization structure.  */
 int flag_hash_synchronization;
 
+/* When non zero, assume all native functions are implemented with
+   JNI, not CNI.  */
+int flag_jni = 0;
+
 /* From gcc/flags.h, and indicates if exceptions are turned on or not.  */
 
 extern int flag_new_exceptions;
@@ -135,7 +139,8 @@
   {"emit-class-files", &flag_emit_class_files, 1},
   {"use-divide-subroutine", &flag_use_divide_subroutine, 1},
   {"use-boehm-gc", &flag_use_boehm_gc, 1},
-  {"hash-synchronization", &flag_hash_synchronization, 1}
+  {"hash-synchronization", &flag_hash_synchronization, 1},
+  {"jni", &flag_jni, 1},
 };
 
 JCF *current_jcf;
Index: parse.y
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/parse.y,v
retrieving revision 1.151
diff -u -r1.151 parse.y
--- parse.y	2000/04/19 01:53:47	1.151
+++ parse.y	2000/04/20 19:05:46
@@ -4482,7 +4482,7 @@
       && TREE_TYPE (current_function_decl) 
       && TREE_TYPE (TREE_TYPE (current_function_decl)) == void_type_node)
     method_body = build1 (RETURN_EXPR, void_type_node, NULL);
-    
+
   BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (current_function_decl)) = method_body;
   maybe_absorb_scoping_blocks ();
   /* Exit function's body */
@@ -7267,10 +7267,19 @@
   /* First, do the ordinary methods. */
   for (decl = first_decl; decl; decl = TREE_CHAIN (decl))
     {
-      /* Skip abstract or native methods */
-      if (METHOD_ABSTRACT (decl) || METHOD_NATIVE (decl) 
+      /* Skip abstract or native methods -- but do handle native
+	 methods when generating JNI stubs.  */
+      if (METHOD_ABSTRACT (decl)
+	  || (! flag_jni && METHOD_NATIVE (decl))
 	  || DECL_CONSTRUCTOR_P (decl) || DECL_CLINIT_P (decl))
 	continue;
+
+      if (METHOD_NATIVE (decl))
+	{
+	  tree body = build_jni_stub (decl);
+	  BLOCK_EXPR_BODY (DECL_FUNCTION_BODY (decl)) = body;
+	}
+
       java_complete_expand_method (decl);
     }
 
@@ -7400,10 +7409,11 @@
 	{
 	  block_body = java_complete_tree (block_body);
 
-	  if (!flag_emit_xref)
+	  if (! flag_emit_xref && ! METHOD_NATIVE (mdecl))
 	    check_for_initialization (block_body);
 	  ctxp->explicit_constructor_p = 0;
 	}
+
       BLOCK_EXPR_BODY (fbody) = block_body;
 
       /* If we saw a return but couldn't evaluate it properly, we'll


More information about the Gcc-patches mailing list