OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / java / lang / VMCompiler.java
1 /* VMClassLoader.java -- Reference implementation of compiler interface
2    Copyright (C) 2004, 2005, 2006 Free Software Foundation
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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
19 02110-1301 USA.
20
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
24 combination.
25
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. */
37
38 package java.lang;
39
40 import java.io.File;
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;
54
55 /**
56  * This class is just a per-VM reflection of java.lang.Compiler.
57  * All methods are defined identically.
58  */
59 final class VMCompiler
60 {
61   // True if we want to use gcj-jit.
62   public static boolean useCompiler = true;
63
64   // True if we're able to use gcj-jit.
65   public static final boolean canUseCompiler;
66
67   // Compiler to use.
68   public static String gcjJitCompiler;
69
70   // Compiler options.
71   public static String gcjJitCompilerOptions;
72
73   // Temporary directory to use.
74   public static String gcjJitTmpdir;
75
76   public static boolean precompiles()
77   {
78     return (canUseCompiler & useCompiler);
79   }
80
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();
85
86   private static Vector precompiledMapFiles;
87
88   // We create a single MD5 engine and then clone it whenever we want
89   // a new one.
90
91   // We don't use 
92   //
93   // md5Digest = MessageDigest.getInstance("MD5");
94   //
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.
98
99   private static final MD5 md5Digest
100     = new gnu.java.security.hash.MD5();
101
102   static
103   {
104     gcjJitCompiler = System.getProperty("gnu.gcj.jit.compiler");
105     if (gcjJitCompiler == null)
106       canUseCompiler = false;
107     else
108       {
109         gcjJitCompilerOptions = System.getProperty("gnu.gcj.jit.options",
110                                                    "-g");
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;
116         else
117           canUseCompiler = true;
118       }
119
120     String prop = System.getProperty ("gnu.gcj.precompiled.db.path");
121     if (prop != null)
122       {
123         precompiledMapFiles = new Vector();
124         // Add the 
125         StringTokenizer st
126           = new StringTokenizer (prop,
127                                  System.getProperty ("path.separator", ":"));
128         {
129           while (st.hasMoreElements ()) 
130             {  
131               String e = st.nextToken ();
132               try
133                 {
134                   PersistentByteMap map 
135                     = new PersistentByteMap
136                     (e, PersistentByteMap.AccessMode.READ_ONLY);
137                   precompiledMapFiles.add(map);
138                 }
139               catch (IllegalArgumentException _)
140                 {
141                   // Not a map file           
142                 }
143               catch (java.io.IOException _)
144                 {
145                 }
146               catch (java.nio.BufferUnderflowException _)
147                 {
148                   // Invalid map file.
149                 }
150             }
151         }
152       }
153   }
154
155   /**
156    * Don't allow new `Compiler's to be made.
157    */
158   private VMCompiler()
159   {
160   }
161
162   private static Class loadSharedLibrary(ClassLoader loader,
163                                          String fileName,
164                                          ProtectionDomain domain,
165                                          String className)
166   {
167     Class c = null;
168     SharedLibHelper helper 
169         = SharedLibHelper.findHelper (loader, fileName, domain.getCodeSource(), 
170                                       domain, false);
171     c = helper.findClass (className);
172     if (c != null)
173       {
174         HashSet hs = (HashSet) sharedHelperMap.get(loader);
175         if (hs == null)
176           {
177             hs = new HashSet();
178             sharedHelperMap.put(loader, hs);
179           }
180         hs.add(helper);
181       }
182     return c;
183   }
184
185   /**
186    * Compile a class given the bytes for it.  Returns the Class, or
187    * null if compilation failed or otherwise could not be done.
188    */
189   public static Class compileClass(ClassLoader loader,
190                                    String name, byte[] data,
191                                    int offset, int len,
192                                    ProtectionDomain domain)
193   {
194     if (precompiledMapFiles == null && !precompiles())
195       return null;
196
197     byte digest[];
198
199     try
200       {
201         MD5 md = (MD5) md5Digest.clone();
202         md.update(data);
203         digest = md.digest();
204       }
205     catch (NullPointerException _)
206       {
207         // If md5Digest==null -- but really this should never happen
208         // either, since the MD5 digest is in libgcj.
209         return null;
210       }
211
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)
215       {
216         try
217           {
218             Enumeration elements = precompiledMapFiles.elements();
219             while (elements.hasMoreElements())
220               {
221                 PersistentByteMap map = (PersistentByteMap)elements.nextElement();
222                 byte[] soName = map.get(digest);
223                 if (soName != null)
224                   return loadSharedLibrary(loader, 
225                                            new String(soName), 
226                                            domain, name);
227               }
228           }
229         catch (Exception _)
230           {
231           }
232         catch (UnknownError _)
233           {
234             // SharedLibHelper will throw UnknownError if the dlopen
235             // fails for some reason.  We ignore it and continue on.
236           }
237       }
238  
239     if (!precompiles())
240       return null;
241
242     try
243       {
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));
252
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");
257         if (soFile.isFile())
258           return loadSharedLibrary (loader, soFile.toString(), domain,
259                                     name);
260
261         File classFile = new File(hexBytes + ".class");
262         classFile.delete();
263         if (classFile.createNewFile() != true)    
264           return null;
265
266         FileOutputStream f = new FileOutputStream (classFile);
267         // FIXME: race condition if bytes change... ?
268         f.write(data, offset, len);
269
270         // Invoke the compiler.
271         StringBuffer command = new StringBuffer(gcjJitCompiler);
272         command.append(" ");
273         command.append(classFile);
274         command.append(" ");
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());
280
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];
285         int bytesRead;
286         while ((bytesRead = stderr.read (inBuf)) != -1)
287           err.append(inBuf, 0, bytesRead);
288
289         if (p.waitFor() != 0)
290           {
291             // FIXME: we could log err.toString() somewhere...
292             return null;
293           }
294
295         return loadSharedLibrary(loader, soFile.toString(), domain, name);
296       }
297     catch (Exception _)
298       {
299         return null;
300       }
301   }
302
303   /**
304    * Compile the class named by <code>oneClass</code>.
305    *
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
310    */
311   public static boolean compileClass(Class oneClass)
312   {
313     // Never succeed.
314     return false;
315   }
316
317   /**
318    * Compile the classes whose name matches <code>classNames</code>.
319    *
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
324    */
325   public static boolean compileClasses(String classNames)
326   {
327     // Note the incredibly lame interface.  Always fail.
328     return false;
329   }
330
331   /**
332    * This method examines the argument and performs an operation
333    * according to the compilers documentation.  No specific operation
334    * is required.
335    *
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
339    */
340   public static Object command(Object arg)
341   {
342     // Our implementation defines this to a no-op.
343     return null;
344   }
345
346   /**
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.
350    */
351   public static void enable()
352   {
353     useCompiler = true;
354   }
355
356   /**
357    * Calling <code>Compiler.disable()</code> will cause the compiler
358    * to be suspended; provided that a compiler even exists.
359    */
360   public static void disable()
361   {
362     useCompiler = false;
363   }
364 }