Skip to content

Commit

Permalink
Allow Quasar to work with multiple classloaders.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikehearn committed Jun 29, 2016
1 parent 521cab9 commit a7f9a54
Show file tree
Hide file tree
Showing 13 changed files with 415 additions and 110 deletions.
11 changes: 10 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -424,6 +424,15 @@ project (':quasar-core') {
compileClasspath += jdk8.output + jdk8test.compileClasspath + jdk8test.output
runtimeClasspath += compileClasspath + jdk8test.runtimeClasspath
}

classloadertest {
java {
srcDir 'src/classloadertest/java'
}

compileClasspath += jdk7.output + test.compileClasspath
runtimeClasspath += compileClasspath + test.runtimeClasspath
}
}

configurations {
Expand Down Expand Up @@ -496,7 +505,7 @@ project (':quasar-core') {
}
}

def testTask = task("${set.name}Test", type: Test, dependsOn: shadowJarTask) {
def testTask = task("${set.name}Test", type: Test, dependsOn: [shadowJarTask, compileClassloadertestJava]) {
testClassesDir = project.sourceSets["${set.name}test"].output.classesDir
classpath = project.sourceSets["${set.name}test"].runtimeClasspath

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package co.paralleluniverse.fibers.dynamic;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;

import java.util.ArrayList;

public class DynamicallyLoadedFiber extends Fiber<ArrayList<String>> {
@Override
protected ArrayList<String> run() throws SuspendExecution, InterruptedException {
ArrayList<String> results = new ArrayList<>();
Test testClass = new Test();
results.add("a");
Fiber.park();
results.add("b");
testClass.test(results);
results.add("c");
return results;
}

private static class Base {
public void baseTest(ArrayList<String> results) throws SuspendExecution {
results.add("base1");
Fiber.park();
results.add("base2");
}
}

private static class Test extends Base {
public void test(ArrayList<String> results) throws SuspendExecution {
results.add("o1");
Fiber.park();
results.add("o2");
baseTest(results);
results.add("o3");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package co.paralleluniverse.fibers.dynamic;

import co.paralleluniverse.common.test.TestInterface;
import co.paralleluniverse.common.test.TestInterface2;
import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.fibers.Suspendable;

import java.util.ArrayList;

public class DynamicallyLoadedSuspendable implements TestInterface {
@Suspendable
public void test(ArrayList<String> results) {
results.add("a");
TestInterface referencedSuspend = new ReferencedSuspendable();
TestInterface2 indirectSuspend = new IndirectSuspendable();
results.add("b");
try {
results.add("c");
Fiber.park();
results.add("d");
} catch (SuspendExecution ex) {

}
results.add("e");
referencedSuspend.test(results);
results.add("f");
indirectSuspend.test(results, referencedSuspend);
results.add("g");
while (true) {
results.add("h");
indirectSuspend.test(results, referencedSuspend);
results.add("i");
}
}

private static class BaseSuspendable {
@Suspendable
public void baseTest(ArrayList<String> results) {
try {
results.add("b1");
Fiber.park();
results.add("b2");
} catch (SuspendExecution ex) {

}
}
}

private static class ReferencedSuspendable extends BaseSuspendable implements TestInterface {
@Override
@Suspendable
public void test(ArrayList<String> results) {
try {
results.add("d1");
Fiber.park();
results.add("d2");
} catch (SuspendExecution ex) {

}
baseTest(results);
}
}

private static class IndirectSuspendable extends BaseSuspendable implements TestInterface2 {
@Override
@Suspendable
public void test(ArrayList<String> results, TestInterface target) {
results.add("o1");
target.test(results);
results.add("o2");
baseTest(results);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package co.paralleluniverse.common.test;

import co.paralleluniverse.fibers.Suspendable;

import java.util.ArrayList;

public interface TestInterface {
@Suspendable
void test(ArrayList<String> results);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package co.paralleluniverse.common.test;

import co.paralleluniverse.fibers.Suspendable;

import java.util.ArrayList;

public interface TestInterface2 {
@Suspendable
void test(ArrayList<String> results, TestInterface target);
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,19 +41,12 @@
*/
package co.paralleluniverse.fibers.instrument;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;

import java.io.*;
import java.net.*;
import java.util.*;

/**
* <p>
Expand Down Expand Up @@ -174,20 +167,20 @@ public void error(String msg, Throwable ex) {
instrumentor.log(LogLevel.INFO, "Instrumenting " + instrumentor.getWorkList().size() + " classes");

for (MethodDatabase.WorkListEntry f : instrumentor.getWorkList())
instrumentClass(instrumentor, f);
instrumentClass(cl, instrumentor, f);

} catch (Exception ex) {
log(ex.getMessage());
throw new BuildException(ex.getMessage(), ex);
}
}

private void instrumentClass(QuasarInstrumentor instrumentor, MethodDatabase.WorkListEntry entry) {
private void instrumentClass(ClassLoader cl, QuasarInstrumentor instrumentor, MethodDatabase.WorkListEntry entry) {
if (!instrumentor.shouldInstrument(entry.name))
return;
try {
try (FileInputStream fis = new FileInputStream(entry.file)) {
final byte[] newClass = instrumentor.instrumentClass(entry.name, fis);
final byte[] newClass = instrumentor.instrumentClass(cl, entry.name, fis);

if (writeClasses) {
try (FileOutputStream fos = new FileOutputStream(entry.file)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public void error(String msg, Throwable exc) {
});

Retransform.instrumentation = instrumentation;
Retransform.db = instrumentor.getMethodDatabase();
Retransform.db = instrumentor.getMethodDatabase(cl);
Retransform.classLoaders = classLoaders;

instrumentation.addTransformer(new Transformer(instrumentor), true);
Expand Down Expand Up @@ -184,7 +184,10 @@ public byte[] transform(ClassLoader loader, String className, Class<?> classBein
classLoaders.add(new WeakReference<>(loader));

try {
final byte[] transformed = instrumentor.instrumentClass(className, classfileBuffer);
if (loader == null) {
loader = Thread.currentThread().getContextClassLoader();
}
final byte[] transformed = instrumentor.instrumentClass(loader, className, classfileBuffer);

if (transformed != null)
Retransform.afterTransform(className, classBeingRedefined, transformed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,13 @@
*/
package co.paralleluniverse.fibers.instrument;

import static co.paralleluniverse.fibers.instrument.Classes.isYieldMethod;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.*;

import java.io.*;
import java.lang.ref.*;
import java.util.*;

import static co.paralleluniverse.fibers.instrument.Classes.*;

/**
* <p>
Expand All @@ -67,7 +60,7 @@
*/
public class MethodDatabase implements Log {
private static final int ASMAPI = Opcodes.ASM5;
private final ClassLoader cl;
private final WeakReference<ClassLoader> clRef;
private final SuspendableClassifier classifier;
private final NavigableMap<String, ClassEntry> classes;
private final HashMap<String, String> superClasses;
Expand All @@ -83,7 +76,7 @@ public MethodDatabase(ClassLoader classloader, SuspendableClassifier classifier)
if (classloader == null)
throw new NullPointerException("classloader");

this.cl = classloader;
this.clRef = new WeakReference<>(classloader);
this.classifier = classifier;

classes = new TreeMap<>();
Expand Down Expand Up @@ -349,6 +342,7 @@ public ArrayList<WorkListEntry> getWorkList() {
}

protected ClassEntry checkClass(String className) {
ClassLoader cl = clRef.get();
if (cl == null) {
log(LogLevel.INFO, "Can't check class: %s", className);
return null;
Expand Down Expand Up @@ -400,6 +394,10 @@ private CheckInstrumentationVisitor checkFileAndClose(InputStream is, String nam
}

private String extractSuperClass(String className) {
ClassLoader cl = clRef.get();
if (cl == null) {
return null;
}
final InputStream is = cl.getResourceAsStream(className + ".class");
if (is != null) {
try {
Expand Down
Loading

0 comments on commit a7f9a54

Please sign in to comment.