natVMClassLoader.cc (getSystemClassLoaderInternal): Updated for name change.
[gcc.git] / libjava / java / lang / VMClassLoader.java
index 026f6d8d1fb0cd309600fa9ab74665a3e10e14c0..c48fc709991367de972f9bfffc90323596abcda1 100644 (file)
-/* Copyright (C) 1999  Cygnus Solutions
+/* VMClassLoader.java -- Reference implementation of native interface
+   required by ClassLoader
+   Copyright (C) 1998, 2001, 2002, 2003, 2004, 2005 Free Software Foundation
 
-   This file is part of libgcj.
+This file is part of GNU Classpath.
 
-This software is copyrighted work licensed under the terms of the
-Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
-details.  */
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
 
-/* Author: Kresten Krab Thorup <krab@gnu.org>  */
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
 
 package java.lang;
 
-import java.io.*;
+import gnu.java.util.EmptyEnumeration;
+import java.lang.reflect.Constructor;
+import java.io.File;
+import java.io.IOException;
 import java.net.URL;
-import gnu.gcj.util.path.SearchPath;
+import java.net.URLClassLoader;
+import java.security.AllPermission;
+import java.security.Permission;
+import java.security.Permissions;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.StringTokenizer;
+import gnu.gcj.runtime.BootClassLoader;
 
-final class VMClassLoader extends java.lang.ClassLoader
+/**
+ * java.lang.VMClassLoader is a package-private helper for VMs to implement
+ * on behalf of java.lang.ClassLoader.
+ *
+ * @author John Keiser
+ * @author Mark Wielaard <mark@klomp.org>
+ * @author Eric Blake <ebb9@email.byu.edu>
+ */
+final class VMClassLoader
 {
-  private SearchPath   path;
-  private final String path_seperator;
-  private final String file_seperator;
-  private final char   file_seperator_char;
-    
-  private VMClassLoader () {   
-    path_seperator = System.getProperty ("path.separator", ":");
-    file_seperator = System.getProperty ("file.separator", "/");
-
-    file_seperator_char = file_seperator.charAt (0);
-
-    String class_path = System.getProperty ("java.class.path", ".");
-    path = new SearchPath (class_path);
-  }
+  // Protection Domain definitions 
+  // FIXME: should there be a special protection domain used for native code?
+  
+  // The permission required to check what a classes protection domain is.
+  static final Permission protectionDomainPermission
+    = new RuntimePermission("getProtectionDomain");
+  // The protection domain returned if we cannot determine it. 
+  static ProtectionDomain unknownProtectionDomain;
 
-  protected Class loadClass(String name,
-                           boolean resolve) 
-    throws java.lang.ClassNotFoundException, java.lang.LinkageError
+  static
   {
-    return loadClassInternal (name, resolve, false);
+    Permissions permissions = new Permissions();
+    permissions.add(new AllPermission());
+    unknownProtectionDomain = new ProtectionDomain(null, permissions);  
   }
-       
-    /** I'm a little in doubt here, if this method is 
-       actually supposed to throw a LinkageError, or not.  
-       The spec, 20.14.3, is a little unclear.  It says:
-
-       `` The general contract of loadClass is that, given the name
-       of a class, it either returns the Class object for the class
-       or throws a ClassNotFoundException.''
-
-       However, by making LinkageError a checked exception, 
-       i.e., mention it directly in the throws clause,
-       we'll force caller to consider that case as well.
-    **/
-
-  protected Class loadClassInternal(String name,
-                                   boolean resolve, 
-                                   boolean fromBootLoader) 
-    throws java.lang.ClassNotFoundException, java.lang.LinkageError
-  {
-    Class clazz;
 
-    /** TODO: call _Jv_VerifyClassName **/
-    if (   (name.indexOf ('/') != -1)
-          || (name.charAt (0) == '.')
-          || (name.indexOf (file_seperator) != -1)
-          || (name.indexOf ("..") != -1))
-       {
-           throw new IllegalArgumentException (name);
-       }
+  static final HashMap definedPackages = new HashMap();
 
-    // already loaded?
-    clazz = findLoadedClass (name);
+  // This is a helper for handling java.endorsed.dirs.  It is null
+  // until we've initialized the system, at which point it is created.
+  static BootClassLoader bootLoader;
 
-    // we need access to the boot class loader here
-    if (clazz == null && !fromBootLoader)
-      clazz = findBootClass (name);
+  // This keeps track of shared libraries we've already tried to load.
+  private static HashSet tried_libraries;
 
-    if (clazz == null)
+  // Holds one of the LIB_* constants; used to determine how shared
+  // library loads are done.
+  private static int lib_control;
+
+  private static final int LIB_FULL = 0;
+  private static final int LIB_CACHE = 1;
+  private static final int LIB_NEVER = 2;
+
+  /**
+   * Helper to define a class using a string of bytes. This assumes that
+   * the security checks have already been performed, if necessary.
+   *
+   * <strong>For backward compatibility, this just ignores the protection
+   * domain; that is the wrong behavior, and you should directly implement
+   * this method natively if you can.</strong>
+   *
+   * @param name the name to give the class, or null if unknown
+   * @param data the data representing the classfile, in classfile format
+   * @param offset the offset into the data where the classfile starts
+   * @param len the length of the classfile data in the array
+   * @param pd the protection domain
+   * @return the class that was defined
+   * @throws ClassFormatError if data is not in proper classfile format
+   */
+  static final native Class defineClass(ClassLoader cl, String name,
+                                       byte[] data, int offset, int len,
+                                       ProtectionDomain pd)
+    throws ClassFormatError;
+
+  /**
+   * Helper to resolve all references to other classes from this class.
+   *
+   * @param c the class to resolve
+   */
+  static final native void resolveClass(Class clazz);
+
+  static final void transformException(Class clazz, Throwable x)
+  {
+    LinkageError e;
+    if (x instanceof LinkageError)
+      e = (LinkageError) x;
+    else if (x instanceof ClassNotFoundException)
       {
-       StringBuffer res = new StringBuffer ();
+       e = new NoClassDefFoundError("while resolving class: "
+                                    + clazz.getName());
+       e.initCause (x);
+      }
+    else
+      {
+       e = new LinkageError ("unexpected exception during linking: "
+                             + clazz.getName());
+       e.initCause (x);
+      }
+    throw e;
+  }
+
+  /**
+   * Helper to load a class from the bootstrap class loader.
+   *
+   * @param name the class name to load
+   * @param resolve whether to resolve it
+   * @return the class, loaded by the bootstrap classloader or null
+   * if the class wasn't found. Returning null is equivalent to throwing
+   * a ClassNotFoundException (but a possible performance optimization).
+   */
+  static final native Class loadClass(String name, boolean resolve)
+    throws ClassNotFoundException;
 
-       // here we do actually replace .'s with /'s because
-       // we're going to find something in the file system.
-       res.append (name.replace ('.', file_seperator_char));
-       res.append (".class");
-               
-       byte[] data = getResourceAsBytes (res.toString ());
+  /**
+   * Helper to load a resource from the bootstrap class loader.
+   *
+   * In libgcj, this does nothing, as the default system loader knows
+   * how to find resources that have been linked in.
+   *
+   * @param name the resource to find
+   * @return the URL to the resource
+   */
+  static URL getResource(String name)
+  {
+    if (bootLoader != null)
+      return bootLoader.bootGetResource(name);
+    return null;
+  }
 
-       if (data == null)
-         throw new ClassNotFoundException (name);
+  /**
+   * Helper to get a list of resources from the bootstrap class loader.
+   *
+   * In libgcj, this does nothing, as the default system loader knows
+   * how to find resources that have been linked in.
+   *
+   * @param name the resource to find
+   * @return an enumeration of resources
+   * @throws IOException if one occurs
+   */
+  static Enumeration getResources(String name) throws IOException
+  {
+    if (bootLoader != null)
+      return bootLoader.bootGetResources(name);
+    return EmptyEnumeration.getInstance();
+  }
 
-       clazz = defineClass (name, data, 0, data.length);
-           
+  /**
+   * Helper to get a package from the bootstrap class loader.  The default
+   * implementation of returning null may be adequate, or you may decide
+   * that this needs some native help.
+   *
+   * @param name the name to find
+   * @return the named package, if it exists
+   */
+  static synchronized Package getPackage(String name)
+  {
+    return (Package) definedPackages.get(name);
+  }
+
+  /**
+   * Helper to get all packages from the bootstrap class loader.  The default
+   * implementation of returning an empty array may be adequate, or you may
+   * decide that this needs some native help.
+   *
+   * @return all named packages, if any exist
+   */
+  static synchronized Package[] getPackages()
+  {
+    Package[] packages = new Package[definedPackages.size()];
+    return (Package[]) definedPackages.values().toArray(packages);
+  }
+
+  // Define a package for something loaded natively.
+  static synchronized void definePackageForNative(String className)
+  {
+    int lastDot = className.lastIndexOf('.');
+    if (lastDot != -1)
+      {
+       String packageName = className.substring(0, lastDot);
+       if (getPackage(packageName) == null)
+         {
+           // FIXME: this assumes we're defining the core, which
+           // isn't necessarily so.  We could detect this and set up
+           // appropriately.  We could also look at a manifest file
+           // compiled into the .so.
+           Package p = new Package(packageName,
+                                   "Java Platform API Specification",
+                                   "GNU", "1.4", "gcj", "GNU",
+                                   null, // FIXME: gcj version.
+                                   null);
+           definedPackages.put(packageName, p);
+         }
       }
+  }
 
-    if (resolve && clazz != null)
-      resolveClass (clazz);
+  /**
+   * Helper for java.lang.Integer, Byte, etc to get the TYPE class
+   * at initialization time. The type code is one of the chars that
+   * represents the primitive type as in JNI.
+   *
+   * <ul>
+   * <li>'Z' - boolean</li>
+   * <li>'B' - byte</li>
+   * <li>'C' - char</li>
+   * <li>'D' - double</li>
+   * <li>'F' - float</li>
+   * <li>'I' - int</li>
+   * <li>'J' - long</li>
+   * <li>'S' - short</li>
+   * <li>'V' - void</li>
+   * </ul>
+   *
+   * @param type the primitive type
+   * @return a "bogus" class representing the primitive type
+   */
+  static final native Class getPrimitiveClass(char type);
 
-    return clazz;
+  /**
+   * The system default for assertion status. This is used for all system
+   * classes (those with a null ClassLoader), as well as the initial value for
+   * every ClassLoader's default assertion status.
+   *
+   * XXX - Not implemented yet; this requires native help.
+   *
+   * @return the system-wide default assertion status
+   */
+  static final boolean defaultAssertionStatus()
+  {
+    return true;
   }
 
-  private native Class findBootClass (String name);
+  /**
+   * The system default for package assertion status. This is used for all
+   * ClassLoader's packageAssertionStatus defaults. It must be a map of
+   * package names to Boolean.TRUE or Boolean.FALSE, with the unnamed package
+   * represented as a null key.
+   *
+   * XXX - Not implemented yet; this requires native help.
+   *
+   * @return a (read-only) map for the default packageAssertionStatus
+   */
+  static final Map packageAssertionStatus()
+  {
+    return new HashMap();
+  }
 
-  public InputStream getResourceAsStream(String name) 
+  /**
+   * The system default for class assertion status. This is used for all
+   * ClassLoader's classAssertionStatus defaults. It must be a map of
+   * class names to Boolean.TRUE or Boolean.FALSE
+   *
+   * XXX - Not implemented yet; this requires native help.
+   *
+   * @return a (read-only) map for the default classAssertionStatus
+   */
+  static final Map classAssertionStatus()
   {
-    return path.getStream (name);
+    return new HashMap();
   }
 
-  public URL getResource(String name) 
+  static native ClassLoader getSystemClassLoaderInternal();
+
+  static native void initBootLoader(String libdir);
+
+  static void initialize(String libdir)
   {
-    return path.getURL (name);
+    initBootLoader(libdir);
+
+    String p
+      = System.getProperty ("gnu.gcj.runtime.VMClassLoader.library_control",
+                           "");
+    if ("never".equals(p))
+      lib_control = LIB_NEVER;
+    else if ("cache".equals(p))
+      lib_control = LIB_CACHE;
+    else if ("full".equals(p))
+      lib_control = LIB_FULL;
+    else
+      lib_control = LIB_CACHE;
+
+    tried_libraries = new HashSet();
   }
 
-  public byte[] getResourceAsBytes(String name) 
+  /**
+   * Possibly load a .so and search it for classes.
+   */
+  static native Class nativeFindClass(String name);
+
+  static ClassLoader getSystemClassLoader()
   {
-    return path.getBytes (name);
+    // This method is called as the initialization of systemClassLoader,
+    // so if there is a null value, this is the first call and we must check
+    // for java.system.class.loader.
+    String loader = System.getProperty("java.system.class.loader");
+    ClassLoader default_sys = getSystemClassLoaderInternal();
+    if (loader != null)
+      {
+       try
+         {
+           Class load_class = Class.forName(loader, true, default_sys);
+           Constructor c
+             = load_class.getConstructor(new Class[] { ClassLoader.class });
+           default_sys
+             = (ClassLoader) c.newInstance(new Object[] { default_sys });
+         }
+       catch (Exception ex)
+         {
+           throw new Error("Failed to load requested system classloader "
+                              + loader, ex);
+         }
+      }
+
+    return default_sys;
   }
 }