/*
* This file is part of the X10 project (http://x10-lang.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* (C) Copyright IBM Corporation 2006-2010.
*/
package x10.types;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import polyglot.frontend.ClassPathResourceLoader;
import polyglot.frontend.Compiler;
import polyglot.frontend.CyclicDependencyException;
import polyglot.frontend.ExtensionInfo;
import polyglot.frontend.FileSource;
import polyglot.frontend.Goal;
import polyglot.frontend.Job;
import polyglot.frontend.Resource;
import polyglot.frontend.Scheduler;
import polyglot.main.Reporter;
import polyglot.types.NoClassException;
import polyglot.types.Package;
import polyglot.types.QName;
import polyglot.types.SemanticException;
import polyglot.types.TopLevelResolver;
import polyglot.types.Type;
import polyglot.types.TypeSystem;
import polyglot.types.TypeSystem_c;
import polyglot.types.reflect.ClassFile;
import polyglot.types.reflect.ClassFileLoader;
import polyglot.util.CollectionUtil;
import x10.util.CollectionFactory;
import polyglot.util.InternalCompilerError;
/**
* Loads class information from class files, or serialized class infomation from
* within class files. It does not load from source files.
*/
public class X10SourceClassResolver implements TopLevelResolver {
protected TypeSystem ts;
protected Reporter reporter;
protected String classpath;
protected Set<QName> nocache;
protected final static Collection<String> report_topics = CollectionUtil.list(Reporter.types, Reporter.resolver, Reporter.loader);
protected Compiler compiler;
protected ExtensionInfo ext;
protected boolean compileCommandLineOnly;
protected boolean ignoreModTimes;
/**
* Create a loaded class resolver.
*
* @param compiler
* The compiler.
* @param ext
* The extension to load sources for.
* @param classpath
* The class path.
* @param compileCommandLineOnly
* TODO
* @param ignoreModTimes
* TODO
*/
public X10SourceClassResolver(Compiler compiler, ExtensionInfo ext, String classpath, boolean compileCommandLineOnly, boolean ignoreModTimes) {
this.ts = (TypeSystem) ext.typeSystem();
this.reporter = ext.getOptions().reporter;
this.classpath = classpath;
this.nocache = CollectionFactory.newHashSet();
this.compiler = compiler;
this.ext = ext;
this.compileCommandLineOnly = compileCommandLineOnly;
this.ignoreModTimes = ignoreModTimes;
}
ClassPathResourceLoader loader;
/**
* Load an X10-compiled class file for X10 class <code>name</code>.
*/
protected Resource loadX10CompiledFile(QName name) {
// DISABLE due to XTENLANG-2326. See comment in X10CPPSourceClassResolver.loadFile(QName).
if (true) return null;
// TODO: if enabled, check that it's an X10-generated class file.
if (nocache.contains(name)) {
return null;
}
if (loader == null)
loader = new ClassPathResourceLoader(classpath, reporter);
try {
String fileName = name.toString().replace('.', '/');
fileName += ".class";
Resource clazz = loader.loadResource(fileName);
if (clazz == null) {
if (reporter.should_report(report_topics, 4)) {
reporter.report(4, "Class " + name + " not found in classpath " + loader.classpath());
}
}
else {
if (reporter.should_report(report_topics, 4)) {
reporter.report(4, "Class " + name + " found in classpath " + loader.classpath());
}
return clazz;
}
}
catch (ClassFormatError e) {
if (reporter.should_report(report_topics, 4))
reporter.report(4, "Class " + name + " format error");
}
nocache.add(name);
return null;
}
public Type getEncodedType(Resource clazz, QName name) throws SemanticException {
return null;
}
ClassFileLoader classLoader;
/**
* Load an Java class file for Java class <code>name</code>.
*/
protected ClassFile loadJavaClassFile(QName name) {
if (nocache.contains(name)) {
return null;
}
if (loader == null)
loader = new ClassPathResourceLoader(classpath, reporter);
if (classLoader == null)
classLoader = new ClassFileLoader(ext);
try {
String fileName = name.toString().replace('.', '/');
fileName += ".class";
Resource r = loader.loadResource(fileName);
ClassFile clazz = classLoader.loadClass(r);
if (clazz == null) {
if (reporter.should_report(report_topics, 4)) {
reporter.report(4, "Java class " + name + " not found in classpath " + loader.classpath());
}
}
else {
if (reporter.should_report(report_topics, 4)) {
reporter.report(4, "Java class " + name + " found in classpath " + loader.classpath());
}
return clazz;
}
}
catch (ClassFormatError e) {
if (reporter.should_report(report_topics, 4))
reporter.report(4, "Class " + name + " format error");
}
nocache.add(name);
return null;
}
/**
* Manifest support.
* @param name
* @return whether the given class should be considered part of the final executable
*/
protected boolean isOutput(QName name) {
String fname = name.toString().replace('.', '/')+".x10"; // FIXME: hard-codes the source extension.
return !((x10.ExtensionInfo) ext).manifestContains(fname);
}
/**
* Manifest support.
* @param name
* @return whether the given class should be compiled
*/
private boolean shouldCompile(QName name) {
return !compileCommandLineOnly && isOutput(name);
}
public static final QName VOID = QName.make("void");
public static final QName JAVA_LANG_OBJECT = QName.make("java.lang.Object");
public static final QName JAVA_LANG_STRING = QName.make("java.lang.String");
public static final QName JAVA_LANG_THROWABLE = QName.make("java.lang.Throwable");
public static final QName JAVA_LANG_EXCEPTION = QName.make("java.lang.Exception");
public static final QName JAVA_LANG_ERROR = QName.make("java.lang.Error");
public static final QName JAVA_LANG_RUNTIMEEXCEPTION = QName.make("java.lang.RuntimeException");
public List<Type> find(QName name) throws SemanticException {
TypeSystem_c ts = (TypeSystem_c) this.ts;
if (name.equals(VOID))
return CollectionUtil.<Type>list(ts.Void());
if (reporter.should_report(report_topics, 3))
reporter.report(3, "SourceCR.find(" + name + ")");
Resource clazz = null;
FileSource source = null;
// First try the class file.
clazz = loadX10CompiledFile(name);
// Now, try to find the source file.
source = ext.sourceLoader().classSource(name);
// Check if a job for the source already exists.
if (source != null && ext.scheduler().sourceHasJob(source)) {
// the source has already been compiled; what are we doing here?
return getTypesFromSource(source, name, shouldCompile(name));
}
// Check if the .class file exists; if so we don't need to compile the source completely.
// We decide which to use based on modification times.
if (clazz != null) {
long classModTime = clazz.file().lastModified();
long sourceModTime = source.lastModified().getTime();
if (!ignoreModTimes && classModTime < sourceModTime) {
if (reporter.should_report(report_topics, 3))
reporter.report(3, "Source file version is newer than compiled for " + name + ".");
clazz = null;
} else {
handleUpToDateTarget(source, name, clazz);
}
}
List<Type> result = null;
if (clazz != null) {
if (reporter.should_report(report_topics, 4))
reporter.report(4, "Using encoded class type for " + name);
try {
result = CollectionUtil.<Type>list(getEncodedType(clazz, name));
}
catch (SemanticException e) {
if (reporter.should_report(report_topics, 4))
reporter.report(4, "Could not load encoded class " + name);
clazz = null;
}
}
if (result == null && source != null) {
if (reporter.should_report(report_topics, 4))
reporter.report(4, "Using source file for " + name);
result = getTypesFromSource(source, name, shouldCompile(name) && clazz == null);
}
// Verify that the type we loaded has the right name. This prevents,
// for example, requesting a type through its mangled (class file) name.
if (result != null) {
return result;
}
// XTENLANG-2118: Intercept some known Java types
if (name.equals(JAVA_LANG_OBJECT)) return CollectionUtil.<Type>list(ts.Any());
if (name.equals(JAVA_LANG_STRING)) return CollectionUtil.<Type>list(ts.String());
if (name.equals(JAVA_LANG_THROWABLE)) return CollectionUtil.<Type>list(ts.CheckedThrowable());
if (name.equals(JAVA_LANG_EXCEPTION)) return CollectionUtil.<Type>list(ts.CheckedException());
if (name.equals(JAVA_LANG_RUNTIMEEXCEPTION)) return CollectionUtil.<Type>list(ts.Exception());
// XTENLANG-2118: Load the type from a Java class file
ClassFile jClazz = loadJavaClassFile(name);
if (jClazz != null) {
return CollectionUtil.<Type>list(ts.classFileLazyClassInitializer(jClazz).type().asType());
}
throw new NoClassException(name.toString());
}
public Type findOne(QName name) throws SemanticException {
List<Type> res = find(name);
if (res == null || res.size() != 1)
throw new InternalCompilerError("Unexpected result when looking up "+name+": "+res);
return res.get(0);
}
protected void handleUpToDateTarget(FileSource source, QName name, Resource file) {
}
public Package findPackage(QName name) throws SemanticException {
if (packageExists(name))
return ts.createPackage(name);
throw new SemanticException("Package "+name+" not found");
}
public boolean packageExists(QName name) {
if (ext.sourceLoader().packageExists(name)) {
return true;
}
return false;
}
protected List<Type> getTypesFromSource(FileSource source, QName name, boolean compile) throws SemanticException {
Scheduler scheduler = ext.scheduler();
Job job = scheduler.loadSource(source, compile);
if (reporter.should_report("sourceloader", 3))
new Exception("loaded " + source).printStackTrace();
if (job != null) {
List<Type> n = ts.systemResolver().check(name);
if (n != null) {
return n;
}
Goal g = scheduler.PreTypeCheck(job);
if (!scheduler.reached(g)) {
try {
scheduler.attempt(g);
}
catch (CyclicDependencyException e) {
throw new InternalCompilerError("Could not initialize symbol table for " + source + "; cyclic dependency found.", e);
}
}
n = ts.systemResolver().check(name);
if (n != null) {
return n;
}
}
// The source has already been compiled, but the type was not created
// there.
throw new NoClassException("Could not find \"" + name + "\" in " + source + ".");
}
}