Skip to content

Commit

Permalink
feature(runtime): allow to instantiate any class at any time
Browse files Browse the repository at this point in the history
  • Loading branch information
monperrus committed Oct 10, 2016
1 parent 2949d6c commit 71211bc
Show file tree
Hide file tree
Showing 25 changed files with 485 additions and 241 deletions.
22 changes: 13 additions & 9 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,15 @@
</resources>

<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.16</version>
<configuration>
<useSystemClassLoader>false</useSystemClassLoader>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
Expand Down Expand Up @@ -502,14 +511,6 @@
</lifecycleMappingMetadata>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<additionalparam>-Xdoclint:-missing</additionalparam>
<quiet>true</quiet>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<extensions>
Expand Down Expand Up @@ -562,7 +563,10 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<additionalparam>-Xdoclint:-missing</additionalparam>
<quiet>true</quiet>
</configuration>
</plugin>
</plugins>
</reporting>
Expand Down
33 changes: 17 additions & 16 deletions src/main/java/spoon/Launcher.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.apache.log4j.Level;
import org.apache.log4j.Logger;

import spoon.SpoonModelBuilder.InputType;
import spoon.compiler.Environment;
import spoon.compiler.SpoonCompiler;
import spoon.compiler.SpoonResource;
Expand Down Expand Up @@ -68,11 +69,13 @@
*/
public class Launcher implements SpoonAPI {

public static final String SPOONED_CLASSES = "spooned-classes";

public static final String OUTPUTDIR = "spooned";

private final Factory factory;

private SpoonCompiler modelBuilder;
private SpoonModelBuilder modelBuilder;

private String[] commandLineArgs = new String[0];

Expand Down Expand Up @@ -311,7 +314,7 @@ protected static JSAP defineArgs() {
opt2 = new FlaggedOption("destination");
opt2.setShortFlag('d');
opt2.setLongFlag("destination");
opt2.setDefault("spooned-classes");
opt2.setDefault(SPOONED_CLASSES);
opt2.setHelp("An optional destination directory for the generated class files.");
opt2.setStringParser(FileStringParser.getParser());
opt2.setRequired(false);
Expand All @@ -337,7 +340,7 @@ protected static JSAP defineArgs() {
// Enable compilation
sw1 = new Switch("compile");
sw1.setLongFlag(sw1.getUsageName());
sw1.setHelp("Enable compilation and output class files.");
sw1.setHelp("Compiles the resulting classes (after transformation) to bytecode.");
sw1.setDefault("false");
jsap.registerParameter(sw1);

Expand Down Expand Up @@ -568,7 +571,7 @@ public SpoonCompiler createCompiler(Factory factory) {
env.debugMessage("template classpath: " + Arrays.toString(comp.getTemplateClasspath()));

if (jsapActualArgs.getBoolean("precompile")) {
comp.compileInputSources();
comp.compile(InputType.FILES);
}

return comp;
Expand All @@ -584,8 +587,8 @@ public SpoonCompiler createCompiler(Factory factory, List<SpoonResource> inputSo
* Creates a new Spoon Java compiler in order to process and compile Java
* source code.
*/
public SpoonCompiler createCompiler(Factory factory, List<SpoonResource> inputSources, List<SpoonResource> templateSources) {
SpoonCompiler c = createCompiler(factory);
public SpoonModelBuilder createCompiler(Factory factory, List<SpoonResource> inputSources, List<SpoonResource> templateSources) {
SpoonModelBuilder c = createCompiler(factory);
c.addInputSources(inputSources);
c.addTemplateSources(templateSources);
return c;
Expand Down Expand Up @@ -634,19 +637,16 @@ public PrettyPrinter createPrettyPrinter() {
* run will perform the following tasks:
*
* <ol>
* <li>Pre-compilation (optional):
* {@link SpoonCompiler#compileInputSources()}.</li>
* <li>Source model building in the given compiler:
* {@link SpoonCompiler#build()}.</li>
* {@link SpoonModelBuilder#build()}.</li>
* <li>Template model building in the given factory (if any template source
* is given): {@link SpoonCompiler#build()}.</li>
* is given): {@link SpoonModelBuilder#build()}.</li>
* <li>Model processing with the list of given processors if any:
* {@link SpoonCompiler#instantiateAndProcess(List)}.</li>
* {@link SpoonModelBuilder#instantiateAndProcess(List)}.</li>
* <li>Processed Source code printing and generation (can be disabled with
* {@link OutputType#NO_OUTPUT}):
* {@link SpoonCompiler#generateProcessedSourceFiles(OutputType)}.</li>
* {@link SpoonModelBuilder#generateProcessedSourceFiles(OutputType)}.</li>
* <li>Processed source code compilation (optional):
* {@link SpoonCompiler#compile()}.</li>
* </ol>
*/
@Override
Expand All @@ -667,7 +667,8 @@ public void run() {
prettyprint();

if (env.shouldCompile()) {
modelBuilder.compile();
// we compile the types from the factory, they may have been modified by some processors
modelBuilder.compile(InputType.CTTYPES);
}

t = System.currentTimeMillis();
Expand Down Expand Up @@ -788,12 +789,12 @@ public boolean matches(CtType<?> element) {

@Override
public void setBinaryOutputDirectory(String path) {
setBinaryOutputDirectory(new File(path));
getFactory().getEnvironment().setBinaryOutputDirectory(path);
}

@Override
public void setBinaryOutputDirectory(File outputDirectory) {
modelBuilder.setBinaryOutputDirectory(outputDirectory);
setBinaryOutputDirectory(outputDirectory.getPath());
}

@Override
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/spoon/SpoonAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.io.File;

import spoon.compiler.Environment;
import spoon.compiler.SpoonCompiler;
import spoon.processing.Processor;
import spoon.reflect.CtModel;
import spoon.reflect.declaration.CtElement;
Expand Down Expand Up @@ -144,7 +143,7 @@ public interface SpoonAPI {
/**
* Creates a new Spoon compiler (for building the model)
*/
SpoonCompiler createCompiler();
SpoonModelBuilder createCompiler();

/** Returns the model built from the sources given via {@link #addInputResource(String)} */
CtModel getModel();
Expand Down
26 changes: 15 additions & 11 deletions src/main/java/spoon/SpoonModelBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,21 +76,29 @@ public interface SpoonModelBuilder {
*/
boolean build(JDTBuilder builder);

/** The types of compilable elements */
enum InputType {
FILES, CTTYPES
}


/**
* Generates the bytecode associated to the classes stored in this
* compiler's factory. The bytecode is generated in the directory given by
* {@link #getBinaryOutputDirectory()}.
*
* @see #getSourceClasspath()
*/
boolean compile();

/**
* Generates the bytecode by compiling the input sources. The bytecode is
* generated in the directory given by {@link #getBinaryOutputDirectory()}.
* The array of types must be of size 0 or 1. If it's empty,
* the types of the factory are compiled.
* If it's InputType.FILES, the files given as input are compiled.
*
*Note that the varargs ... enables this version to be backward compatible for callers.
*
* @see #getSourceClasspath()
*/
boolean compile(InputType... types);

/** replaced by {@link #compile(InputType...)} */
@Deprecated
boolean compileInputSources();

/**
Expand Down Expand Up @@ -233,10 +241,6 @@ public interface SpoonModelBuilder {

/**
* Gets the classpath that is used to build/compile the input sources.
*
* @see #compileInputSources()
* @see #build()
* @see #compile()
*/
String[] getSourceClasspath();

Expand Down
7 changes: 7 additions & 0 deletions src/main/java/spoon/compiler/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package spoon.compiler;

import org.apache.log4j.Level;

import spoon.processing.FileGenerator;
import spoon.processing.ProblemFixer;
import spoon.processing.ProcessingManager;
Expand Down Expand Up @@ -335,4 +336,10 @@ void report(Processor<?> processor, Level level,
* true means that no self checks are made.
*/
void setSelfChecks(boolean skip);

/** Return the directory where binary .class files are created */
void setBinaryOutputDirectory(String directory);

/** Set the directory where binary .class files are created */
String getBinaryOutputDirectory();
}
1 change: 1 addition & 0 deletions src/main/java/spoon/compiler/SpoonCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,5 @@
*
* Will soon be deprecated.
*/
@Deprecated
public interface SpoonCompiler extends spoon.SpoonModelBuilder { }
2 changes: 1 addition & 1 deletion src/main/java/spoon/reflect/CtModelImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public String toString() {

}

private CtPackage rootPackage = new CtRootPackage();
private final CtPackage rootPackage = new CtRootPackage();

public CtModelImpl(Factory f) {
rootPackage.setFactory(f);
Expand Down
17 changes: 16 additions & 1 deletion src/main/java/spoon/reflect/declaration/CtClass.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import spoon.reflect.code.CtStatement;
import spoon.reflect.reference.CtTypeReference;

import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -93,4 +92,20 @@ public interface CtClass<T extends Object> extends CtType<T>, CtStatement {

@Override
CtClass<T> clone();

/**
* Creates an instance of this class.
*
* Requirements:
* - the class must have a default constructor.
* - All dependencies (superclass, super-interfaces, imports) must be in the classpath,
* because the code is actually compiled (otherwise an exception is thrown)
*
* If the class has super-interfaces, the object can be cast to one of them.
* Otherwise, if the class has no super-interfaces, the methods can only be called with reflection.
*
* This instance is meant to be used for quick-testing, it uses a throwable classloader that
* will be garbage-collected with the instance.
*/
T newInstance();
}
5 changes: 3 additions & 2 deletions src/main/java/spoon/reflect/factory/TypeFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
Expand All @@ -46,11 +47,11 @@
*/
public class TypeFactory extends SubFactory {

private static final Set<String> NULL_PACKAGE_CLASSES = new HashSet<String>(
private static final Set<String> NULL_PACKAGE_CLASSES = Collections.unmodifiableSet(new HashSet<String>(
Arrays.asList("void", "boolean", "byte", "short", "char", "int", "float", "long",
"double",
// TODO (leventov) it is questionable to me that nulltype should also be here
CtTypeReference.NULL_TYPE_NAME));
CtTypeReference.NULL_TYPE_NAME)));

public final CtTypeReference<?> NULL_TYPE = createReference(CtTypeReference.NULL_TYPE_NAME);
public final CtTypeReference<Void> VOID = createReference(Void.class);
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/spoon/support/StandardEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;

import spoon.Launcher;
import spoon.SpoonException;
import spoon.compiler.Environment;
import spoon.compiler.InvalidClassPathException;
Expand Down Expand Up @@ -95,7 +96,7 @@ public class StandardEnvironment implements Serializable, Environment {

private Level level = Level.OFF;

private boolean shouldCompile;
private boolean shouldCompile = false;

private boolean skipSelfChecks;

Expand Down Expand Up @@ -505,4 +506,17 @@ public boolean isCommentsEnabled() {
public void setCommentEnabled(boolean commentEnabled) {
this.enableComments = commentEnabled;
}

private String binaryOutputDirectory = Launcher.SPOONED_CLASSES;

@Override
public void setBinaryOutputDirectory(String s) {
this.binaryOutputDirectory = s;

}

@Override
public String getBinaryOutputDirectory() {
return binaryOutputDirectory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ private static CtStatement internalCompileStatement(CtElement st) {
// Clean up
c.getPackage().getTypes().remove(c);

if (ret instanceof CtClass) {
CtClass klass = (CtClass) ret;
klass.setSimpleName(klass.getSimpleName().replaceAll("^[0-9]*", ""));
klass.setParent(ret.getFactory().Package().getRootPackage());
ret.getFactory().Package().getRootPackage().addType(klass);
}
return ret;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package spoon.support.compiler.jdt;

import org.eclipse.jdt.internal.compiler.batch.CompilationUnit;

import spoon.reflect.declaration.CtType;
import spoon.reflect.visitor.DefaultJavaPrettyPrinter;

Expand All @@ -25,33 +26,29 @@

class CompilationUnitWrapper extends CompilationUnit {

private final JDTBasedSpoonCompiler jdtCompiler;
private CtType type;

CompilationUnitWrapper(JDTBasedSpoonCompiler jdtCompiler, CtType type) {
super(null, type.getPosition().getFile() != null ? type.getPosition().getFile().getAbsolutePath() : "",
null,
null,
CompilationUnitWrapper(CtType type) {
// char[] contents, String fileName, String encoding, String destinationPath, boolean ignoreOptionalProblems
super(null,
type.getSimpleName() + ".java",
"UTF-8",
type.getFactory().getEnvironment().getBinaryOutputDirectory(),
false);
this.jdtCompiler = jdtCompiler;
this.type = type;
}

@Override
public char[] getContents() {
if (jdtCompiler.loadedContent.containsKey(type.getQualifiedName())) {
return jdtCompiler.loadedContent.get(type.getQualifiedName());
}

DefaultJavaPrettyPrinter printer = new DefaultJavaPrettyPrinter(type.getFactory().getEnvironment());
List<CtType<?>> types = new ArrayList<>();
types.add(type);
printer.calculate(type.getPosition().getCompilationUnit(), types);

String result = printer.getResult();
char[] content = result.toCharArray();
this.jdtCompiler.loadedContent.put(type.getQualifiedName(), content);
return content;
}


}
Loading

0 comments on commit 71211bc

Please sign in to comment.