Patch: null `this' check, take 3
Tom Tromey
tromey@cygnus.com
Tue Apr 11 16:46:00 GMT 2000
Here's the latest variant of my patch to check for a null `this' on
final method invocations.
This patch works by generating an explicit null check for the method
invocation's `this' pointer. I have one concern about it -- I use a
save_expr to wrap the `this' expression, to avoid fully computing this
expression twice. However, I'm not certain I'm doing this the right
way.
I rebuilt the toolchain with this and it worked fine.
The test suite now passes the `Final' test.
Alex, if you plan on trying this out, note that there is a
corresponding runtime change you'll need.
Ok to commit?
2000-04-05 Tom Tromey <tromey@cygnus.com>
Fix for PR gcj/2:
* expr.c (expand_invoke): Generate check to see if object pointer
is null in nonvirtual invocation case.
* java-tree.h (soft_nullpointer_node): Declare.
* decl.c (soft_nullpointer_node): New global.
(init_decl_processing): Initialize soft_nullpointer_node.
* parse.y (invocation_mode): Return INVOKE_NONVIRTUAL for `final'
or `private' methods.
(patch_invoke): Handle INVOKE_NONVIRTUAL case.
Tom
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/11 23:31:05
@@ -375,6 +375,7 @@
tree soft_anewarray_node;
tree soft_multianewarray_node;
tree soft_badarrayindex_node;
+tree soft_nullpointer_node;
tree throw_node [2];
tree soft_checkarraystore_node;
tree soft_monitorenter_node;
@@ -812,6 +813,15 @@
effects. */
TREE_THIS_VOLATILE (soft_badarrayindex_node) = 1;
TREE_SIDE_EFFECTS (soft_badarrayindex_node) = 1;
+
+ soft_nullpointer_node
+ = builtin_function ("_Jv_ThrowNullPointerException",
+ build_function_type (void_type_node, endlink),
+ 0, NOT_BUILT_IN, NULL_PTR);
+ /* Mark soft_nullpointer_node as a `noreturn' function with side
+ effects. */
+ TREE_THIS_VOLATILE (soft_nullpointer_node) = 1;
+ TREE_SIDE_EFFECTS (soft_nullpointer_node) = 1;
t = tree_cons (NULL_TREE, class_ptr_type,
tree_cons (NULL_TREE, object_ptr_type_node, endlink));
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/11 23:31:12
@@ -1719,6 +1719,7 @@
(current_jcf, COMPONENT_REF_CLASS_INDEX(¤t_jcf->cpool, method_ref_index));
const char *self_name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (self_type)));
tree call, func, method, arg_list, method_type;
+ tree cond = NULL_TREE;
if (! CLASS_LOADED_P (self_type))
{
@@ -1781,13 +1782,29 @@
flush_quick_stack ();
func = NULL_TREE;
- if (opcode == OPCODE_invokestatic || opcode == OPCODE_invokespecial
- || (opcode == OPCODE_invokevirtual
- && (METHOD_PRIVATE (method)
- || METHOD_FINAL (method)
- || CLASS_FINAL (TYPE_NAME (self_type)))))
+ if (opcode == OPCODE_invokestatic)
func = build_known_method_ref (method, method_type, self_type,
method_signature, arg_list);
+ else if (opcode == OPCODE_invokespecial
+ || (opcode == OPCODE_invokevirtual
+ && (METHOD_PRIVATE (method)
+ || METHOD_FINAL (method)
+ || CLASS_FINAL (TYPE_NAME (self_type)))))
+ {
+ /* If the object for the method call is null, we throw an
+ exception. We don't do this if the object is the current
+ method's `this'. In other cases we just rely on an
+ optimization pass to eliminate redundant checks. FIXME:
+ Unfortunately there doesn't seem to be a way to determine
+ what the current method is right now. */
+ /* We use a SAVE_EXPR here to make sure we only evaluate
+ the new `self' expression once. */
+ tree save_arg = save_expr (TREE_VALUE (arg_list));
+ TREE_VALUE (arg_list) = save_arg;
+ cond = build (EQ_EXPR, boolean_type_node, save_arg, null_pointer_node);
+ func = build_known_method_ref (method, method_type, self_type,
+ method_signature, arg_list);
+ }
else
{
tree dtable = invoke_build_dtable (opcode == OPCODE_invokeinterface,
@@ -1800,6 +1817,23 @@
func = build1 (NOP_EXPR, build_pointer_type (method_type), func);
call = build (CALL_EXPR, TREE_TYPE (method_type), func, arg_list, NULL_TREE);
TREE_SIDE_EFFECTS (call) = 1;
+
+ if (cond != NULL_TREE)
+ {
+ /* We have to make the `then' branch a compound expression to
+ make the types turn out right. This seems bizarre. */
+ call = build (COND_EXPR, TREE_TYPE (call), cond,
+ build (COMPOUND_EXPR, TREE_TYPE (call),
+ build (CALL_EXPR, void_type_node,
+ build_address_of (soft_nullpointer_node),
+ NULL_TREE, NULL_TREE),
+ (FLOAT_TYPE_P (TREE_TYPE (call))
+ ? build_real (TREE_TYPE (call), 0)
+ : build1 (CONVERT_EXPR, TREE_TYPE (call),
+ integer_zero_node))),
+ call);
+ TREE_SIDE_EFFECTS (call) = 1;
+ }
if (TREE_CODE (TREE_TYPE (method_type)) == VOID_TYPE)
expand_expr_stmt (call);
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/11 23:31:17
@@ -290,6 +290,7 @@
extern tree soft_anewarray_node;
extern tree soft_multianewarray_node;
extern tree soft_badarrayindex_node;
+extern tree soft_nullpointer_node;
extern tree throw_node[];
extern tree soft_checkarraystore_node;
extern tree soft_monitorenter_node;
Index: parse.y
===================================================================
RCS file: /cvs/gcc/egcs/gcc/java/parse.y,v
retrieving revision 1.150
diff -u -r1.150 parse.y
--- parse.y 2000/04/06 05:29:30 1.150
+++ parse.y 2000/04/11 23:32:39
@@ -9636,10 +9636,11 @@
{
tree dtable, func;
tree original_call, t, ta;
+ tree cond = NULL_TREE;
/* Last step for args: convert build-in types. If we're dealing with
a new TYPE() type call, the first argument to the constructor
- isn't found in the incomming argument list, but delivered by
+ isn't found in the incoming argument list, but delivered by
`new' */
t = TYPE_ARG_TYPES (TREE_TYPE (method));
if (TREE_CODE (patch) == NEW_CLASS_EXPR)
@@ -9667,6 +9668,22 @@
func = build_invokevirtual (dtable, method);
break;
+ case INVOKE_NONVIRTUAL:
+ /* If the object for the method call is null, we throw an
+ exception. We don't do this if the object is the current
+ method's `this'. In other cases we just rely on an
+ optimization pass to eliminate redundant checks. */
+ if (TREE_VALUE (args) != current_this)
+ {
+ /* We use a SAVE_EXPR here to make sure we only evaluate
+ the new `self' expression once. */
+ tree save_arg = save_expr (TREE_VALUE (args));
+ TREE_VALUE (args) = save_arg;
+ cond = build (EQ_EXPR, boolean_type_node, save_arg,
+ null_pointer_node);
+ }
+ /* Fall through. */
+
case INVOKE_SUPER:
case INVOKE_STATIC:
func = build_known_method_ref (method, TREE_TYPE (method),
@@ -9692,7 +9709,7 @@
TREE_OPERAND (patch, 1) = args;
original_call = patch;
- /* We're processing a `new TYPE ()' form. New is called an its
+ /* We're processing a `new TYPE ()' form. New is called and its
returned value is the first argument to the constructor. We build
a COMPOUND_EXPR and use saved expression so that the overall NEW
expression value is a pointer to a newly created and initialized
@@ -9722,6 +9739,26 @@
TREE_SET_CODE (original_call, CALL_EXPR);
patch = build (COMPOUND_EXPR, TREE_TYPE (new), patch, saved_new);
}
+
+ /* If COND is set, then we are building a check to see if the object
+ is NULL. */
+ if (cond != NULL_TREE)
+ {
+ /* We have to make the `then' branch a compound expression to
+ make the types turn out right. This seems bizarre. */
+ patch = build (COND_EXPR, TREE_TYPE (patch), cond,
+ build (COMPOUND_EXPR, TREE_TYPE (patch),
+ build (CALL_EXPR, void_type_node,
+ build_address_of (soft_nullpointer_node),
+ NULL_TREE, NULL_TREE),
+ (FLOAT_TYPE_P (TREE_TYPE (patch))
+ ? build_real (TREE_TYPE (patch), 0)
+ : build1 (CONVERT_EXPR, TREE_TYPE (patch),
+ integer_zero_node))),
+ patch);
+ TREE_SIDE_EFFECTS (patch) = 1;
+ }
+
return patch;
}
@@ -9735,17 +9772,22 @@
if (super)
return INVOKE_SUPER;
- if (access & ACC_STATIC || access & ACC_FINAL || access & ACC_PRIVATE)
+ if (access & ACC_STATIC)
return INVOKE_STATIC;
- if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method))))
+ /* We have to look for a constructor before we handle nonvirtual
+ calls; otherwise the constructor will look nonvirtual. */
+ if (DECL_CONSTRUCTOR_P (method))
return INVOKE_STATIC;
-
+
+ if (access & ACC_FINAL || access & ACC_PRIVATE)
+ return INVOKE_NONVIRTUAL;
+
+ if (CLASS_FINAL (TYPE_NAME (DECL_CONTEXT (method))))
+ return INVOKE_NONVIRTUAL;
+
if (CLASS_INTERFACE (TYPE_NAME (DECL_CONTEXT (method))))
return INVOKE_INTERFACE;
-
- if (DECL_CONSTRUCTOR_P (method))
- return INVOKE_STATIC;
return INVOKE_VIRTUAL;
}
More information about the Gcc-patches
mailing list