[PATCH] java/12857: Fix class literals

Jeff Sturm jsturm@one-point.com
Sun Nov 16 22:16:00 GMT 2003


(This is an updated patch; original patch and discussion can be found in
http://gcc.gnu.org/ml/java-patches/2003-q4/msg00319.html.)

The test case in PR12857 amounts to

  interface I { static final Class C = I.class; }

which gcj compiles to invalid bytecode.  (Native compilation
succeeds because it escapes validation.)

On the surface this patch looks longer than necessary.  Note that it
also fixes four latent bugs I discovered while testing earlier iterations
of this patch:

a) The java parser is not reentrant, so load_class cannot be called during
the first pass.  However build_dot_class_method calls load_class and is
used within the first pass by build_assertion.  (This is related to the
mauve failures mentioned in my previous message.)

b) Inner classes are not placing their 'class$' member in the outermost
class, as jikes and javac do.

c) Primitive class literals are causing dead 'class$' methods when
compiling with gcj.  For example,

  static final Class I = int.class;

causes the generation of an unnecessary class$ method.

d) Under some circumstances, java_parser_context_save_global /
java_parser_context_restore_global do not correctly save and restore the
parser stack, e.g. within anonymous inner classes.

Tested by rebuilding jc1 and libjava, and running full libjava testsuite
including mauve and jacks on i686-pc-linux-gnu with no regressions (201
FAILs on mauve before and after).

OK for 3.4?

Jeff

2003-11-16  Jeff Sturm  <jsturm@one-point.com>

	Fix PR java/12857.

	decl.c (java_init_decl_processing): Don't initialize
	class_not_found_type_node, no_class_def_found_type_node.

	java-tree.h (JTI_CLASS_NOT_FOUND_TYPE_NODE,
	JTI_NO_CLASS_DEF_FOUND_TYPE_NODE): Remove from java_tree_index.
	(class_not_found_type_node, no_class_def_found_type_node):
	Don't define.

	parse.y (build_dot_class_method_invocation): Add this_class
	argument.  Qualify method invocations to a different class.
	(create_new_parser_context): Initialize saved_data_ctx to 0.
	(java_parser_context_save_global): Initialize saved_data_ctx to 1.
	(build_dot_class_method): Don't load classes.  Register
	incomplete types.
	(build_incomplete_class_ref): Special cases for interfaces
	and inner classes.  Move build_dot_class_method call to here...
	(patch_incomplete_class_ref): ...from here.  Pass current_class
	to build_dot_class_method_invocation.
	(build_assertion): Pass class_type to
	build_dot_class_method_invocation.
	(encapsulate_with_try_catch): Handle EXPR_WITH_FILE_LOCATION node.

Index: decl.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/decl.c,v
retrieving revision 1.169
diff -u -p -r1.169 decl.c
--- decl.c	24 Oct 2003 09:29:40 -0000	1.169
+++ decl.c	16 Nov 2003 15:18:24 -0000
@@ -552,10 +552,6 @@ java_init_decl_processing (void)
     lookup_class (get_identifier ("java.lang.RuntimeException"));
   error_exception_type_node =
     lookup_class (get_identifier ("java.lang.Error"));
-  class_not_found_type_node =
-    lookup_class (get_identifier ("java.lang.ClassNotFoundException"));
-  no_class_def_found_type_node =
-    lookup_class (get_identifier ("java.lang.NoClassDefFoundError"));

   rawdata_ptr_type_node
     = promote_type (lookup_class (get_identifier ("gnu.gcj.RawData")));
Index: java-tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/java-tree.h,v
retrieving revision 1.186
diff -u -p -r1.186 java-tree.h
--- java-tree.h	24 Oct 2003 09:29:40 -0000	1.186
+++ java-tree.h	16 Nov 2003 15:18:25 -0000
@@ -308,8 +308,6 @@ enum java_tree_index
   JTI_RUNTIME_EXCEPTION_TYPE_NODE,
   JTI_ERROR_EXCEPTION_TYPE_NODE,
   JTI_RAWDATA_PTR_TYPE_NODE,
-  JTI_CLASS_NOT_FOUND_TYPE_NODE,
-  JTI_NO_CLASS_DEF_FOUND_TYPE_NODE,

   JTI_BYTE_ARRAY_TYPE_NODE,
   JTI_SHORT_ARRAY_TYPE_NODE,
@@ -492,10 +490,6 @@ extern GTY(()) tree java_global_trees[JT
   java_global_trees[JTI_ERROR_EXCEPTION_TYPE_NODE]
 #define rawdata_ptr_type_node \
   java_global_trees[JTI_RAWDATA_PTR_TYPE_NODE]
-#define class_not_found_type_node \
-  java_global_trees[JTI_CLASS_NOT_FOUND_TYPE_NODE]
-#define no_class_def_found_type_node \
-  java_global_trees[JTI_NO_CLASS_DEF_FOUND_TYPE_NODE]

 #define byte_array_type_node \
   java_global_trees[JTI_BYTE_ARRAY_TYPE_NODE]
Index: parse.y
===================================================================
RCS file: /cvs/gcc/gcc/gcc/java/parse.y,v
retrieving revision 1.452
diff -u -p -r1.452 parse.y
--- parse.y	3 Nov 2003 03:58:50 -0000	1.452
+++ parse.y	16 Nov 2003 15:18:30 -0000
@@ -326,7 +326,7 @@ static void patch_anonymous_class (tree,
 static void add_inner_class_fields (tree, tree);

 static tree build_dot_class_method (tree);
-static tree build_dot_class_method_invocation (tree);
+static tree build_dot_class_method_invocation (tree, tree);
 static void create_new_parser_context (int);
 static tree maybe_build_class_init_for_field (tree, tree);

@@ -2660,7 +2660,9 @@ create_new_parser_context (int copy_from
   if (copy_from_previous)
     {
       memcpy (new, ctxp, sizeof (struct parser_ctxt));
-      new->saved_data_ctx = 1;
+      /* This flag, indicating the context saves global values,
+	 should only be set by java_parser_context_save_global.  */
+      new->saved_data_ctx = 0;
     }
   else
     memset (new, 0, sizeof (struct parser_ctxt));
@@ -2733,7 +2735,10 @@ java_parser_context_save_global (void)
   /* If this context already stores data, create a new one suitable
      for data storage. */
   else if (ctxp->saved_data)
-    create_new_parser_context (1);
+    {
+      create_new_parser_context (1);
+      ctxp->saved_data_ctx = 1;
+    }

   ctxp->lineno = input_line;
   ctxp->class_type = current_class;
@@ -8722,7 +8727,7 @@ build_dot_class_method (tree class)
 {
 #define BWF(S) build_wfl_node (get_identifier ((S)))
 #define MQN(X,Y) make_qualified_name ((X), (Y), 0)
-  tree args, tmp, saved_current_function_decl, mdecl;
+  tree args, tmp, saved_current_function_decl, mdecl, qual_name;
   tree stmt, throw_stmt;

   if (!get_message_wfl)
@@ -8739,15 +8744,17 @@ build_dot_class_method (tree class)
   /* Build the qualified name java.lang.Class.forName */
   tmp = MQN (MQN (MQN (BWF ("java"),
 		       BWF ("lang")), BWF ("Class")), BWF ("forName"));
-  load_class (class_not_found_type_node, 1);
-  load_class (no_class_def_found_type_node, 1);

   /* Create the "class$" function */
   mdecl = create_artificial_method (class, ACC_STATIC,
 				    build_pointer_type (class_type_node),
 				    classdollar_identifier_node, args);
-  DECL_FUNCTION_THROWS (mdecl) =
-    build_tree_list (NULL_TREE, no_class_def_found_type_node);
+  qual_name = MQN (MQN (BWF ("java"), BWF ("lang")),
+		   BWF ("NoClassDefFoundError"));
+  DECL_FUNCTION_THROWS (mdecl) = build_tree_list (NULL_TREE, qual_name);
+  register_incomplete_type (JDEP_EXCEPTION, qual_name, NULL_TREE, NULL_TREE);
+  JDEP_GET_PATCH (CLASSD_LAST (ctxp->classd_list)) =
+    &TREE_VALUE (DECL_FUNCTION_THROWS (mdecl));

   /* We start by building the try block. We need to build:
        return (java.lang.Class.forName (type)); */
@@ -8770,8 +8777,9 @@ build_dot_class_method (tree class)
   throw_stmt = build1 (THROW_EXPR, NULL_TREE, throw_stmt);

   /* Encapsulate STMT in a try block. The catch clause executes THROW_STMT */
-  stmt = encapsulate_with_try_catch (0, class_not_found_type_node,
-				     stmt, throw_stmt);
+  qual_name = MQN (MQN (BWF ("java"), BWF ("lang")),
+		   BWF ("ClassNotFoundException"));
+  stmt = encapsulate_with_try_catch (0, qual_name, stmt, throw_stmt);

   fix_method_argument_names (args, mdecl);
   layout_class_method (class, NULL_TREE, mdecl, NULL_TREE);
@@ -8786,9 +8794,10 @@ build_dot_class_method (tree class)
 }

 static tree
-build_dot_class_method_invocation (tree type)
+build_dot_class_method_invocation (tree this_class, tree type)
 {
-  tree sig_id, s;
+  tree dot_class_method = TYPE_DOT_CLASS (this_class);
+  tree sig_id, s, t;

   if (TYPE_ARRAY_P (type))
     sig_id = build_java_signature (type);
@@ -8801,8 +8810,14 @@ build_dot_class_method_invocation (tree

   s = build_string (IDENTIFIER_LENGTH (sig_id),
 		    IDENTIFIER_POINTER (sig_id));
-  return build_method_invocation (build_wfl_node (classdollar_identifier_node),
-				  build_tree_list (NULL_TREE, s));
+  t = build_method_invocation (build_wfl_node (DECL_NAME (dot_class_method)),
+			       build_tree_list (NULL_TREE, s));
+  if (DECL_CONTEXT (dot_class_method) != this_class)
+    {
+      tree class_name = DECL_NAME (TYPE_NAME (DECL_CONTEXT (dot_class_method)));
+      t = make_qualified_primary (build_wfl_node (class_name), t, 0);
+    }
+  return t;
 }

 /* This section of the code deals with constructor.  */
@@ -14091,6 +14106,44 @@ static tree
 build_incomplete_class_ref (int location, tree class_name)
 {
   tree node = build1 (CLASS_LITERAL, NULL_TREE, class_name);
+  tree class_decl = GET_CPC ();
+  tree this_class = TREE_TYPE (class_decl);
+
+  /* Generate the synthetic static method `class$'.  (Previously we
+     deferred this, causing different method tables to be emitted
+     for native code and bytecode.)  */
+  if (!TYPE_DOT_CLASS (this_class)
+      && !JPRIMITIVE_TYPE_P (class_name)
+      && !(TREE_CODE (class_name) == VOID_TYPE))
+    {
+      tree target_class;
+
+      if (CLASS_INTERFACE (TYPE_NAME (this_class)))
+	{
+	  /* For interfaces, adding a static 'class$' method directly
+	     is illegal.  So create an inner class to contain the new
+	     method.  Empirically this matches the behavior of javac.  */
+	  tree t = build_wfl_node (DECL_NAME (TYPE_NAME (object_type_node)));
+	  tree inner = create_anonymous_class (0, t);
+	  target_class = TREE_TYPE (inner);
+	  end_class_declaration (1);
+	}
+      else
+	{
+	  /* For inner classes, add a 'class$' method to their outermost
+	     context, creating it if necessary.  */
+	  while (INNER_CLASS_DECL_P (class_decl))
+	    class_decl = DECL_CONTEXT (class_decl);
+	  target_class = TREE_TYPE (class_decl);
+	}
+
+      if (TYPE_DOT_CLASS (target_class) == NULL_TREE)
+	build_dot_class_method (target_class);
+
+      if (this_class != target_class)
+      	TYPE_DOT_CLASS (this_class) = TYPE_DOT_CLASS (target_class);
+    }
+
   EXPR_WFL_LINECOL (node) = location;
   return node;
 }
@@ -14105,12 +14158,6 @@ patch_incomplete_class_ref (tree node)
   if (!(ref_type = resolve_type_during_patch (type)))
     return error_mark_node;

-  /* Generate the synthetic static method `class$'.  (Previously we
-     deferred this, causing different method tables to be emitted
-     for native code and bytecode.)  */
-  if (!TYPE_DOT_CLASS (current_class))
-      build_dot_class_method (current_class);
-
   /* If we're not emitting class files and we know ref_type is a
      compiled class, build a direct reference.  */
   if ((! flag_emit_class_files && is_compiled_class (ref_type))
@@ -14126,7 +14173,7 @@ patch_incomplete_class_ref (tree node)

   /* If we're emitting class files and we have to deal with non
      primitive types, we invoke the synthetic static method `class$'.  */
-  ref_type = build_dot_class_method_invocation (ref_type);
+  ref_type = build_dot_class_method_invocation (current_class, ref_type);
   return java_complete_tree (ref_type);
 }

@@ -15390,7 +15437,7 @@ build_assertion (int location, tree cond

       if (!TYPE_DOT_CLASS (class_type))
 	build_dot_class_method (class_type);
-      classdollar = build_dot_class_method_invocation (class_type);
+      classdollar = build_dot_class_method_invocation (class_type, class_type);

       /* Call CLASS.desiredAssertionStatus().  */
       id = build_wfl_node (get_identifier ("desiredAssertionStatus"));
@@ -15448,7 +15495,7 @@ build_assertion (int location, tree cond
    catches TYPE and executes CATCH_STMTS.  */

 static tree
-encapsulate_with_try_catch (int location, tree type, tree try_stmts,
+encapsulate_with_try_catch (int location, tree type_or_name, tree try_stmts,
 			    tree catch_stmts)
 {
   tree try_block, catch_clause_param, catch_block, catch;
@@ -15457,8 +15504,20 @@ encapsulate_with_try_catch (int location
   try_block = build_expr_block (try_stmts, NULL_TREE);

   /* Build a catch block: we need a catch clause parameter */
-  catch_clause_param = build_decl (VAR_DECL,
-				   wpv_id, build_pointer_type (type));
+  if (TREE_CODE (type_or_name) == EXPR_WITH_FILE_LOCATION)
+    {
+      tree catch_type = obtain_incomplete_type (type_or_name);
+      jdep *dep;
+      catch_clause_param = build_decl (VAR_DECL, wpv_id, catch_type);
+      register_incomplete_type (JDEP_VARIABLE, type_or_name,
+				catch_clause_param, catch_type);
+      dep = CLASSD_LAST (ctxp->classd_list);
+      JDEP_GET_PATCH (dep) = &TREE_TYPE (catch_clause_param);
+    }
+  else
+    catch_clause_param = build_decl (VAR_DECL, wpv_id,
+				     build_pointer_type (type_or_name));
+
   /* And a block */
   catch_block = build_expr_block (NULL_TREE, catch_clause_param);




More information about the Gcc-patches mailing list