Skip to content

Commit

Permalink
performance: cache inputClassLoader corresponding to sourceClasspath (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky authored and monperrus committed Nov 21, 2016
1 parent 352b59b commit 80a8aee
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 1 deletion.
12 changes: 11 additions & 1 deletion src/main/java/spoon/support/StandardEnvironment.java
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,12 @@ public void setTabulationSize(int tabulationSize) {
}

private ClassLoader classloader;
/*
* cache class loader which loads classes from source class path
* we must cache it to make all the loaded classes compatible
* The cache is reset when setSourceClasspath(...) is called
*/
private ClassLoader inputClassloader;

@Override
public void setInputClassLoader(ClassLoader aClassLoader) {
Expand All @@ -355,7 +361,10 @@ public ClassLoader getInputClassLoader() {
if (classloader != null) {
return classloader;
}
return new URLClassLoader(urlClasspath(), Thread.currentThread().getContextClassLoader());
if (inputClassloader == null) {
inputClassloader = new URLClassLoader(urlClasspath(), Thread.currentThread().getContextClassLoader());
}
return inputClassloader;
}

@Override
Expand Down Expand Up @@ -389,6 +398,7 @@ public String[] getSourceClasspath() {
public void setSourceClasspath(String[] sourceClasspath) {
verifySourceClasspath(sourceClasspath);
this.sourceClasspath = sourceClasspath;
this.inputClassloader = null;
}

private void verifySourceClasspath(String[] sourceClasspath) throws InvalidClassPathException {
Expand Down
59 changes: 59 additions & 0 deletions src/test/java/spoon/test/compilation/CompilationTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.junit.Assert;
import org.junit.Test;
import spoon.Launcher;
import spoon.SpoonModelBuilder;
import spoon.compiler.SpoonCompiler;
import spoon.reflect.code.BinaryOperatorKind;
import spoon.reflect.code.CtBinaryOperator;
Expand All @@ -19,6 +20,7 @@
import spoon.reflect.reference.CtTypeReference;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.support.compiler.FileSystemFolder;
import spoon.support.compiler.jdt.JDTBasedSpoonCompiler;
import spoon.support.compiler.jdt.JDTBatchCompiler;
import spoon.support.SpoonClassNotFoundException;
Expand Down Expand Up @@ -263,6 +265,7 @@ public void testPrecompile() {
}


@Test
public void testClassLoader() throws Exception {
// contract: the environment exposes a classloader configured by the spoonclass path
Launcher launcher = new Launcher();
Expand All @@ -286,6 +289,62 @@ public void testClassLoader() throws Exception {
Class c = launcher.getEnvironment().getInputClassLoader().loadClass("spoontest.a.ClassA");
assertEquals("spoontest.a.ClassA", c.getName());
}

@Test
public void testSingleClassLoader() throws Exception {
/*
* contract: the environment exposes a classloader configured by the spoonclass path,
* there is one class loader, so the loaded classes are compatible
*/
Launcher launcher = new Launcher();
launcher.addInputResource(new FileSystemFolder("./src/test/resources/classloader-test"));
File outputBinDirectory = new File("./target/classloader-test");
if (!outputBinDirectory.exists()) {
outputBinDirectory.mkdirs();
}
launcher.setBinaryOutputDirectory(outputBinDirectory);
launcher.getModelBuilder().build();

CtTypeReference<?> mIFoo = launcher.getFactory().Type().createReference("spoontest.IFoo");
CtTypeReference<?> mFoo = launcher.getFactory().Type().createReference("spoontest.Foo");
assertTrue("Foo subtype of IFoo", mFoo.isSubtypeOf(mIFoo));

launcher.getModelBuilder().compile(SpoonModelBuilder.InputType.FILES);

//Create new launcher which uses classes compiled by previous launcher.
//It simulates the classes without sources, which has to be accessed using reflection
launcher = new Launcher();

// not in the classpath
try {
Class.forName("spoontest.IFoo");
fail();
} catch (ClassNotFoundException expected) {
}

// not in the spoon classpath before setting it
try {
launcher.getEnvironment().getInputClassLoader().loadClass("spoontest.IFoo");
fail();
} catch (ClassNotFoundException expected) {
}

launcher.getEnvironment().setSourceClasspath(new String[]{outputBinDirectory.getAbsolutePath()});

mIFoo = launcher.getFactory().Type().createReference("spoontest.IFoo");
mFoo = launcher.getFactory().Type().createReference("spoontest.Foo");
//if it fails then it is because each class is loaded by different class loader
assertTrue("Foo subtype of IFoo", mFoo.isSubtypeOf(mIFoo));


// not in the spoon classpath before setting it
Class<?> ifoo = launcher.getEnvironment().getInputClassLoader().loadClass("spoontest.IFoo");
Class<?> foo = launcher.getEnvironment().getInputClassLoader().loadClass("spoontest.Foo");

assertTrue(ifoo.isAssignableFrom(foo));
assertTrue(ifoo.getClassLoader()==foo.getClassLoader());
}


@Test
public void testExoticClassLoader() throws Exception {
Expand Down
8 changes: 8 additions & 0 deletions src/test/resources/classloader-test/spoontest/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package spoontest;

public class Foo implements IFoo
{
public Foo()
{
}
}
6 changes: 6 additions & 0 deletions src/test/resources/classloader-test/spoontest/IFoo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package spoontest;

public interface IFoo
{

}

0 comments on commit 80a8aee

Please sign in to comment.