resolving merge conflicts with j8 branch

This commit is contained in:
Andy Clement
2014-02-24 16:36:51 -08:00
94 changed files with 1570 additions and 233 deletions

View File

@@ -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
}
}

View File

@@ -1,5 +1,7 @@
include "springloaded"
include "testdata"
include "testdata-java8"
include "testdata-aspectj"
include "testdata-groovy"
include "testdata-plugin"
include "testdata-subloader"

View File

@@ -2,9 +2,9 @@
<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="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<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/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J180_b128"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="bin"/>
</classpath>

View File

@@ -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')

Binary file not shown.

Binary file not shown.

View File

@@ -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);

View File

@@ -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>

View File

@@ -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<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) {
@@ -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;i<os.length;i++) {
if (i>0) 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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) {
}

View File

@@ -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 <init>(Foo) {}
// JAVAC: ctor in Bar is <init>(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;
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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();

View File

@@ -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))));
}

View File

@@ -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) {

View File

@@ -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);
}
/**

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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<String> configurationFiles = new HashSet<String>();
Enumeration<URL> 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<URL> 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<String> 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<String> 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<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(String slashedClassName, Handle bsm, Object[] bsmArgs) {
if (bsmmap == null) {
bsmmap = new HashMap<String,BsmInfo[]>();
}
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<String,BsmInfo[]> bsmmap;
static class BsmInfo {
Handle bsm;
Object[] bsmArgs;
public BsmInfo(Handle bsm, Object[] bsmArgs) {
this.bsm = bsm;
this.bsmArgs = bsmArgs;
}
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -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 + " ");

View File

@@ -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 + " ");

View File

@@ -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<possibleValues.length;i++) {
if (actual.equals(possibleValues[i])) {
return;
}
buf.append("'"+possibleValues[i]+"'").append("\n");
}
fail("The value:\n'"+actual+"'\n does not match one of these possible options:\n"+buf.toString());
}
//
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 {

View File

@@ -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());
}
}

View File

@@ -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");
}
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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");

View File

@@ -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);

View File

@@ -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) {}
}
}

View File

@@ -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);
}

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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);

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src/main/java"/>
<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="con" path="org.eclipse.ajdt.core.ASPECTJRT_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>

18
testdata-aspectj/.project Normal file
View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>testdata-aspectj</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.ajdt.core.ajbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.ajdt.ui.ajnature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

View File

@@ -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

View File

@@ -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>

Binary file not shown.

View 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>

View File

@@ -0,0 +1 @@
#Thu Feb 06 13:01:33 PST 2014

View File

@@ -0,0 +1 @@


17
testdata-java8/.project Normal file
View 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>

View 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

View File

@@ -0,0 +1,4 @@
apply plugin: 'java'
sourceCompatibility = 1.8
targetCompatibility = 1.8

1
testdata-java8/build.sh Executable file
View File

@@ -0,0 +1 @@
../gradlew build

Binary file not shown.

View 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;
}
}

View 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();
}
}

View 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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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<i;j++) {
buf.append(c);
}
return buf.toString();
};
return f.m('a');
}
}

View File

@@ -0,0 +1,23 @@
package basic;
public class LambdaE2 {
public interface Boo { String m(char s); }
public static void main(String[] args) {
run();
}
public static String run() {
int i = 8;
Boo f = null;
f = (c) -> {
StringBuilder buf = new StringBuilder();
for (int j=0;j<i;j++) {
buf.append(c);
}
return buf.toString();
};
return f.m('a');
}
}

View File

@@ -0,0 +1,33 @@
package basic;
public class LambdaF {
public int fieldOne = 7;
public String concatenator(char ch, int number) {
StringBuilder buf = new StringBuilder();
for (int j=0;j<number;j++) {
buf.append(ch);
}
return buf.toString();
}
public interface Boo { String m(char s); }
public static void main(String[] args) {
run();
}
public static String run() {
return new LambdaF().x();
}
public String x() {
int i = 4;
Boo f = null;
f = (c) -> {
return concatenator(c,fieldOne);
};
return f.m('a');
}
}

View File

@@ -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<number;j++) {
buf.append(ch);
buf.append(':');
}
return buf.toString();
}
public interface Boo { String m(char s); }
public static void main(String[] args) {
run();
}
public static String run() {
return new LambdaF2().x();
}
public String x() {
int i = 4;
Boo f = null;
f = (c) -> {
return concatenator(c,fieldOne);
};
return f.m('a');
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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");
}
}

View File

@@ -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");
}
}

3
testdata/.classpath vendored
View File

@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<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_51]"/>
<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>

3
testdata/.project vendored
View File

@@ -6,13 +6,12 @@
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.ajdt.core.ajbuilder</name>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.ajdt.ui.ajnature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>

43
testdata/build.gradle vendored
View File

@@ -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

View File

@@ -13,10 +13,17 @@ public class InnerEnum {
@SuppressWarnings("unused")
public static void main(String[] args) {
System.out.println("Hello World!");
Map<String, String> map = new TreeMap<String, String>(sorters.string);
Object o = sorters.string;
// Map<String, String> map = new TreeMap<String, String>(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<T> {
int compare(T a,T b);
boolean equals(Object o);
}
private static enum sorters implements Comparator<String> {
private static enum sorters implements MyComparator<String> {
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);
}
}
}
}

View File

@@ -6,7 +6,7 @@ public class GenericClass002<K extends Comparable<K>> 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<K> iterator() {
return null;