123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370 |
- /* VMClassLoader.java -- Reference implementation of compiler interface
- Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation
- This file is part of GNU Classpath.
- 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.
- 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., 51 Franklin Street, Fifth Floor, Boston, MA
- 02110-1301 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.File;
- import java.io.FileOutputStream;
- import java.io.InputStreamReader;
- import java.security.MessageDigest;
- import java.security.ProtectionDomain;
- import java.security.NoSuchAlgorithmException;
- import java.util.WeakHashMap;
- import java.util.HashSet;
- import java.util.Enumeration;
- import java.util.StringTokenizer;
- import java.util.Vector;
- import gnu.gcj.runtime.SharedLibHelper;
- import gnu.gcj.runtime.PersistentByteMap;
- import gnu.java.security.hash.MD5;
- /**
- * This class is just a per-VM reflection of java.lang.Compiler.
- * All methods are defined identically.
- */
- final class VMCompiler
- {
- // True if we want to use gcj-jit.
- public static boolean useCompiler = true;
- // True if we're able to use gcj-jit.
- public static final boolean canUseCompiler;
- // Compiler to use.
- public static String gcjJitCompiler;
- // Compiler options.
- public static String gcjJitCompilerOptions;
- // Temporary directory to use.
- public static String gcjJitTmpdir;
- public static boolean precompiles()
- {
- return (canUseCompiler & useCompiler);
- }
- // This maps a ClassLoader to a set of SharedLibHelper objects that
- // it has used. We do things this way to ensure that a
- // SharedLibHelper is collected if and only if the ClassLoader is.
- private static WeakHashMap sharedHelperMap = new WeakHashMap();
- private static Vector precompiledMapFiles;
- // We create a single MD5 engine and then clone it whenever we want
- // a new one.
- // We don't use
- //
- // md5Digest = MessageDigest.getInstance("MD5");
- //
- // here because that loads a great deal of security provider code as
- // interpreted bytecode -- before we're able to use this class to
- // load precompiled classes.
- private static final MD5 md5Digest
- = new gnu.java.security.hash.MD5();
- static
- {
- gcjJitCompiler = System.getProperty("gnu.gcj.jit.compiler");
- if (gcjJitCompiler == null)
- canUseCompiler = false;
- else
- {
- gcjJitCompilerOptions = System.getProperty("gnu.gcj.jit.options",
- "-g");
- gcjJitTmpdir = System.getProperty("gnu.gcj.jit.cachedir");
- // Note that we *don't* choose java.io.tmpdir as a default --
- // that would allow easy attacks against the VM.
- if (gcjJitTmpdir == null)
- canUseCompiler = false;
- else
- canUseCompiler = true;
- }
- String prop = System.getProperty ("gnu.gcj.precompiled.db.path");
- if (prop != null)
- {
- precompiledMapFiles = new Vector();
- // Add the
- StringTokenizer st
- = new StringTokenizer (prop,
- System.getProperty ("path.separator", ":"));
- {
- while (st.hasMoreElements ())
- {
- String e = st.nextToken ();
- try
- {
- PersistentByteMap map
- = new PersistentByteMap
- (e, PersistentByteMap.AccessMode.READ_ONLY);
- precompiledMapFiles.add(map);
- }
- catch (IllegalArgumentException _)
- {
- // Not a map file
- }
- catch (java.io.IOException _)
- {
- }
- catch (java.nio.BufferUnderflowException _)
- {
- // Invalid map file.
- }
- }
- }
- }
- }
- /**
- * Don't allow new `Compiler's to be made.
- */
- private VMCompiler()
- {
- }
- private static Class loadSharedLibrary(ClassLoader loader,
- String fileName,
- ProtectionDomain domain,
- String className)
- {
- Class c = null;
- SharedLibHelper helper
- = SharedLibHelper.findHelper (loader, fileName, domain.getCodeSource(),
- domain, false);
- c = helper.findClass (className);
- if (c != null)
- {
- HashSet hs = (HashSet) sharedHelperMap.get(loader);
- if (hs == null)
- {
- hs = new HashSet();
- sharedHelperMap.put(loader, hs);
- }
- hs.add(helper);
- }
- return c;
- }
- /**
- * Compile a class given the bytes for it. Returns the Class, or
- * null if compilation failed or otherwise could not be done.
- */
- public static Class compileClass(ClassLoader loader,
- String name, byte[] data,
- int offset, int len,
- ProtectionDomain domain)
- {
- if (precompiledMapFiles == null && !precompiles())
- return null;
- byte digest[];
- try
- {
- MD5 md = (MD5) md5Digest.clone();
- md.update(data);
- digest = md.digest();
- }
- catch (NullPointerException _)
- {
- // If md5Digest==null -- but really this should never happen
- // either, since the MD5 digest is in libgcj.
- return null;
- }
- // We use lookaside cache files to determine whether these bytes
- // correspond to a class file that is part of a precompiled DSO.
- if (precompiledMapFiles != null)
- {
- try
- {
- Enumeration elements = precompiledMapFiles.elements();
- while (elements.hasMoreElements())
- {
- PersistentByteMap map = (PersistentByteMap)elements.nextElement();
- byte[] soName = map.get(digest);
- if (soName != null)
- return loadSharedLibrary(loader,
- new String(soName),
- domain, name);
- }
- }
- catch (Exception _)
- {
- }
- catch (UnknownError _)
- {
- // SharedLibHelper will throw UnknownError if the dlopen
- // fails for some reason. We ignore it and continue on.
- }
- }
-
- if (!precompiles())
- return null;
- try
- {
- // FIXME: Make sure that the class represented by the
- // bytes in DATA really is the class named in NAME. Make
- // sure it's not "java.*".
- StringBuffer hexBytes = new StringBuffer(gcjJitTmpdir);
- hexBytes.append(File.separatorChar);
- int digestLength = digest.length;
- for (int i = 0; i < digestLength; ++i)
- {
- int v = digest[i] & 0xff;
- if (v < 16)
- hexBytes.append('0');
- hexBytes.append(Integer.toHexString(v));
- }
- // FIXME: use System.mapLibraryName?
- // I'm thinking we should use that, plus a class specified
- // via a property that determines lookup policy.
- File soFile = new File(hexBytes + ".so");
- if (soFile.isFile())
- return loadSharedLibrary (loader, soFile.toString(), domain,
- name);
- File classFile = new File(hexBytes + ".class");
- classFile.delete();
- if (classFile.createNewFile() != true)
- return null;
- FileOutputStream f = new FileOutputStream (classFile);
- // FIXME: race condition if bytes change... ?
- f.write(data, offset, len);
- // Invoke the compiler.
- StringBuffer command = new StringBuffer(gcjJitCompiler);
- command.append(" ");
- command.append(classFile);
- command.append(" ");
- command.append(gcjJitCompilerOptions);
- // These options are required.
- command.append(" -findirect-dispatch -fjni -shared -fPIC -o ");
- command.append(soFile);
- Process p = Runtime.getRuntime().exec(command.toString());
- // Read the process' stderr into a string.
- StringBuffer err = new StringBuffer();
- InputStreamReader stderr = new InputStreamReader (p.getErrorStream());
- char[] inBuf = new char[500];
- int bytesRead;
- while ((bytesRead = stderr.read (inBuf)) != -1)
- err.append(inBuf, 0, bytesRead);
- if (p.waitFor() != 0)
- {
- // FIXME: we could log err.toString() somewhere...
- return null;
- }
- return loadSharedLibrary(loader, soFile.toString(), domain, name);
- }
- catch (Exception _)
- {
- return null;
- }
- }
- /**
- * Compile the class named by <code>oneClass</code>.
- *
- * @param oneClass the class to compile
- * @return <code>false</code> if no compiler is available or
- * compilation failed, <code>true</code> if compilation succeeded
- * @throws NullPointerException if oneClass is null
- */
- public static boolean compileClass(Class oneClass)
- {
- // Never succeed.
- return false;
- }
- /**
- * Compile the classes whose name matches <code>classNames</code>.
- *
- * @param classNames the name of classes to compile
- * @return <code>false</code> if no compiler is available or
- * compilation failed, <code>true</code> if compilation succeeded
- * @throws NullPointerException if classNames is null
- */
- public static boolean compileClasses(String classNames)
- {
- // Note the incredibly lame interface. Always fail.
- return false;
- }
- /**
- * This method examines the argument and performs an operation
- * according to the compilers documentation. No specific operation
- * is required.
- *
- * @param arg a compiler-specific argument
- * @return a compiler-specific value, including null
- * @throws NullPointerException if the compiler doesn't like a null arg
- */
- public static Object command(Object arg)
- {
- // Our implementation defines this to a no-op.
- return null;
- }
- /**
- * Calling <code>Compiler.enable()</code> will cause the compiler
- * to resume operation if it was previously disabled; provided that a
- * compiler even exists.
- */
- public static void enable()
- {
- useCompiler = true;
- }
- /**
- * Calling <code>Compiler.disable()</code> will cause the compiler
- * to be suspended; provided that a compiler even exists.
- */
- public static void disable()
- {
- useCompiler = false;
- }
- }
|