diff --git a/build.gradle b/build.gradle
index 5826d79..63356da 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,6 +5,9 @@ allprojects {
repositories {
mavenCentral()
+ maven {
+ url "http://maven.springframework.org/milestone"
+ }
}
}
@@ -14,10 +17,10 @@ subprojects {
apply plugin: 'eclipse'
apply from: "$rootDir/gradle/publish-maven.gradle"
- sourceCompatibility = 1.6
- targetCompatibility = 1.6
+ sourceCompatibility = 1.6
+ targetCompatibility = 1.6
}
configure(subprojects.findAll { it.name.startsWith('testdata')}) {
tasks.findByPath("artifactoryPublish")?.enabled = false
-}
\ No newline at end of file
+}
diff --git a/settings.gradle b/settings.gradle
index 34851ef..047c69d 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,7 @@
include "springloaded"
include "testdata"
+include "testdata-java8"
+include "testdata-aspectj"
include "testdata-groovy"
include "testdata-plugin"
include "testdata-subloader"
diff --git a/springloaded/.classpath b/springloaded/.classpath
index 18ff684..720d087 100644
--- a/springloaded/.classpath
+++ b/springloaded/.classpath
@@ -2,9 +2,9 @@
-
-
-
+
+
+
diff --git a/springloaded/build.gradle b/springloaded/build.gradle
index dab5d37..984c690 100644
--- a/springloaded/build.gradle
+++ b/springloaded/build.gradle
@@ -34,14 +34,20 @@ task wrapper(type: Wrapper) {
dependencies {
tools 'com.googlecode.jarjar:jarjar:1.3'
+/*
compile 'asm:asm:3.2'
compile 'asm:asm-tree:3.2'
-
+*/
+ compile 'org.ow2.asm:asm:5.0_BETA'
+ compile 'org.ow2.asm:asm-tree:5.0_BETA'
+
testCompile 'junit:junit:4.11'
- testCompileOnly files("../testdata-groovy/groovy-1.8.2.jar")
+ testCompileOnly files("../testdata-groovy/groovy-all-1.8.6.jar")
testCompileOnly project(':testdata')
+ testCompileOnly project(':testdata-aspectj')
testCompileOnly project(':testdata-groovy')
+ testCompileOnly project(':testdata-java8')
testCompileOnly project(':testdata-plugin')
testCompileOnly project(':testdata-subloader')
testCompileOnly project(':testdata-superloader')
diff --git a/springloaded/lib/asm-5.0_BETA.jar b/springloaded/lib/asm-5.0_BETA.jar
new file mode 100644
index 0000000..2e26615
Binary files /dev/null and b/springloaded/lib/asm-5.0_BETA.jar differ
diff --git a/springloaded/lib/asm-tree-5.0_BETA.jar b/springloaded/lib/asm-tree-5.0_BETA.jar
new file mode 100644
index 0000000..e486d18
Binary files /dev/null and b/springloaded/lib/asm-tree-5.0_BETA.jar differ
diff --git a/springloaded/notes.md b/springloaded/notes.md
index b3aa627..a0aa06b 100644
--- a/springloaded/notes.md
+++ b/springloaded/notes.md
@@ -2,6 +2,7 @@
Implementation details:
catchers
+========
- A catcher is added to a reloadable type as it is being loaded for the first time. A reloadable type gets
a catcher for each method it inherits from a parent but does not override, or for an abstract class each
method it receives from an interface. They basically stand in for methods that could be added in later
@@ -12,12 +13,36 @@ private, final or static methods since those cannot be overridden. If you modif
type to make it non final and then override it, it will be handled in a different way than catchers.
superdispatchers
+================
- Where a method is protected in a non-reloadable type it is necessary to add a superdispatcher method to the
subtype so that when the executor for a new version is running it can access that protected method. The
superdispatcher is simply a public method on a type that calls super.
+Generic dispatcher method __execute
+===================================
+
+The way in which we handle new methods appearing on types is that all reloadable types get a generic handler method added to
+them when first loaded - this can forward it on to the new method that has appeared. The method is like this:
+
+__execute(Object[] params, Object target, String nameAndDescriptor)
+
+All interfaces also get this method.
+
+(#001) Lambdas introduce a problem here. The Lambda meta factory creates anonymous classes that forward to the lambda handling
+method. It does this outside of our control using a more direct form of class defining which we don't see (can't instrument). This means
+these generated classes don't get an __execute. This means if a new method is added to the SAM type, although we notice it
+we can't generate an __execute in the meta factory created class. This means the standard redirection of INVOKEINTERFACE which says:
+does this method exist on the original form of the type? yes, then call it. no, then call the __execute telling it what we'd like to run.
+Well that will fail because of the missing __execute. There are two solutions:
+- modify the InnerClassLambdaMetaFactory to ensure an __execute (and relevant marker interface) are added
+- change how we handle the INVOKEINTERFACE rewrite.
+
+The second option is cheap to implement but performance will likely suck. The simplest way to do it is call the type registry to do the
+suitable invoke via reflection - and it will recognize the lambda case and know what to do.
+
+----------
Helpful snippets when debugging tests:
ClassPrinter.print(z.getLatestExecutorBytes());
Utils.dump("foo/SubControllerB", rtype.bytesLoaded);
diff --git a/springloaded/springloaded tests (no generated tests) (1.8).launch b/springloaded/springloaded tests (no generated tests) (1.8).launch
new file mode 100644
index 0000000..b09fecc
--- /dev/null
+++ b/springloaded/springloaded tests (no generated tests) (1.8).launch
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java b/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
index a51b3ef..921524e 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ClassRenamer.java
@@ -18,11 +18,11 @@ package org.springsource.loaded;
import java.util.HashMap;
import java.util.Map;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -54,7 +54,7 @@ public class ClassRenamer {
return renamed;
}
- static class RenameAdapter extends ClassAdapter implements Opcodes {
+ static class RenameAdapter extends ClassVisitor implements Opcodes {
private ClassWriter cw;
private String oldname;
@@ -62,7 +62,7 @@ public class ClassRenamer {
private Map retargets = new HashMap();
public RenameAdapter(String newname, String[] retargets) {
- super(new ClassWriter(0));
+ super(ASM5,new ClassWriter(0));
cw = (ClassWriter) cv;
this.newname = newname.replace('.', '/');
if (retargets != null) {
@@ -111,12 +111,15 @@ public class ClassRenamer {
}
return string;
}
-
+
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
if (descriptor.indexOf(oldname) != -1) {
descriptor = descriptor.replace(oldname, newname);
} else {
+ if (descriptor.indexOf(oldname) != -1) {
+ descriptor = descriptor.replace(oldname, newname);
+ }
for (String s : retargets.keySet()) {
if (descriptor.indexOf(s) != -1) {
descriptor = descriptor.replace(s, retargets.get(s));
@@ -141,13 +144,13 @@ public class ClassRenamer {
return super.visitField(access, name, desc, signature, value);
}
- class RenameMethodAdapter extends MethodAdapter implements Opcodes {
+ class RenameMethodAdapter extends MethodVisitor implements Opcodes {
String oldname;
String newname;
public RenameMethodAdapter(MethodVisitor mv, String oldname, String newname) {
- super(mv);
+ super(ASM5,mv);
this.oldname = oldname;
this.newname = newname;
}
@@ -224,6 +227,50 @@ public class ClassRenamer {
}
}
+ private String toString(Handle bsm) {
+ return "["+bsm.getTag()+"]"+bsm.getOwner()+"."+bsm.getName()+bsm.getDesc();
+ }
+
+ private String toString(Object[] os) {
+ StringBuilder buf = new StringBuilder();
+ if (os!=null) {
+ buf.append("[");
+ for (int i=0;i0) buf.append(",");
+ buf.append(os[i]);
+ }
+ buf.append("]");
+ }
+ else {
+ return "null";
+ }
+ return buf.toString();
+ }
+
+ private Handle retargetHandle(Handle oldHandle) {
+ int tag = oldHandle.getTag();
+ String owner = oldHandle.getOwner();
+ String name = oldHandle.getName();
+ String desc = oldHandle.getDesc();
+ owner = renameRetargetIfNecessary(owner);
+ Handle newHandle = new Handle(tag,owner,name,desc);
+ return newHandle;
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ // System.out.println("visitInvokeDynamicInsn(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs)+")");
+ // Example:
+ // visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
+ // bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
+ // bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
+ desc = renameRetargetIfNecessary(desc);
+ if (bsmArgs[1] instanceof Handle) {
+ bsmArgs[1] = retargetHandle((Handle)bsmArgs[1]);
+ }
+ mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ }
+
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldname)) {
owner = newname;
diff --git a/springloaded/src/main/java/org/springsource/loaded/Constants.java b/springloaded/src/main/java/org/springsource/loaded/Constants.java
index 24ea275..22e6c25 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Constants.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Constants.java
@@ -79,8 +79,10 @@ public interface Constants extends Opcodes {
static String mChangedForInvocationName = "anyChanges";
static String mChangedForInvokeStaticName = "istcheck";
static String mChangedForInvokeInterfaceName = "iincheck";
+ static String mChangedForInvokeDynamicName = "idycheck";
static String mChangedForInvokeVirtualName = "ivicheck";
static String mChangedForInvokeSpecialName = "ispcheck";
+ static String mPerformInvokeDynamicName = "idyrun";
static String descriptorChangedForInvokeSpecialName = "(ILjava/lang/String;)Lorg/springsource/loaded/__DynamicallyDispatchable;";
static String mChangedForConstructorName = "ccheck";
@@ -103,6 +105,7 @@ public interface Constants extends Opcodes {
static int ACC_PUBLIC_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PROTECTED;
static int ACC_PUBLIC_PRIVATE_PROTECTED = Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
+ static int ACC_PRIVATE_STATIC_SYNTHETIC = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_SYNTHETIC;
static int ACC_PRIVATE_PROTECTED = Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED;
static int ACC_PRIVATE_STATIC_FINAL = ACC_FINAL | ACC_STATIC | ACC_PRIVATE;
diff --git a/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java b/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
index 323c309..bb398c1 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ConstructorCopier.java
@@ -16,14 +16,13 @@
package org.springsource.loaded;
import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
/**
* @author Andy Clement
* @since 0.5.0
*/
-class ConstructorCopier extends MethodAdapter implements Constants {
+class ConstructorCopier extends MethodVisitor implements Constants {
private final static int preInvokeSpecial = 0;
private final static int postInvokeSpecial = 1;
@@ -39,7 +38,7 @@ class ConstructorCopier extends MethodAdapter implements Constants {
private String classname;
public ConstructorCopier(MethodVisitor mv, TypeDescriptor typeDescriptor, String suffix, String classname) {
- super(mv);
+ super(ASM5,mv);
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
this.classname = classname;
diff --git a/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java b/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
index 045bd45..16a175d 100644
--- a/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
+++ b/springloaded/src/main/java/org/springsource/loaded/DispatcherBuilder.java
@@ -57,7 +57,7 @@ public class DispatcherBuilder {
/**
* Whilst visiting the interface, the implementation is created.
*/
- static class DispatcherBuilderVisitor implements ClassVisitor, Opcodes, Constants {
+ static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
@@ -68,6 +68,7 @@ public class DispatcherBuilder {
private IncrementalTypeDescriptor typeDescriptor;
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
+ super(ASM5);
this.classname = rtype.getSlashedName();
this.typeDescriptor = typeDescriptor;
this.suffix = suffix;
diff --git a/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java b/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
index a21d72c..d2024fa 100644
--- a/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/EmptyClassVisitor.java
@@ -27,7 +27,11 @@ import org.objectweb.asm.MethodVisitor;
* @author Andy Clement
* @since 0.7.3
*/
-public class EmptyClassVisitor implements ClassVisitor, Constants {
+public class EmptyClassVisitor extends ClassVisitor implements Constants {
+
+ public EmptyClassVisitor() {
+ super(ASM5);
+ }
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java b/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
index 29737f5..8f1de84 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ExecutorBuilder.java
@@ -69,7 +69,7 @@ public class ExecutorBuilder {
* ClassVisitor that constructs the executor by visiting the original class. The basic goal is to visit the original class and
* 'copy' the methods into the executor, making adjustments as we go.
*/
- static class ExecutorBuilderVisitor implements ClassVisitor, Constants {
+ static class ExecutorBuilderVisitor extends ClassVisitor implements Constants {
private ClassWriter cw = new ClassWriter(0);
@@ -78,6 +78,7 @@ public class ExecutorBuilder {
protected TypeDescriptor typeDescriptor;
public ExecutorBuilderVisitor(String classname, String suffix, TypeDescriptor typeDescriptor) {
+ super(ASM5);
this.classname = classname;
this.suffix = suffix;
this.typeDescriptor = typeDescriptor;
@@ -128,7 +129,16 @@ public class ExecutorBuilder {
if (name.charAt(1) != 'c') {
// regular constructor
// want to create the ___init___ handler for this constructor
+
+ // With the JDT compiler the inner class constructor gets an extra first parameter that is the type of
+ // containing class. But with javac the inner class constructor gets an extra first parameter that is of
+ // a special anonymous type (inner class of the containing class)
+ // For example: class Foo { class Bar {}}
+ // JDT: ctor in Bar is (Foo) {}
+ // JAVAC: ctor in Bar is (Foo$1) {}
+
descriptor = Utils.insertExtraParameter(classname, descriptor);
+
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC_STATIC, mInitializerName, descriptor, signature, exceptions);
ConstructorCopier cc = new ConstructorCopier(mv, typeDescriptor, suffix, classname);
@@ -145,11 +155,12 @@ public class ExecutorBuilder {
cw.visitSource(sourcefile, debug);
}
- private static class CopyingAnnotationVisitor implements AnnotationVisitor {
+ private static class CopyingAnnotationVisitor extends AnnotationVisitor {
private AnnotationVisitor av;
public CopyingAnnotationVisitor(AnnotationVisitor av) {
+ super(ASM5);
this.av = av;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java b/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
index 769f305..f942a2d 100644
--- a/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
+++ b/springloaded/src/main/java/org/springsource/loaded/GlobalConfiguration.java
@@ -362,4 +362,16 @@ public class GlobalConfiguration {
}
debugplugins = debugPlugins;
}
+
+ public final static boolean isJava18orHigher;
+
+ static {
+ String version = System.getProperty("java.version");
+ if (version.startsWith("1.8")) {
+ isJava18orHigher = true;
+ }
+ else {
+ isJava18orHigher = false;
+ }
+ }
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java b/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
index d00e467..e7d09a0 100644
--- a/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/InterfaceExtractor.java
@@ -66,13 +66,14 @@ public class InterfaceExtractor {
return extractorVisitor.getBytes();
}
- class ExtractorVisitor implements ClassVisitor, Constants {
+ class ExtractorVisitor extends ClassVisitor implements Constants {
private TypeDescriptor typeDescriptor;
private ClassWriter interfaceWriter = new ClassWriter(0);
private String slashedtypename;
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
+ super(ASM5);
this.typeDescriptor = typeDescriptor;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java b/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
index 3d048a2..6505103 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodCopier.java
@@ -16,7 +16,6 @@
package org.springsource.loaded;
import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Utils.ReturnType;
@@ -26,7 +25,7 @@ import org.springsource.loaded.Utils.ReturnType;
* @author Andy Clement
* @since 0.5.0
*/
-class MethodCopier extends MethodAdapter implements Constants {
+class MethodCopier extends MethodVisitor implements Constants {
private boolean isInterface;
private String descriptor;
@@ -37,7 +36,7 @@ class MethodCopier extends MethodAdapter implements Constants {
public MethodCopier(MethodVisitor mv, boolean isInterface, String descriptor, TypeDescriptor typeDescriptor, String classname,
String suffix) {
- super(mv);
+ super(ASM5,mv);
this.isInterface = isInterface;
this.descriptor = descriptor;
this.typeDescriptor = typeDescriptor;
diff --git a/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java b/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
index 1466783..72f245e 100644
--- a/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/MethodInvokerRewriter.java
@@ -28,15 +28,15 @@ import java.util.HashSet;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
+import java.util.logging.Level;
import java.util.logging.Logger;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
+import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.ConstantPoolChecker2.References;
@@ -436,7 +436,7 @@ public class MethodInvokerRewriter {
static class DontRewriteException extends RuntimeException {
}
- static class RewriteClassAdaptor extends ClassAdapter implements Opcodes {
+ static class RewriteClassAdaptor extends ClassVisitor implements Opcodes {
private ClassVisitor cw;
@@ -667,14 +667,15 @@ public class MethodInvokerRewriter {
return intercepted.contains(owner + "." + methodName);
}
- private TypeRegistry typeRegistry;
+ protected TypeRegistry typeRegistry;
boolean isEnum = false;
private boolean isGroovyClosure = false;
int fieldcount = 0;
+ private ReloadableType rtype; // Can be null if rewriting in a non reloadable type
public RewriteClassAdaptor(TypeRegistry typeRegistry, ClassVisitor classWriter) {
// TODO should it also compute frames?
- super(classWriter);
+ super(ASM5,classWriter);
cw = cv;
this.typeRegistry = typeRegistry;
}
@@ -691,7 +692,7 @@ public class MethodInvokerRewriter {
public ClassVisitor getClassVisitor() {
return cv;
}
-
+
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
@@ -721,7 +722,7 @@ public class MethodInvokerRewriter {
return new RewritingMethodAdapter(mv, name);
}
- class RewritingMethodAdapter extends MethodAdapter implements Opcodes, Constants {
+ class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
// tracks max variable used in a method so we know what we can use
// safely
@@ -731,7 +732,7 @@ public class MethodInvokerRewriter {
private boolean isClinitOrEnumInit = false;
public RewritingMethodAdapter(MethodVisitor mv, String methodname) {
- super(mv);
+ super(ASM5,mv);
this.methodname = methodname;
if (isEnum) {
isClinitOrEnumInit = this.methodname.length() > 2 && this.methodname.charAt(0) == '<'
@@ -868,6 +869,7 @@ public class MethodInvokerRewriter {
}
private void rewritePUTSTATIC(int opcode, String owner, String name, String desc) {
+ // TODO [perf] cache this information for 'us' so lookup not always necessary
int classId = typeRegistry.getTypeIdFor(owner, true);
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
// Make a call to check if this field operation must be intercepted:
@@ -950,6 +952,102 @@ public class MethodInvokerRewriter {
super.visitTypeInsn(opcode, type);
}
+ private String toString(Handle handle) {
+ return "handle(tag="+handle.getTag()+",name="+handle.getName()+",desc="+handle.getDesc()+",owner="+handle.getOwner();
+ }
+
+ private String toString(Object[] oa) {
+ StringBuilder buf = new StringBuilder();
+ buf.append("[");
+ if (oa!=null) {
+ for (Object o:oa) {
+ buf.append(" ");
+ buf.append(o);
+ }
+ }
+ buf.append("]");
+ return buf.toString();
+ }
+
+ boolean hasParams(String descriptor) {
+ return descriptor.charAt(1)!=')';
+ }
+
+ /**
+ * Generate bytecode to convert parameters on the stack into an array (based on the descriptor). If the
+ * descriptor shows there are no parameters then null is stacked.
+ *
+ * @param descriptor MethodType descriptor showing parameters and return value
+ */
+ private void stackParameters(String descriptor) {
+ if (hasParams(descriptor)) {
+ Utils.collapseStackToArray(mv, descriptor);
+ }
+ else {
+ // no params
+ mv.visitInsn(ACONST_NULL);
+ }
+ }
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ // TODO *shudder* what about invoke dynamic calls that target reflective APIs
+ int classId = typeRegistry.getTypeIdFor(slashedclassname, false);
+ if (classId==-1) {
+ throw new IllegalStateException();
+ }
+ // Initially only rewriting use of INVOKEDYNAMIC to support Lambda execution
+ // TODO support the more general invokedynamic usage
+
+ // Example data at this point:
+ // name=m
+ // desc=()Lbasic/LambdaA2$Foo;
+ // bsm=handle(tag=6,
+ // name=metafactory,
+ // desc=(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
+ // owner=java/lang/invoke/LambdaMetafactory
+ // bsmArgs=[ ()I basic/LambdaA2.lambda$run$1()I (6) ()I]
+ if (bsm.getTag()==H_INVOKESTATIC && bsm.getName().equals("metafactory") && bsm.getOwner().equals("java/lang/invoke/LambdaMetafactory")) {
+ // System.out.println("InvokeDynamic(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs));
+ // Only when the BSM is LambdaMetafactory.metafactory are we rewriting the invokedynamic. Since LambdaMetafactory will not
+ // be getting reloaded, we can avoid a bunch of complexity. When the bsm points to a reloadable type we'll have to
+ // do more hoop jumping.
+
+ // Check on reloading having happened
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeDynamicName, "()Ljava/lang/Object;");
+
+ // mv.visitInsn(DUP);
+
+ Label nochange = new Label();
+ mv.visitJumpInsn(IFNULL, nochange);
+
+// // 9. do what we were going to do
+// mv.visitLabel(l1);
+
+ stackParameters(desc);
+ int bsmReferenceId = typeRegistry.recordBootstrapMethod(slashedclassname,bsm,bsmArgs);
+ // Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
+ mv.visitLdcInsn(typeRegistry.getId());
+ mv.visitLdcInsn(classId);
+ mv.visitMethodInsn(INVOKESTATIC,"java/lang/invoke/MethodHandles","lookup","()Ljava/lang/invoke/MethodHandles$Lookup;");
+ mv.visitLdcInsn(name+desc); // Ljava/lang/String;
+ mv.visitLdcInsn(bsmReferenceId); // I
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mPerformInvokeDynamicName, "([Ljava/lang/Object;IILjava/lang/Object;Ljava/lang/String;I)Ljava/lang/Object;");
+
+ Label gotolabel = new Label();
+ mv.visitJumpInsn(GOTO, gotolabel);
+ mv.visitLabel(nochange);
+ super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ mv.visitLabel(gotolabel);
+ }
+ else {
+ if (GlobalConfiguration.verboseMode && log.isLoggable(Level.WARNING)) {
+ log.warning("[current limitation] not rewriting invokedynamic usage in type '"+slashedclassname+"'. InvokeDynamic(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs));
+ }
+ super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
+ }
+ }
+
@Override
public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
if (GlobalConfiguration.interceptReflection && rewriteReflectiveCall(opcode, owner, name, desc)) {
@@ -1138,8 +1236,15 @@ public class MethodInvokerRewriter {
mv.visitLdcInsn(name + desc); // [targetInstance paramArray targetInstance nameAndDescriptor]
- // calling __execute(params array, this, name+desc)
- mv.visitMethodInsn(INVOKEINTERFACE, owner, mDynamicDispatchName, mDynamicDispatchDescriptor);
+ if (GlobalConfiguration.isJava18orHigher) {
+ // if the target is a generated lambda callsite object then calling __execute isn't going to work as those
+ // types don't have the method in them!
+ mv.visitMethodInsn(INVOKESTATIC, tRegistryType, "iiIntercept", "(Ljava/lang/Object;[Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;");
+ }
+ else {
+ // calling __execute(params array, this, name+desc)
+ mv.visitMethodInsn(INVOKEINTERFACE, owner, mDynamicDispatchName, mDynamicDispatchDescriptor);
+ }
insertAppropriateReturn(returnType);
Label gotolabel = new Label();
diff --git a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
index 2fca034..a0ab008 100644
--- a/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
+++ b/springloaded/src/main/java/org/springsource/loaded/ReloadableType.java
@@ -31,8 +31,8 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.springsource.loaded.MethodInvokerRewriter.DontRewriteException;
import org.springsource.loaded.MethodInvokerRewriter.RewriteClassAdaptor;
@@ -839,10 +839,10 @@ public class ReloadableType {
}
}
- static class ChainedAdapters extends ClassAdapter implements Constants {
+ static class ChainedAdapters extends ClassVisitor implements Constants {
public ChainedAdapters(ReloadableType rtype) {
- super(new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype, new ClassWriter(
+ super(ASM5,new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype, new ClassWriter(
ClassWriter.COMPUTE_MAXS))));
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionInvestigator.java b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionInvestigator.java
index 415e8c0..7684c89 100644
--- a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionInvestigator.java
+++ b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionInvestigator.java
@@ -15,10 +15,9 @@
*/
package org.springsource.loaded;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -39,7 +38,7 @@ public class SystemClassReflectionInvestigator {
return classAdaptor.hitCount;
}
- static class RewriteClassAdaptor extends ClassAdapter implements Constants {
+ static class RewriteClassAdaptor extends ClassVisitor implements Constants {
int hitCount = 0;
private ClassWriter cw;
@@ -52,7 +51,7 @@ public class SystemClassReflectionInvestigator {
public RewriteClassAdaptor() {
// TODO should it also compute frames?
- super(new ClassWriter(ClassWriter.COMPUTE_MAXS));
+ super(ASM5,new ClassWriter(ClassWriter.COMPUTE_MAXS));
cw = (ClassWriter) cv;
}
@@ -77,10 +76,10 @@ public class SystemClassReflectionInvestigator {
return new RewritingMethodAdapter(mv);
}
- class RewritingMethodAdapter extends MethodAdapter implements Opcodes, Constants {
+ class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
public RewritingMethodAdapter(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
private boolean interceptReflection(String owner, String name, String desc) {
diff --git a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
index 63c4285..127655a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/SystemClassReflectionRewriter.java
@@ -18,12 +18,11 @@ package org.springsource.loaded;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
@@ -105,7 +104,7 @@ public class SystemClassReflectionRewriter {
}
}
- static class RewriteClassAdaptor extends ClassAdapter implements Constants {
+ static class RewriteClassAdaptor extends ClassVisitor implements Constants {
private ClassWriter cw;
int bits = 0x0000;
@@ -122,7 +121,7 @@ public class SystemClassReflectionRewriter {
public RewriteClassAdaptor() {
// TODO should it also compute frames?
- super(new ClassWriter(ClassWriter.COMPUTE_MAXS));
+ super(ASM5,new ClassWriter(ClassWriter.COMPUTE_MAXS));
cw = (ClassWriter) cv;
}
@@ -189,10 +188,10 @@ public class SystemClassReflectionRewriter {
}
}
- class RewritingMethodAdapter extends MethodAdapter implements Opcodes, Constants {
+ class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
public RewritingMethodAdapter(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
/**
diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeDescriptorExtractor.java b/springloaded/src/main/java/org/springsource/loaded/TypeDescriptorExtractor.java
index 2c37f53..3428cdc 100644
--- a/springloaded/src/main/java/org/springsource/loaded/TypeDescriptorExtractor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/TypeDescriptorExtractor.java
@@ -54,7 +54,7 @@ public class TypeDescriptorExtractor {
/**
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
*/
- class ExtractionVisitor implements ClassVisitor, Opcodes {
+ class ExtractionVisitor extends ClassVisitor implements Opcodes {
private boolean isReloadableType;
private int flags;
@@ -72,6 +72,7 @@ public class TypeDescriptorExtractor {
private List finalInHierarchy = new ArrayList();
public ExtractionVisitor(boolean isReloadableType) {
+ super(ASM5);
this.isReloadableType = isReloadableType;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeDiffComputer.java b/springloaded/src/main/java/org/springsource/loaded/TypeDiffComputer.java
index 0839438..345d5e3 100644
--- a/springloaded/src/main/java/org/springsource/loaded/TypeDiffComputer.java
+++ b/springloaded/src/main/java/org/springsource/loaded/TypeDiffComputer.java
@@ -625,7 +625,10 @@ public class TypeDiffComputer implements Opcodes {
// td.setTypeVersionChange(oldClassNode.version, newClassNode.version);
// }
if (oldClassNode.access != newClassNode.access) {
- td.setTypeAccessChange(oldClassNode.access, newClassNode.access);
+ // Is it only because of 0x20000 - that appears to represent Deprecated!
+ if ((oldClassNode.access & 0xffff) != (newClassNode.access&0xffff)) {
+ td.setTypeAccessChange(oldClassNode.access, newClassNode.access);
+ }
}
if (!oldClassNode.name.equals(newClassNode.name)) {
td.setTypeNameChange(oldClassNode.name, newClassNode.name);
diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java b/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java
index ef9753e..1c8e03b 100644
--- a/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java
+++ b/springloaded/src/main/java/org/springsource/loaded/TypeRegistry.java
@@ -42,11 +42,13 @@ import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
+import org.objectweb.asm.Handle;
import org.springsource.loaded.agent.FileSystemWatcher;
import org.springsource.loaded.agent.ReloadDecision;
import org.springsource.loaded.agent.ReloadableFileChangeListener;
import org.springsource.loaded.agent.SpringLoadedPreProcessor;
import org.springsource.loaded.infra.UsedByGeneratedCode;
+import org.springsource.loaded.support.Java8;
// TODO debug: stepping into deleted methods - should delete line number table for deleted methods
@@ -492,33 +494,41 @@ public class TypeRegistry {
configuration = new Properties(GlobalConfiguration.globalConfigurationProperties);
try {
Set configurationFiles = new HashSet();
- Enumeration resources = classLoader.get().getResources("springloaded.properties");
- while (resources.hasMoreElements()) {
- URL url = resources.nextElement();
- String configFile = url.toString();
- if (GlobalConfiguration.logging && log.isLoggable(Level.INFO)) {
- log.log(Level.INFO, this.toString() + ": processing config file: " + url.toString());
+ ClassLoader classloader = classLoader.get();
+ Enumeration resources = classloader==null?null:classloader.getResources("springloaded.properties");
+ if (resources == null) {
+ if (GlobalConfiguration.verboseMode && log.isLoggable(Level.INFO)) {
+ log.info("Unable to load springloaded.properties, cannot find it through classloader "+classloader);
}
- if (configurationFiles.contains(configFile)) {
- continue;
- }
- configurationFiles.add(configFile);
- InputStream is = url.openStream();
-
- Properties p = new Properties();
- p.load(is);
- is.close();
- Set keys = p.stringPropertyNames();
- for (String key : keys) {
- if (!configuration.containsKey(key)) {
- configuration.put(key, p.getProperty(key));
- } else {
- // Extend our configuration
- String valueSoFar = configuration.getProperty(key);
- StringBuilder sb = new StringBuilder(valueSoFar);
- sb.append(",");
- sb.append(p.getProperty(key));
- configuration.put(key, sb.toString());
+ }
+ else {
+ while (resources.hasMoreElements()) {
+ URL url = resources.nextElement();
+ String configFile = url.toString();
+ if (GlobalConfiguration.logging && log.isLoggable(Level.INFO)) {
+ log.log(Level.INFO, this.toString() + ": processing config file: " + url.toString());
+ }
+ if (configurationFiles.contains(configFile)) {
+ continue;
+ }
+ configurationFiles.add(configFile);
+ InputStream is = url.openStream();
+
+ Properties p = new Properties();
+ p.load(is);
+ is.close();
+ Set keys = p.stringPropertyNames();
+ for (String key : keys) {
+ if (!configuration.containsKey(key)) {
+ configuration.put(key, p.getProperty(key));
+ } else {
+ // Extend our configuration
+ String valueSoFar = configuration.getProperty(key);
+ StringBuilder sb = new StringBuilder(valueSoFar);
+ sb.append(",");
+ sb.append(p.getProperty(key));
+ configuration.put(key, sb.toString());
+ }
}
}
}
@@ -1116,6 +1126,21 @@ public class TypeRegistry {
directlyDefineTypes = should;
}
+ /**
+ *Used to determine if the invokedynamic needs to be intercepted.
+ *
+ * @return null if nothing has been reloaded
+ */
+ @UsedByGeneratedCode
+ public static Object idycheck() {
+ if (TypeRegistry.nothingReloaded) {
+ return null;
+ }
+ else {
+ return "reloading-happened";
+ }
+ }
+
/**
* Determine if something has changed in a particular type related to a particular descriptor and so the dispatcher interface
* should be used. The type registry ID and class ID are merged in the 'ids' parameter. This method is for INVOKESTATIC rewrites
@@ -1242,6 +1267,33 @@ public class TypeRegistry {
return null; // let it fail anyway
}
+ /**
+ * See notes.md#001
+ *
+ */
+ public static Object iiIntercept(Object instance, Object[] params, Object instance2, String nameAndDescriptor) {
+ Class> clazz= instance.getClass();
+ try {
+ if (clazz.getName().contains("$$Lambda")) {
+ // There will only be one method, the SAM method
+ Method[] ms = instance.getClass().getDeclaredMethods();
+ Method m = ms[0];
+ m.setAccessible(true);
+ Object o = m.invoke(instance, params);
+ return o;
+ }
+ else {
+ // Do what you were going to do...
+ Method m = instance.getClass().getDeclaredMethod("__execute",Object[].class,Object.class,String.class);
+ m.setAccessible(true);
+ return m.invoke(instance, params, instance, nameAndDescriptor);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
@UsedByGeneratedCode
public static __DynamicallyDispatchable ispcheck(int ids, String nameAndDescriptor) {
if (GlobalConfiguration.isRuntimeLogging && log.isLoggable(Level.FINER)) {
@@ -1459,7 +1511,17 @@ public class TypeRegistry {
}
return false;
}
-
+
+ @UsedByGeneratedCode
+ public static Object idyrun(Object[] indyParams, int typeRegistryId, int classId, Object caller, String nameAndDescriptor, int bsmId) {
+ // Typical next line: lookup=basic.LambdaA nameAD=m()Lbasic/LambdaA$Foo; bsmId=0
+ // System.err.println("idyrun("+caller+","+nameAndDescriptor+","+bsmId+")");
+ // TODO Currently leaking entries in bsmmap with reloads (new ones get added, old ones not removed)
+ ReloadableType rtype = TypeRegistry.getReloadableType(typeRegistryId, classId);
+ BsmInfo bsmi = bsmmap.get(rtype.getSlashedName())[bsmId];
+ return Java8.emulateInvokeDynamic(rtype.getLatestExecutorClass(),bsmi.bsm,bsmi.bsmArgs,caller,nameAndDescriptor, indyParams);
+ }
+
/**
* Used in code the generated code replaces invokevirtual calls. Determine if the code can run as it was originally compiled.
*
@@ -1856,4 +1918,47 @@ public class TypeRegistry {
public Set getJDKProxiesFor(String slashedInterfaceTypeName) {
return jdkProxiesForInterface.get(slashedInterfaceTypeName);
}
+
+
+ /**
+ * When an invokedynamic instruction is reached, we allocate an id that
+ * recognizes that bsm and the parameters to that bsm. The index can be
+ * used when rewriting that invokedynamic
+ *
+ * @return id that represents this bootstrap method usage
+ */
+ public synchronized int recordBootstrapMethod(String slashedClassName, Handle bsm, Object[] bsmArgs) {
+ if (bsmmap == null) {
+ bsmmap = new HashMap();
+ }
+ BsmInfo[] bsminfo = bsmmap.get(slashedClassName);
+ if (bsminfo== null) {
+ bsminfo = new BsmInfo[1];
+ // TODO do we need BsmInfo or can we just use Handle directly?
+ bsminfo[0] = new BsmInfo(bsm, bsmArgs);
+ bsmmap.put(slashedClassName,bsminfo);
+ return 0;
+ }
+ else {
+ int len = bsminfo.length;
+ BsmInfo[] newarray = new BsmInfo[len+1];
+ System.arraycopy(bsminfo, 0, newarray, 0, len);
+ bsminfo = newarray;
+ bsmmap.put(slashedClassName,bsminfo);
+ bsminfo[len] = new BsmInfo(bsm,bsmArgs);
+ return len;
+ }
+ // TODO [memory] search the existing bsmInfos for a matching one! Reuse!
+ }
+
+ private static Map bsmmap;
+
+ static class BsmInfo {
+ Handle bsm;
+ Object[] bsmArgs;
+ public BsmInfo(Handle bsm, Object[] bsmArgs) {
+ this.bsm = bsm;
+ this.bsmArgs = bsmArgs;
+ }
+ }
}
\ No newline at end of file
diff --git a/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java b/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java
index 327df49..f8a7bd0 100644
--- a/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/TypeRewriter.java
@@ -19,13 +19,11 @@ import java.lang.reflect.Modifier;
import java.util.logging.Level;
import java.util.logging.Logger;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils.ReturnType;
@@ -55,7 +53,7 @@ public class TypeRewriter implements Constants {
return classAdaptor.getBytes();
}
- static class RewriteClassAdaptor extends ClassAdapter implements Constants {
+ static class RewriteClassAdaptor extends ClassVisitor implements Constants {
private ClassWriter cw;
private String slashedname;
@@ -72,7 +70,7 @@ public class TypeRewriter implements Constants {
}
public RewriteClassAdaptor(ReloadableType rtype, ClassWriter classWriter) {
- super(classWriter);
+ super(ASM5,classWriter);
this.rtype = rtype;
this.slashedname = rtype.getSlashedName();
this.cw = (ClassWriter) cv;
@@ -414,7 +412,7 @@ public class TypeRewriter implements Constants {
@Override
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
- MethodVisitor mv = super.visitMethod(promoteIfNecessary(flags), name, descriptor, signature, exceptions);
+ MethodVisitor mv = super.visitMethod(promoteIfNecessary(flags,name), name, descriptor, signature, exceptions);
MethodVisitor newMethodVisitor = getMethodVisitor(name, descriptor, mv);
return newMethodVisitor;
}
@@ -444,8 +442,8 @@ public class TypeRewriter implements Constants {
}
// Default visibility elements need promotion to public so that they can be seen from the executor
- private int promoteIfNecessary(int flags) {
- int newflags = Utils.promoteDefaultOrProtectedToPublic(flags, isEnum);
+ private int promoteIfNecessary(int flags,String name) {
+ int newflags = Utils.promoteDefaultOrProtectedToPublic(flags, isEnum, name);
return newflags;
}
@@ -816,7 +814,7 @@ public class TypeRewriter implements Constants {
/**
* Rewrites a method to include the extra checks to verify it is the most up to date version.
*/
- class AugmentingMethodAdapter extends MethodAdapter implements Opcodes {
+ class AugmentingMethodAdapter extends MethodVisitor implements Opcodes {
int methodId;
String name;
@@ -825,7 +823,7 @@ public class TypeRewriter implements Constants {
ReturnType returnType;
public AugmentingMethodAdapter(MethodVisitor mv, String name, String descriptor) {
- super(mv);
+ super(ASM5,mv);
this.name = name;
this.method = rtype.getMethod(name, descriptor);
this.methodId = method.getId();
@@ -916,7 +914,7 @@ public class TypeRewriter implements Constants {
}
- class AugmentingConstructorAdapter extends MethodAdapter implements Opcodes {
+ class AugmentingConstructorAdapter extends MethodVisitor implements Opcodes {
int ctorId;
String name;
@@ -926,7 +924,7 @@ public class TypeRewriter implements Constants {
boolean isTopMost;
public AugmentingConstructorAdapter(MethodVisitor mv, String descriptor, String type, boolean isTopMost) {
- super(mv);
+ super(ASM5,mv);
this.descriptor = descriptor;
this.type = type;
this.isTopMost = isTopMost;
@@ -1053,12 +1051,12 @@ public class TypeRewriter implements Constants {
void prepend();
}
- class MethodPrepender extends MethodAdapter implements Opcodes {
+ class MethodPrepender extends MethodVisitor implements Opcodes {
Prepender appender;
public MethodPrepender(MethodVisitor mv, Prepender appender) {
- super(mv);
+ super(ASM5,mv);
this.appender = appender;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/Utils.java b/springloaded/src/main/java/org/springsource/loaded/Utils.java
index 2541ef6..ed35c0a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/Utils.java
+++ b/springloaded/src/main/java/org/springsource/loaded/Utils.java
@@ -1369,7 +1369,7 @@ public class Utils implements Opcodes, Constants {
return access;
}
- public static int promoteDefaultOrProtectedToPublic(int access, boolean isEnum) {
+ public static int promoteDefaultOrProtectedToPublic(int access, boolean isEnum, String name) {
if ((access & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
// is default
return (access | Modifier.PUBLIC);
@@ -1382,6 +1382,10 @@ public class Utils implements Opcodes, Constants {
// was private, need to 'publicize' it
return access - Constants.ACC_PRIVATE + Constants.ACC_PUBLIC;
}
+ if ((access&Constants.ACC_PRIVATE_STATIC_SYNTHETIC)==ACC_PRIVATE_STATIC_SYNTHETIC && name.startsWith("lambda")) {
+ // Special case for lambda, may need to generalize for general invokedynamic support
+ return access - Constants.ACC_PRIVATE + Constants.ACC_PUBLIC;
+ }
return access;
}
@@ -1733,7 +1737,11 @@ public class Utils implements Opcodes, Constants {
// TODO [performance] speed up by throwing exception from first visit method? (but this isn't used in the mainline really)
// TODO or just write a quicker bytecode parser that just looks at the interfaces then returns
- private static class InterfaceCollectingClassVisitor implements ClassVisitor {
+ private static class InterfaceCollectingClassVisitor extends ClassVisitor {
+
+ public InterfaceCollectingClassVisitor() {
+ super(ASM5);
+ }
public String[] interfaces;
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java
index cd9de43..41eb180 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/CglibPluginCapturing.java
@@ -19,10 +19,9 @@ import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
@@ -33,7 +32,7 @@ import org.springsource.loaded.Constants;
* @author Andy Clement
* @since 0.8.3
*/
-public class CglibPluginCapturing extends ClassAdapter implements Constants {
+public class CglibPluginCapturing extends ClassVisitor implements Constants {
public static Map, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap, Object[]>();
public static Map, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap, Object[]>();
@@ -48,7 +47,7 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
}
private CglibPluginCapturing() {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
}
@Override
@@ -74,10 +73,10 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
}
}
- class CreateMethodInterceptor extends MethodAdapter implements Constants {
+ class CreateMethodInterceptor extends MethodVisitor implements Constants {
public CreateMethodInterceptor(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
@Override
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/ClassVisitingConstructorAppender.java b/springloaded/src/main/java/org/springsource/loaded/agent/ClassVisitingConstructorAppender.java
index dea4960..740ec8a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/ClassVisitingConstructorAppender.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/ClassVisitingConstructorAppender.java
@@ -15,9 +15,8 @@
*/
package org.springsource.loaded.agent;
-import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
@@ -27,7 +26,7 @@ import org.springsource.loaded.Constants;
* @author Andy Clement
* @since 0.5.0
*/
-public class ClassVisitingConstructorAppender extends ClassAdapter implements Constants {
+public class ClassVisitingConstructorAppender extends ClassVisitor implements Constants {
private String calleeOwner;
private String calleeName;
@@ -41,7 +40,7 @@ public class ClassVisitingConstructorAppender extends ClassAdapter implements Co
* @param name
*/
public ClassVisitingConstructorAppender(String owner, String name) {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
this.calleeOwner = owner;
this.calleeName = name;
}
@@ -63,10 +62,10 @@ public class ClassVisitingConstructorAppender extends ClassAdapter implements Co
* This constructor appender includes a couple of instructions at the end of each constructor it is asked to visit. It
* recognizes the end by observing a RETURN instruction. The instructions are inserted just before the RETURN.
*/
- class ConstructorAppender extends MethodAdapter implements Constants {
+ class ConstructorAppender extends MethodVisitor implements Constants {
public ConstructorAppender(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
@Override
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/FalseReturner.java b/springloaded/src/main/java/org/springsource/loaded/agent/FalseReturner.java
index b22029b..1cec1a7 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/FalseReturner.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/FalseReturner.java
@@ -15,7 +15,7 @@
*/
package org.springsource.loaded.agent;
-import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
@@ -26,12 +26,12 @@ import org.springsource.loaded.Constants;
* @author Andy Clement
* @since 0.7.0
*/
-public class FalseReturner extends ClassAdapter implements Constants {
+public class FalseReturner extends ClassVisitor implements Constants {
private String methodname;
public FalseReturner(String methodname) {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
this.methodname = methodname;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/ModifyDefineInClassLoaderForClassArtifactsType.java b/springloaded/src/main/java/org/springsource/loaded/agent/ModifyDefineInClassLoaderForClassArtifactsType.java
index f155e29..baa4057 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/ModifyDefineInClassLoaderForClassArtifactsType.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/ModifyDefineInClassLoaderForClassArtifactsType.java
@@ -15,9 +15,8 @@
*/
package org.springsource.loaded.agent;
-import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
import org.springsource.loaded.GlobalConfiguration;
@@ -30,10 +29,10 @@ import org.springsource.loaded.TypeRegistry;
* @author Andy Clement
* @since 0.7.3
*/
-public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassAdapter implements Constants {
+public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassVisitor implements Constants {
public ModifyDefineInClassLoaderForClassArtifactsType() {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
}
public byte[] getBytes() {
@@ -49,10 +48,10 @@ public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassAdapter
}
}
- class DefineClassModifierVisitor extends MethodAdapter implements Constants {
+ class DefineClassModifierVisitor extends MethodVisitor implements Constants {
public DefineClassModifierVisitor(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
@Override
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/NonFinalizer.java b/springloaded/src/main/java/org/springsource/loaded/agent/NonFinalizer.java
index 24d6e15..0ede959 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/NonFinalizer.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/NonFinalizer.java
@@ -17,7 +17,7 @@ package org.springsource.loaded.agent;
import java.lang.reflect.Modifier;
-import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.springsource.loaded.Constants;
@@ -29,7 +29,7 @@ import org.springsource.loaded.Constants;
* @author Andy Clement
* @since 0.7.0
*/
-public class NonFinalizer extends ClassAdapter implements Constants {
+public class NonFinalizer extends ClassVisitor implements Constants {
private String fieldname;
@@ -42,7 +42,7 @@ public class NonFinalizer extends ClassAdapter implements Constants {
* @param name
*/
public NonFinalizer(String fieldname) {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
this.fieldname = fieldname;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java b/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
index 25471a8..c7e75fc 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/SpringLoadedPreProcessor.java
@@ -41,6 +41,7 @@ import org.springsource.loaded.TypeRegistry;
import org.springsource.loaded.Utils;
import org.springsource.loaded.SystemClassReflectionRewriter.RewriteResult;
import org.springsource.loaded.ri.ReflectiveInterceptor;
+import org.springsource.loaded.support.Java8;
/**
* The entry point for the agent - all classes that can be modified will be passed into preProcess(). They have to be dealt with in
@@ -148,6 +149,10 @@ public class SpringLoadedPreProcessor implements Constants {
// return rr.bytes;
// }
}
+ else if (slashedClassName.equals("java/lang/invoke/InnerClassLambdaMetafactory")) {
+ bytes = Java8.enhanceInnerClassLambdaMetaFactory(bytes);
+ return bytes;
+ }
}
return bytes;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java b/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java
index a006b9f..dcdcb20 100644
--- a/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java
+++ b/springloaded/src/main/java/org/springsource/loaded/agent/SpringPlugin.java
@@ -104,6 +104,15 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP
}
private static boolean debug = false;
+
+ static {
+ try {
+ String debugString = System.getProperty("springloaded.plugins.spring.debug","false");
+ debug = Boolean.valueOf(debugString);
+ } catch (Exception e) {
+ // likely security exception
+ }
+ }
// called by the modified code
public static void recordDefaultAnnotationHandlerMappingInstance(Object obj) {
@@ -212,12 +221,12 @@ public class SpringPlugin implements LoadtimeInstrumentationPlugin, ReloadEventP
method_initHandlerMethods.setAccessible(true);
method_initHandlerMethods.invoke(o);
} catch (NoSuchFieldException nsfe) {
- if (log.isLoggable(Level.WARNING)) {
+ if (debug) {
if (nsfe.getMessage().equals("handlerMethods")) {
- log.warning("problem resetting request mapping handlers - unable to find field 'handlerMethods' on type 'AbstractHandlerMethodMapping' - you probably are not on Spring 3.1");
+ System.out.println("problem resetting request mapping handlers - unable to find field 'handlerMethods' on type 'AbstractHandlerMethodMapping' - you probably are not on Spring 3.1");
}
else {
- log.warning("problem resetting request mapping handlers - NoSuchFieldException: "+nsfe.getMessage());
+ System.out.println("problem resetting request mapping handlers - NoSuchFieldException: "+nsfe.getMessage());
}
}
} catch (Exception e) {
diff --git a/springloaded/src/main/java/org/springsource/loaded/pluginhelpers/EmptyCtor.java b/springloaded/src/main/java/org/springsource/loaded/pluginhelpers/EmptyCtor.java
index 2a2542b..3328a08 100644
--- a/springloaded/src/main/java/org/springsource/loaded/pluginhelpers/EmptyCtor.java
+++ b/springloaded/src/main/java/org/springsource/loaded/pluginhelpers/EmptyCtor.java
@@ -17,8 +17,8 @@ package org.springsource.loaded.pluginhelpers;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
@@ -32,7 +32,7 @@ import org.springsource.loaded.test.infra.ClassPrinter;
* @author Andy Clement
* @since 0.8.3
*/
-public class EmptyCtor extends ClassAdapter implements Constants {
+public class EmptyCtor extends ClassVisitor implements Constants {
private String[] descriptors;
@@ -52,7 +52,7 @@ public class EmptyCtor extends ClassAdapter implements Constants {
}
private EmptyCtor(String... descriptors) {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
this.descriptors = descriptors;
}
@@ -80,11 +80,12 @@ public class EmptyCtor extends ClassAdapter implements Constants {
}
}
- static class Emptier implements MethodVisitor, Constants {
+ static class Emptier extends MethodVisitor implements Constants {
MethodVisitor mv;
public Emptier(MethodVisitor mv) {
+ super(ASM5);
this.mv = mv;
}
diff --git a/springloaded/src/main/java/org/springsource/loaded/support/Java8.java b/springloaded/src/main/java/org/springsource/loaded/support/Java8.java
new file mode 100644
index 0000000..6b467c1
--- /dev/null
+++ b/springloaded/src/main/java/org/springsource/loaded/support/Java8.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2014 Pivotal Software Inc. and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springsource.loaded.support;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This class encapsulates dependencies on Java 8 APIs (e.g. LambdaMetafactory).
+ *
+ * @author Andy Clement
+ * @since 1.2
+ */
+public class Java8 {
+
+ /**
+ * Notes:
+ *
+ * Useful to have an example of how this code behaves. Here is a bit of code:
+ *
+ * class basic.LambdaA {
+ * interface Foo { int m(); }
+ * static int run() {
+ * Foo f = null;
+ * f = () -> 77;
+ * return f.m();
+ * }
+ * }
+ *
+ * Here is a bootstrap method entry in the constant pool:
+ *
+ * 0: #31 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:
+ * (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;
+ * Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
+ * Method arguments:
+ * #32 ()I
+ * #33 invokestatic basic/LambdaA.lambda$run$0:()I
+ * #32 ()I
+ *
+ * At the invokedynamic site:
+ * bsmId = 0
+ * nameAndDescriptor = m()Lbasic/LambdaA$Foo;
+ *
+ * When invoking the metafactory bootstrap method the first two parameters are stacked by the VM automatically, namely the MethodHandles$Lookup
+ * instance (caller) and the first String (invokedName). What the VM actually sees is this:
+ *
+ * metaFactory parameters:
+ * 0:MethodHandles$Lookup caller = basic.LambdaA
+ * 1:String invokedName = "m"
+ * 2:MethodType invokedType = "()Foo"
+ * 3:MethodType samMethodType = "()int"
+ * 4:MethodHandle implMethod = (actually a DirectMethodHandle where memberName is "basic.LambdaA.lambda$run$0()int/invokeStatic")
+ * 5:MethodType instantiatedMethodType = "()int"
+ *
+ * With all that information then the calls in this case are relatively straightforward:
+ * CallSite callsite = LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
+ * callsite.dynamicInvoker().invokeWithArguments((Object[])null);
+ */
+
+ /**
+ * Programmatic emulation of INVOKEDYNAMIC so initialize the callsite via use of the bootstrap method then
+ * invoke the result.
+ *
+ * @param executorClass null if not yet reloaded
+ * @param handle
+ * @param bsmArgs
+ * @param lookup
+ * @return
+ */
+ public static Object emulateInvokeDynamic(Class> executorClass, Handle handle, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor, Object[] indyParams) {
+ try {
+ CallSite callsite = callLambdaMetaFactory(bsmArgs,lookup,indyNameAndDescriptor,executorClass);
+ return callsite.dynamicInvoker().invokeWithArguments(indyParams);
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ }
+ // TODO [perf] How about a table of CallSites indexed by invokedynamic number through the class file. Computed on first reference but cleared on reload. Possibly extend this to all invoke types!
+
+ // TODO [lambda] Need to handle altMetaFactory which is used when the lambdas are more 'complex' (e.g. Serializable)
+ public static CallSite callLambdaMetaFactory(Object[] bsmArgs, Object lookup, String indyNameAndDescriptor,Class> executorClass) throws Exception {
+ MethodHandles.Lookup caller = (MethodHandles.Lookup)lookup;
+
+ ClassLoader callerLoader = caller.lookupClass().getClassLoader();
+
+ int descriptorStart = indyNameAndDescriptor.indexOf('(');
+ String invokedName = indyNameAndDescriptor.substring(0,descriptorStart);
+ MethodType invokedType = MethodType.fromMethodDescriptorString(indyNameAndDescriptor.substring(descriptorStart), callerLoader);
+
+ // Use bsmArgs to build the parameters
+ MethodType samMethodType = MethodType.fromMethodDescriptorString((String)(((Type)bsmArgs[0]).getDescriptor()), callerLoader);
+
+ Handle bsmArgsHandle = (Handle)bsmArgs[1];
+ String owner = bsmArgsHandle.getOwner();
+ String name = bsmArgsHandle.getName();
+ String descriptor = bsmArgsHandle.getDesc();
+ MethodType implMethodType = MethodType.fromMethodDescriptorString(descriptor, callerLoader);
+ // Looking up the lambda$run method in the caller class (note the caller class is the executor, which gets us around the
+ // problem of having to hack into LambdaMetafactory to intercept reflection)
+ MethodHandle implMethod = null;
+ // TODO [lambda] need to handle invokevirtual, surely
+ switch (bsmArgsHandle.getTag()) {
+ case Opcodes.H_INVOKESTATIC:
+ implMethod = caller.findStatic(caller.lookupClass(), name, implMethodType);
+ break;
+ case Opcodes.H_INVOKESPECIAL:
+ // If there is an executor, the lambda function is actually modified from 'private instance' to 'public static' so adjust lookup:
+ if (executorClass == null) {
+ implMethod = caller.findSpecial(caller.lookupClass(), name, implMethodType, caller.lookupClass());
+ }
+ else {
+ implMethod = caller.findStatic(caller.lookupClass(), name, MethodType.fromMethodDescriptorString("(L"+owner+";"+descriptor.substring(1),callerLoader));
+ }
+ break;
+ default:
+ throw new IllegalStateException("nyi "+bsmArgsHandle.getTag());
+ }
+
+ MethodType instantiatedMethodType = MethodType.fromMethodDescriptorString((String)(((Type)bsmArgs[2]).getDescriptor()), callerLoader);
+
+ return LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
+ }
+
+ /**
+ * The metafactory we are enhancing is responsible for generating the anonymous classes that will call the lambda methods in our type
+ *
+ * @param bytes
+ * @return
+ */
+ public static byte[] enhanceInnerClassLambdaMetaFactory(byte[] bytes) {
+ // TODO Auto-generated method stub
+ return null;
+ }
+}
diff --git a/springloaded/src/main/java/org/springsource/loaded/test/infra/ClassPrinter.java b/springloaded/src/main/java/org/springsource/loaded/test/infra/ClassPrinter.java
index 204d2e3..e9de0a3 100644
--- a/springloaded/src/main/java/org/springsource/loaded/test/infra/ClassPrinter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/test/infra/ClassPrinter.java
@@ -32,7 +32,7 @@ import org.springsource.loaded.Utils;
/**
* @author Andy Clement
*/
-public class ClassPrinter implements ClassVisitor, Opcodes {
+public class ClassPrinter extends ClassVisitor implements Opcodes {
private PrintStream destination;
private boolean includeBytecode;
@@ -47,6 +47,7 @@ public class ClassPrinter implements ClassVisitor, Opcodes {
}
public ClassPrinter(PrintStream destination, boolean includeBytecode) {
+ super(ASM5);
this.destination = destination;
this.includeBytecode = includeBytecode;
}
@@ -188,7 +189,11 @@ public class ClassPrinter implements ClassVisitor, Opcodes {
return new AnnotationVisitorPrinter();
}
- class AnnotationVisitorPrinter implements AnnotationVisitor {
+ class AnnotationVisitorPrinter extends AnnotationVisitor {
+
+ public AnnotationVisitorPrinter() {
+ super(ASM5);
+ }
public void visit(String name, Object value) {
destination.print(name + "=" + value + " ");
diff --git a/springloaded/src/main/java/org/springsource/loaded/test/infra/MethodPrinter.java b/springloaded/src/main/java/org/springsource/loaded/test/infra/MethodPrinter.java
index 04121be..d00cc9a 100644
--- a/springloaded/src/main/java/org/springsource/loaded/test/infra/MethodPrinter.java
+++ b/springloaded/src/main/java/org/springsource/loaded/test/infra/MethodPrinter.java
@@ -21,17 +21,17 @@ import java.util.List;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.springsource.loaded.Utils;
-
/**
*
* @author Andy Clement
*/
-public class MethodPrinter implements MethodVisitor, Opcodes {
+public class MethodPrinter extends MethodVisitor implements Opcodes {
PrintStream to;
@@ -47,12 +47,22 @@ public class MethodPrinter implements MethodVisitor, Opcodes {
}
public MethodPrinter(PrintStream destination) {
+ super(ASM5);
this.to = destination;
}
public void visitCode() {
to.print(" CODE\n");
}
+
+ @Override
+ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
+ to.println(" INVOKEDYNAMIC " + name+"."+desc+" bsm="+toString(bsm));
+ }
+
+ private String toString(Handle bsm) {
+ return "#"+bsm.getTag()+" "+bsm.getOwner()+"."+bsm.getName()+bsm.getDesc();
+ }
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (opcode == Opcodes.INVOKESTATIC) {
@@ -75,7 +85,11 @@ public class MethodPrinter implements MethodVisitor, Opcodes {
return new AnnotationVisitorPrinter();
}
- class AnnotationVisitorPrinter implements AnnotationVisitor {
+ class AnnotationVisitorPrinter extends AnnotationVisitor {
+
+ public AnnotationVisitorPrinter() {
+ super(ASM5);
+ }
public void visit(String name, Object value) {
to.print(name + "=" + value + " ");
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ExecutorBuilderTests.java b/springloaded/src/test/java/org/springsource/loaded/test/ExecutorBuilderTests.java
index 7ec24d1..497864e 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/ExecutorBuilderTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/ExecutorBuilderTests.java
@@ -17,6 +17,7 @@ package org.springsource.loaded.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
@@ -262,7 +263,10 @@ public class ExecutorBuilderTests extends SpringLoadedTests {
s.add(anno.toString());
}
Assert.assertTrue(s.remove("@common.Marker()"));
- Assert.assertTrue(s.remove("@common.Anno(someValue=37, longValue=2, id=abc)"));
+ // Allow for alternate toString() variant
+ if (!s.remove("@common.Anno(someValue=37, longValue=2, id=abc)")) {
+ Assert.assertTrue(s.remove("@common.Anno(longValue=2, someValue=37, id=abc)"));
+ }
Assert.assertEquals(0, s.size());
}
@@ -296,8 +300,36 @@ public class ExecutorBuilderTests extends SpringLoadedTests {
checkAnnotations(rtype.getLatestExecutorBytes(), "m2(Lexecutor/I;)V", "@common.Marker()", "@common.Anno(id=abc)");
Method m = rtype.getLatestExecutorClass().getDeclaredMethod("m2", rtype.getClazz());
assertEquals("@common.Marker()", m.getAnnotations()[0].toString());
- assertEquals("@common.Anno(someValue=37, longValue=2, id=abc)", m.getAnnotations()[1].toString());
+ assertIsOneOfThese(printAnnotation(m.getAnnotations()[1]),"@common.Anno(someValue=37, longValue=2, id=abc)", "@common.Anno(longValue=2, someValue=37, id=abc)");
}
+
+ /**
+ * Check the actual value is one of the possible options.
+ */
+ private void assertIsOneOfThese(String actual, String... possibleValues) {
+ StringBuilder buf = new StringBuilder();
+ for (int i=0;i clazz = a.annotationType();a.toString()
+// clazz.getDeclaredFields()[0].get
+// System.out.println(a.annotationType());
+//
+// }
@Test
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/FieldReloadingTests.java b/springloaded/src/test/java/org/springsource/loaded/test/FieldReloadingTests.java
index fc7b80e..a4e55e8 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/FieldReloadingTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/FieldReloadingTests.java
@@ -564,7 +564,11 @@ public class FieldReloadingTests extends SpringLoadedTests {
} catch (ResultException re) {
assertTrue(re.getCause() instanceof InvocationTargetException);
assertTrue(re.getCause().getCause() instanceof IncompatibleClassChangeError);
- assertEquals("Expected static field fields.Yb.j", re.getCause().getCause().getMessage());
+ // When compiled with AspectJ vs Eclipse JDT the GETSTATIC actually varies.
+ // With AspectJ it is: PUTSTATIC fields/Yb.j : I
+ // With JDT (4.3) it is: PUTSTATIC fields/Zb.j : I
+ // hence the error is different
+ assertEquals("Expected static field fields.Zb.j", re.getCause().getCause().getMessage());
}
// Now should be an IncompatibleClassChangeError
@@ -574,7 +578,11 @@ public class FieldReloadingTests extends SpringLoadedTests {
} catch (ResultException re) {
assertTrue(re.getCause() instanceof InvocationTargetException);
assertTrue(re.getCause().getCause() instanceof IncompatibleClassChangeError);
- assertEquals("Expected static field fields.Yb.j", re.getCause().getCause().getMessage());
+ // When compiled with AspectJ vs Eclipse JDT the GETSTATIC actually varies.
+ // With AspectJ it is: GETSTATIC fields/Yb.j : I
+ // With JDT (4.3) it is: GETSTATIC fields/Zb.j : I
+ // hence the error is different
+ assertEquals("Expected static field fields.Zb.j", re.getCause().getCause().getMessage());
}
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/InnerClassesTests.java b/springloaded/src/test/java/org/springsource/loaded/test/InnerClassesTests.java
index caddd68..df64de5 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/InnerClassesTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/InnerClassesTests.java
@@ -18,6 +18,7 @@ package org.springsource.loaded.test;
import org.junit.Test;
import org.springsource.loaded.ReloadableType;
import org.springsource.loaded.TypeRegistry;
+import org.springsource.loaded.test.infra.ClassPrinter;
/**
@@ -68,11 +69,15 @@ public class InnerClassesTests extends SpringLoadedTests {
public void reloadPrivateVisInner() throws Exception {
String tclass = "inners.Three";
TypeRegistry typeRegistry = getTypeRegistry("inners..*");
- typeRegistry.addType("inners.Three$Inner", retrieveRename("inners.Three$Inner", "inners.Three2$Inner"));
+
+
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
runUnguarded(rtype.getClazz(), "runner");
- rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2", "inners.Three2$Inner:inners.Three$Inner"));
+// ReloadableType rtypeInner =
+ typeRegistry.addType("inners.Three$Inner", retrieveRename("inners.Three$Inner", "inners.Three2$Inner","inners.Three2:inners.Three"));
+
+ rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2", "inners.Three2$Inner:inners.Three$Inner","inners.Three2:inners.Three"));
runUnguarded(rtype.getClazz(), "runner");
}
@@ -84,11 +89,11 @@ public class InnerClassesTests extends SpringLoadedTests {
public void reloadProtectedVisInner() throws Exception {
String tclass = "inners.Four";
TypeRegistry typeRegistry = getTypeRegistry("inners..*");
- typeRegistry.addType("inners.Four$Inner", retrieveRename("inners.Four$Inner", "inners.Four2$Inner"));
+ typeRegistry.addType("inners.Four$Inner", retrieveRename("inners.Four$Inner", "inners.Four2$Inner","inners.Four2:inners.Four"));
ReloadableType rtype = typeRegistry.addType(tclass, loadBytesForClass(tclass));
runUnguarded(rtype.getClazz(), "runner");
- rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2", "inners.Four2$Inner:inners.Four$Inner"));
+ rtype.loadNewVersion("2", retrieveRename(tclass, tclass + "2", "inners.Four2$Inner:inners.Four$Inner","inners.Four2:inners.Four"));
runUnguarded(rtype.getClazz(), "runner");
}
}
\ No newline at end of file
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java b/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java
new file mode 100644
index 0000000..570a239
--- /dev/null
+++ b/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright 2014 Pivotal Software Inc. and contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springsource.loaded.test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.springsource.loaded.ReloadableType;
+import org.springsource.loaded.TypeRegistry;
+import org.springsource.loaded.test.infra.Result;
+
+/**
+ * Test reloading of Java 8.
+ *
+ * @author Andy Clement
+ * @since 1.2
+ */
+public class Java8Tests extends SpringLoadedTests {
+
+ @Test
+ public void theBasics() {
+ String t = "basic.FirstClass";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = new ReloadableType(t, sc, 1, typeRegistry, null);
+
+ assertEquals(1, rtype.getId());
+ assertEquals(t, rtype.getName());
+ assertEquals(slashed(t), rtype.getSlashedName());
+ assertNotNull(rtype.getTypeDescriptor());
+ assertEquals(typeRegistry, rtype.getTypeRegistry());
+ }
+
+ @Test
+ public void callBasicType() throws Exception {
+ String t = "basic.FirstClass";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(8, r.returnValue);
+
+ rtype.loadNewVersion("002", rtype.bytesInitial);
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(8, r.returnValue);
+ }
+
+ @Test
+ public void lambdaA() throws Exception {
+ String t = "basic.LambdaA";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(77, r.returnValue);
+
+ rtype.loadNewVersion("002", rtype.bytesInitial);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(77, r.returnValue);
+ }
+
+ @Test
+ public void changingALambda() throws Exception {
+ String t = "basic.LambdaA";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(77, r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Foo:"+t+"$Foo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(88, r.returnValue);
+ }
+
+ @Test
+ public void lambdaWithParameter() throws Exception {
+ String t = "basic.LambdaB";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(99L, r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Foo:"+t+"$Foo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(176L, r.returnValue);
+ }
+
+
+ @Test
+ public void lambdaWithTwoParameters() throws Exception {
+ String t = "basic.LambdaC";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(6L, r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Boo:"+t+"$Boo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(5L, r.returnValue);
+ }
+
+ @Test
+ public void lambdaWithThreeMixedTypeParameters() throws Exception {
+ String t = "basic.LambdaD";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("true342abc", r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Boo:"+t+"$Boo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("def264true", r.returnValue);
+ }
+
+ @Test
+ public void lambdaWithCapturedVariable() throws Exception {
+ String t = "basic.LambdaE";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("aaaa", r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Boo:"+t+"$Boo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("aaaaaaaa", r.returnValue);
+ }
+
+ @Test
+ public void lambdaWithThis() throws Exception {
+ String t = "basic.LambdaF";
+ TypeRegistry typeRegistry = getTypeRegistry(t);
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("aaaaaaa", r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Boo:"+t+"$Boo");
+ rtype.loadNewVersion("002", renamed);
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("a:a:a:", r.returnValue);
+ }
+
+ @Test
+ public void lambdaWithNonPublicInnerInterface() throws Exception {
+ String t = "basic.LambdaG";
+ TypeRegistry typeRegistry = getTypeRegistry("basic..*");
+
+ // Since Boo needs promoting to public, have to ensure it is directly loaded:
+ typeRegistry.addType(t+"$Boo", loadBytesForClass(t+"$Boo"));
+
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(99, r.returnValue);
+
+ byte[] renamed = retrieveRename(t,t+"2",t+"2$Boo:"+t+"$Boo");
+ rtype.loadNewVersion("002", renamed);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(44, r.returnValue);
+ }
+
+ @Test
+ public void multipleLambdasInOneMethod() throws Exception {
+ String t = "basic.LambdaH";
+ TypeRegistry typeRegistry = getTypeRegistry("basic..*");
+
+ // Since Foo needs promoting to public, have to ensure it is directly loaded:
+ typeRegistry.addType(t+"$Foo", loadBytesForClass(t+"$Foo"));
+
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(56, r.returnValue);
+
+ rtype.loadNewVersion("002", rtype.bytesInitial);
+ r = runUnguarded(simpleClass, "run");
+ assertEquals(56, r.returnValue);
+ }
+
+ @Test
+ public void lambdaSignatureChange() throws Exception {
+ String t = "basic.LambdaI";
+ TypeRegistry typeRegistry = getTypeRegistry("basic..*");
+
+ // Since Foo needs promoting to public, have to ensure it is directly loaded:
+ ReloadableType itype = typeRegistry.addType(t+"$Foo", loadBytesForClass(t+"$Foo"));
+
+ byte[] sc = loadBytesForClass(t);
+ ReloadableType rtype = typeRegistry.addType(t, sc);
+
+ Class> simpleClass = rtype.getClazz();
+ Result r = runUnguarded(simpleClass, "run");
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("a", r.returnValue);
+
+ itype.loadNewVersion("002", retrieveRename(t+"$Foo",t+"2$Foo"));
+ rtype.loadNewVersion("002", retrieveRename(t,t+"2",t+"2$Foo:"+t+"$Foo"));
+
+ r = runUnguarded(simpleClass, "run");
+ assertEquals("ab", r.returnValue);
+ }
+
+ @Ignore
+ @Test
+ public void lambdaWithVirtualMethodUse() throws Exception {
+ // not yet written
+ }
+
+ @Ignore
+ @Test
+ public void altMetaFactoryUsage() throws Exception {
+ // not yet written
+ }
+
+ // TODO catchers and lambda methods (non static ones)
+
+}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/JavaMicroBenchmarkTests.java b/springloaded/src/test/java/org/springsource/loaded/test/JavaMicroBenchmarkTests.java
index 5a6ebbf..546c569 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/JavaMicroBenchmarkTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/JavaMicroBenchmarkTests.java
@@ -53,14 +53,6 @@ public class JavaMicroBenchmarkTests extends SpringLoadedTests {
average(rtype, 5);
}
- private void pause(int seconds) {
- System.out.println("waiting...");
- try {
- Thread.sleep(seconds * 1000);
- } catch (Exception e) {
- }
- }
-
// TODO fibonacci
private void average(ReloadableType rtype, int count) throws Exception {
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java b/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
index 6608cb8..d0bc365 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/ReloadableTypeTests.java
@@ -205,7 +205,7 @@ public class ReloadableTypeTests extends SpringLoadedTests {
@Test
public void invokeStaticReloading_gh4_2() throws Exception {
TypeRegistry tr = getTypeRegistry("invokestatic..*");
- ReloadableType AA = tr.addType("invokestatic.issue4.AA", loadBytesForClass("invokestatic.issue4.AA"));
+ tr.addType("invokestatic.issue4.AA", loadBytesForClass("invokestatic.issue4.AA"));
ReloadableType BB = tr.addType("invokestatic.issue4.BB", loadBytesForClass("invokestatic.issue4.BB"));
Result r = runUnguarded(BB.getClazz(), "getMessage");
@@ -272,7 +272,7 @@ public class ReloadableTypeTests extends SpringLoadedTests {
@Test
public void invokeStaticReloading_gh4_6() throws Exception {
TypeRegistry tr = getTypeRegistry("invokestatic.issue4..*");
- ReloadableType AB = tr.addType("invokestatic.issue4.AB", loadBytesForClass("invokestatic.issue4.AB"));
+ tr.addType("invokestatic.issue4.AB", loadBytesForClass("invokestatic.issue4.AB"));
ReloadableType B = tr.addType("invokestatic.issue4.BBBBB", loadBytesForClass("invokestatic.issue4.BBBBB"));
Result r = runUnguarded(B.getClazz(), "getMessage");
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java b/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
index 525580c..5324de6 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/ReloadingJVM.java
@@ -236,6 +236,25 @@ public class ReloadingJVM {
}
Utils.write(new File(testdataDirectory,classfile),data);
}
+
+ public void clearTestdataDirectory() {
+ File[] fs = testdataDirectory.listFiles();
+ for (File f: fs) {
+ delete(f);
+ }
+ }
+
+ private void delete(File toDelete) {
+ if (toDelete.isDirectory()) {
+ File[] fs = toDelete.listFiles();
+ for (File f: fs) {
+ delete(f);
+ }
+ }
+ else {
+ toDelete.delete();
+ }
+ }
public JVMOutput newInstance(String instanceName, String classname) {
copyToTestdataDirectory(classname);
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
index 2cc8df1..e608925 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTests.java
@@ -47,12 +47,7 @@ import java.util.StringTokenizer;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
-import org.objectweb.asm.AnnotationVisitor;
-import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.ClassVisitor;
-import org.objectweb.asm.FieldVisitor;
-import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.ClassNode;
@@ -61,7 +56,6 @@ import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodNode;
import org.springsource.loaded.ClassRenamer;
import org.springsource.loaded.Constants;
-import org.springsource.loaded.GlobalConfiguration;
import org.springsource.loaded.ISMgr;
import org.springsource.loaded.MethodMember;
import org.springsource.loaded.NameRegistry;
@@ -92,12 +86,13 @@ public abstract class SpringLoadedTests implements Constants {
protected ClassLoader binLoader;
protected String TestDataPath = TestUtils.getPathToClasses("../testdata");
+ protected String TestDataAspectJPath = TestUtils.getPathToClasses("../testdata-aspectj");
protected String GroovyTestDataPath = TestUtils.getPathToClasses("../testdata-groovy");
protected String AspectjrtJar = "../testdata/aspectjrt.jar";
protected String CodeJar = "../testdata/code.jar";
// TODO [java8] replace this with project dependency when Java8 is out
protected String Java8CodeJar = "../testdata-java8/build/libs/testdata-java8.jar";
- protected String GroovyrtJar = "../testdata-groovy/groovy-1.8.2.jar";
+ protected String GroovyrtJar = "../testdata-groovy/groovy-all-1.8.6.jar";
protected Result result;
protected TypeRegistry registry;
@@ -105,7 +100,7 @@ public abstract class SpringLoadedTests implements Constants {
public void setup() throws Exception {
SpringLoadedPreProcessor.disabled = true;
NameRegistry.reset();
- binLoader = new TestClassLoader(toURLs(TestDataPath, AspectjrtJar, CodeJar, Java8CodeJar), this.getClass().getClassLoader());
+ binLoader = new TestClassLoader(toURLs(TestDataPath, TestDataAspectJPath, AspectjrtJar, CodeJar, Java8CodeJar), this.getClass().getClassLoader());
}
@After
@@ -1260,5 +1255,16 @@ public abstract class SpringLoadedTests implements Constants {
m.invoke(null);
return captureOff();
}
+
+ protected String slashed(String dotted) {
+ return dotted.replaceAll("\\.", "/");
+ }
+
+ protected final static void pause(int seconds) {
+ try {
+ Thread.sleep(seconds*1000);
+ } catch (Exception e) {}
+ }
+
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
index bcecbd3..d653bd1 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/SpringLoadedTestsInSeparateJVM.java
@@ -17,12 +17,15 @@ package org.springsource.loaded.test;
import static org.junit.Assert.fail;
+import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springsource.loaded.test.ReloadingJVM.JVMOutput;
+import sun.misc.Cleaner;
+
/**
* These tests use a harness that forks a JVM with the agent attached, closely simulating a real environment. The
* forked process is running a special class that can be sent commands.
@@ -43,6 +46,12 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
public static void stopJVM() {
jvm.shutdown();
}
+
+ @After
+ public void teardown() throws Exception {
+ super.teardown();
+ jvm.clearTestdataDirectory();
+ }
@Test
public void testEcho() throws Exception {
@@ -70,6 +79,7 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
@Test
public void githubIssue34_2() throws Exception {
jvm.copyToTestdataDirectory("issue34.InnerEnum$sorters");
+ jvm.copyToTestdataDirectory("issue34.InnerEnum$MyComparator");
jvm.copyToTestdataDirectory("issue34.InnerEnum$sorters$1");
JVMOutput output = jvm.run("issue34.InnerEnum");
assertStdout("Hello World!\n", output);
@@ -81,6 +91,9 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
assertStdout("jvmtwo.Runner.run1() running", jvm.call("a", "run1"));
}
+ /* careful with this test - when the forked JVM takes a while the output can interfere with later tests, needs fixing up
+ *
+ */
@Ignore
@Test
public void reloadedPerformance() throws Exception {
@@ -93,7 +106,7 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
pause(2);
// In Perf2 the static method is gone, why does it give us a NSME?
jo = jvm.call("a","time"); // 150ms
- System.out.println(jo);
+ pause(5);
}
private final static void debug() {
@@ -105,23 +118,14 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
jvm = ReloadingJVM.launch(options,true);
}
- private final static void pause(int seconds) {
- try {
- Thread.sleep(seconds*1000);
- } catch (Exception e) {}
- }
-
-
@Test
public void testReloadingInOtherVM() throws Exception {
- jvm.newInstance("a", "remote.One");
- assertStdout("first", jvm.call("a", "run"));
+ jvm.newInstance("b", "remote.One");
+ assertStdout("first", jvm.call("b", "run"));
+ pause(1);
jvm.updateClass("remote.One",retrieveRename("remote.One","remote.One2"));
- try {
- Thread.sleep(2000);
- } catch (Exception e) {
- }
- assertStdoutContains("second", jvm.call("a", "run"));
+ pause(2);
+ assertStdoutContains("second", jvm.call("b", "run"));
}
// TODO tidyup test data area after each test?
// TODO flush/replace classloader in forked VM to clear it out after each test?
@@ -137,11 +141,14 @@ public class SpringLoadedTestsInSeparateJVM extends SpringLoadedTests {
String subtype="foo.Controller";
jvm.copyToTestdataDirectory(supertype);
jvm.copyToTestdataDirectory(subtype);
- jvm.newInstance("a",subtype);
- assertStdout("Top.foo() running\nController.foo() running\n", jvm.call("a", "foo"));
+ JVMOutput jo = jvm.newInstance("bb",subtype);
+ System.out.println(jo);
+ pause(1);
+ assertStdout("Top.foo() running\nController.foo() running\n", jvm.call("bb", "foo"));
+ pause(1);
jvm.updateClass(subtype,retrieveRename(subtype,subtype+"2"));
waitForReloadToOccur();
- JVMOutput jo = jvm.call("a", "foo");
+ jo = jvm.call("bb", "foo");
assertStdoutContains("Top.foo() running\nController.foo() running again!\n", jo);
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/TestInfrastructureTests.java b/springloaded/src/test/java/org/springsource/loaded/test/TestInfrastructureTests.java
index bea6154..403ac26 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/TestInfrastructureTests.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/TestInfrastructureTests.java
@@ -47,7 +47,7 @@ public class TestInfrastructureTests extends SpringLoadedTests {
TestClassLoader tcl = new TestClassLoader(toURLs(TestDataPath), this.getClass().getClassLoader());
byte[] classdata = Utils.loadDottedClassAsBytes(tcl, "data.SimpleClass");
Assert.assertNotNull(classdata);
- Assert.assertEquals(394, classdata.length);
+ Assert.assertEquals(331, classdata.length);
}
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/FakeMethodVisitor.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/FakeMethodVisitor.java
index 3d0e2bc..8cc0258 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/infra/FakeMethodVisitor.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/FakeMethodVisitor.java
@@ -19,13 +19,18 @@ import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
+import org.springsource.loaded.Constants;
import org.springsource.loaded.Utils;
/**
* MethodVisitor that records events - very useful for testing
*/
-public class FakeMethodVisitor implements MethodVisitor {
+public class FakeMethodVisitor extends MethodVisitor implements Constants {
+
+ public FakeMethodVisitor() {
+ super(ASM5);
+ }
StringBuilder events = new StringBuilder();
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/RewriteReflectUtilsDefineClass.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/RewriteReflectUtilsDefineClass.java
index 4cef98a..9be7813 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/infra/RewriteReflectUtilsDefineClass.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/RewriteReflectUtilsDefineClass.java
@@ -15,10 +15,9 @@
*/
package org.springsource.loaded.test.infra;
-import org.objectweb.asm.ClassAdapter;
import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
-import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.MethodVisitor;
import org.springsource.loaded.Constants;
import org.springsource.loaded.ReloadableType;
@@ -32,7 +31,7 @@ import org.springsource.loaded.TypeRegistry;
* @author Andy Clement
* @version 0.8.3
*/
-public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Constants {
+public class RewriteReflectUtilsDefineClass extends ClassVisitor implements Constants {
public static byte[] rewriteReflectUtilsDefineClass(byte[] data) {
ClassReader cr = new ClassReader(data);
@@ -43,7 +42,7 @@ public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Cons
}
private RewriteReflectUtilsDefineClass() {
- super(new ClassWriter(0)); // TODO review 0 here
+ super(ASM5,new ClassWriter(0)); // TODO review 0 here
}
public byte[] getBytes() {
@@ -74,10 +73,10 @@ public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Cons
}
}
- class DefineClassInterceptor extends MethodAdapter implements Constants {
+ class DefineClassInterceptor extends MethodVisitor implements Constants {
public DefineClassInterceptor(MethodVisitor mv) {
- super(mv);
+ super(ASM5,mv);
}
@Override
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/SubLoader.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/SubLoader.java
index d2bd283..ce04187 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/infra/SubLoader.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/SubLoader.java
@@ -47,7 +47,7 @@ public class SubLoader extends ClassLoader {
TestUtils.getPathToClasses("../testdata-subloader")
};
static String[] jars = new String[] {
- "../testdata-groovy/groovy-1.8.2.jar"
+ "../testdata-groovy/groovy-all-1.8.6.jar"
};
// @formatter:on
@@ -140,8 +140,8 @@ public class SubLoader extends ClassLoader {
c = defineClass(name, data, 0, data.length);
break;
}
+ zipfile.close();
}
- // zipfile.close();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Problem defining class", e);
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/SuperLoader.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/SuperLoader.java
index b2af228..992f95f 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/infra/SuperLoader.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/SuperLoader.java
@@ -45,12 +45,12 @@ public class SuperLoader extends ClassLoader {
TestUtils.getPathToClasses("../testdata-superloader")
};
static String[] jars = new String[] {
- "../testdata-groovy/groovy-1.8.2.jar"
+ "../testdata-groovy/groovy-all-1.8.6.jar"
};
// @formatter:on
public SuperLoader() {
- jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
+ jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
}
public SuperLoader(String... jars) {
@@ -132,8 +132,8 @@ public class SuperLoader extends ClassLoader {
c = defineClass(name, data, 0, data.length);
break;
}
+ zipfile.close();
}
- // zipfile.close();
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java b/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java
index 29a5a2b..ba9facb 100644
--- a/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java
+++ b/springloaded/src/test/java/org/springsource/loaded/test/infra/TestClassloaderWithRewriting.java
@@ -51,12 +51,12 @@ public class TestClassloaderWithRewriting extends ClassLoader {
TestUtils.getPathToClasses("../testdata-groovy")
};
static String[] jars = new String[] {
- "../testdata-groovy/groovy-1.8.2.jar"
+ "../testdata-groovy/groovy-all-1.8.6.jar"
};
// @formatter:on
public TestClassloaderWithRewriting() {
- jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
+ jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
}
public TestClassloaderWithRewriting(String metainfFolder) {
@@ -64,7 +64,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
newFolders[0] = folders[0];
newFolders[1] = "../testdata/" + metainfFolder;
folders = newFolders;
- jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
+ jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
}
public TestClassloaderWithRewriting(String metainfFolder, boolean b) {
@@ -74,7 +74,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
newFolders[2] = TestUtils.getPathToClasses("../testdata");
newFolders[3] = TestUtils.getPathToClasses("../testdata-plugin");
folders = newFolders;
- jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
+ jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
}
public TestClassloaderWithRewriting(String metainfFolder, boolean b, boolean useRegistry, URLClassLoader classLoader) {
@@ -85,7 +85,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
newFolders[2] = TestUtils.getPathToClasses("../testdata");
folders = newFolders;
this.useRegistry = useRegistry;
- jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
+ jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
}
public TestClassloaderWithRewriting(boolean b, boolean useRegistry, boolean addCglib) {
diff --git a/testdata-aspectj/.classpath b/testdata-aspectj/.classpath
new file mode 100644
index 0000000..7ca2130
--- /dev/null
+++ b/testdata-aspectj/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/testdata-aspectj/.project b/testdata-aspectj/.project
new file mode 100644
index 0000000..9941b1e
--- /dev/null
+++ b/testdata-aspectj/.project
@@ -0,0 +1,18 @@
+
+
+ testdata-aspectj
+
+
+
+
+
+ org.eclipse.ajdt.core.ajbuilder
+
+
+
+
+
+ org.eclipse.ajdt.ui.ajnature
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/testdata-aspectj/build.gradle b/testdata-aspectj/build.gradle
new file mode 100644
index 0000000..fa94bf2
--- /dev/null
+++ b/testdata-aspectj/build.gradle
@@ -0,0 +1,40 @@
+def aspectjVersion = "1.8.0.M1"
+
+configurations {
+ aspects
+ ajInpath
+}
+
+dependencies {
+ tools "org.aspectj:aspectjtools:$aspectjVersion"
+ compile "org.aspectj:aspectjrt:$aspectjVersion"
+ compile("cglib:cglib:2.2.2") { exclude group: 'asm' } // cglib 2.2.2 depends on asm 3.3
+ compile 'org.ow2.asm:asm:5.0_BETA'
+ compile 'org.ow2.asm:asm-tree:5.0_BETA'
+ compile files("code.jar")
+}
+
+compileJava.deleteAllActions()
+
+task aspectJ(dependsOn: JavaPlugin.PROCESS_RESOURCES_TASK_NAME) {
+ dependsOn configurations.tools.getTaskDependencyFromProjectDependency(true, "compileJava")
+ def srcDirs = sourceSets.main.java.srcDirs
+ srcDirs.each { inputs.dir it }
+ def destDir = sourceSets.main.output.classesDir
+ outputs.dir destDir
+ doLast {
+ ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.tools.asPath)
+
+ ant.iajc(source:sourceCompatibility, target:targetCompatibility, destDir: destDir.absolutePath, maxmem:"512m", fork:"true",
+ aspectPath: configurations.aspects.asPath, inpath:configurations.ajInpath.asPath, sourceRootCopyFilter:"**/.svn/*,**/*.java",classpath:configurations.compile.asPath ){
+ sourceroots {
+ srcDirs.each {
+ if (it.exists()) pathelement location: it.absolutePath
+ }
+ }
+ }
+ }
+}
+
+compileJava.dependsOn aspectJ
+
diff --git a/testdata/src/main/java/data/AnAspect.java b/testdata-aspectj/src/main/java/data/AnAspect.java
similarity index 100%
rename from testdata/src/main/java/data/AnAspect.java
rename to testdata-aspectj/src/main/java/data/AnAspect.java
diff --git a/testdata/src/main/java/data/AspectReceiver.java b/testdata-aspectj/src/main/java/data/AspectReceiver.java
similarity index 100%
rename from testdata/src/main/java/data/AspectReceiver.java
rename to testdata-aspectj/src/main/java/data/AspectReceiver.java
diff --git a/testdata-groovy/.classpath b/testdata-groovy/.classpath
index b81d5bc..89435f1 100644
--- a/testdata-groovy/.classpath
+++ b/testdata-groovy/.classpath
@@ -3,5 +3,6 @@
+
diff --git a/testdata-groovy/groovy-all-1.8.6.jar b/testdata-groovy/groovy-all-1.8.6.jar
new file mode 100644
index 0000000..6dc0128
Binary files /dev/null and b/testdata-groovy/groovy-all-1.8.6.jar differ
diff --git a/testdata-java8/.classpath b/testdata-java8/.classpath
new file mode 100644
index 0000000..7461090
--- /dev/null
+++ b/testdata-java8/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties b/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties
new file mode 100644
index 0000000..be198ef
--- /dev/null
+++ b/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties
@@ -0,0 +1 @@
+#Thu Feb 06 13:01:33 PST 2014
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties.lock b/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties.lock
new file mode 100644
index 0000000..40fdece
--- /dev/null
+++ b/testdata-java8/.gradle/1.7/taskArtifacts/cache.properties.lock
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin b/testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin
new file mode 100644
index 0000000..b6c8cc9
Binary files /dev/null and b/testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin differ
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin b/testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin
new file mode 100644
index 0000000..755b7b8
Binary files /dev/null and b/testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin differ
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin b/testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin
new file mode 100644
index 0000000..f8edce7
Binary files /dev/null and b/testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin differ
diff --git a/testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin b/testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin
new file mode 100644
index 0000000..4a4eb10
Binary files /dev/null and b/testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin differ
diff --git a/testdata-java8/.project b/testdata-java8/.project
new file mode 100644
index 0000000..c51edec
--- /dev/null
+++ b/testdata-java8/.project
@@ -0,0 +1,17 @@
+
+
+ testdata-java8
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/testdata-java8/.settings/org.eclipse.jdt.core.prefs b/testdata-java8/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..7341ab1
--- /dev/null
+++ b/testdata-java8/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,11 @@
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.7
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.7
diff --git a/testdata-java8/build.gradle b/testdata-java8/build.gradle
new file mode 100644
index 0000000..0c9263d
--- /dev/null
+++ b/testdata-java8/build.gradle
@@ -0,0 +1,4 @@
+
+apply plugin: 'java'
+sourceCompatibility = 1.8
+targetCompatibility = 1.8
diff --git a/testdata-java8/build.sh b/testdata-java8/build.sh
new file mode 100755
index 0000000..70a6758
--- /dev/null
+++ b/testdata-java8/build.sh
@@ -0,0 +1 @@
+../gradlew build
diff --git a/testdata-java8/src/main/java/basic/FirstClass.class b/testdata-java8/src/main/java/basic/FirstClass.class
new file mode 100644
index 0000000..7755e76
Binary files /dev/null and b/testdata-java8/src/main/java/basic/FirstClass.class differ
diff --git a/testdata-java8/src/main/java/basic/FirstClass.java b/testdata-java8/src/main/java/basic/FirstClass.java
new file mode 100644
index 0000000..846f4ea
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/FirstClass.java
@@ -0,0 +1,12 @@
+package basic;
+
+public class FirstClass {
+
+ public static void main(String[] args) {
+ System.out.println("This is Java8");
+ }
+
+ public static int run() {
+ return 8;
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaA.java b/testdata-java8/src/main/java/basic/LambdaA.java
new file mode 100644
index 0000000..b276dc6
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaA.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaA {
+
+ public interface Foo { int m(); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static int run() {
+ Foo f = null;
+ f = () -> 77;
+ return f.m();
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaA2.java b/testdata-java8/src/main/java/basic/LambdaA2.java
new file mode 100644
index 0000000..7e7de68
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaA2.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaA2 {
+
+ public interface Foo { int m(); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static int run() {
+ Foo f = null;
+ f = () -> 88;
+ return f.m();
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaB.java b/testdata-java8/src/main/java/basic/LambdaB.java
new file mode 100644
index 0000000..1388812
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaB.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaB {
+
+ public interface Foo { long m(int i); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static long run() {
+ Foo f = null;
+ f = (i) -> i*33;
+ return f.m(3);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaB2.java b/testdata-java8/src/main/java/basic/LambdaB2.java
new file mode 100644
index 0000000..ae90e8b
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaB2.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaB2 {
+
+ public interface Foo { long m(int i); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static long run() {
+ Foo f = null;
+ f = (i) -> i*44;
+ return f.m(4);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaC.java b/testdata-java8/src/main/java/basic/LambdaC.java
new file mode 100644
index 0000000..14d415e
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaC.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaC {
+
+ public interface Boo { long m(int i,int j); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static long run() {
+ Boo f = null;
+ f = (i,j) -> i*j;
+ return f.m(3,2);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaC2.java b/testdata-java8/src/main/java/basic/LambdaC2.java
new file mode 100644
index 0000000..c091501
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaC2.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaC2 {
+
+ public interface Boo { long m(int i,int j); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static long run() {
+ Boo f = null;
+ f = (i,j) -> i+j;
+ return f.m(3,2);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaD.java b/testdata-java8/src/main/java/basic/LambdaD.java
new file mode 100644
index 0000000..72451fc
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaD.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaD {
+
+ public interface Boo { String m(int i,String s, int j, boolean b); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static String run() {
+ Boo f = null;
+ f = (i,j,k,l) -> ""+l+i+k+j;
+ return f.m(3,"abc",42,true);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaD2.java b/testdata-java8/src/main/java/basic/LambdaD2.java
new file mode 100644
index 0000000..586a3f4
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaD2.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaD2 {
+
+ public interface Boo { String m(int i,String s, int j, boolean b); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static String run() {
+ Boo f = null;
+ f = (i,j,k,l) -> j+(i*k)+l;
+ return f.m(3,"def",88,true);
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaE.java b/testdata-java8/src/main/java/basic/LambdaE.java
new file mode 100644
index 0000000..47e962a
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaE.java
@@ -0,0 +1,23 @@
+package basic;
+
+public class LambdaE {
+
+ public interface Boo { String m(char s); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static String run() {
+ int i = 4;
+ Boo f = null;
+ f = (c) -> {
+ StringBuilder buf = new StringBuilder();
+ for (int j=0;j {
+ StringBuilder buf = new StringBuilder();
+ for (int j=0;j {
+ return concatenator(c,fieldOne);
+ };
+ return f.m('a');
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaF2.java b/testdata-java8/src/main/java/basic/LambdaF2.java
new file mode 100644
index 0000000..9b6cf02
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaF2.java
@@ -0,0 +1,34 @@
+package basic;
+
+public class LambdaF2 {
+
+ public int fieldOne = 3;
+
+ public String concatenator(char ch, int number) {
+ StringBuilder buf = new StringBuilder();
+ for (int j=0;j {
+ return concatenator(c,fieldOne);
+ };
+ return f.m('a');
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaG.java b/testdata-java8/src/main/java/basic/LambdaG.java
new file mode 100644
index 0000000..a0885ed
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaG.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaG {
+
+ interface Boo { int m(); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static int run() {
+ Boo f = null;
+ f = () -> 99;
+ return f.m();
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaG2.java b/testdata-java8/src/main/java/basic/LambdaG2.java
new file mode 100644
index 0000000..4b2b2da
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaG2.java
@@ -0,0 +1,16 @@
+package basic;
+
+public class LambdaG2 {
+
+ interface Boo { int m(); }
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static int run() {
+ Boo f = null;
+ f = () -> 44;
+ return f.m();
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaH.java b/testdata-java8/src/main/java/basic/LambdaH.java
new file mode 100644
index 0000000..3c12398
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaH.java
@@ -0,0 +1,34 @@
+package basic;
+
+public class LambdaH {
+
+ public interface Foo { int m(); }
+
+ public int fieldOne = 7;
+
+ public int concatenator(int a, int b) {
+ return a*b;
+ }
+
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static int run() {
+ int count = 0;
+ count += ((Foo)()->7).m();
+ count += new LambdaH().x();
+ count += ((Foo)()->21).m();
+ return count;
+ }
+
+ public int x() {
+ int i = 4;
+ Foo f = null;
+ f = () -> {
+ return concatenator(i,fieldOne);
+ };
+ return f.m();
+ }
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaI.java b/testdata-java8/src/main/java/basic/LambdaI.java
new file mode 100644
index 0000000..bd3d128
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaI.java
@@ -0,0 +1,17 @@
+package basic;
+
+public class LambdaI {
+
+ public interface Foo { String m(String in); }
+
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static String run() {
+ Foo f = (s) -> s;
+ return f.m("a");
+ }
+
+}
diff --git a/testdata-java8/src/main/java/basic/LambdaI2.java b/testdata-java8/src/main/java/basic/LambdaI2.java
new file mode 100644
index 0000000..b22a255
--- /dev/null
+++ b/testdata-java8/src/main/java/basic/LambdaI2.java
@@ -0,0 +1,17 @@
+package basic;
+
+public class LambdaI2 {
+
+ public interface Foo { String m(String in, String in2); }
+
+
+ public static void main(String[] args) {
+ run();
+ }
+
+ public static String run() {
+ Foo f = (s,t) -> s+t;
+ return f.m("a", "b");
+ }
+
+}
diff --git a/testdata/.classpath b/testdata/.classpath
index a886ae5..0fb1905 100644
--- a/testdata/.classpath
+++ b/testdata/.classpath
@@ -1,11 +1,10 @@
-
-
+
diff --git a/testdata/.project b/testdata/.project
index e9d6a17..def9b8b 100644
--- a/testdata/.project
+++ b/testdata/.project
@@ -6,13 +6,12 @@
- org.eclipse.ajdt.core.ajbuilder
+ org.eclipse.jdt.core.javabuilder
- org.eclipse.ajdt.ui.ajnature
org.eclipse.jdt.core.javanature
diff --git a/testdata/build.gradle b/testdata/build.gradle
index b230c06..1c4ec5b 100644
--- a/testdata/build.gradle
+++ b/testdata/build.gradle
@@ -1,40 +1,17 @@
-def aspectjVersion = "1.7.1"
-
-configurations {
- aspects
- ajInpath
-}
-
dependencies {
+/*
tools "org.aspectj:aspectjtools:$aspectjVersion"
compile "org.aspectj:aspectjrt:$aspectjVersion"
+*/
compile("cglib:cglib:2.2.2") { exclude group: 'asm' } // cglib 2.2.2 depends on asm 3.3
- compile 'asm:asm:3.2'
- compile 'asm:asm-tree:3.2'
+ compile 'org.ow2.asm:asm:5.0_BETA'
+ compile 'org.ow2.asm:asm-tree:5.0_BETA'
compile files("code.jar")
}
-
-compileJava.deleteAllActions()
-
-task aspectJ(dependsOn: JavaPlugin.PROCESS_RESOURCES_TASK_NAME) {
- dependsOn configurations.tools.getTaskDependencyFromProjectDependency(true, "compileJava")
- def srcDirs = sourceSets.main.java.srcDirs
- srcDirs.each { inputs.dir it }
- def destDir = sourceSets.main.output.classesDir
- outputs.dir destDir
- doLast {
- ant.taskdef(resource: "org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties", classpath: configurations.tools.asPath)
-
- ant.iajc(source:sourceCompatibility, target:targetCompatibility, destDir: destDir.absolutePath, maxmem:"512m", fork:"true",
- aspectPath: configurations.aspects.asPath, inpath:configurations.ajInpath.asPath, sourceRootCopyFilter:"**/.svn/*,**/*.java",classpath:configurations.compile.asPath ){
- sourceroots {
- srcDirs.each {
- if (it.exists()) pathelement location: it.absolutePath
- }
- }
- }
- }
+sourceSets {
+ main {
+ java {
+ srcDir 'src'
+ }
+ }
}
-
-compileJava.dependsOn aspectJ
-
diff --git a/testdata/src/main/java/issue34/InnerEnum.java b/testdata/src/main/java/issue34/InnerEnum.java
index c745261..e7b7e26 100644
--- a/testdata/src/main/java/issue34/InnerEnum.java
+++ b/testdata/src/main/java/issue34/InnerEnum.java
@@ -13,10 +13,17 @@ public class InnerEnum {
@SuppressWarnings("unused")
public static void main(String[] args) {
System.out.println("Hello World!");
- Map map = new TreeMap(sorters.string);
+ Object o = sorters.string;
+// Map map = new TreeMap(sorters.string);
+ }
+
+ // May be able to switch back to using Comparator (and the TreeMap line above) once AspectJ 1.8.0 is out
+ interface MyComparator {
+ int compare(T a,T b);
+ boolean equals(Object o);
}
- private static enum sorters implements Comparator {
+ private static enum sorters implements MyComparator {
string {
private static final long serialVersionUID = 1L;
@@ -24,6 +31,7 @@ public class InnerEnum {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
+
}
}
}
\ No newline at end of file
diff --git a/testdata/src/main/java/reflection/generics/GenericClass002.java b/testdata/src/main/java/reflection/generics/GenericClass002.java
index eb031b8..427527e 100644
--- a/testdata/src/main/java/reflection/generics/GenericClass002.java
+++ b/testdata/src/main/java/reflection/generics/GenericClass002.java
@@ -6,7 +6,7 @@ public class GenericClass002> implements GenericInterfac
//what we need in this v002 class...
- // Same as in original class, but also with methods added (fore these cases)
+ // Same as in original class, but also with methods added (for these cases)
public Iterator iterator() {
return null;