resolving merge conflicts with j8 branch
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
include "springloaded"
|
||||
include "testdata"
|
||||
include "testdata-java8"
|
||||
include "testdata-aspectj"
|
||||
include "testdata-groovy"
|
||||
include "testdata-plugin"
|
||||
include "testdata-subloader"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')
|
||||
|
||||
BIN
springloaded/lib/asm-5.0_BETA.jar
Normal file
BIN
springloaded/lib/asm-5.0_BETA.jar
Normal file
Binary file not shown.
BIN
springloaded/lib/asm-tree-5.0_BETA.jar
Normal file
BIN
springloaded/lib/asm-tree-5.0_BETA.jar
Normal file
Binary file not shown.
@@ -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);
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/springloaded"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="4"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=springloaded"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J180_b128"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="springloaded"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dspringloaded.tests.generatedTests=false -noverify -Dspringloaded=asserts"/>
|
||||
</launchConfiguration>
|
||||
@@ -18,11 +18,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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
package org.springsource.loaded;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
|
||||
/**
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
class ConstructorCopier extends MethodAdapter implements Constants {
|
||||
class ConstructorCopier extends MethodVisitor implements Constants {
|
||||
|
||||
private final static int preInvokeSpecial = 0;
|
||||
private final static int postInvokeSpecial = 1;
|
||||
@@ -39,7 +38,7 @@ class ConstructorCopier extends MethodAdapter implements Constants {
|
||||
private String classname;
|
||||
|
||||
public ConstructorCopier(MethodVisitor mv, TypeDescriptor typeDescriptor, String suffix, String classname) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
this.suffix = suffix;
|
||||
this.classname = classname;
|
||||
|
||||
@@ -57,7 +57,7 @@ public class DispatcherBuilder {
|
||||
/**
|
||||
* Whilst visiting the interface, the implementation is created.
|
||||
*/
|
||||
static class DispatcherBuilderVisitor implements ClassVisitor, Opcodes, Constants {
|
||||
static class DispatcherBuilderVisitor extends ClassVisitor implements Opcodes, Constants {
|
||||
|
||||
private ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
||||
|
||||
@@ -68,6 +68,7 @@ public class DispatcherBuilder {
|
||||
private IncrementalTypeDescriptor typeDescriptor;
|
||||
|
||||
public DispatcherBuilderVisitor(ReloadableType rtype, IncrementalTypeDescriptor typeDescriptor, String suffix) {
|
||||
super(ASM5);
|
||||
this.classname = rtype.getSlashedName();
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
this.suffix = suffix;
|
||||
|
||||
@@ -27,7 +27,11 @@ import org.objectweb.asm.MethodVisitor;
|
||||
* @author Andy Clement
|
||||
* @since 0.7.3
|
||||
*/
|
||||
public class EmptyClassVisitor implements ClassVisitor, Constants {
|
||||
public class EmptyClassVisitor extends ClassVisitor implements Constants {
|
||||
|
||||
public EmptyClassVisitor() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class ExecutorBuilder {
|
||||
* ClassVisitor that constructs the executor by visiting the original class. The basic goal is to visit the original class and
|
||||
* 'copy' the methods into the executor, making adjustments as we go.
|
||||
*/
|
||||
static class ExecutorBuilderVisitor implements ClassVisitor, Constants {
|
||||
static class ExecutorBuilderVisitor extends ClassVisitor implements Constants {
|
||||
|
||||
private ClassWriter cw = new ClassWriter(0);
|
||||
|
||||
@@ -78,6 +78,7 @@ public class ExecutorBuilder {
|
||||
protected TypeDescriptor typeDescriptor;
|
||||
|
||||
public ExecutorBuilderVisitor(String classname, String suffix, TypeDescriptor typeDescriptor) {
|
||||
super(ASM5);
|
||||
this.classname = classname;
|
||||
this.suffix = suffix;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,13 +66,14 @@ public class InterfaceExtractor {
|
||||
return extractorVisitor.getBytes();
|
||||
}
|
||||
|
||||
class ExtractorVisitor implements ClassVisitor, Constants {
|
||||
class ExtractorVisitor extends ClassVisitor implements Constants {
|
||||
|
||||
private TypeDescriptor typeDescriptor;
|
||||
private ClassWriter interfaceWriter = new ClassWriter(0);
|
||||
private String slashedtypename;
|
||||
|
||||
public ExtractorVisitor(TypeDescriptor typeDescriptor) {
|
||||
super(ASM5);
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.springsource.loaded;
|
||||
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Utils.ReturnType;
|
||||
|
||||
@@ -26,7 +25,7 @@ import org.springsource.loaded.Utils.ReturnType;
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
class MethodCopier extends MethodAdapter implements Constants {
|
||||
class MethodCopier extends MethodVisitor implements Constants {
|
||||
|
||||
private boolean isInterface;
|
||||
private String descriptor;
|
||||
@@ -37,7 +36,7 @@ class MethodCopier extends MethodAdapter implements Constants {
|
||||
|
||||
public MethodCopier(MethodVisitor mv, boolean isInterface, String descriptor, TypeDescriptor typeDescriptor, String classname,
|
||||
String suffix) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
this.isInterface = isInterface;
|
||||
this.descriptor = descriptor;
|
||||
this.typeDescriptor = typeDescriptor;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -31,8 +31,8 @@ import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.springsource.loaded.MethodInvokerRewriter.DontRewriteException;
|
||||
import org.springsource.loaded.MethodInvokerRewriter.RewriteClassAdaptor;
|
||||
@@ -839,10 +839,10 @@ public class ReloadableType {
|
||||
}
|
||||
}
|
||||
|
||||
static class ChainedAdapters extends ClassAdapter implements Constants {
|
||||
static class ChainedAdapters extends ClassVisitor implements Constants {
|
||||
|
||||
public ChainedAdapters(ReloadableType rtype) {
|
||||
super(new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype, new ClassWriter(
|
||||
super(ASM5,new RewriteClassAdaptor(rtype.typeRegistry, new TypeRewriter.RewriteClassAdaptor(rtype, new ClassWriter(
|
||||
ClassWriter.COMPUTE_MAXS))));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package org.springsource.loaded;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
@@ -39,7 +38,7 @@ public class SystemClassReflectionInvestigator {
|
||||
return classAdaptor.hitCount;
|
||||
}
|
||||
|
||||
static class RewriteClassAdaptor extends ClassAdapter implements Constants {
|
||||
static class RewriteClassAdaptor extends ClassVisitor implements Constants {
|
||||
|
||||
int hitCount = 0;
|
||||
private ClassWriter cw;
|
||||
@@ -52,7 +51,7 @@ public class SystemClassReflectionInvestigator {
|
||||
|
||||
public RewriteClassAdaptor() {
|
||||
// TODO should it also compute frames?
|
||||
super(new ClassWriter(ClassWriter.COMPUTE_MAXS));
|
||||
super(ASM5,new ClassWriter(ClassWriter.COMPUTE_MAXS));
|
||||
cw = (ClassWriter) cv;
|
||||
}
|
||||
|
||||
@@ -77,10 +76,10 @@ public class SystemClassReflectionInvestigator {
|
||||
return new RewritingMethodAdapter(mv);
|
||||
}
|
||||
|
||||
class RewritingMethodAdapter extends MethodAdapter implements Opcodes, Constants {
|
||||
class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
|
||||
|
||||
public RewritingMethodAdapter(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
private boolean interceptReflection(String owner, String name, String desc) {
|
||||
|
||||
@@ -18,12 +18,11 @@ package org.springsource.loaded;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
@@ -105,7 +104,7 @@ public class SystemClassReflectionRewriter {
|
||||
}
|
||||
}
|
||||
|
||||
static class RewriteClassAdaptor extends ClassAdapter implements Constants {
|
||||
static class RewriteClassAdaptor extends ClassVisitor implements Constants {
|
||||
|
||||
private ClassWriter cw;
|
||||
int bits = 0x0000;
|
||||
@@ -122,7 +121,7 @@ public class SystemClassReflectionRewriter {
|
||||
|
||||
public RewriteClassAdaptor() {
|
||||
// TODO should it also compute frames?
|
||||
super(new ClassWriter(ClassWriter.COMPUTE_MAXS));
|
||||
super(ASM5,new ClassWriter(ClassWriter.COMPUTE_MAXS));
|
||||
cw = (ClassWriter) cv;
|
||||
}
|
||||
|
||||
@@ -189,10 +188,10 @@ public class SystemClassReflectionRewriter {
|
||||
}
|
||||
}
|
||||
|
||||
class RewritingMethodAdapter extends MethodAdapter implements Opcodes, Constants {
|
||||
class RewritingMethodAdapter extends MethodVisitor implements Opcodes, Constants {
|
||||
|
||||
public RewritingMethodAdapter(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -54,7 +54,7 @@ public class TypeDescriptorExtractor {
|
||||
/**
|
||||
* Visit a class and accumulate sufficient information to build a TypeDescriptor.
|
||||
*/
|
||||
class ExtractionVisitor implements ClassVisitor, Opcodes {
|
||||
class ExtractionVisitor extends ClassVisitor implements Opcodes {
|
||||
|
||||
private boolean isReloadableType;
|
||||
private int flags;
|
||||
@@ -72,6 +72,7 @@ public class TypeDescriptorExtractor {
|
||||
private List<String> finalInHierarchy = new ArrayList<String>();
|
||||
|
||||
public ExtractionVisitor(boolean isReloadableType) {
|
||||
super(ASM5);
|
||||
this.isReloadableType = isReloadableType;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,11 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils.ReturnType;
|
||||
@@ -55,7 +53,7 @@ public class TypeRewriter implements Constants {
|
||||
return classAdaptor.getBytes();
|
||||
}
|
||||
|
||||
static class RewriteClassAdaptor extends ClassAdapter implements Constants {
|
||||
static class RewriteClassAdaptor extends ClassVisitor implements Constants {
|
||||
|
||||
private ClassWriter cw;
|
||||
private String slashedname;
|
||||
@@ -72,7 +70,7 @@ public class TypeRewriter implements Constants {
|
||||
}
|
||||
|
||||
public RewriteClassAdaptor(ReloadableType rtype, ClassWriter classWriter) {
|
||||
super(classWriter);
|
||||
super(ASM5,classWriter);
|
||||
this.rtype = rtype;
|
||||
this.slashedname = rtype.getSlashedName();
|
||||
this.cw = (ClassWriter) cv;
|
||||
@@ -414,7 +412,7 @@ public class TypeRewriter implements Constants {
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int flags, String name, String descriptor, String signature, String[] exceptions) {
|
||||
MethodVisitor mv = super.visitMethod(promoteIfNecessary(flags), name, descriptor, signature, exceptions);
|
||||
MethodVisitor mv = super.visitMethod(promoteIfNecessary(flags,name), name, descriptor, signature, exceptions);
|
||||
MethodVisitor newMethodVisitor = getMethodVisitor(name, descriptor, mv);
|
||||
return newMethodVisitor;
|
||||
}
|
||||
@@ -444,8 +442,8 @@ public class TypeRewriter implements Constants {
|
||||
}
|
||||
|
||||
// Default visibility elements need promotion to public so that they can be seen from the executor
|
||||
private int promoteIfNecessary(int flags) {
|
||||
int newflags = Utils.promoteDefaultOrProtectedToPublic(flags, isEnum);
|
||||
private int promoteIfNecessary(int flags,String name) {
|
||||
int newflags = Utils.promoteDefaultOrProtectedToPublic(flags, isEnum, name);
|
||||
return newflags;
|
||||
}
|
||||
|
||||
@@ -816,7 +814,7 @@ public class TypeRewriter implements Constants {
|
||||
/**
|
||||
* Rewrites a method to include the extra checks to verify it is the most up to date version.
|
||||
*/
|
||||
class AugmentingMethodAdapter extends MethodAdapter implements Opcodes {
|
||||
class AugmentingMethodAdapter extends MethodVisitor implements Opcodes {
|
||||
|
||||
int methodId;
|
||||
String name;
|
||||
@@ -825,7 +823,7 @@ public class TypeRewriter implements Constants {
|
||||
ReturnType returnType;
|
||||
|
||||
public AugmentingMethodAdapter(MethodVisitor mv, String name, String descriptor) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
this.name = name;
|
||||
this.method = rtype.getMethod(name, descriptor);
|
||||
this.methodId = method.getId();
|
||||
@@ -916,7 +914,7 @@ public class TypeRewriter implements Constants {
|
||||
|
||||
}
|
||||
|
||||
class AugmentingConstructorAdapter extends MethodAdapter implements Opcodes {
|
||||
class AugmentingConstructorAdapter extends MethodVisitor implements Opcodes {
|
||||
|
||||
int ctorId;
|
||||
String name;
|
||||
@@ -926,7 +924,7 @@ public class TypeRewriter implements Constants {
|
||||
boolean isTopMost;
|
||||
|
||||
public AugmentingConstructorAdapter(MethodVisitor mv, String descriptor, String type, boolean isTopMost) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
this.descriptor = descriptor;
|
||||
this.type = type;
|
||||
this.isTopMost = isTopMost;
|
||||
@@ -1053,12 +1051,12 @@ public class TypeRewriter implements Constants {
|
||||
void prepend();
|
||||
}
|
||||
|
||||
class MethodPrepender extends MethodAdapter implements Opcodes {
|
||||
class MethodPrepender extends MethodVisitor implements Opcodes {
|
||||
|
||||
Prepender appender;
|
||||
|
||||
public MethodPrepender(MethodVisitor mv, Prepender appender) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
this.appender = appender;
|
||||
}
|
||||
|
||||
|
||||
@@ -1369,7 +1369,7 @@ public class Utils implements Opcodes, Constants {
|
||||
return access;
|
||||
}
|
||||
|
||||
public static int promoteDefaultOrProtectedToPublic(int access, boolean isEnum) {
|
||||
public static int promoteDefaultOrProtectedToPublic(int access, boolean isEnum, String name) {
|
||||
if ((access & Constants.ACC_PUBLIC_PRIVATE_PROTECTED) == 0) {
|
||||
// is default
|
||||
return (access | Modifier.PUBLIC);
|
||||
@@ -1382,6 +1382,10 @@ public class Utils implements Opcodes, Constants {
|
||||
// was private, need to 'publicize' it
|
||||
return access - Constants.ACC_PRIVATE + Constants.ACC_PUBLIC;
|
||||
}
|
||||
if ((access&Constants.ACC_PRIVATE_STATIC_SYNTHETIC)==ACC_PRIVATE_STATIC_SYNTHETIC && name.startsWith("lambda")) {
|
||||
// Special case for lambda, may need to generalize for general invokedynamic support
|
||||
return access - Constants.ACC_PRIVATE + Constants.ACC_PUBLIC;
|
||||
}
|
||||
return access;
|
||||
}
|
||||
|
||||
@@ -1733,7 +1737,11 @@ public class Utils implements Opcodes, Constants {
|
||||
|
||||
// TODO [performance] speed up by throwing exception from first visit method? (but this isn't used in the mainline really)
|
||||
// TODO or just write a quicker bytecode parser that just looks at the interfaces then returns
|
||||
private static class InterfaceCollectingClassVisitor implements ClassVisitor {
|
||||
private static class InterfaceCollectingClassVisitor extends ClassVisitor {
|
||||
|
||||
public InterfaceCollectingClassVisitor() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public String[] interfaces;
|
||||
|
||||
|
||||
@@ -19,10 +19,9 @@ import java.lang.reflect.Field;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
|
||||
@@ -33,7 +32,7 @@ import org.springsource.loaded.Constants;
|
||||
* @author Andy Clement
|
||||
* @since 0.8.3
|
||||
*/
|
||||
public class CglibPluginCapturing extends ClassAdapter implements Constants {
|
||||
public class CglibPluginCapturing extends ClassVisitor implements Constants {
|
||||
|
||||
public static Map<Class<?>, Object[]> clazzToGeneratorStrategyAndClassGeneratorMap = new HashMap<Class<?>, Object[]>();
|
||||
public static Map<Class<?>, Object[]> clazzToGeneratorStrategyAndFastClassGeneratorMap = new HashMap<Class<?>, Object[]>();
|
||||
@@ -48,7 +47,7 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
|
||||
}
|
||||
|
||||
private CglibPluginCapturing() {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,10 +73,10 @@ public class CglibPluginCapturing extends ClassAdapter implements Constants {
|
||||
}
|
||||
}
|
||||
|
||||
class CreateMethodInterceptor extends MethodAdapter implements Constants {
|
||||
class CreateMethodInterceptor extends MethodVisitor implements Constants {
|
||||
|
||||
public CreateMethodInterceptor(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
package org.springsource.loaded.agent;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
|
||||
@@ -27,7 +26,7 @@ import org.springsource.loaded.Constants;
|
||||
* @author Andy Clement
|
||||
* @since 0.5.0
|
||||
*/
|
||||
public class ClassVisitingConstructorAppender extends ClassAdapter implements Constants {
|
||||
public class ClassVisitingConstructorAppender extends ClassVisitor implements Constants {
|
||||
|
||||
private String calleeOwner;
|
||||
private String calleeName;
|
||||
@@ -41,7 +40,7 @@ public class ClassVisitingConstructorAppender extends ClassAdapter implements Co
|
||||
* @param name
|
||||
*/
|
||||
public ClassVisitingConstructorAppender(String owner, String name) {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
this.calleeOwner = owner;
|
||||
this.calleeName = name;
|
||||
}
|
||||
@@ -63,10 +62,10 @@ public class ClassVisitingConstructorAppender extends ClassAdapter implements Co
|
||||
* This constructor appender includes a couple of instructions at the end of each constructor it is asked to visit. It
|
||||
* recognizes the end by observing a RETURN instruction. The instructions are inserted just before the RETURN.
|
||||
*/
|
||||
class ConstructorAppender extends MethodAdapter implements Constants {
|
||||
class ConstructorAppender extends MethodVisitor implements Constants {
|
||||
|
||||
public ConstructorAppender(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.springsource.loaded.agent;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
@@ -26,12 +26,12 @@ import org.springsource.loaded.Constants;
|
||||
* @author Andy Clement
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class FalseReturner extends ClassAdapter implements Constants {
|
||||
public class FalseReturner extends ClassVisitor implements Constants {
|
||||
|
||||
private String methodname;
|
||||
|
||||
public FalseReturner(String methodname) {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
this.methodname = methodname;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
*/
|
||||
package org.springsource.loaded.agent;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
import org.springsource.loaded.GlobalConfiguration;
|
||||
@@ -30,10 +29,10 @@ import org.springsource.loaded.TypeRegistry;
|
||||
* @author Andy Clement
|
||||
* @since 0.7.3
|
||||
*/
|
||||
public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassAdapter implements Constants {
|
||||
public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassVisitor implements Constants {
|
||||
|
||||
public ModifyDefineInClassLoaderForClassArtifactsType() {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
@@ -49,10 +48,10 @@ public class ModifyDefineInClassLoaderForClassArtifactsType extends ClassAdapter
|
||||
}
|
||||
}
|
||||
|
||||
class DefineClassModifierVisitor extends MethodAdapter implements Constants {
|
||||
class DefineClassModifierVisitor extends MethodVisitor implements Constants {
|
||||
|
||||
public DefineClassModifierVisitor(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,7 @@ package org.springsource.loaded.agent;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
@@ -29,7 +29,7 @@ import org.springsource.loaded.Constants;
|
||||
* @author Andy Clement
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class NonFinalizer extends ClassAdapter implements Constants {
|
||||
public class NonFinalizer extends ClassVisitor implements Constants {
|
||||
|
||||
private String fieldname;
|
||||
|
||||
@@ -42,7 +42,7 @@ public class NonFinalizer extends ClassAdapter implements Constants {
|
||||
* @param name
|
||||
*/
|
||||
public NonFinalizer(String fieldname) {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
this.fieldname = fieldname;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import org.springsource.loaded.Utils;
|
||||
/**
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class ClassPrinter implements ClassVisitor, Opcodes {
|
||||
public class ClassPrinter extends ClassVisitor implements Opcodes {
|
||||
|
||||
private PrintStream destination;
|
||||
private boolean includeBytecode;
|
||||
@@ -47,6 +47,7 @@ public class ClassPrinter implements ClassVisitor, Opcodes {
|
||||
}
|
||||
|
||||
public ClassPrinter(PrintStream destination, boolean includeBytecode) {
|
||||
super(ASM5);
|
||||
this.destination = destination;
|
||||
this.includeBytecode = includeBytecode;
|
||||
}
|
||||
@@ -188,7 +189,11 @@ public class ClassPrinter implements ClassVisitor, Opcodes {
|
||||
return new AnnotationVisitorPrinter();
|
||||
}
|
||||
|
||||
class AnnotationVisitorPrinter implements AnnotationVisitor {
|
||||
class AnnotationVisitorPrinter extends AnnotationVisitor {
|
||||
|
||||
public AnnotationVisitorPrinter() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
public void visit(String name, Object value) {
|
||||
destination.print(name + "=" + value + " ");
|
||||
|
||||
@@ -21,17 +21,17 @@ import java.util.List;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Andy Clement
|
||||
*/
|
||||
public class MethodPrinter implements MethodVisitor, Opcodes {
|
||||
public class MethodPrinter extends MethodVisitor implements Opcodes {
|
||||
|
||||
PrintStream to;
|
||||
|
||||
@@ -47,12 +47,22 @@ public class MethodPrinter implements MethodVisitor, Opcodes {
|
||||
}
|
||||
|
||||
public MethodPrinter(PrintStream destination) {
|
||||
super(ASM5);
|
||||
this.to = destination;
|
||||
}
|
||||
|
||||
public void visitCode() {
|
||||
to.print(" CODE\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String name, String desc, 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 + " ");
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
@@ -53,14 +53,6 @@ public class JavaMicroBenchmarkTests extends SpringLoadedTests {
|
||||
average(rtype, 5);
|
||||
}
|
||||
|
||||
private void pause(int seconds) {
|
||||
System.out.println("waiting...");
|
||||
try {
|
||||
Thread.sleep(seconds * 1000);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO fibonacci
|
||||
|
||||
private void average(ReloadableType rtype, int count) throws Exception {
|
||||
|
||||
@@ -205,7 +205,7 @@ public class ReloadableTypeTests extends SpringLoadedTests {
|
||||
@Test
|
||||
public void invokeStaticReloading_gh4_2() throws Exception {
|
||||
TypeRegistry tr = getTypeRegistry("invokestatic..*");
|
||||
ReloadableType AA = tr.addType("invokestatic.issue4.AA", loadBytesForClass("invokestatic.issue4.AA"));
|
||||
tr.addType("invokestatic.issue4.AA", loadBytesForClass("invokestatic.issue4.AA"));
|
||||
ReloadableType BB = tr.addType("invokestatic.issue4.BB", loadBytesForClass("invokestatic.issue4.BB"));
|
||||
|
||||
Result r = runUnguarded(BB.getClazz(), "getMessage");
|
||||
@@ -272,7 +272,7 @@ public class ReloadableTypeTests extends SpringLoadedTests {
|
||||
@Test
|
||||
public void invokeStaticReloading_gh4_6() throws Exception {
|
||||
TypeRegistry tr = getTypeRegistry("invokestatic.issue4..*");
|
||||
ReloadableType AB = tr.addType("invokestatic.issue4.AB", loadBytesForClass("invokestatic.issue4.AB"));
|
||||
tr.addType("invokestatic.issue4.AB", loadBytesForClass("invokestatic.issue4.AB"));
|
||||
ReloadableType B = tr.addType("invokestatic.issue4.BBBBB", loadBytesForClass("invokestatic.issue4.BBBBB"));
|
||||
|
||||
Result r = runUnguarded(B.getClazz(), "getMessage");
|
||||
|
||||
@@ -236,6 +236,25 @@ public class ReloadingJVM {
|
||||
}
|
||||
Utils.write(new File(testdataDirectory,classfile),data);
|
||||
}
|
||||
|
||||
public void clearTestdataDirectory() {
|
||||
File[] fs = testdataDirectory.listFiles();
|
||||
for (File f: fs) {
|
||||
delete(f);
|
||||
}
|
||||
}
|
||||
|
||||
private void delete(File toDelete) {
|
||||
if (toDelete.isDirectory()) {
|
||||
File[] fs = toDelete.listFiles();
|
||||
for (File f: fs) {
|
||||
delete(f);
|
||||
}
|
||||
}
|
||||
else {
|
||||
toDelete.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public JVMOutput newInstance(String instanceName, String classname) {
|
||||
copyToTestdataDirectory(classname);
|
||||
|
||||
@@ -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) {}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,13 +19,18 @@ import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Label;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
import org.springsource.loaded.Utils;
|
||||
|
||||
|
||||
/**
|
||||
* MethodVisitor that records events - very useful for testing
|
||||
*/
|
||||
public class FakeMethodVisitor implements MethodVisitor {
|
||||
public class FakeMethodVisitor extends MethodVisitor implements Constants {
|
||||
|
||||
public FakeMethodVisitor() {
|
||||
super(ASM5);
|
||||
}
|
||||
|
||||
StringBuilder events = new StringBuilder();
|
||||
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
package org.springsource.loaded.test.infra;
|
||||
|
||||
import org.objectweb.asm.ClassAdapter;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.MethodAdapter;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.springsource.loaded.Constants;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
@@ -32,7 +31,7 @@ import org.springsource.loaded.TypeRegistry;
|
||||
* @author Andy Clement
|
||||
* @version 0.8.3
|
||||
*/
|
||||
public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Constants {
|
||||
public class RewriteReflectUtilsDefineClass extends ClassVisitor implements Constants {
|
||||
|
||||
public static byte[] rewriteReflectUtilsDefineClass(byte[] data) {
|
||||
ClassReader cr = new ClassReader(data);
|
||||
@@ -43,7 +42,7 @@ public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Cons
|
||||
}
|
||||
|
||||
private RewriteReflectUtilsDefineClass() {
|
||||
super(new ClassWriter(0)); // TODO review 0 here
|
||||
super(ASM5,new ClassWriter(0)); // TODO review 0 here
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
@@ -74,10 +73,10 @@ public class RewriteReflectUtilsDefineClass extends ClassAdapter implements Cons
|
||||
}
|
||||
}
|
||||
|
||||
class DefineClassInterceptor extends MethodAdapter implements Constants {
|
||||
class DefineClassInterceptor extends MethodVisitor implements Constants {
|
||||
|
||||
public DefineClassInterceptor(MethodVisitor mv) {
|
||||
super(mv);
|
||||
super(ASM5,mv);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -47,7 +47,7 @@ public class SubLoader extends ClassLoader {
|
||||
TestUtils.getPathToClasses("../testdata-subloader")
|
||||
};
|
||||
static String[] jars = new String[] {
|
||||
"../testdata-groovy/groovy-1.8.2.jar"
|
||||
"../testdata-groovy/groovy-all-1.8.6.jar"
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
@@ -140,8 +140,8 @@ public class SubLoader extends ClassLoader {
|
||||
c = defineClass(name, data, 0, data.length);
|
||||
break;
|
||||
}
|
||||
zipfile.close();
|
||||
}
|
||||
// zipfile.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
throw new RuntimeException("Problem defining class", e);
|
||||
|
||||
@@ -45,12 +45,12 @@ public class SuperLoader extends ClassLoader {
|
||||
TestUtils.getPathToClasses("../testdata-superloader")
|
||||
};
|
||||
static String[] jars = new String[] {
|
||||
"../testdata-groovy/groovy-1.8.2.jar"
|
||||
"../testdata-groovy/groovy-all-1.8.6.jar"
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
public SuperLoader() {
|
||||
jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
|
||||
jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
|
||||
}
|
||||
|
||||
public SuperLoader(String... jars) {
|
||||
@@ -132,8 +132,8 @@ public class SuperLoader extends ClassLoader {
|
||||
c = defineClass(name, data, 0, data.length);
|
||||
break;
|
||||
}
|
||||
zipfile.close();
|
||||
}
|
||||
// zipfile.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -51,12 +51,12 @@ public class TestClassloaderWithRewriting extends ClassLoader {
|
||||
TestUtils.getPathToClasses("../testdata-groovy")
|
||||
};
|
||||
static String[] jars = new String[] {
|
||||
"../testdata-groovy/groovy-1.8.2.jar"
|
||||
"../testdata-groovy/groovy-all-1.8.6.jar"
|
||||
};
|
||||
// @formatter:on
|
||||
|
||||
public TestClassloaderWithRewriting() {
|
||||
jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
|
||||
jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
|
||||
}
|
||||
|
||||
public TestClassloaderWithRewriting(String metainfFolder) {
|
||||
@@ -64,7 +64,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
|
||||
newFolders[0] = folders[0];
|
||||
newFolders[1] = "../testdata/" + metainfFolder;
|
||||
folders = newFolders;
|
||||
jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
|
||||
jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
|
||||
}
|
||||
|
||||
public TestClassloaderWithRewriting(String metainfFolder, boolean b) {
|
||||
@@ -74,7 +74,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
|
||||
newFolders[2] = TestUtils.getPathToClasses("../testdata");
|
||||
newFolders[3] = TestUtils.getPathToClasses("../testdata-plugin");
|
||||
folders = newFolders;
|
||||
jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
|
||||
jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
|
||||
}
|
||||
|
||||
public TestClassloaderWithRewriting(String metainfFolder, boolean b, boolean useRegistry, URLClassLoader classLoader) {
|
||||
@@ -85,7 +85,7 @@ public class TestClassloaderWithRewriting extends ClassLoader {
|
||||
newFolders[2] = TestUtils.getPathToClasses("../testdata");
|
||||
folders = newFolders;
|
||||
this.useRegistry = useRegistry;
|
||||
jars = new String[] { "../testdata-groovy/groovy-1.8.2.jar" };
|
||||
jars = new String[] { "../testdata-groovy/groovy-all-1.8.6.jar" };
|
||||
}
|
||||
|
||||
public TestClassloaderWithRewriting(boolean b, boolean useRegistry, boolean addCglib) {
|
||||
|
||||
9
testdata-aspectj/.classpath
Normal file
9
testdata-aspectj/.classpath
Normal 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
18
testdata-aspectj/.project
Normal 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>
|
||||
40
testdata-aspectj/build.gradle
Normal file
40
testdata-aspectj/build.gradle
Normal 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
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry exported="true" kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry exported="true" kind="con" path="GROOVY_SUPPORT"/>
|
||||
<classpathentry exported="true" kind="con" path="GROOVY_DSL_SUPPORT"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
BIN
testdata-groovy/groovy-all-1.8.6.jar
Normal file
BIN
testdata-groovy/groovy-all-1.8.6.jar
Normal file
Binary file not shown.
6
testdata-java8/.classpath
Normal file
6
testdata-java8/.classpath
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src/main/java"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
@@ -0,0 +1 @@
|
||||
#Thu Feb 06 13:01:33 PST 2014
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileHashes.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/fileSnapshots.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/outputFileStates.bin
Normal file
Binary file not shown.
BIN
testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin
Normal file
BIN
testdata-java8/.gradle/1.7/taskArtifacts/taskArtifacts.bin
Normal file
Binary file not shown.
17
testdata-java8/.project
Normal file
17
testdata-java8/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>testdata-java8</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
11
testdata-java8/.settings/org.eclipse.jdt.core.prefs
Normal file
11
testdata-java8/.settings/org.eclipse.jdt.core.prefs
Normal file
@@ -0,0 +1,11 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
4
testdata-java8/build.gradle
Normal file
4
testdata-java8/build.gradle
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
apply plugin: 'java'
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
1
testdata-java8/build.sh
Executable file
1
testdata-java8/build.sh
Executable file
@@ -0,0 +1 @@
|
||||
../gradlew build
|
||||
BIN
testdata-java8/src/main/java/basic/FirstClass.class
Normal file
BIN
testdata-java8/src/main/java/basic/FirstClass.class
Normal file
Binary file not shown.
12
testdata-java8/src/main/java/basic/FirstClass.java
Normal file
12
testdata-java8/src/main/java/basic/FirstClass.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package basic;
|
||||
|
||||
public class FirstClass {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaA.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaA.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package basic;
|
||||
|
||||
public class LambdaA {
|
||||
|
||||
public interface Foo { int m(); }
|
||||
|
||||
public static void main(String[] args) {
|
||||
run();
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo f = null;
|
||||
f = () -> 77;
|
||||
return f.m();
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaA2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaA2.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package basic;
|
||||
|
||||
public class LambdaA2 {
|
||||
|
||||
public interface Foo { int m(); }
|
||||
|
||||
public static void main(String[] args) {
|
||||
run();
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo f = null;
|
||||
f = () -> 88;
|
||||
return f.m();
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaB.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaB.java
Normal 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);
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaB2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaB2.java
Normal 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);
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaC.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaC.java
Normal 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);
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaC2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaC2.java
Normal 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);
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaD.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaD.java
Normal 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);
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaD2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaD2.java
Normal 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);
|
||||
}
|
||||
}
|
||||
23
testdata-java8/src/main/java/basic/LambdaE.java
Normal file
23
testdata-java8/src/main/java/basic/LambdaE.java
Normal 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');
|
||||
}
|
||||
}
|
||||
23
testdata-java8/src/main/java/basic/LambdaE2.java
Normal file
23
testdata-java8/src/main/java/basic/LambdaE2.java
Normal 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');
|
||||
}
|
||||
}
|
||||
33
testdata-java8/src/main/java/basic/LambdaF.java
Normal file
33
testdata-java8/src/main/java/basic/LambdaF.java
Normal 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');
|
||||
}
|
||||
}
|
||||
34
testdata-java8/src/main/java/basic/LambdaF2.java
Normal file
34
testdata-java8/src/main/java/basic/LambdaF2.java
Normal 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');
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaG.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaG.java
Normal 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();
|
||||
}
|
||||
}
|
||||
16
testdata-java8/src/main/java/basic/LambdaG2.java
Normal file
16
testdata-java8/src/main/java/basic/LambdaG2.java
Normal 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();
|
||||
}
|
||||
}
|
||||
34
testdata-java8/src/main/java/basic/LambdaH.java
Normal file
34
testdata-java8/src/main/java/basic/LambdaH.java
Normal 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();
|
||||
}
|
||||
}
|
||||
17
testdata-java8/src/main/java/basic/LambdaI.java
Normal file
17
testdata-java8/src/main/java/basic/LambdaI.java
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
17
testdata-java8/src/main/java/basic/LambdaI2.java
Normal file
17
testdata-java8/src/main/java/basic/LambdaI2.java
Normal 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
3
testdata/.classpath
vendored
@@ -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
3
testdata/.project
vendored
@@ -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
43
testdata/build.gradle
vendored
@@ -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
|
||||
|
||||
|
||||
12
testdata/src/main/java/issue34/InnerEnum.java
vendored
12
testdata/src/main/java/issue34/InnerEnum.java
vendored
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user