Commit e815887f by Tom Tromey Committed by Tom Tromey

re GNATS gcj/2 (Method call on null instance should throw NullPointerException)

	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.

From-SVN: r33495
parent db6a1df0
2000-04-27 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.
Wed Apr 26 14:29:33 2000 Alexandre Petit-Bianco <apbianco@cygnus.com>
* decl.c (complete_start_java_method): Don't call _Jv_InitClass
......
......@@ -375,6 +375,7 @@ tree soft_newarray_node;
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;
......@@ -813,6 +814,15 @@ init_decl_processing ()
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));
soft_checkcast_node
......
......@@ -1719,6 +1719,7 @@ expand_invoke (opcode, method_ref_index, nargs)
(current_jcf, COMPONENT_REF_CLASS_INDEX(&current_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 @@ expand_invoke (opcode, method_ref_index, nargs)
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,
......@@ -1801,6 +1818,23 @@ expand_invoke (opcode, method_ref_index, nargs)
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), dconst0)
: 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);
else
......
......@@ -290,6 +290,7 @@ extern tree soft_newarray_node;
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;
......
......@@ -9662,10 +9662,11 @@ patch_invoke (patch, method, args)
{
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)
......@@ -9693,6 +9694,22 @@ patch_invoke (patch, method, args)
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),
......@@ -9718,7 +9735,7 @@ patch_invoke (patch, method, args)
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
......@@ -9748,6 +9765,26 @@ patch_invoke (patch, method, args)
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), dconst0)
: build1 (CONVERT_EXPR, TREE_TYPE (patch),
integer_zero_node))),
patch);
TREE_SIDE_EFFECTS (patch) = 1;
}
return patch;
}
......@@ -9761,17 +9798,22 @@ invocation_mode (method, super)
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;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment