Initial version of Eclipse compiler adapter.
This adapter allows compilation of incomplete classes.
--- a/experiments/java/.classpath Wed Mar 27 13:18:41 2013 +0000
+++ b/experiments/java/.classpath Fri Mar 29 16:38:17 2013 +0000
@@ -3,7 +3,7 @@
<classpathentry kind="src" output="bin" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry combineaccessrules="false" kind="src" path="/stx.libjava"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/stx.libjava.libs"/>
<classpathentry combineaccessrules="false" kind="src" path="/stx.libjava.tools"/>
- <classpathentry combineaccessrules="false" kind="src" path="/stx.libjava.libs"/>
<classpathentry kind="output" path="bin"/>
</classpath>
--- a/experiments/java/src/stx/libjava/tools/compiler/JavaCompilerAdapter.java Wed Mar 27 13:18:41 2013 +0000
+++ b/experiments/java/src/stx/libjava/tools/compiler/JavaCompilerAdapter.java Fri Mar 29 16:38:17 2013 +0000
@@ -27,13 +27,13 @@
this.setCompiler(compiler);
}
-
-
public void setCompiler(JavaCompiler compiler) {
this.compiler = compiler;
this.classFileManager = new JavaClassFileManager(compiler.getStandardFileManager(null, null, null));
}
+
+
public Collection<JavaClassFile> getClassFiles() {
return classFileManager.getClasses().values();
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/experiments/java/src/stx/libjava/tools/compiler/ecj/CompilationUnit.java Fri Mar 29 16:38:17 2013 +0000
@@ -0,0 +1,66 @@
+package stx.libjava.tools.compiler.ecj;
+
+import java.io.File;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+
+public class CompilationUnit implements ICompilationUnit {
+ /**
+ * A full Java (dotted) type name, i.e., java.lang.String.
+ * If compilation unit contains more than one type, this is the name
+ * of the main type.
+ */
+ protected String name;
+
+ /**
+ * Source of the class
+ */
+ protected String source;
+
+ /**
+ * Creates a new compilation unit.
+ *
+ * @param name A fully qualified Java name of the main type contained in the source.
+ * @param source Source code
+ */
+ public CompilationUnit(String name,String source) {
+ this.name = name;
+ this.source = source;
+ }
+
+ @Override
+ public char[] getFileName() {
+ return (name.substring(name.lastIndexOf('.') + 1) + ".java").toCharArray();
+ }
+
+ @Override
+ public char[] getContents() {
+ return source.toCharArray();
+ }
+
+ @Override
+ public char[] getMainTypeName() {
+ return name.substring(name.lastIndexOf('.') + 1).toCharArray();
+ }
+
+ @Override
+ public char[][] getPackageName() {
+ char[] nameC = name.toCharArray();
+ int ncomponents = 0;
+ int i;
+ for (i = 0; i < nameC.length; i++) {
+ if (nameC[i] == '.') ncomponents++;
+ }
+ char[][] packagename = new char[ncomponents][];
+ int j = 0;
+ int ci = 0;
+ for (i = 0; i < nameC.length; i++) {
+ if (nameC[i] == '.') {
+ char[] component = new char[i - j];
+ packagename[ci++] = component;
+ System.arraycopy(nameC, j, component, 0, i-j);
+ j = i+1;
+ }
+ }
+ return packagename;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/experiments/java/src/stx/libjava/tools/compiler/ecj/Compiler.java Fri Mar 29 16:38:17 2013 +0000
@@ -0,0 +1,84 @@
+package stx.libjava.tools.compiler.ecj;
+
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.tools.ToolProvider;
+
+import org.eclipse.jdt.internal.compiler.CompilationResult;
+import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
+import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
+import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
+import org.eclipse.jdt.internal.compiler.IProblemFactory;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
+import org.eclipse.jdt.internal.compiler.tool.EclipseFileManager;
+
+import stx.libjava.tools.compiler.JavaClassFile;
+import stx.libjava.tools.compiler.JavaClassFileManager;
+import stx.libjava.tools.compiler.ecj.CompilationUnit;
+
+public class Compiler implements ICompilerRequestor {
+ protected JavaClassFileManager classFileManager = new JavaClassFileManager(new EclipseFileManager(Locale.getDefault(), Charset.defaultCharset()));
+ protected CompilationResult result;
+
+
+ public CompilationResult getResult() {
+ return result;
+ }
+
+ /**
+ * Compiles classes in given source. The resulting .class files are added
+ * to an internal list which can be later retrieved by getClassFiles().
+ *
+ * @param name unqualified name of class being compiled.
+ * @param source source code of the class as String.
+ * @return true, if compilation succeeded, false otherwise.
+ */
+ public boolean compile(String name, String source) {
+ INameEnvironment environment = new CompilerEnvironment();
+ ICompilerRequestor requestor = this;
+ IErrorHandlingPolicy policy = DefaultErrorHandlingPolicies.exitAfterAllProblems();
+ IProblemFactory problemFactory = new DefaultProblemFactory(Locale.getDefault());
+ ICompilationUnit[] units = new ICompilationUnit[1];
+ units[0] = new CompilationUnit(name, source);
+
+ org.eclipse.jdt.internal.compiler.Compiler compiler = new org.eclipse.jdt.internal.compiler.Compiler(environment, policy, getDefaultCompilerSettings(), requestor, problemFactory);
+ compiler.compile(units);
+ return false;
+ }
+
+ /**
+ * Returns a list of JavaClassFiles that contains results of the compilation.
+ *
+ * @return resulting class files
+ */
+ public Collection<JavaClassFile> getClassFiles() {
+ return classFileManager.getClasses().values();
+ }
+
+ public static Map<String, Object> getDefaultCompilerSettings() {
+ String javaSpecVersion = System.getProperty("java.specification.version");
+ Map<String, Object> settings = new HashMap<String, Object>();
+ settings.put(CompilerOptions.OPTION_Source, javaSpecVersion);
+ settings.put(CompilerOptions.OPTION_TargetPlatform, javaSpecVersion);
+ settings.put(CompilerOptions.OPTION_ReportDeprecation, CompilerOptions.IGNORE);
+ return settings;
+ }
+
+ public static CompilerOptions getDefaultCompilerOptions() {
+ return new CompilerOptions(getDefaultCompilerSettings());
+ }
+
+ @Override
+ public void acceptResult(CompilationResult result) {
+ this.result = result;
+ }
+
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/experiments/java/src/stx/libjava/tools/compiler/ecj/CompilerEnvironment.java Fri Mar 29 16:38:17 2013 +0000
@@ -0,0 +1,106 @@
+package stx.libjava.tools.compiler.ecj;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
+import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
+import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
+
+public class CompilerEnvironment implements INameEnvironment {
+ /**
+ * Find a type with the given compound name.
+ * Answer the binary form of the type if it is known to be consistent.
+ * Otherwise, answer the compilation unit which defines the type
+ * or null if the type does not exist.
+ * Types in the default package are specified as {{typeName}}.
+ *
+ * It is unknown whether the package containing the type actually exists.
+ *
+ * NOTE: This method can be used to find a member type using its
+ * internal name A$B, but the source file for A is answered if the binary
+ * file is inconsistent.
+ */
+
+ public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
+ StringBuilder sb = new StringBuilder();
+ if (compoundTypeName.length > 1) {
+ for (int i = 0; i < compoundTypeName.length - 1; i++) {
+ sb.append(compoundTypeName[i]);
+ }
+ }
+ sb.append(compoundTypeName[compoundTypeName.length-1]);
+ return findType(sb.toString());
+ }
+
+ protected NameEnvironmentAnswer findType(String javaname) {
+ try {
+ Class<?> c = Class.forName(javaname);
+ ClassLoader cl = c.getClassLoader();
+ if (cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ String cfilename = (new String(c.getName())).replace('.', '/') + ".class";
+ InputStream cfs = cl.getResourceAsStream(cfilename);
+ return new NameEnvironmentAnswer(ClassFileReader.read(cfs, cfilename), null);
+ } catch (ClassNotFoundException cnfe) {
+ return null;
+ } catch (ClassFormatException e) {
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return null;
+ }
+ }
+ /**
+ * Find a type named <typeName> in the package <packageName>.
+ * Answer the binary form of the type if it is known to be consistent.
+ * Otherwise, answer the compilation unit which defines the type
+ * or null if the type does not exist.
+ * The default package is indicated by char[0][].
+ *
+ * It is known that the package containing the type exists.
+ *
+ * NOTE: This method can be used to find a member type using its
+ * internal name A$B, but the source file for A is answered if the binary
+ * file is inconsistent.
+ */
+
+ public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < packageName.length; i++) {
+ sb.append(packageName[i]);
+ sb.append('.');
+ }
+ sb.append(typeName);
+ return findType(sb.toString());
+
+ }
+ /**
+ * Answer whether packageName is the name of a known subpackage inside
+ * the package parentPackageName. A top level package is found relative to null.
+ * The default package is always assumed to exist.
+ *
+ * For example:
+ * isPackage({{java}, {awt}}, {event});
+ * isPackage(null, {java});
+ */
+
+ public boolean isPackage(char[][] parentPackageName, char[] packageName) {
+ return Character.isLowerCase(packageName[0]);
+ }
+
+ /**
+ * This method cleans the environment uo. It is responsible for releasing the memory
+ * and freeing resources. Passed that point, the name environment is no longer usable.
+ *
+ * A name environment can have a long life cycle, therefore it is the responsibility of
+ * the code which created it to decide when it is a good time to clean it up.
+ */
+ public void cleanup() {
+
+ }
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/experiments/java/src/stx/libjava/tools/compiler/ecj/tests/CompilerTests.java Fri Mar 29 16:38:17 2013 +0000
@@ -0,0 +1,72 @@
+package stx.libjava.tools.compiler.ecj.tests;
+
+import static org.junit.Assert.*;
+
+import java.lang.reflect.InvocationTargetException;
+
+import stx.libjava.tools.compiler.ecj.Compiler;
+
+import org.eclipse.jdt.internal.compiler.ClassFile;
+import org.junit.Test;
+
+public class CompilerTests {
+
+ protected class ClassLoader extends java.lang.ClassLoader {
+
+ @SuppressWarnings("deprecation")
+ public Class<?> load(byte[] classfile) {
+ return this.defineClass(classfile, 0, classfile.length);
+ }
+ }
+
+ @Test
+ public void test_01() {
+ Compiler c = new Compiler();
+ ClassLoader l = new ClassLoader();
+
+ c.compile("test.pkg.Foo", "package test.pkg; public class Foo {}");
+
+ assertFalse(c.getResult().hasErrors());
+
+ ClassFile[] classfiles = c.getResult().getClassFiles();
+
+ assertEquals(1, classfiles.length);
+
+ Class<?> clazz = l.load(classfiles[0].getBytes());
+
+ assertEquals("test.pkg.Foo" , clazz.getName());
+ }
+
+
+ /**
+ * Tests compilation of an incomplete class
+ */
+ @Test
+ public void test_02() {
+ Compiler c = new Compiler();
+ ClassLoader l = new ClassLoader();
+
+ c.compile("test.pkg.Foo", "package test.pkg; public class Foo { public void f() { g(); } }");
+ ClassFile[] classfiles = c.getResult().getClassFiles();
+
+ assertEquals(1, classfiles.length);
+
+ Class<?> fooClass = l.load(classfiles[0].getBytes());
+
+ assertEquals("test.pkg.Foo" , fooClass.getName());
+ assertEquals(1, fooClass.getDeclaredMethods().length);
+
+ try {
+ fooClass.getMethod("f").invoke(fooClass.newInstance());
+ assertTrue(false);
+ } catch (InvocationTargetException ite) {
+ assertTrue(ite.getCause() instanceof Error);
+ } catch (IllegalAccessException | IllegalArgumentException
+ | NoSuchMethodException | SecurityException | InstantiationException e) {
+ e.printStackTrace();
+ assertTrue(false);
+ }
+ }
+
+
+}
--- a/libs/java/.classpath Wed Mar 27 13:18:41 2013 +0000
+++ b/libs/java/.classpath Fri Mar 29 16:38:17 2013 +0000
@@ -26,7 +26,6 @@
<classpathentry exported="true" kind="lib" path="libs/jackson-databind-2.0.5.jar"/>
<classpathentry exported="true" kind="lib" path="libs/jasperreports-5.0.1.jar"/>
<classpathentry exported="true" kind="lib" path="libs/jcommon-1.0.15.jar"/>
- <classpathentry exported="true" kind="lib" path="libs/jdtcore-3.1.0.jar"/>
<classpathentry exported="true" kind="lib" path="libs/jfreechart-1.0.12.jar"/>
<classpathentry exported="true" kind="lib" path="libs/xml-apis-1.3.02.jar"/>
<classpathentry kind="output" path="bin"/>