org.xmlvm.proc.lib.LibraryLoader Maven / Gradle / Ivy
Show all versions of dragome-bytecode-js-compiler Show documentation
/* Copyright (c) 2002-2011 by XMLVM.org
*
* Project Info: http://www.xmlvm.org
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package org.xmlvm.proc.lib;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.xmlvm.Log;
import org.xmlvm.main.Arguments;
import org.xmlvm.proc.CompilationBundle;
import org.xmlvm.proc.XmlvmResource;
import org.xmlvm.proc.in.InputProcess.ClassInputProcess;
import org.xmlvm.proc.in.file.ClassFile;
import org.xmlvm.proc.out.DEXmlvmOutputProcess;
import org.xmlvm.util.universalfile.FileSuffixFilter;
import org.xmlvm.util.universalfile.UniversalFile;
import org.xmlvm.util.universalfile.UniversalFileCreator;
import org.xmlvm.util.universalfile.UniversalFileFilter;
/**
* The LibraryLoader is responsible for loading classes from a set of given
* libraries, that are not a part of the application to be translated. Such
* classes include the JavaJDK or the AndroidSDK.
*/
public class LibraryLoader
{
private static final String TAG= LibraryLoader.class.getSimpleName();
private static final String BIN_PROXIES_PATH= "bin-proxies";
private static final String BIN_PROXIES_ONEJAR_PATH= "/lib/proxies-java.jar";
private static final Map proxies= null;
private final Libraries libs;
private final Arguments arguments;
private final static Map cache= new HashMap();
private List libraries= null;
/**
* Initializes the LibraryLoader with the given arguments.
*/
public LibraryLoader(Arguments arguments)
{
this.arguments= arguments;
libs= new Libraries(arguments);
}
/**
* Returns whether the given type has a proxy class that should replace it.
*/
public static boolean hasProxy(String typeName)
{
return proxies != null && proxies.containsKey(typeName);
}
/**
* Returns the proxy for the given type.
*/
public static UniversalFile getProxy(String typename)
{
return proxies.get(typename);
}
/**
* Gets the last modified date of all libraries combined.
*/
public long getLastModified()
{
long lastModified= libs.getLastModified();
for (UniversalFile proxyFile : proxies.values())
{
lastModified= Math.max(lastModified, proxyFile.getLastModified());
}
return lastModified;
}
/**
* Loads the type with the given name from the JDK class library.
*
* @param typeName
* can be e.g. "java.lang.Object"
*/
public XmlvmResource load(String typeName)
{
// First check, whether there is a proxy type with this name.
if (proxies.containsKey(typeName))
{
return processClassFile(proxies.get(typeName), false);
}
if (libraries == null)
{
libraries= new ArrayList();
// Monolithic libraries need to have a higher priority.
libraries.addAll(libs.getMonolithicLibraryFiles());
libraries.addAll(libs.getLibraryFiles());
}
for (UniversalFile library : libraries)
{
XmlvmResource resource= load(typeName, library);
if (resource != null)
{
return resource;
}
}
Log.debug(TAG, "Could not find resource: " + typeName);
return null;
}
private XmlvmResource load(String typeName, UniversalFile directory)
{
if (directory != null)
if (typeName.contains("."))
{
String packageName= typeName.substring(0, typeName.indexOf("."));
UniversalFile subDir;
subDir= directory.getEntry(packageName);
if (subDir != null && subDir.isDirectory())
{
return load(typeName.substring(typeName.indexOf(".") + 1), subDir);
}
else
{
return null;
}
}
else
{
UniversalFile classFile;
classFile= directory.getEntry(typeName + ".class");
if (classFile != null && classFile.isFile())
{
// Success, we found the class file we were looking for.
// Let's
// process it.
return processClassFile(classFile, true);
}
else
{
return null;
}
}
return null;
}
private XmlvmResource processClassFile(UniversalFile file, boolean enableRedList)
{
if (cache.containsKey(file.getAbsolutePath()))
{
return cache.get(file.getAbsolutePath());
}
ClassFile classFile= new ClassFile(file);
ClassInputProcess inputProcess= new ClassInputProcess(arguments, classFile);
DEXmlvmOutputProcess outputProcess= new DEXmlvmOutputProcess(arguments, enableRedList, false);
outputProcess.addPreprocess(inputProcess);
CompilationBundle bundle= new CompilationBundle();
inputProcess.processPhase1(bundle);
outputProcess.processPhase1(bundle);
if (bundle.getResources().size() != 1)
{
return null;
}
XmlvmResource resource= bundle.getResources().iterator().next();
cache.put(file.getAbsolutePath(), resource);
return resource;
}
/**
* This method looks at the resources and their referenced types. It causes
* the missing types to be loaded from the JDK. This is done recursively
* until all types have been loaded.
*
* The loaded types will be added to the given resource map.
*
* @param resources
* the resources from which on referenced types are looked up.
* Loaded references are also added here.
*
* @return whether all references are loaded and no further loading is
* necessary.
*/
public void loadAllReferencedTypes(Map resources)
{
long startTime= System.currentTimeMillis();
while (!loadReferencedTypes(resources))
{
}
long endTime= System.currentTimeMillis();
Log.debug(TAG, "Processing took: " + (endTime - startTime) + " ms.");
}
/**
* This loads a list of libraries that will be required, but wouldn't be
* picked up by reference loading.
*/
public List loadMonolithicLibraries()
{
List result= new ArrayList();
for (UniversalFile library : libs.getMonolithicLibraryFiles())
{
for (UniversalFile file : library.listFilesRecursively(new FileSuffixFilter(".class")))
{
XmlvmResource resource= processClassFile(file, true);
if (resource != null)
{
result.add(resource);
}
}
// TODO(Sascha): Maybe just copy over non-class files?
}
return result;
}
private boolean loadReferencedTypes(Map resources)
{
Set toLoad= new HashSet();
for (String typeName : resources.keySet())
{
XmlvmResource resource= resources.get(typeName);
if (resource == null)
{
continue;
}
Log.debug("***********************************");
Log.debug("XMLVM Resource: " + resource.getFullName());
Log.debug("Super-type : " + resource.getSuperTypeName());
Log.debug("Referenced types:");
Set referencedTypes= resource.getReferencedTypes();
eliminateArrayTypes(referencedTypes);
for (String referencedType : referencedTypes)
{
if (!isBasicType(referencedType))
{
if (resources.keySet().contains(referencedType))
{
Log.debug(" OK -> " + referencedType);
}
else
{
toLoad.add(referencedType);
Log.debug(" LOAD -> " + referencedType);
}
}
}
}
if (toLoad.isEmpty())
{
return true;
}
// Load missing dependencies.
String[] classesToLoad= toLoad.toArray(new String[0]);
for (String classToLoad : classesToLoad)
{
resources.put(classToLoad, load(classToLoad));
}
return classesToLoad.length == 0;
}
private static boolean isBasicType(String typeName)
{
final Set basicTypes= new HashSet();
basicTypes.add("");
basicTypes.add("byte");
basicTypes.add("char");
basicTypes.add("short");
basicTypes.add("int");
basicTypes.add("float");
basicTypes.add("long");
basicTypes.add("double");
basicTypes.add("boolean");
basicTypes.add("void");
basicTypes.add("null");
return basicTypes.contains(typeName);
}
private static void eliminateArrayTypes(Set types)
{
Set add= new HashSet();
Set remove= new HashSet();
for (String typeName : types)
{
if (typeName.endsWith("[]"))
{
remove.add(typeName);
int p= typeName.indexOf('[');
add.add(typeName.substring(0, p));
}
}
for (String typeName : remove)
{
types.remove(typeName);
}
for (String typeName : add)
{
types.add(typeName);
}
}
private static Map initializeProxies()
{
Map result= new HashMap();
UniversalFile basePath= UniversalFileCreator.createDirectory(BIN_PROXIES_ONEJAR_PATH, BIN_PROXIES_PATH);
// If not proxies are available, we disable proxy replacement.
if (basePath == null)
{
Log.debug(TAG, "Proxies not loaded, therefore there will be no proxy replacement");
return result;
}
final String classEnding= ".class";
UniversalFileFilter classFilter= new FileSuffixFilter(classEnding);
for (UniversalFile proxyFile : basePath.listFilesRecursively(classFilter))
{
String proxyFileName= proxyFile.getRelativePath(basePath.getAbsolutePath());
String proxyTypeName= proxyFileName.substring(0, proxyFileName.length() - (classEnding.length())).replace(File.separatorChar, '.');
result.put(proxyTypeName, proxyFile);
}
return result;
}
private static long getLastModifiedProxy()
{
long result= 0;
for (UniversalFile proxyFile : proxies.values())
{
long lastModified= proxyFile.getLastModified();
if (lastModified > result)
{
result= lastModified;
}
}
return result;
}
}