VMCompiler.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* VMClassLoader.java -- Reference implementation of compiler interface
  2. Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation
  3. This file is part of GNU Classpath.
  4. GNU Classpath is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2, or (at your option)
  7. any later version.
  8. GNU Classpath is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNU Classpath; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301 USA.
  16. Linking this library statically or dynamically with other modules is
  17. making a combined work based on this library. Thus, the terms and
  18. conditions of the GNU General Public License cover the whole
  19. combination.
  20. As a special exception, the copyright holders of this library give you
  21. permission to link this library with independent modules to produce an
  22. executable, regardless of the license terms of these independent
  23. modules, and to copy and distribute the resulting executable under
  24. terms of your choice, provided that you also meet, for each linked
  25. independent module, the terms and conditions of the license of that
  26. module. An independent module is a module which is not derived from
  27. or based on this library. If you modify this library, you may extend
  28. this exception to your version of the library, but you are not
  29. obligated to do so. If you do not wish to do so, delete this
  30. exception statement from your version. */
  31. package java.lang;
  32. import java.io.File;
  33. import java.io.FileOutputStream;
  34. import java.io.InputStreamReader;
  35. import java.security.MessageDigest;
  36. import java.security.ProtectionDomain;
  37. import java.security.NoSuchAlgorithmException;
  38. import java.util.WeakHashMap;
  39. import java.util.HashSet;
  40. import java.util.Enumeration;
  41. import java.util.StringTokenizer;
  42. import java.util.Vector;
  43. import gnu.gcj.runtime.SharedLibHelper;
  44. import gnu.gcj.runtime.PersistentByteMap;
  45. import gnu.java.security.hash.MD5;
  46. /**
  47. * This class is just a per-VM reflection of java.lang.Compiler.
  48. * All methods are defined identically.
  49. */
  50. final class VMCompiler
  51. {
  52. // True if we want to use gcj-jit.
  53. public static boolean useCompiler = true;
  54. // True if we're able to use gcj-jit.
  55. public static final boolean canUseCompiler;
  56. // Compiler to use.
  57. public static String gcjJitCompiler;
  58. // Compiler options.
  59. public static String gcjJitCompilerOptions;
  60. // Temporary directory to use.
  61. public static String gcjJitTmpdir;
  62. public static boolean precompiles()
  63. {
  64. return (canUseCompiler & useCompiler);
  65. }
  66. // This maps a ClassLoader to a set of SharedLibHelper objects that
  67. // it has used. We do things this way to ensure that a
  68. // SharedLibHelper is collected if and only if the ClassLoader is.
  69. private static WeakHashMap sharedHelperMap = new WeakHashMap();
  70. private static Vector precompiledMapFiles;
  71. // We create a single MD5 engine and then clone it whenever we want
  72. // a new one.
  73. // We don't use
  74. //
  75. // md5Digest = MessageDigest.getInstance("MD5");
  76. //
  77. // here because that loads a great deal of security provider code as
  78. // interpreted bytecode -- before we're able to use this class to
  79. // load precompiled classes.
  80. private static final MD5 md5Digest
  81. = new gnu.java.security.hash.MD5();
  82. static
  83. {
  84. gcjJitCompiler = System.getProperty("gnu.gcj.jit.compiler");
  85. if (gcjJitCompiler == null)
  86. canUseCompiler = false;
  87. else
  88. {
  89. gcjJitCompilerOptions = System.getProperty("gnu.gcj.jit.options",
  90. "-g");
  91. gcjJitTmpdir = System.getProperty("gnu.gcj.jit.cachedir");
  92. // Note that we *don't* choose java.io.tmpdir as a default --
  93. // that would allow easy attacks against the VM.
  94. if (gcjJitTmpdir == null)
  95. canUseCompiler = false;
  96. else
  97. canUseCompiler = true;
  98. }
  99. String prop = System.getProperty ("gnu.gcj.precompiled.db.path");
  100. if (prop != null)
  101. {
  102. precompiledMapFiles = new Vector();
  103. // Add the
  104. StringTokenizer st
  105. = new StringTokenizer (prop,
  106. System.getProperty ("path.separator", ":"));
  107. {
  108. while (st.hasMoreElements ())
  109. {
  110. String e = st.nextToken ();
  111. try
  112. {
  113. PersistentByteMap map
  114. = new PersistentByteMap
  115. (e, PersistentByteMap.AccessMode.READ_ONLY);
  116. precompiledMapFiles.add(map);
  117. }
  118. catch (IllegalArgumentException _)
  119. {
  120. // Not a map file
  121. }
  122. catch (java.io.IOException _)
  123. {
  124. }
  125. catch (java.nio.BufferUnderflowException _)
  126. {
  127. // Invalid map file.
  128. }
  129. }
  130. }
  131. }
  132. }
  133. /**
  134. * Don't allow new `Compiler's to be made.
  135. */
  136. private VMCompiler()
  137. {
  138. }
  139. private static Class loadSharedLibrary(ClassLoader loader,
  140. String fileName,
  141. ProtectionDomain domain,
  142. String className)
  143. {
  144. Class c = null;
  145. SharedLibHelper helper
  146. = SharedLibHelper.findHelper (loader, fileName, domain.getCodeSource(),
  147. domain, false);
  148. c = helper.findClass (className);
  149. if (c != null)
  150. {
  151. HashSet hs = (HashSet) sharedHelperMap.get(loader);
  152. if (hs == null)
  153. {
  154. hs = new HashSet();
  155. sharedHelperMap.put(loader, hs);
  156. }
  157. hs.add(helper);
  158. }
  159. return c;
  160. }
  161. /**
  162. * Compile a class given the bytes for it. Returns the Class, or
  163. * null if compilation failed or otherwise could not be done.
  164. */
  165. public static Class compileClass(ClassLoader loader,
  166. String name, byte[] data,
  167. int offset, int len,
  168. ProtectionDomain domain)
  169. {
  170. if (precompiledMapFiles == null && !precompiles())
  171. return null;
  172. byte digest[];
  173. try
  174. {
  175. MD5 md = (MD5) md5Digest.clone();
  176. md.update(data);
  177. digest = md.digest();
  178. }
  179. catch (NullPointerException _)
  180. {
  181. // If md5Digest==null -- but really this should never happen
  182. // either, since the MD5 digest is in libgcj.
  183. return null;
  184. }
  185. // We use lookaside cache files to determine whether these bytes
  186. // correspond to a class file that is part of a precompiled DSO.
  187. if (precompiledMapFiles != null)
  188. {
  189. try
  190. {
  191. Enumeration elements = precompiledMapFiles.elements();
  192. while (elements.hasMoreElements())
  193. {
  194. PersistentByteMap map = (PersistentByteMap)elements.nextElement();
  195. byte[] soName = map.get(digest);
  196. if (soName != null)
  197. return loadSharedLibrary(loader,
  198. new String(soName),
  199. domain, name);
  200. }
  201. }
  202. catch (Exception _)
  203. {
  204. }
  205. catch (UnknownError _)
  206. {
  207. // SharedLibHelper will throw UnknownError if the dlopen
  208. // fails for some reason. We ignore it and continue on.
  209. }
  210. }
  211. if (!precompiles())
  212. return null;
  213. try
  214. {
  215. // FIXME: Make sure that the class represented by the
  216. // bytes in DATA really is the class named in NAME. Make
  217. // sure it's not "java.*".
  218. StringBuffer hexBytes = new StringBuffer(gcjJitTmpdir);
  219. hexBytes.append(File.separatorChar);
  220. int digestLength = digest.length;
  221. for (int i = 0; i < digestLength; ++i)
  222. {
  223. int v = digest[i] & 0xff;
  224. if (v < 16)
  225. hexBytes.append('0');
  226. hexBytes.append(Integer.toHexString(v));
  227. }
  228. // FIXME: use System.mapLibraryName?
  229. // I'm thinking we should use that, plus a class specified
  230. // via a property that determines lookup policy.
  231. File soFile = new File(hexBytes + ".so");
  232. if (soFile.isFile())
  233. return loadSharedLibrary (loader, soFile.toString(), domain,
  234. name);
  235. File classFile = new File(hexBytes + ".class");
  236. classFile.delete();
  237. if (classFile.createNewFile() != true)
  238. return null;
  239. FileOutputStream f = new FileOutputStream (classFile);
  240. // FIXME: race condition if bytes change... ?
  241. f.write(data, offset, len);
  242. // Invoke the compiler.
  243. StringBuffer command = new StringBuffer(gcjJitCompiler);
  244. command.append(" ");
  245. command.append(classFile);
  246. command.append(" ");
  247. command.append(gcjJitCompilerOptions);
  248. // These options are required.
  249. command.append(" -findirect-dispatch -fjni -shared -fPIC -o ");
  250. command.append(soFile);
  251. Process p = Runtime.getRuntime().exec(command.toString());
  252. // Read the process' stderr into a string.
  253. StringBuffer err = new StringBuffer();
  254. InputStreamReader stderr = new InputStreamReader (p.getErrorStream());
  255. char[] inBuf = new char[500];
  256. int bytesRead;
  257. while ((bytesRead = stderr.read (inBuf)) != -1)
  258. err.append(inBuf, 0, bytesRead);
  259. if (p.waitFor() != 0)
  260. {
  261. // FIXME: we could log err.toString() somewhere...
  262. return null;
  263. }
  264. return loadSharedLibrary(loader, soFile.toString(), domain, name);
  265. }
  266. catch (Exception _)
  267. {
  268. return null;
  269. }
  270. }
  271. /**
  272. * Compile the class named by <code>oneClass</code>.
  273. *
  274. * @param oneClass the class to compile
  275. * @return <code>false</code> if no compiler is available or
  276. * compilation failed, <code>true</code> if compilation succeeded
  277. * @throws NullPointerException if oneClass is null
  278. */
  279. public static boolean compileClass(Class oneClass)
  280. {
  281. // Never succeed.
  282. return false;
  283. }
  284. /**
  285. * Compile the classes whose name matches <code>classNames</code>.
  286. *
  287. * @param classNames the name of classes to compile
  288. * @return <code>false</code> if no compiler is available or
  289. * compilation failed, <code>true</code> if compilation succeeded
  290. * @throws NullPointerException if classNames is null
  291. */
  292. public static boolean compileClasses(String classNames)
  293. {
  294. // Note the incredibly lame interface. Always fail.
  295. return false;
  296. }
  297. /**
  298. * This method examines the argument and performs an operation
  299. * according to the compilers documentation. No specific operation
  300. * is required.
  301. *
  302. * @param arg a compiler-specific argument
  303. * @return a compiler-specific value, including null
  304. * @throws NullPointerException if the compiler doesn't like a null arg
  305. */
  306. public static Object command(Object arg)
  307. {
  308. // Our implementation defines this to a no-op.
  309. return null;
  310. }
  311. /**
  312. * Calling <code>Compiler.enable()</code> will cause the compiler
  313. * to resume operation if it was previously disabled; provided that a
  314. * compiler even exists.
  315. */
  316. public static void enable()
  317. {
  318. useCompiler = true;
  319. }
  320. /**
  321. * Calling <code>Compiler.disable()</code> will cause the compiler
  322. * to be suspended; provided that a compiler even exists.
  323. */
  324. public static void disable()
  325. {
  326. useCompiler = false;
  327. }
  328. }