summaryrefslogtreecommitdiff
path: root/libjava/java/lang/VMCompiler.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/lang/VMCompiler.java')
-rw-r--r--libjava/java/lang/VMCompiler.java369
1 files changed, 369 insertions, 0 deletions
diff --git a/libjava/java/lang/VMCompiler.java b/libjava/java/lang/VMCompiler.java
new file mode 100644
index 000000000..1f600f12e
--- /dev/null
+++ b/libjava/java/lang/VMCompiler.java
@@ -0,0 +1,369 @@
+/* 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;
+ }
+}