lambda reloading, invokeinterface support - fixes #82
This commit is contained in:
@@ -252,14 +252,17 @@ public class ClassRenamer {
|
||||
String owner = oldHandle.getOwner();
|
||||
String name = oldHandle.getName();
|
||||
String desc = oldHandle.getDesc();
|
||||
// System.out.println("handle: owner: "+owner);
|
||||
// System.out.println("handle: name: "+name);
|
||||
// System.out.println("handle: desc: "+desc);
|
||||
owner = renameRetargetIfNecessary(owner);
|
||||
desc = renameRetargetIfNecessary(desc);
|
||||
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;,
|
||||
|
||||
@@ -1571,7 +1571,7 @@ public class TypeRegistry {
|
||||
// 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);
|
||||
return Java8.emulateInvokeDynamic(rtype,rtype.getLatestExecutorClass(),bsmi.bsm,bsmi.bsmArgs,caller,nameAndDescriptor, indyParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,10 +20,12 @@ import java.lang.invoke.LambdaMetafactory;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.objectweb.asm.Handle;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
|
||||
/**
|
||||
* This class encapsulates dependencies on Java 8 APIs (e.g. LambdaMetafactory).
|
||||
@@ -89,9 +91,9 @@ public class Java8 {
|
||||
* @param indyParams parameters when the invokedynamic call is made
|
||||
* @return the result of the invokedynamic call
|
||||
*/
|
||||
public static Object emulateInvokeDynamic(Class<?> executorClass, Handle handle, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor, Object[] indyParams) {
|
||||
public static Object emulateInvokeDynamic(ReloadableType rtype, Class<?> executorClass, Handle handle, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor, Object[] indyParams) {
|
||||
try {
|
||||
CallSite callsite = callLambdaMetaFactory(bsmArgs,lookup,indyNameAndDescriptor,executorClass);
|
||||
CallSite callsite = callLambdaMetaFactory(rtype, bsmArgs,lookup,indyNameAndDescriptor,executorClass);
|
||||
return callsite.dynamicInvoker().invokeWithArguments(indyParams);
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
@@ -100,7 +102,7 @@ public class Java8 {
|
||||
// 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 {
|
||||
public static CallSite callLambdaMetaFactory(ReloadableType rtype, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor,Class<?> executorClass) throws Exception {
|
||||
MethodHandles.Lookup caller = (MethodHandles.Lookup)lookup;
|
||||
|
||||
ClassLoader callerLoader = caller.lookupClass().getClassLoader();
|
||||
@@ -146,6 +148,15 @@ public class Java8 {
|
||||
implMethod = caller.findStatic(caller.lookupClass(), name, MethodType.fromMethodDescriptorString("(L"+owner+";"+descriptor.substring(1),callerLoader));
|
||||
}
|
||||
break;
|
||||
case Opcodes.H_INVOKEINTERFACE:
|
||||
Handle h = (Handle)bsmArgs[1];
|
||||
String interfaceOwner = h.getOwner();
|
||||
// TODO Should there not be a more direct way to this than classloading?
|
||||
// TODO What about when this is a method added to the interface on a reload? It won't really exist, should we point
|
||||
// to the executor? or something else? (maybe just directly the real method that will satisfy the interface - if it can be worked out)
|
||||
Class<?> interfaceClass = callerLoader.loadClass(interfaceOwner.replace('/','.')); // interface type, eg StreamB$Foo
|
||||
implMethod = caller.findVirtual(interfaceClass, name, implMethodType);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("nyi "+bsmArgsHandle.getTag());
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.springsource.loaded.ReloadableType;
|
||||
import org.springsource.loaded.TypeRegistry;
|
||||
import org.springsource.loaded.test.infra.ClassPrinter;
|
||||
import org.springsource.loaded.test.infra.Result;
|
||||
|
||||
/**
|
||||
@@ -292,6 +293,79 @@ public class Java8Tests extends SpringLoadedTests {
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals("fooab", r.returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWithLambda() throws Exception {
|
||||
String t = "basic.StreamA";
|
||||
TypeRegistry typeRegistry = getTypeRegistry("basic..*");
|
||||
byte[] sc = loadBytesForClass(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, sc);
|
||||
|
||||
Class<?> simpleClass = rtype.getClazz();
|
||||
Result r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(3, r.returnValue);
|
||||
|
||||
byte[] renamed = retrieveRename(t, t + "2", t + "2$Foo:" + t + "$Foo");
|
||||
rtype.loadNewVersion("002", renamed);
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(4, r.returnValue);
|
||||
}
|
||||
|
||||
// inner interface (for the invokeinterface BSM)
|
||||
@Test
|
||||
public void streamWithLambdaInvokedVirtually() throws Exception {
|
||||
String t = "basic.StreamB";
|
||||
TypeRegistry typeRegistry = getTypeRegistry("basic..*");
|
||||
byte[] sc = loadBytesForClass(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, sc);
|
||||
|
||||
Class<?> simpleClass = rtype.getClazz();
|
||||
Result r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(3, r.returnValue);
|
||||
|
||||
byte[] renamed = retrieveRename(t, t + "2", t + "2$Foo:" + t + "$Foo");
|
||||
rtype.loadNewVersion("002", renamed);
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(4, r.returnValue);
|
||||
}
|
||||
|
||||
// not an inner interface this time (for the invokeinterface BSM)
|
||||
@Test
|
||||
public void streamWithLambdaInvokedVirtually2() throws Exception {
|
||||
String t = "basic.StreamBB";
|
||||
TypeRegistry typeRegistry = getTypeRegistry("basic..*");
|
||||
byte[] sc = loadBytesForClass(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, sc);
|
||||
|
||||
Class<?> simpleClass = rtype.getClazz();
|
||||
Result r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(3, r.returnValue);
|
||||
|
||||
byte[] renamed = retrieveRename(t, t + "2");
|
||||
rtype.loadNewVersion("002", renamed);
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(4, r.returnValue);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void streamWithoutLambda() throws Exception {
|
||||
String t = "basic.StreamC";
|
||||
TypeRegistry typeRegistry = getTypeRegistry("basic..*");
|
||||
byte[] sc = loadBytesForClass(t);
|
||||
ReloadableType rtype = typeRegistry.addType(t, sc);
|
||||
|
||||
Class<?> simpleClass = rtype.getClazz();
|
||||
Result r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(3, r.returnValue);
|
||||
|
||||
byte[] renamed = retrieveRename(t, t + "2");
|
||||
rtype.loadNewVersion("002", renamed);
|
||||
r = runUnguarded(simpleClass, "run");
|
||||
assertEquals(4, r.returnValue);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
|
||||
24
testdata-java8/src/main/java/basic/StreamA.java
Normal file
24
testdata-java8/src/main/java/basic/StreamA.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamA {
|
||||
|
||||
public interface Foo {
|
||||
int m(int n);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3);
|
||||
List<Integer> mapped = integers.stream().map(n -> foo.m(n)).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
24
testdata-java8/src/main/java/basic/StreamA2.java
Normal file
24
testdata-java8/src/main/java/basic/StreamA2.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamA2 {
|
||||
|
||||
public interface Foo {
|
||||
int m(int n);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3, 4); // Array is longer!
|
||||
List<Integer> mapped = integers.stream().map(n -> foo.m(n)).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
24
testdata-java8/src/main/java/basic/StreamB.java
Normal file
24
testdata-java8/src/main/java/basic/StreamB.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamB {
|
||||
|
||||
public interface Foo {
|
||||
int m(int n);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3);
|
||||
List<Integer> mapped = integers.stream().map(foo::m).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
24
testdata-java8/src/main/java/basic/StreamB2.java
Normal file
24
testdata-java8/src/main/java/basic/StreamB2.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamB2 {
|
||||
|
||||
public interface Foo {
|
||||
int m(int n);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
Foo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
|
||||
List<Integer> mapped = integers.stream().map(foo::m).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
20
testdata-java8/src/main/java/basic/StreamBB.java
Normal file
20
testdata-java8/src/main/java/basic/StreamBB.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamBB {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
StreamBBFoo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3);
|
||||
List<Integer> mapped = integers.stream().map(foo::m).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
24
testdata-java8/src/main/java/basic/StreamBB2.java
Normal file
24
testdata-java8/src/main/java/basic/StreamBB2.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//interface StreamBBFoo {
|
||||
// int m(int n);
|
||||
//}
|
||||
|
||||
public class StreamBB2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
StreamBBFoo foo = (n) -> n + 1;
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
|
||||
List<Integer> mapped = integers.stream().map(foo::m).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
5
testdata-java8/src/main/java/basic/StreamBBFoo.java
Normal file
5
testdata-java8/src/main/java/basic/StreamBBFoo.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package basic;
|
||||
|
||||
public interface StreamBBFoo {
|
||||
int m(int n);
|
||||
}
|
||||
19
testdata-java8/src/main/java/basic/StreamC.java
Normal file
19
testdata-java8/src/main/java/basic/StreamC.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamC {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3);
|
||||
List<Integer> mapped = integers.stream().map(n -> n).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
19
testdata-java8/src/main/java/basic/StreamC2.java
Normal file
19
testdata-java8/src/main/java/basic/StreamC2.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package basic;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class StreamC2 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("This is Java8");
|
||||
}
|
||||
|
||||
public static int run() {
|
||||
List<Integer> integers = Arrays.asList(1, 2, 3, 4);
|
||||
List<Integer> mapped = integers.stream().map(n -> n).collect(Collectors.toList());
|
||||
return mapped.size();
|
||||
}
|
||||
|
||||
}
|
||||
16
testdata/src/main/java/basic/Basic005.java
vendored
Normal file
16
testdata/src/main/java/basic/Basic005.java
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
package basic;
|
||||
|
||||
public class Basic005 {
|
||||
|
||||
public static void foo() {
|
||||
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
public static int barstatic(String s) {
|
||||
return Integer.valueOf(s);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user