1 /* VMClassLoader.java -- Reference implementation of compiler interface
2 Copyright (C) 2004, 2005, 2006 Free Software Foundation
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.io.FileOutputStream;
42 import java.io.InputStreamReader;
43 import java.security.MessageDigest;
44 import java.security.ProtectionDomain;
45 import java.security.NoSuchAlgorithmException;
46 import java.util.WeakHashMap;
47 import java.util.HashSet;
48 import java.util.Enumeration;
49 import java.util.StringTokenizer;
50 import java.util.Vector;
51 import gnu.gcj.runtime.SharedLibHelper;
52 import gnu.gcj.runtime.PersistentByteMap;
53 import gnu.java.security.hash.MD5;
56 * This class is just a per-VM reflection of java.lang.Compiler.
57 * All methods are defined identically.
59 final class VMCompiler
61 // True if we want to use gcj-jit.
62 public static boolean useCompiler = true;
64 // True if we're able to use gcj-jit.
65 public static final boolean canUseCompiler;
68 public static String gcjJitCompiler;
71 public static String gcjJitCompilerOptions;
73 // Temporary directory to use.
74 public static String gcjJitTmpdir;
76 public static boolean precompiles()
78 return (canUseCompiler & useCompiler);
81 // This maps a ClassLoader to a set of SharedLibHelper objects that
82 // it has used. We do things this way to ensure that a
83 // SharedLibHelper is collected if and only if the ClassLoader is.
84 private static WeakHashMap sharedHelperMap = new WeakHashMap();
86 private static Vector precompiledMapFiles;
88 // We create a single MD5 engine and then clone it whenever we want
93 // md5Digest = MessageDigest.getInstance("MD5");
95 // here because that loads a great deal of security provider code as
96 // interpreted bytecode -- before we're able to use this class to
97 // load precompiled classes.
99 private static final MD5 md5Digest
100 = new gnu.java.security.hash.MD5();
104 gcjJitCompiler = System.getProperty("gnu.gcj.jit.compiler");
105 if (gcjJitCompiler == null)
106 canUseCompiler = false;
109 gcjJitCompilerOptions = System.getProperty("gnu.gcj.jit.options",
111 gcjJitTmpdir = System.getProperty("gnu.gcj.jit.cachedir");
112 // Note that we *don't* choose java.io.tmpdir as a default --
113 // that would allow easy attacks against the VM.
114 if (gcjJitTmpdir == null)
115 canUseCompiler = false;
117 canUseCompiler = true;
120 String prop = System.getProperty ("gnu.gcj.precompiled.db.path");
123 precompiledMapFiles = new Vector();
126 = new StringTokenizer (prop,
127 System.getProperty ("path.separator", ":"));
129 while (st.hasMoreElements ())
131 String e = st.nextToken ();
134 PersistentByteMap map
135 = new PersistentByteMap
136 (e, PersistentByteMap.AccessMode.READ_ONLY);
137 precompiledMapFiles.add(map);
139 catch (IllegalArgumentException _)
143 catch (java.io.IOException _)
146 catch (java.nio.BufferUnderflowException _)
156 * Don't allow new `Compiler's to be made.
162 private static Class loadSharedLibrary(ClassLoader loader,
164 ProtectionDomain domain,
168 SharedLibHelper helper
169 = SharedLibHelper.findHelper (loader, fileName, domain.getCodeSource(),
171 c = helper.findClass (className);
174 HashSet hs = (HashSet) sharedHelperMap.get(loader);
178 sharedHelperMap.put(loader, hs);
186 * Compile a class given the bytes for it. Returns the Class, or
187 * null if compilation failed or otherwise could not be done.
189 public static Class compileClass(ClassLoader loader,
190 String name, byte[] data,
192 ProtectionDomain domain)
194 if (precompiledMapFiles == null && !precompiles())
201 MD5 md = (MD5) md5Digest.clone();
203 digest = md.digest();
205 catch (NullPointerException _)
207 // If md5Digest==null -- but really this should never happen
208 // either, since the MD5 digest is in libgcj.
212 // We use lookaside cache files to determine whether these bytes
213 // correspond to a class file that is part of a precompiled DSO.
214 if (precompiledMapFiles != null)
218 Enumeration elements = precompiledMapFiles.elements();
219 while (elements.hasMoreElements())
221 PersistentByteMap map = (PersistentByteMap)elements.nextElement();
222 byte[] soName = map.get(digest);
224 return loadSharedLibrary(loader,
232 catch (UnknownError _)
234 // SharedLibHelper will throw UnknownError if the dlopen
235 // fails for some reason. We ignore it and continue on.
244 // FIXME: Make sure that the class represented by the
245 // bytes in DATA really is the class named in NAME. Make
246 // sure it's not "java.*".
247 StringBuffer hexBytes = new StringBuffer(gcjJitTmpdir);
248 hexBytes.append(File.separatorChar);
249 int digestLength = digest.length;
250 for (int i = 0; i < digestLength; ++i)
251 hexBytes.append(Integer.toHexString(digest[i] & 0xff));
253 // FIXME: use System.mapLibraryName?
254 // I'm thinking we should use that, plus a class specified
255 // via a property that determines lookup policy.
256 File soFile = new File(hexBytes + ".so");
258 return loadSharedLibrary (loader, soFile.toString(), domain,
261 File classFile = new File(hexBytes + ".class");
263 if (classFile.createNewFile() != true)
266 FileOutputStream f = new FileOutputStream (classFile);
267 // FIXME: race condition if bytes change... ?
268 f.write(data, offset, len);
270 // Invoke the compiler.
271 StringBuffer command = new StringBuffer(gcjJitCompiler);
273 command.append(classFile);
275 command.append(gcjJitCompilerOptions);
276 // These options are required.
277 command.append(" -findirect-dispatch -fjni -shared -fPIC -o ");
278 command.append(soFile);
279 Process p = Runtime.getRuntime().exec(command.toString());
281 // Read the process' stderr into a string.
282 StringBuffer err = new StringBuffer();
283 InputStreamReader stderr = new InputStreamReader (p.getErrorStream());
284 char[] inBuf = new char[500];
286 while ((bytesRead = stderr.read (inBuf)) != -1)
287 err.append(inBuf, 0, bytesRead);
289 if (p.waitFor() != 0)
291 // FIXME: we could log err.toString() somewhere...
295 return loadSharedLibrary(loader, soFile.toString(), domain, name);
304 * Compile the class named by <code>oneClass</code>.
306 * @param oneClass the class to compile
307 * @return <code>false</code> if no compiler is available or
308 * compilation failed, <code>true</code> if compilation succeeded
309 * @throws NullPointerException if oneClass is null
311 public static boolean compileClass(Class oneClass)
318 * Compile the classes whose name matches <code>classNames</code>.
320 * @param classNames the name of classes to compile
321 * @return <code>false</code> if no compiler is available or
322 * compilation failed, <code>true</code> if compilation succeeded
323 * @throws NullPointerException if classNames is null
325 public static boolean compileClasses(String classNames)
327 // Note the incredibly lame interface. Always fail.
332 * This method examines the argument and performs an operation
333 * according to the compilers documentation. No specific operation
336 * @param arg a compiler-specific argument
337 * @return a compiler-specific value, including null
338 * @throws NullPointerException if the compiler doesn't like a null arg
340 public static Object command(Object arg)
342 // Our implementation defines this to a no-op.
347 * Calling <code>Compiler.enable()</code> will cause the compiler
348 * to resume operation if it was previously disabled; provided that a
349 * compiler even exists.
351 public static void enable()
357 * Calling <code>Compiler.disable()</code> will cause the compiler
358 * to be suspended; provided that a compiler even exists.
360 public static void disable()