WIP on Java8. Looking at lambda reloading first (required invokedynamic handling)
This commit is contained in:
6
j8code/.classpath
Normal file
6
j8code/.classpath
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J180_b128"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
j8code/.project
Normal file
17
j8code/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>j8code</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
11
j8code/.settings/org.eclipse.jdt.core.prefs
Normal file
11
j8code/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,11 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=disabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.4
|
||||
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=warning
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.3
|
||||
69
j8code/src/j8code/J8Helper.java
Normal file
69
j8code/src/j8code/J8Helper.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package j8code;
|
||||
|
||||
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;
|
||||
|
||||
public class J8Helper {
|
||||
|
||||
public static Object simulateInvokeDynamic(Object lookup) {
|
||||
try {
|
||||
CallSite callsite = callLambdaMetaFactory(lookup);
|
||||
// java.lang.invoke.ConstantCallSite@1e965684
|
||||
// nameAndDescriptor at invokedynamic: m()Lbasic/LambdaA$Foo;
|
||||
MethodHandles.Lookup caller = (MethodHandles.Lookup)lookup;
|
||||
return callsite.dynamicInvoker().invokeWithArguments((Object[])null);//asType(MethodType.methodType())invoke(new Object[]{"m", MethodType.methodType(Class.forName("basic.LambdaA$Foo",false,caller.lookupClass().getClassLoader()))});
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static CallSite callLambdaMetaFactory(Object lookup) throws Exception {
|
||||
// At invokedynamic:
|
||||
// bsmId = 0
|
||||
// nameAndDescriptor = m()Lbasic/LambdaA$Foo;
|
||||
|
||||
// 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
|
||||
|
||||
// First two stacked by VM when used with invokedynamic
|
||||
|
||||
// 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
|
||||
|
||||
// caller = basic.LambdaA (MethodHandles$Lookup)
|
||||
|
||||
// invokedName = m (String)
|
||||
// invokedType = ()Foo (MethodType)
|
||||
|
||||
// samMethodType=()int (MethodType)
|
||||
// implMethod=MethodHandle()int (DirectMethodHandle - membername in this object is "basic.LambdaA.lambda$run$0()int/invokeStatic")
|
||||
// form members is:
|
||||
//
|
||||
// DMH.invokeStatic__I=Lambda(a0:L)=>{
|
||||
// t1:L=DirectMethodHandle.internalMemberName(a0:L);
|
||||
// t2:I=MethodHandle.linkToStatic(t1:L);t2:I}
|
||||
// instantiatedMethodType=()int (MethodType)
|
||||
MethodHandles.Lookup caller = (MethodHandles.Lookup)lookup;
|
||||
MethodType invokedType = MethodType.methodType(Class.forName("basic.LambdaA$Foo",false,caller.lookupClass().getClassLoader()));
|
||||
|
||||
MethodType samMethodType = MethodType.methodType(Integer.TYPE);
|
||||
// Cheating here by changing first param to pretend the original type is looking for it rather than the executor
|
||||
MethodHandle implMethod = caller.findStatic(caller.lookupClass(), "lambda$run$0",MethodType.methodType(Integer.TYPE));
|
||||
MethodType instantiatedMethodType = MethodType.methodType(Integer.TYPE);
|
||||
return LambdaMetafactory.metafactory(caller, "m", invokedType, samMethodType, implMethod, instantiatedMethodType);
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,10 @@
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="src" path="src/test/java"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/asm-3.2.jar"/>
|
||||
<classpathentry exported="true" kind="lib" path="lib/asm-tree-3.2.jar"/>
|
||||
<classpathentry kind="lib" path="lib/asm-5.0_BETA.jar"/>
|
||||
<classpathentry kind="lib" path="lib/asm-tree-5.0_BETA.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/j8code"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@@ -34,12 +34,17 @@ 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-groovy')
|
||||
testCompileOnly project(':testdata-plugin')
|
||||
|
||||
BIN
springloaded/lib/asm-5.0_BETA.jar
Normal file
BIN
springloaded/lib/asm-5.0_BETA.jar
Normal file
Binary file not shown.
BIN
springloaded/lib/asm-tree-5.0_BETA.jar
Normal file
BIN
springloaded/lib/asm-tree-5.0_BETA.jar
Normal file
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/springloaded"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="4"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=springloaded"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J180_b128"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="springloaded"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dspringloaded.tests.generatedTests=false -noverify -Dspringloaded=asserts"/>
|
||||
</launchConfiguration>
|
||||
@@ -18,11 +18,10 @@ 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.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
@@ -54,7 +53,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 +61,7 @@ public class ClassRenamer {
|
||||
private Map<String, String> retargets = new HashMap<String, String>();
|
||||
|
||||
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) {
|
||||
@@ -141,13 +140,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;
|
||||
}
|
||||
|
||||
@@ -81,6 +81,7 @@ public interface Constants extends Opcodes {
|
||||
static String mChangedForInvokeInterfaceName = "iincheck";
|
||||
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 +104,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -145,11 +146,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -30,13 +30,12 @@ import java.util.NoSuchElementException;
|
||||
import java.util.StringTokenizer;
|
||||
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 +435,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 +666,14 @@ public class MethodInvokerRewriter {
|
||||
return intercepted.contains(owner + "." + methodName);
|
||||
}
|
||||
|
||||
private TypeRegistry typeRegistry;
|
||||
protected TypeRegistry typeRegistry;
|
||||
boolean isEnum = false;
|
||||
private boolean isGroovyClosure = false;
|
||||
int fieldcount = 0;
|
||||
|
||||
public RewriteClassAdaptor(TypeRegistry typeRegistry, ClassVisitor classWriter) {
|
||||
// TODO should it also compute frames?
|
||||
super(classWriter);
|
||||
super(ASM5,classWriter);
|
||||
cw = cv;
|
||||
this.typeRegistry = typeRegistry;
|
||||
}
|
||||
@@ -691,7 +690,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 +720,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 +730,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) == '<'
|
||||
@@ -950,6 +949,123 @@ 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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, org.objectweb.asm.Handle bsm, Object... bsmArgs) {
|
||||
// TODO *shudder* what about invoke dynamic calls that target reflective APIs
|
||||
boolean handled = false;
|
||||
// TODO Perhaps (for sake of my sanity initially) make a distinction here between the general invokedynamic case and the special lambda support case?
|
||||
|
||||
// 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) {
|
||||
// InvokeDynamic(name=m,desc=()Lbasic/LambdaA$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/LambdaA.lambda$run$0()I (6) ()I]
|
||||
System.out.println("InvokeDynamic(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs));
|
||||
// The other invokes use the 'owner' of the target method to determine which type registry should be part of this
|
||||
// check. Here the 'owner' is wrapped up in the bootstrap method - as version 1 we can assume the owner is the lambdametafactory
|
||||
// which *wont* be getting reloaded - so we already know we don't need to do some jiggery pokery.
|
||||
|
||||
// int classId = typeRegistry.getTypeIdFor(owner, true);
|
||||
// Call type registry to determine 'can we do what we were going to do?'
|
||||
|
||||
int bsmReferenceId = typeRegistry.recordBootstrapMethod(bsm,bsmArgs);
|
||||
// Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
|
||||
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;Ljava/lang/String;I)Ljava/lang/Object;");
|
||||
handled=true;
|
||||
// TODO handle return type
|
||||
// mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(),classId));
|
||||
// mv.visitLdcInsn(name+desc);
|
||||
// mv.visitLdcInsn(BSM_NUMBER);
|
||||
// mv.visitLdcInsn(bsmArgs);
|
||||
|
||||
|
||||
|
||||
// // What can we check to see whether it is necessary to intercept this call? Is it the return type of the descriptor? (For when
|
||||
// // the bsm is recognizable as for lambda support)
|
||||
// mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
|
||||
// mv.visitLdcInsn(name + desc);
|
||||
// mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mChangedForInvokeVirtualName, "(ILjava/lang/String;)Z");
|
||||
// // Return value is the extracted interface to call if there is a
|
||||
// // change and it can't be called directly
|
||||
//
|
||||
// // 2. preserve a copy of the return value (new target)
|
||||
// // mv.visitInsn(DUP);
|
||||
//
|
||||
// // 3. Was it null?
|
||||
// Label l1 = new Label();
|
||||
// mv.visitJumpInsn(IFEQ, l1);
|
||||
//
|
||||
// // 4. Not false
|
||||
//
|
||||
// // 5. Store the target implementation of the interface that we
|
||||
// // will invoke later
|
||||
// // mv.visitVarInsn(ASTORE, max + 1);
|
||||
//
|
||||
// // 6. Package up any parameters
|
||||
// if (hasParams) {
|
||||
// Utils.collapseStackToArray(mv, desc);
|
||||
// }
|
||||
//
|
||||
// // Prepare for the invocation:
|
||||
// if (!hasParams) {
|
||||
// // [targetInstance]
|
||||
// mv.visitInsn(DUP);
|
||||
// mv.visitInsn(ACONST_NULL); // no parameters
|
||||
// mv.visitInsn(SWAP); // [targetInstance NULL targetInstance]
|
||||
// } else {
|
||||
// // [targetInstance paramArray]
|
||||
// mv.visitInsn(SWAP);
|
||||
// mv.visitInsn(DUP_X1); // [targetInstance paramArray
|
||||
// // targetInstance]
|
||||
// }
|
||||
//
|
||||
// mv.visitLdcInsn(name + desc);
|
||||
//
|
||||
// // calling __execute(params array,this,name+desc)
|
||||
// mv.visitMethodInsn(INVOKEVIRTUAL, owner, mDynamicDispatchName, mDynamicDispatchDescriptor);
|
||||
//
|
||||
// insertAppropriateReturn(returnType);
|
||||
// Label gotolabel = new Label();
|
||||
// mv.visitJumpInsn(GOTO, gotolabel);
|
||||
// mv.visitLabel(l1);
|
||||
// // mv.visitInsn(POP);
|
||||
// // Here is where we end up if the test for changes failed (ie.
|
||||
// // there were no changes - just 'do what you were going to do'
|
||||
// super.visitMethodInsn(opcode, owner, name, desc);
|
||||
// mv.visitLabel(gotolabel);
|
||||
|
||||
}
|
||||
else {
|
||||
// TODO handle it!
|
||||
}
|
||||
if (!handled) {
|
||||
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)) {
|
||||
|
||||
@@ -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))));
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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<String> finalInHierarchy = new ArrayList<String>();
|
||||
|
||||
public ExtractionVisitor(boolean isReloadableType) {
|
||||
super(ASM5);
|
||||
this.isReloadableType = isReloadableType;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springsource.loaded;
|
||||
|
||||
import j8code.J8Helper;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -42,6 +44,7 @@ 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;
|
||||
@@ -1459,7 +1462,25 @@ public class TypeRegistry {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@UsedByGeneratedCode
|
||||
public static Object idyrun(Object methodHandles_lookup, String nameAndDescriptor, int bsmId) {
|
||||
// bsmId = 0
|
||||
// nameAndDescriptor = m()Lbasic/LambdaA$Foo;
|
||||
|
||||
// 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
|
||||
|
||||
|
||||
System.out.println("idyrun("+methodHandles_lookup+","+nameAndDescriptor+","+bsmId+")");
|
||||
return J8Helper.simulateInvokeDynamic(methodHandles_lookup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in code the generated code replaces invokevirtual calls. Determine if the code can run as it was originally compiled.
|
||||
*
|
||||
@@ -1856,4 +1877,36 @@ public class TypeRegistry {
|
||||
public Set<ReloadableType> 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(Handle bsm, Object[] bsmArgs) {
|
||||
// TODO [memory] search the existing bsmInfos for a matching one! Reuse!
|
||||
if (nextBsmIndex >= bsmInfo.length) {
|
||||
BsmInfo[] newarray = new BsmInfo[bsmInfo.length+4];
|
||||
System.arraycopy(bsmInfo, 0, newarray, 0, bsmInfo.length);
|
||||
bsmInfo = newarray;
|
||||
}
|
||||
bsmInfo[nextBsmIndex] = new BsmInfo(bsm,bsmArgs);
|
||||
nextBsmIndex++;
|
||||
return nextBsmIndex-1;
|
||||
}
|
||||
|
||||
private int nextBsmIndex = 0;
|
||||
private BsmInfo[] bsmInfo = new BsmInfo[4];
|
||||
|
||||
static class BsmInfo {
|
||||
Handle bsm;
|
||||
Object[] bsmArgs;
|
||||
public BsmInfo(Handle bsm, Object[] bsmArgs) {
|
||||
this.bsm = bsm;
|
||||
this.bsmArgs = bsmArgs;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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<Class<?>, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap<Class<?>, Object[]>();
|
||||
public static Map<Class<?>, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap<Class<?>, 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 + " ");
|
||||
|
||||
@@ -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, org.objectweb.asm.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 + " ");
|
||||
|
||||
@@ -296,8 +296,22 @@ 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());
|
||||
assertEquals("@common.Anno(someValue=37, longValue=2, id=abc)", printAnnotation(m.getAnnotations()[1]));
|
||||
}
|
||||
//
|
||||
private String printAnnotation(Annotation a) {
|
||||
return a.toString();
|
||||
// StringBuilder buf = new StringBuilder();
|
||||
// printAnnotationHelper(buf,a);
|
||||
// return buf.toString();
|
||||
}
|
||||
//
|
||||
// private void printAnnotationHelper(StringBuilder buf, Annotation a) {
|
||||
// Class<?> clazz = a.annotationType();a.toString()
|
||||
// clazz.getDeclaredFields()[0].get
|
||||
// System.out.println(a.annotationType());
|
||||
//
|
||||
// }
|
||||
|
||||
@Test
|
||||
public void methodLevelAnnotationsOnInterfaces2() throws Exception {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package org.springsource.loaded.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.test.infra.ClassPrinter;
|
||||
import org.springsource.loaded.test.infra.Result;
|
||||
|
||||
/**
|
||||
* Test reloading of Java 8.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @since 1.1.5
|
||||
*/
|
||||
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);
|
||||
ClassPrinter.print(rtype.bytesLoaded);
|
||||
|
||||
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();
|
||||
ClassPrinter.print(rtype.bytesLoaded);
|
||||
Result r = runUnguarded(simpleClass, "run");
|
||||
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(77, r.returnValue);
|
||||
|
||||
rtype.loadNewVersion("002", retrieveRename(t,t+"2"));
|
||||
ClassPrinter.print(rtype.getLatestExecutorBytes());
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(77, r.returnValue);
|
||||
}
|
||||
|
||||
// TODO changing a lambda body
|
||||
// TODO changing a lambda signature
|
||||
// TODO adding a lambda that wasn't there before
|
||||
// TODO deleting a lambda
|
||||
// TODO make that inner interface non-public in LambdaA - seems to break things.
|
||||
|
||||
// Bytecode for LambdaA
|
||||
/*
|
||||
BootstrapMethods:
|
||||
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
|
||||
|
||||
public static int run();
|
||||
descriptor: ()I
|
||||
flags: ACC_PUBLIC, ACC_STATIC
|
||||
Code:
|
||||
stack=1, locals=1, args_size=0
|
||||
0: aconst_null
|
||||
1: astore_0
|
||||
2: invokedynamic #3, 0 // InvokeDynamic #0:m:()Lbasic/LambdaA$Foo;
|
||||
7: astore_0
|
||||
8: aload_0
|
||||
9: invokeinterface #4, 1 // InterfaceMethod basic/LambdaA$Foo.m:()I
|
||||
14: ireturn
|
||||
*/
|
||||
/*
|
||||
static void test() throws Throwable {
|
||||
// THE FOLLOWING LINE IS PSEUDOCODE FOR A JVM INSTRUCTION
|
||||
InvokeDynamic[#bootstrapDynamic].baz("baz arg", 2, 3.14);
|
||||
}
|
||||
private static void printArgs(Object... args) {
|
||||
System.out.println(java.util.Arrays.deepToString(args));
|
||||
}
|
||||
private static final MethodHandle printArgs;
|
||||
static {
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
Class thisClass = lookup.lookupClass(); // (who am I?)
|
||||
printArgs = lookup.findStatic(thisClass,
|
||||
"printArgs", MethodType.methodType(void.class, Object[].class));
|
||||
}
|
||||
private static CallSite bootstrapDynamic(MethodHandles.Lookup caller, String name, MethodType type) {
|
||||
// ignore caller and name, but match the type:
|
||||
return new ConstantCallSite(printArgs.asType(type));
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// --
|
||||
|
||||
private String slashed(String dotted) {
|
||||
return dotted.replaceAll("\\.", "/");
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -97,7 +97,7 @@ public abstract class SpringLoadedTests implements Constants {
|
||||
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;
|
||||
|
||||
@@ -1260,5 +1260,12 @@ public abstract class SpringLoadedTests implements Constants {
|
||||
m.invoke(null);
|
||||
return captureOff();
|
||||
}
|
||||
|
||||
protected final static void pause(int seconds) {
|
||||
try {
|
||||
Thread.sleep(seconds*1000);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -17,11 +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.
|
||||
@@ -42,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 {
|
||||
@@ -80,6 +90,10 @@ 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 {
|
||||
// debug();
|
||||
@@ -91,7 +105,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() {
|
||||
@@ -103,23 +117,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?
|
||||
@@ -135,11 +140,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry exported="true" kind="con" path="GROOVY_SUPPORT"/>
|
||||
<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
BIN
testdata-groovy/groovy-all-1.8.6.jar
Normal file
BIN
testdata-groovy/groovy-all-1.8.6.jar
Normal file
Binary file not shown.
6
testdata-java8/.classpath
Normal file
6
testdata-java8/.classpath
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1 @@
|
||||
#Thu Feb 06 13:01:33 PST 2014
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin
Normal file
Binary file not shown.
17
testdata-java8/.project
Normal file
17
testdata-java8/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>testdata-java8</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
11
testdata-java8/.settings/org.eclipse.jdt.core.prefs
Normal file
11
testdata-java8/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -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
|
||||
3
testdata-java8/build.gradle
Normal file
3
testdata-java8/build.gradle
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
apply plugin: 'java'
|
||||
sourceCompatibility = 1.8
|
||||
2
testdata-java8/build.sh
Executable file
2
testdata-java8/build.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
cd src/main/java
|
||||
find . -name "*.java" | javac
|
||||
BIN
testdata-java8/code.jar
Normal file
BIN
testdata-java8/code.jar
Normal file
Binary file not shown.
BIN
testdata-java8/src/main/java/basic/FirstClass.class
Normal file
BIN
testdata-java8/src/main/java/basic/FirstClass.class
Normal file
Binary file not shown.
12
testdata-java8/src/main/java/basic/FirstClass.java
Normal file
12
testdata-java8/src/main/java/basic/FirstClass.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaA.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaA.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaA2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaA2.java
Normal file
@@ -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();
|
||||
}
|
||||
}
|
||||
2
testdata/.classpath
vendored
2
testdata/.classpath
vendored
@@ -2,10 +2,10 @@
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.launching.macosx.MacOSXType/Java SE 7 [1.7.0_21]"/>
|
||||
<classpathentry kind="lib" path="code.jar"/>
|
||||
<classpathentry kind="lib" path="lib/cglib-nodep-2.2.jar"/>
|
||||
<classpathentry kind="lib" path="/springloaded/lib/asm-3.2.jar"/>
|
||||
<classpathentry kind="lib" path="/springloaded/lib/asm-tree-3.2.jar"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
Reference in New Issue
Block a user