This is the mail archive of the java-patches@gcc.gnu.org mailing list for the Java project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[gcjx] Patch: FYI: code generator additions


I'm checking this in on the gcjx branch.

This patch updates the code generator class to be better suited to use
from gcc.  In particular it lets each code generator tell the compiler
whether class (and by extension jar and zip) files are acceptable to
it.  The class reader uses this information to decide whether method
bodies should be kept around.  This patch also includes the code to
read an entire jar file should one be given on the command line.

Tom

--- gcc/gcc/java/ChangeLog
+++ gcc/gcc/java/ChangeLog
@@ -1,3 +1,9 @@
+2005-02-06  Tom Tromey  <tromey@redhat.com>
+
+	* treegen.hh (tree_code_generator::handles_class_p): New method.
+	(tree_code_generator::needs_class_method_bodies_p): Likewise.
+	(tree_code_generator::handles_resources_p): Likewise.
+

--- gcc/gcjx/ChangeLog
+++ gcc/gcjx/ChangeLog
@@ -1,3 +1,72 @@
+2005-02-06  Tom Tromey  <tromey@redhat.com>
+
+	* bytecode/generate.cc (write): Don't print note about
+	verification.
+	* model/bytecode.cc (resolve): Verify the method.
+	(verify): Print note about verification.
+	* model/bytecode.hh (class model_phony_block): New class.
+	* reader/classbytes.cc (apply): Removed assertion; pass 'emit' to
+	add_unit.
+	* factory.cc (find_derived_file): New method.
+	(find_derived_file): Use it.
+	(find_derived_file): New method.
+	(find_derived_file): Use it.
+	(load_class): Removed old comment.
+	(load_source_file): Handle class and jar files.
+	(jar_class_factory): Clear 'archive' on failure.
+	(read_all): New method.
+	* factory.hh (class_factory::find_derived_file): New abstract
+	method.
+	(directory_class_factory::find_derived_file): Declare.
+	(jar_class_factory::find_derived_file): Likewise.
+	(jar_class_factory::read_all): Declare.
+	* factory.cc (jar_file_p): Moved to util.cc.
+	* util.cc (jar_file_p): New function.
+	(class_file_p): Likewise.
+	(java_file_p): Likewise.
+	* util.hh (jar_file_p): Declare.
+	(class_file_p): Likewise.
+	(java_file_p): Likewise.
+	* bytecode/classreader.cc (parse_code): Use
+	keep_class_method_body_p.
+	(parse_exception_table): Handle case where method bodies aren't
+	used.
+	* header/cni.hh (cni_code_generator::handles_class_p): New
+	method.
+	* header/jni.hh (jni_code_generator::handles_class_p): New
+	method.
+	* codegen.hh (code_generator::handles_class_p): New method.
+	(code_generator::needs_class_method_bodies_p): Likewise.
+	(code_generator::handles_resources_p): Likewise.
+	(code_generator::compile_resource): Likewise.
+	* compiler.cc (compiler): Initialize new fields.  Don't mention
+	pre_semantic_analysis.
+	(find_class): Added assertion.
+	(set_class_factory): Likewise.
+	(semantic_analysis): Update 'state'.  Don't use
+	pre_semantic_analysis.
+	(generate_code): Update 'state'.
+	(load_source_file): Likewise.
+	(add_code_generator): Added assertion.
+	(set_source_1_5): Likewise.
+	(set_source_1_4): Likewise.
+	(set_source_1_3): Likewise.
+	(set_target_1_5): Likewise.
+	(set_target_1_4): Likewise.
+	(set_target_1_3): Likewise.
+	(set_wall): Likewise.
+	(add_unit): Use state, not pre_semantic_analysis.
+	(compile_resource): New method.
+	* compiler.hh (compiler::compiler_state): New enum.
+	(compiler::state): New field.
+	(compiler::pre_semantic_analysis): Removed.
+	(compiler::can_accept_classes): New field.
+	(compiler::need_class_method_bodies): Likewise.
+	(compiler::keep_class_method_body_p): New method.
+	(compiler::can_accept_resources): New field.
+	(compiler::handles_resources_p): New method.
+	(compiler::compile_resource): Declare.
+

--- gcc/gcc/java/treegen.hh
+++ gcc/gcc/java/treegen.hh
@@ -1,6 +1,6 @@
 // Code generator for GENERIC.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -38,6 +38,21 @@
   ~tree_code_generator ();
 
   void generate (model_class *);
+
+  bool handles_class_p () const
+  {
+    return true;
+  }
+
+  bool needs_class_method_bodies_p () const
+  {
+    return true;
+  }
+
+  bool handles_resources_p () const
+  {
+    return true;
+  }
 };
 
 #endif // GCC_TREE_TREEGEN_HH
--- gcc/gcjx/PROJECTS
+++ gcc/gcjx/PROJECTS
@@ -82,11 +82,9 @@
 
 The class reader doesn't handle debug information.
 
-we don't handle some 1.5 features when reading .class files
+we don't handle some features when reading .class files
 - SourceFile
 - line number and local variable tables
-- enum, class, and attribute values for attributes
-- parameter annotations
 - enclosing method attribute
 
 We want a mode where we don't actually read other classes,
@@ -118,8 +116,7 @@
 
 Bytecode generation mostly works, but it occasionally generates
 incorrect bytecode.  Running the verifier over the output is helpful
-here.  (Having a mode for "gcjx --verify-only" would be very useful
-right now!)
+here.
 
 JNI header generation is probably ok
 
@@ -131,8 +128,8 @@
 should look at LLVM
 probably easy to write a back end for it
 if we compiled .class as well perhaps we could wind up with a jit
+if we do that, ideally we would share the code generator with libgcj
 
-
 Some code for tree generation exists, but it is incomplete.  Some
 random thoughts:
 - find a nice way to factor the ABI code
@@ -148,10 +145,6 @@
 class more easily.  Right now we parse and analyze everything before
 generating any code.
 
-the driver should be able to read entire .jar files
-
-we don't handle property files at all but we need to for GCC
-
 we don't handle -extdirs yet
 
 
@@ -194,7 +187,10 @@
   resolve_classes
   lock superclass when resolving parts of it
 
+it turns out that parsing can affect global state by creating packages
+we need a lock somewhere to deal with this
 
+
 ================================================================
 == Source Code Road Map
 
--- gcc/gcjx/TODO
+++ gcc/gcjx/TODO
@@ -137,8 +137,6 @@
 a new inner class and put it there.  should we?  we could just
 inline the code in <clinit>, couldn't we?
 
-the code generator api is ugly
-
 it would be nice to do coverage analysis of the code
 this is easy, just rebuild it correctly and run classpath
 and a couple other things through it
--- gcc/gcjx/bytecode/classreader.cc
+++ gcc/gcjx/bytecode/classreader.cc
@@ -348,16 +348,25 @@
 {
   uint16 tab_len = read_u2 ();
 
-  model_bytecode_block::exception *excs
-    = new model_bytecode_block::exception[tab_len];
-  for (int i = 0; i < tab_len; ++i)
+  if (current_block)
     {
-      excs[i].handler = read_u2 ();
-      excs[i].start = read_u2 ();
-      excs[i].end = read_u2 ();
-      excs[i].type = read_u2 ();
+      model_bytecode_block::exception *excs
+	= new model_bytecode_block::exception[tab_len];
+      for (int i = 0; i < tab_len; ++i)
+	{
+	  excs[i].handler = read_u2 ();
+	  excs[i].start = read_u2 ();
+	  excs[i].end = read_u2 ();
+	  excs[i].type = read_u2 ();
+	}
+      current_block->set_exceptions (tab_len, excs);
     }
-  current_block->set_exceptions (tab_len, excs);
+  else
+    {
+      // Not keeping method bodies.
+      for (int i = 0; i < tab_len * 4; ++i)
+	read_u2 ();
+    }
 }
 
 void
@@ -369,7 +378,8 @@
   uint16 max_locals = read_u2 ();
   uint32 length = read_u4 ();
 
-  if (1 /* FIXME: generating code for this class */)
+  bool keeping = global->get_compiler ()->keep_class_method_body_p ();
+  if (keeping)
     {
       current_block = new model_bytecode_block (where);
       current_block->set_max_stack (max_stack);
@@ -386,6 +396,9 @@
   parse_attributes (ATTR_LINENUMBERTABLE | ATTR_LOCALVARIABLETABLE
 		    | ATTR_LOCALVARIABLETYPETABLE);
 
+  if (! keeping)
+    current_method->set_body (new model_phony_block (where));
+
   current_block = NULL;
 }
 
--- gcc/gcjx/bytecode/generate.cc
+++ gcc/gcjx/bytecode/generate.cc
@@ -268,12 +268,6 @@
   // If requested, verify what we just wrote.
   if (global->get_compiler ()->target_verify ())
     {
-      if (global->get_compiler ()->verbose ())
-	std::cout << "[verifying method " << method->get_name ()
-		  << " (from class "
-		  << method->get_declaring_class ()->get_fully_qualified_name ()
-		  << ")]" << std::endl;
-
       ref_bytecode_block block
 	= new model_bytecode_block (method->get_location ());
       block->set_max_stack (max_stack);
--- gcc/gcjx/codegen.hh
+++ gcc/gcjx/codegen.hh
@@ -1,6 +1,6 @@
 // Code generator.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -49,6 +49,35 @@
   virtual ~code_generator ()
   {
   }
+
+  /// Return true if this code generator can generate code for a class
+  /// read from a .class file.  The default is to return 'false'.
+  virtual bool handles_class_p () const
+  {
+    return false;
+  }
+
+  /// Return true if this code generator requires method bodies to be
+  /// kept for classes read from .class files.  This is only
+  /// meaningful if the handles_class_p() returns true.  The default
+  /// is to return 'false'.
+  virtual bool needs_class_method_bodies_p () const
+  {
+    return false;
+  }
+
+  /// Returns 'true' if the code generator will properly handle
+  /// resource files read from a .jar or .zip.
+  virtual bool handles_resources_p () const
+  {
+    return false;
+  }
+
+  /// Compile the resource file given its name and contents.
+  virtual void compile_resource (const std::string &, byte_buffer *)
+  {
+    abort ();
+  }
 };
 
 #endif // GCJX_CODEGEN_HH
--- gcc/gcjx/compiler.cc
+++ gcc/gcjx/compiler.cc
@@ -22,6 +22,8 @@
 #include "typedefs.hh"
 #include "codegen.hh"
 #include "bytecode/bytegen.hh"
+#include "reader/reader.hh"
+#include "buffer.hh"
 
 compiler::compiler (const std::string &name)
   : work_monitor (),
@@ -35,11 +37,14 @@
     primordial_package (new model_primordial_package ()),
     unnamed_package (new model_unnamed_package ()),
     generating_bytecode (false),
-    pre_semantic_analysis (true),
     ok (true),
     encoding ("UTF-8"),
     tab_width (8),
     name (name),
+    state (SETTING_OPTIONS),
+    can_accept_classes (true),
+    need_class_method_bodies (true),
+    can_accept_resources (false),
     // Currently we use the same mutex for all caches.
     // This is not as efficient as it could be.
     // FIXME?
@@ -162,7 +167,7 @@
 {
   concurrence::exclusive_mutex::lock_sentinel sync (mt_monitor);
   units.push_back (unit);
-  if (emit_code && (pre_semantic_analysis || generating_bytecode))
+  if (emit_code && (state < ANALYZING_CLASSES || generating_bytecode))
     code_generation_units.push_back (unit);
 }
 
@@ -170,6 +175,7 @@
 compiler::set_class_factory (classpath_class_factory *fac)
 {
   assert (! factory);
+  assert (state == SETTING_OPTIONS);
   factory = fac;
 }
 
@@ -182,6 +188,7 @@
 model_class *
 compiler::find_class (const std::list<std::string> &name)
 {
+  assert (state > PARSING_FILES);
   Iname *obj = find_name (name);
   if (obj == NULL)
     {
@@ -220,14 +227,69 @@
 void
 compiler::load_source_file (const std::string &name)
 {
-  work_item item (name);
-  add_job (item);
+  assert (state < ANALYZING_CLASSES);
+
+  if (state == SETTING_OPTIONS)
+    {
+      // See if we can accept class files, and, if so, whether reading
+      // a class this way should keep a method body around.
+      for (std::list<code_generator *>::const_iterator i = back_ends.begin ();
+	   i != back_ends.end ();
+	   ++i)
+	{
+	  // Note that the default is to accept class files and to
+	  // keep class method bodies.  This ensures that '-o none'
+	  // will verify the bytecode handed to the compiler.
+	  if (! (*i)->handles_class_p ())
+	    can_accept_classes = false;
+	  if (! (*i)->needs_class_method_bodies_p ())
+	    need_class_method_bodies = false;
+	  if ((*i)->handles_resources_p ())
+	    can_accept_resources = true;
+	}
+      // There's no point accepting resources if we can't also accept
+      // class files.
+      if (! can_accept_classes)
+	can_accept_resources = false;
+    }
+  state = PARSING_FILES;
+
+  if (! can_accept_classes && (jar_file_p (name) || class_file_p (name)))
+    {
+      std::cerr << get_name () << ": while reading "
+		<< name << ": back end can't handle jar, zip, or class file"
+		<< std::endl;
+      set_failed ();
+    }
+  else
+    {
+      work_item item (name);
+      add_job (item);
+    }
 }
 
+void
+compiler::compile_resource (const std::string &name, reader *contents)
+{
+  byte_buffer *bytes = contents->read_all ();
+  // FIXME: catch exceptions?
+  for (std::list<code_generator *>::const_iterator i = back_ends.begin ();
+       i != back_ends.end ();
+       ++i)
+    (*i)->compile_resource (name, bytes);
+  delete bytes;
+  delete contents;
+}
+
 bool
 compiler::semantic_analysis ()
 {
-  pre_semantic_analysis = false;
+  assert (state == PARSING_FILES);
+  state = ANALYZING_CLASSES;
+  // After we're finished parsing things from the command line, there
+  // is no longer a reason for the class reader to keep method bodies
+  // attached.
+  need_class_method_bodies = false;
 
   for (std::list<ref_unit>::const_iterator i = code_generation_units.begin ();
        ok && i != code_generation_units.end ();
@@ -244,6 +306,9 @@
 {
   assert (ok);
 
+  assert (state == ANALYZING_CLASSES);
+  state = GENERATING_CODE;
+
   // Perhaps we aren't generating code at all.  This is perfectly ok.
   if (back_ends.empty ())
     return true;
@@ -277,6 +342,7 @@
 void
 compiler::add_code_generator (code_generator *cgen)
 {
+  assert (state == SETTING_OPTIONS);
   back_ends.push_back (cgen);
   // We only want the dependency compilation behavior if we are *only*
   // generating bytecode.
@@ -289,6 +355,7 @@
 void
 compiler::set_source_1_5 ()
 {
+  assert (state == SETTING_OPTIONS);
   feature_assert = true;
   feature_enum = true;
   feature_static_import = true;
@@ -302,6 +369,7 @@
 void
 compiler::set_source_1_4 ()
 {
+  assert (state == SETTING_OPTIONS);
   feature_assert = true;
   feature_enum = false;
   feature_static_import = false;
@@ -315,6 +383,7 @@
 void
 compiler::set_source_1_3 ()
 {
+  assert (state == SETTING_OPTIONS);
   feature_assert = false;
   feature_enum = false;
   feature_static_import = false;
@@ -328,6 +397,7 @@
 void
 compiler::set_target_1_5 ()
 {
+  assert (state == SETTING_OPTIONS);
   target_15 = true;
   target_14 = false;
   target_assert = true;
@@ -336,6 +406,7 @@
 void
 compiler::set_target_1_4 ()
 {
+  assert (state == SETTING_OPTIONS);
   target_15 = false;
   target_14 = true;
   target_assert = true;
@@ -344,6 +415,7 @@
 void
 compiler::set_target_1_3 ()
 {
+  assert (state == SETTING_OPTIONS);
   target_15 = false;
   target_14 = false;
   target_assert = false;
@@ -352,6 +424,7 @@
 void
 compiler::set_wall (warning_state val)
 {
+  assert (state == SETTING_OPTIONS);
   set_warning (WARN_CANONICAL_MODIFIER_ORDER, val);
   set_warning (WARN_DEPRECATED, val);
   set_warning (WARN_JAVA_LANG_IMPORT, val);
@@ -499,7 +572,8 @@
 {
   if (! factory->load_source_file (filename))
     {
-      std::cerr << get_name () << ": couldn't find file " << filename << std::endl;
+      std::cerr << get_name () << ": couldn't find file "
+		<< filename << std::endl;
       set_failed ();
     }
 }
--- gcc/gcjx/compiler.hh
+++ gcc/gcjx/compiler.hh
@@ -24,6 +24,7 @@
 
 class classpath_class_factory;
 class code_generator;
+class reader;
 
 /// A compiler flag is just a simple self-initializing bool wrapper
 /// that also exposes a functional form, so we can more safely use it
@@ -74,6 +75,17 @@
     DIE
   } job_type;
 
+  // The state of the compiler object.  This is used to enforce
+  // requirements of the compiler API, namely that some operations
+  // must occur in a specific order.
+  typedef enum
+  {
+    SETTING_OPTIONS,
+    PARSING_FILES,
+    ANALYZING_CLASSES,
+    GENERATING_CODE
+  } compiler_state;
+
   // A single job.  Note that jobs and the work list are only used
   // when multi-threaded; otherwise all actions are taken immediately.
   struct work_item
@@ -169,10 +181,6 @@
   // list.  FIXME: should be on a "-dependency" option or so.
   bool generating_bytecode;
 
-  // True before we've started semantic analysis.  In particular this
-  // means we're still parsing files from the command line.
-  bool pre_semantic_analysis;
-
   // True if everything is going ok.  False if we've encountered an
   // error.
   bool ok;
@@ -203,6 +211,19 @@
   // What we call ourselves in error messages.
   std::string name;
 
+  // Our current state.
+  compiler_state state;
+
+  // True if this compiler can accept '.class' files.
+  bool can_accept_classes;
+
+  // True if method bodies from .class files should be kept.  This is
+  // only meaningful when CAN_ACCEPT_CLASSES is true.
+  bool need_class_method_bodies;
+
+  // True if this compiler will accept resource files.
+  bool can_accept_resources;
+
   void do_load_source_file (const std::string &);
   void do_generate_code (model_unit *);
   void do_analyze_unit (model_unit *);
@@ -434,6 +455,22 @@
 
   /// FIXME: this probably shouldn't be public...
   void pause_workers ();
+
+  /// Return true if a class being read should preserve its method
+  /// body.
+  bool keep_class_method_body_p () const
+  {
+    return need_class_method_bodies;
+  }
+
+  /// Return true if this compilation can handle a resource file.
+  bool handles_resources_p () const
+  {
+    return can_accept_resources;
+  }
+
+  /// Compile a resource file given its file name and its contents.
+  void compile_resource (const std::string &, reader *);
 };
 
 #endif // GCJX_COMPILER_HH
--- gcc/gcjx/factory.cc
+++ gcc/gcjx/factory.cc
@@ -1,6 +1,6 @@
 // Loading classes.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -52,20 +52,6 @@
 #include "reader/zereader.hh"
 #include "reader/source.hh"
 
-static inline bool
-jar_file_p (const std::string &name)
-{
-  int name_len = name.length ();
-
-  // FIXME:
-  //   1. Fix this for case-insensitive file systems.
-  //   2. Ensure that name actually represents an existing file...
-  //   3. ...and not a folder, like "libjava.jar" in the GCJ testsuite.
-  return (name_len > 4
-          && (name.compare (name_len - 4, 4, ".jar") == 0
-              || name.compare (name_len - 4, 4, ".zip") == 0));
-}
-
 #ifdef FIXME_USE_SOLIB
 
 static inline bool
@@ -108,6 +94,8 @@
   return source_only;
 }
 
+
+
 directory_class_factory::directory_class_factory (const std::string &d)
   : class_factory (),
     directory (d)
@@ -138,25 +126,25 @@
 }
 
 class_instance_creator *
-directory_class_factory::find_derived_file (const std::list<std::string> &name)
+directory_class_factory::find_derived_file (const std::string &name)
 {
-  assert (! is_source_only());
-  
-  // FIXME should cache directory contents, gcj does this.
-  std::string file = (directory
-		      + FILE_SEPARATOR + join (name, FILE_SEPARATOR)
-		      + ".class");
+  assert (! is_source_only ());
+  std::string file = directory + FILE_SEPARATOR + name;
   int fd = open (file.c_str (), O_RDONLY | O_BINARY);
   if (fd < 0)
     return NULL;
+  return new class_byte_creator (file, new fd_reader (fd));
+}
 
-  return new class_byte_creator (file, new fd_reader (fd));
+class_instance_creator *
+directory_class_factory::find_derived_file (const std::list<std::string> &name)
+{
+  return find_derived_file (join (name, FILE_SEPARATOR) + ".class");
 }
 
 
 
 /// Converts a 16-bit little-endian value to a native 16-bit quantity.
-
 static inline uint16
 make_u2 (const uint8 bytes[])
 {
@@ -165,7 +153,6 @@
 }
 
 /// Converts a 32-bit little-endian value to a native 32-bit quantity.
-
 static inline uint32
 make_u4 (const uint8 bytes[])
 {
@@ -177,7 +164,6 @@
 
 /// Returns the zip_entry_reader for the given filename, if found
 /// within this archive, else NULL.
-
 zip_entry_reader *
 jar_class_factory::find_zip_entry (const std::string &name)
 {
@@ -192,7 +178,6 @@
 
 /// Reads in the current ZIP archive and populates the cached zip
 /// entries map.  Throws an io_error on failure.
-
 void
 jar_class_factory::read_zip_archive ()
 {
@@ -228,9 +213,12 @@
     }
 }
 
-static const int LREC_SIZE = 26;  // Length of local file header.
-static const int CREC_SIZE = 42;  // Length of central directory header.
-static const int ECREC_SIZE = 18; // Length of end-of-central-directory record.
+// Length of local file header.
+static const int LREC_SIZE = 26;
+// Length of central directory header.
+static const int CREC_SIZE = 42;
+// Length of end-of-central-directory record.
+static const int ECREC_SIZE = 18;
 
 void
 jar_class_factory::read_zip_file_header ()
@@ -339,7 +327,8 @@
 }
       
 void
-jar_class_factory::process_zip_entries (const uint8 *central_directory, uint16 count)
+jar_class_factory::process_zip_entries (const uint8 *central_directory,
+					uint16 count)
 {
   const uint8 *dir_ptr = central_directory;
   for (int i = 0; i < count; i++)
@@ -348,6 +337,42 @@
     }
 }
 
+void
+jar_class_factory::read_all ()
+{
+  // If the was a failure, we already issued the error message, so we
+  // don't need to do anything here.
+  if (! archive)
+    return;
+
+  compiler *comp = global->get_compiler ();
+  bool handle_resource = comp->handles_resources_p ();
+  for (std::map<std::string, zip_entry_reader *>::const_iterator i
+	 = zip_entries.begin ();
+       i != zip_entries.end ();
+       ++i)
+    {
+      const std::string &name ((*i).first);
+      zip_entry_reader *reader = (*i).second;
+
+      class_instance_creator *source = NULL;
+      if (class_file_p (name))
+	source = new class_byte_creator (name, reader);
+      else if (java_file_p (name))
+	source = new source_file_creator (name, reader);
+      else
+	{
+	  // Must be a resource file.
+	  if (handle_resource)
+	    comp->compile_resource (name, reader);
+	  continue;
+	}
+
+      // Compile the class or java code.
+      source->apply (true);
+    }
+}
+
 jar_class_factory::jar_class_factory (const std::string &f)
   : class_factory (),
     file (f),
@@ -361,6 +386,7 @@
     {
       std::cerr << ex;
       delete archive;
+      archive = NULL;
     }
 }
 
@@ -390,18 +416,19 @@
 }
 
 class_instance_creator *
-jar_class_factory::find_derived_file (const std::list<std::string> &name)
+jar_class_factory::find_derived_file (const std::string &name)
 {
   assert (! is_source_only());
+  zip_entry_reader *ze_reader = find_zip_entry (name);
+  if (ze_reader == NULL)
+    return NULL;
+  return new class_byte_creator (name, ze_reader);
+}
 
-  std::string class_file = join (name, FILE_SEPARATOR) + ".class";
-
-  zip_entry_reader *ze_reader = find_zip_entry (class_file);
-
-  if (ze_reader != NULL)
-    return new class_byte_creator (class_file, ze_reader);
-  else
-    return NULL;
+class_instance_creator *
+jar_class_factory::find_derived_file (const std::list<std::string> &name)
+{
+  return find_derived_file (join (name, FILE_SEPARATOR) + ".class");
 }
 
 jar_class_factory::~jar_class_factory ()
@@ -497,10 +524,6 @@
   else
     return;
 
-  // fixme this API is a bit lame.
-  // we need to decide who issues the error message when not found
-  // us, or compiler::find_class() ?
-
   source->apply (emit_code);
   delete source;
 }
@@ -508,11 +531,33 @@
 bool
 classpath_class_factory::load_source_file (const std::string &name)
 {
+  if (jar_file_p (name))
+    {
+      // Read the entire jar file, parsing each .class or .java file,
+      // and handing other files to the compiler as resources if
+      // desired.
+      jar_class_factory fac (name);
+      fac.read_all ();
+      // We always return true since the factory itself would have
+      // done something on error.  FIXME: not the best API here...
+      return true;
+    }
+
+  bool is_derived = class_file_p (name);
   class_instance_creator *source = NULL;
   for (std::list<class_factory *>::const_iterator i = factories.begin ();
        ! source && i != factories.end ();
        ++i)
-    source = (*i)->find_source_file (name);
+    {
+      // FIXME: the API is a bit wrong here, maybe.
+      // Instead we could return byte sources and wrap them
+      // appropriately here.
+      // But... would this properly handle the .so case?
+      if (is_derived)
+	source = (*i)->find_derived_file (name);
+      else
+	source = (*i)->find_source_file (name);
+    }
 
   if (! source)
     return false;
--- gcc/gcjx/factory.hh
+++ gcc/gcjx/factory.hh
@@ -1,6 +1,6 @@
 // Classes that know how to load Java classes.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -58,6 +58,10 @@
   virtual class_instance_creator *find_source_file (const std::list<std::string> &)
     = 0;
 
+  /// Return a new class_instance_creator representing a .class file,
+  /// given the name of the .class file.
+  virtual class_instance_creator *find_derived_file (const std::string &) = 0;
+
   /// Return a new class_instance_creator representing a .class file
   /// (or other derived class source) for a given fully-qualified
   /// class name.  (The name is a bit incorrect as it isn't necessary
@@ -82,15 +86,12 @@
 
   class_instance_creator *find_source_file (const std::string &);
   class_instance_creator *find_source_file (const std::list<std::string> &);
+  class_instance_creator *find_derived_file (const std::string &);
   class_instance_creator *find_derived_file (const std::list<std::string> &);
 };
 
 class jar_class_factory : public class_factory
 {
-  friend class_factory *class_factory::get_class_factory (const std::string &, bool);
-
-private:
-
   // The JAR or ZIP file.
   std::string file;
 
@@ -100,10 +101,6 @@
   // The cached ZIP directory entries.
   std::map<std::string, zip_entry_reader *> zip_entries;
 
-  jar_class_factory (const std::string &);
-
-  ~jar_class_factory ();
-
   void open_zip_file ();
   void read_zip_archive ();
   void read_zip_file_header ();
@@ -117,9 +114,20 @@
 
   zip_entry_reader *find_zip_entry (const std::string &);
 
+  friend class_factory *class_factory::get_class_factory (const std::string &, bool);
+
+public:
+
+  jar_class_factory (const std::string &);
+
+  ~jar_class_factory ();
+
   class_instance_creator *find_source_file (const std::string &);
   class_instance_creator *find_source_file (const std::list<std::string> &);
+  class_instance_creator *find_derived_file (const std::string &);
   class_instance_creator *find_derived_file (const std::list<std::string> &);
+
+  void read_all ();
 };
 
 // Load classes from a gcj-compiled .so.
--- gcc/gcjx/header/cni.hh
+++ gcc/gcjx/header/cni.hh
@@ -1,6 +1,6 @@
 // Write a CNI header.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -57,6 +57,11 @@
   
   /// This is called to generate code for a class and write it.
   void generate (model_class *);	
+
+  bool handles_class_p () const
+  {
+    return true;
+  }
 };
 
 #endif // GCJX_HEADER_CNI_HH
--- gcc/gcjx/header/jni.hh
+++ gcc/gcjx/header/jni.hh
@@ -51,6 +51,11 @@
 
   /// This is called to generate code for a class and write it.
   void generate (model_class *);
+
+  bool handles_class_p () const
+  {
+    return true;
+  }
 };
 
 #endif // GCJX_HEADER_JNI_HH
--- gcc/gcjx/model/bytecode.cc
+++ gcc/gcjx/model/bytecode.cc
@@ -1,6 +1,6 @@
 // Represent a block of bytecode.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -23,8 +23,14 @@
 #include "verify.h"
 
 void
-model_bytecode_block::resolve (resolution_scope *)
+model_bytecode_block::resolve (resolution_scope *scope)
 {
+  // If we did't want to verify the method, we would have created a
+  // model_phony_block instead.
+  model_method *m = scope->get_current_method ();
+  model_unit_class *unit
+    = assert_cast<model_unit_class *> (scope->get_compilation_unit ());
+  verify (m, unit);
 }
 
 void
@@ -43,6 +49,13 @@
 	    = assert_cast<model_unit_class *> (klass->get_compilation_unit ());
 	}
 
+      if (global->get_compiler ()->verbose ())
+	std::cout << "[verifying method "
+		  << decl->get_fully_qualified_name ()
+		  << "."
+		  << container->get_name ()
+		  << "]" << std::endl;
+
       vfy_method m (container, this, &scope, unit);
       _Jv_VerifyMethod (&m);
       // FIXME: this API isn't ideal if we verify multiple times and
--- gcc/gcjx/model/bytecode.hh
+++ gcc/gcjx/model/bytecode.hh
@@ -1,6 +1,6 @@
 // Represent a block of bytecode.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -178,4 +178,26 @@
   void verify (model_method *, model_unit_class * = NULL);
 };
 
+/// This is a phony block which is used when reading a .class file
+/// for which we do not need to keep method bodies around.
+class model_phony_block : public model_block
+{
+public:
+
+  model_phony_block (const location &w)
+    : model_block (w)
+  {
+  }
+
+  void compute_normal_completion (normal_completion_state &)
+  {
+    normal_completion = false;
+  }
+
+  void resolve (resolution_scope *)
+  {
+    // Nothing.
+  }
+};
+
 #endif // GCJX_MODEL_BYTECODE_HH
--- gcc/gcjx/reader/classbytes.cc
+++ gcc/gcjx/reader/classbytes.cc
@@ -1,6 +1,6 @@
 // Create a class from a byte source.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -34,17 +34,16 @@
   if (global->get_compiler ()->verbose ())
     std::cout << "  + " << file << std::endl;
 
-  assert (! emit);
   try
     {
       class_reader parser (the_reader->read_all ());
       ref_unit unit = parser.parse (file);
-      global->get_compiler ()->add_unit (unit);
+      global->get_compiler ()->add_unit (unit, emit);
     }
   catch (class_file_error &cfe)
     {
       // fixme file name
-      std::cerr << "while reading: " << cfe << std::endl;
+      std::cerr << global->get_compiler ()->get_name () << ": "
+		<< "while reading: " << cfe << std::endl;
     }
-  // fixme or propagate the exception on up?
 }
--- gcc/gcjx/util.cc
+++ gcc/gcjx/util.cc
@@ -1,6 +1,6 @@
 // Utility functions.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -175,3 +175,39 @@
         results.push_back (fname);
     }
 }
+
+bool
+jar_file_p (const std::string &name)
+{
+  int name_len = name.length ();
+
+  // FIXME:
+  //   1. Fix this for case-insensitive file systems.
+  //   2. Ensure that name actually represents an existing file...
+  //   3. ...and not a folder, like "libjava.jar" in the GCJ testsuite.
+  return (name_len > 4
+          && (name.compare (name_len - 4, 4, ".jar") == 0
+              || name.compare (name_len - 4, 4, ".zip") == 0));
+}
+
+bool
+class_file_p (const std::string &name)
+{
+  int name_len = name.length ();
+
+  // FIXME:
+  //   1. Fix this for case-insensitive file systems.
+  //   2. Ensure that name actually represents an existing file
+  return name_len > 6 && name.compare (name_len - 6, 6, ".class") == 0;
+}
+
+bool
+java_file_p (const std::string &name)
+{
+  int name_len = name.length ();
+
+  // FIXME:
+  //   1. Fix this for case-insensitive file systems.
+  //   2. Ensure that name actually represents an existing file6
+  return name_len > 5 && name.compare (name_len - 5, 5, ".java") == 0;
+}
--- gcc/gcjx/util.hh
+++ gcc/gcjx/util.hh
@@ -1,6 +1,6 @@
 // Utility functions.
 
-// Copyright (C) 2004 Free Software Foundation, Inc.
+// Copyright (C) 2004, 2005 Free Software Foundation, Inc.
 //
 // This file is part of GCC.
 //
@@ -112,4 +112,14 @@
 /// On failure throws an io_error.
 void read_at_file (const char *filename, std::list<std::string> &results);
 
+/// Return true if the string names a jar or zip file, false
+/// otherwise.
+bool jar_file_p (const std::string &);
+
+/// Return true if the string names a class file.
+bool class_file_p (const std::string &);
+
+/// Return true if the string names a java source file.
+bool java_file_p (const std::string &);
+
 #endif // GCJX_UTIL_HH


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]