1 /* VMClassLoader.java -- Reference implementation of compiler interface
2 Copyright (C) 2004, 2005 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
;
55 * This class is just a per-VM reflection of java.lang.Compiler.
56 * All methods are defined identically.
58 final class VMCompiler
60 // True if we want to use gcj-jit.
61 public static boolean useCompiler
= true;
63 // True if we're able to use gcj-jit.
64 public static final boolean canUseCompiler
;
67 public static String gcjJitCompiler
;
70 public static String gcjJitCompilerOptions
;
72 // Temporary directory to use.
73 public static String gcjJitTmpdir
;
75 // This maps a ClassLoader to a set of SharedLibHelper objects that
76 // it has used. We do things this way to ensure that a
77 // SharedLibHelper is collected if and only if the ClassLoader is.
78 private static WeakHashMap sharedHelperMap
= new WeakHashMap();
80 private static Vector precompiledMapFiles
;
82 // We create a single MD5 engine and then clone it whenever we want
87 // md5Digest = MessageDigest.getInstance("MD5");
89 // here because that loads a great deal of security provider code as
90 // interpreted bytecode -- before we're able to use this class to
91 // load precompiled classes.
93 private static final MessageDigest md5Digest
94 = new gnu
.java
.security
.provider
.MD5();
98 gcjJitCompiler
= System
.getProperty("gnu.gcj.jit.compiler");
99 if (gcjJitCompiler
== null)
100 canUseCompiler
= false;
103 gcjJitCompilerOptions
= System
.getProperty("gnu.gcj.jit.options",
105 gcjJitTmpdir
= System
.getProperty("gnu.gcj.jit.cachedir");
106 // Note that we *don't* choose java.io.tmpdir as a default --
107 // that would allow easy attacks against the VM.
108 if (gcjJitTmpdir
== null)
109 canUseCompiler
= false;
111 canUseCompiler
= true;
114 String prop
= System
.getProperty ("gnu.gcj.precompiled.db.path");
117 precompiledMapFiles
= new Vector();
120 = new StringTokenizer (prop
,
121 System
.getProperty ("path.separator", ":"));
123 while (st
.hasMoreElements ())
125 String e
= st
.nextToken ();
128 PersistentByteMap map
129 = new PersistentByteMap
130 (e
, PersistentByteMap
.AccessMode
.READ_ONLY
);
131 precompiledMapFiles
.add(map
);
133 catch (IllegalArgumentException _
)
137 catch (java
.io
.IOException _
)
140 catch (java
.nio
.BufferUnderflowException _
)
150 * Don't allow new `Compiler's to be made.
156 private static Class
loadSharedLibrary(ClassLoader loader
,
158 ProtectionDomain domain
,
162 SharedLibHelper helper
163 = SharedLibHelper
.findHelper (loader
, fileName
, domain
.getCodeSource(),
165 c
= helper
.findClass (className
);
168 HashSet hs
= (HashSet
) sharedHelperMap
.get(loader
);
172 sharedHelperMap
.put(loader
, hs
);
180 * Compile a class given the bytes for it. Returns the Class, or
181 * null if compilation failed or otherwise could not be done.
183 public static Class
compileClass(ClassLoader loader
,
184 String name
, byte[] data
,
186 ProtectionDomain domain
)
188 if (precompiledMapFiles
== null
189 && (! useCompiler
|| ! canUseCompiler
))
196 MessageDigest md
= (MessageDigest
) md5Digest
.clone();
197 digest
= md
.digest(data
);
199 catch (CloneNotSupportedException _
)
204 catch (NullPointerException _
)
206 // If md5Digest==null -- but really this should never happen
207 // either, since the MD5 digest is in libgcj.
211 // We use lookaside cache files to determine whether these bytes
212 // correspond to a class file that is part of a precompiled DSO.
213 if (precompiledMapFiles
!= null)
217 Enumeration elements
= precompiledMapFiles
.elements();
218 while (elements
.hasMoreElements())
220 PersistentByteMap map
= (PersistentByteMap
)elements
.nextElement();
221 byte[] soName
= map
.get(digest
);
223 return loadSharedLibrary(loader
,
231 catch (UnknownError _
)
233 // SharedLibHelper will throw UnknownError if the dlopen
234 // fails for some reason. We ignore it and continue on.
238 if (! useCompiler
|| ! canUseCompiler
)
243 // FIXME: Make sure that the class represented by the
244 // bytes in DATA really is the class named in NAME. Make
245 // sure it's not "java.*".
246 StringBuffer hexBytes
= new StringBuffer(gcjJitTmpdir
);
247 hexBytes
.append(File
.separatorChar
);
248 int digestLength
= digest
.length
;
249 for (int i
= 0; i
< digestLength
; ++i
)
250 hexBytes
.append(Integer
.toHexString(digest
[i
] & 0xff));
252 // FIXME: use System.mapLibraryName?
253 // I'm thinking we should use that, plus a class specified
254 // via a property that determines lookup policy.
255 File soFile
= new File(hexBytes
+ ".so");
257 return loadSharedLibrary (loader
, soFile
.toString(), domain
,
260 File classFile
= new File(hexBytes
+ ".class");
262 if (classFile
.createNewFile() != true)
265 FileOutputStream f
= new FileOutputStream (classFile
);
266 // FIXME: race condition if bytes change... ?
267 f
.write(data
, offset
, len
);
269 // Invoke the compiler.
270 StringBuffer command
= new StringBuffer(gcjJitCompiler
);
272 command
.append(classFile
);
274 command
.append(gcjJitCompilerOptions
);
275 // These options are required.
276 command
.append(" -findirect-dispatch -fjni -shared -fPIC -o ");
277 command
.append(soFile
);
278 Process p
= Runtime
.getRuntime().exec(command
.toString());
280 // Read the process' stderr into a string.
281 StringBuffer err
= new StringBuffer();
282 InputStreamReader stderr
= new InputStreamReader (p
.getErrorStream());
283 char[] inBuf
= new char[500];
285 while ((bytesRead
= stderr
.read (inBuf
)) != -1)
286 err
.append(inBuf
, 0, bytesRead
);
288 if (p
.waitFor() != 0)
290 // FIXME: we could log err.toString() somewhere...
294 return loadSharedLibrary(loader
, soFile
.toString(), domain
, name
);
303 * Compile the class named by <code>oneClass</code>.
305 * @param oneClass the class to compile
306 * @return <code>false</code> if no compiler is available or
307 * compilation failed, <code>true</code> if compilation succeeded
308 * @throws NullPointerException if oneClass is null
310 public static boolean compileClass(Class oneClass
)
317 * Compile the classes whose name matches <code>classNames</code>.
319 * @param classNames the name of classes to compile
320 * @return <code>false</code> if no compiler is available or
321 * compilation failed, <code>true</code> if compilation succeeded
322 * @throws NullPointerException if classNames is null
324 public static boolean compileClasses(String classNames
)
326 // Note the incredibly lame interface. Always fail.
331 * This method examines the argument and performs an operation
332 * according to the compilers documentation. No specific operation
335 * @param arg a compiler-specific argument
336 * @return a compiler-specific value, including null
337 * @throws NullPointerException if the compiler doesn't like a null arg
339 public static Object
command(Object arg
)
341 // Our implementation defines this to a no-op.
346 * Calling <code>Compiler.enable()</code> will cause the compiler
347 * to resume operation if it was previously disabled; provided that a
348 * compiler even exists.
350 public static void enable()
356 * Calling <code>Compiler.disable()</code> will cause the compiler
357 * to be suspended; provided that a compiler even exists.
359 public static void disable()