summaryrefslogtreecommitdiff
path: root/gcc/java/expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/java/expr.c')
-rw-r--r--gcc/java/expr.c159
1 files changed, 159 insertions, 0 deletions
diff --git a/gcc/java/expr.c b/gcc/java/expr.c
index 5666502605e..37358e53a3a 100644
--- a/gcc/java/expr.c
+++ b/gcc/java/expr.c
@@ -1844,6 +1844,165 @@ expand_invoke (opcode, method_ref_index, nargs)
}
}
+/* 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.