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(&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 @@
   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