Patch: null `this' -vs- final methods

Tom Tromey tromey@cygnus.com
Thu Apr 27 17:19:00 GMT 2000


I'm checking in this patch, which Alex approved offline.  It is the
final version of my patch which changes gcj to correctly handle
invocation of final methods with a null `this'.

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.

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/24 23:05:24
@@ -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 +848,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/24 23:05:32
@@ -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, 
@@ -1801,6 +1818,23 @@
   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
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/24 23:05:36
@@ -290,6 +298,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.154
diff -u -r1.154 parse.y
--- parse.y	2000/04/21 23:03:19	1.154
+++ parse.y	2000/04/24 23:07:04
@@ -9675,10 +9675,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)
@@ -9706,6 +9707,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),
@@ -9731,7 +9748,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
@@ -9761,6 +9778,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), dconst0)
+			     : build1 (CONVERT_EXPR, TREE_TYPE (patch),
+				       integer_zero_node))),
+		     patch);
+      TREE_SIDE_EFFECTS (patch) = 1;
+    }
+
   return patch;
 }
 
@@ -9774,17 +9811,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