mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			370 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* 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;
 | |
|   }
 | |
| }
 |