diff --git a/springloaded/src/main/java/org/springsource/loaded/support/Java8.java b/springloaded/src/main/java/org/springsource/loaded/support/Java8.java index b0b454d..55590d9 100644 --- a/springloaded/src/main/java/org/springsource/loaded/support/Java8.java +++ b/springloaded/src/main/java/org/springsource/loaded/support/Java8.java @@ -120,20 +120,32 @@ public class Java8 { // 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 there is an executor, the lambda function is actually modified from 'private instance' to 'public static' so adjust lookup. The method + // will be static with a new leading parameter. if (executorClass == null) { + // TODO is final parameter here correct? 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; + case Opcodes.H_INVOKEVIRTUAL: + // If there is an executor, the lambda function is actually modified from 'private instance' to 'public static' so adjust lookup. The method + // will be static with a new leading parameter. + if (executorClass == null) { + // TODO when can this scenario occur? Aren't we only here if reloading has happened? + implMethod = caller.findVirtual(caller.lookupClass(), name, implMethodType); + } + else { + implMethod = caller.findStatic(caller.lookupClass(), name, MethodType.fromMethodDescriptorString("(L"+owner+";"+descriptor.substring(1),callerLoader)); + } + break; default: throw new IllegalStateException("nyi "+bsmArgsHandle.getTag()); } diff --git a/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java b/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java index 570a239..0640004 100644 --- a/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java +++ b/springloaded/src/test/java/org/springsource/loaded/test/Java8Tests.java @@ -267,6 +267,31 @@ public class Java8Tests extends SpringLoadedTests { r = runUnguarded(simpleClass, "run"); assertEquals("ab", r.returnValue); } + + + @Test + public void lambdaInvokeVirtual() throws Exception { + String t = "basic.LambdaJ"; + 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("fooa", 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("fooab", r.returnValue); + } @Ignore @Test diff --git a/testdata-java8/src/main/java/basic/LambdaJ.java b/testdata-java8/src/main/java/basic/LambdaJ.java new file mode 100644 index 0000000..b8bec7f --- /dev/null +++ b/testdata-java8/src/main/java/basic/LambdaJ.java @@ -0,0 +1,24 @@ +package basic; + +public class LambdaJ { + + public interface Foo { String m(String s); } + + public String getFoo(String s) { + return "foo"+s; + } + + public static void main(String[] args) { + run(); + } + + public static String run() { + return new LambdaJ().run2(); + } + + public String run2() { + Foo f = this::getFoo; + return f.m("a"); + } + +} diff --git a/testdata-java8/src/main/java/basic/LambdaJ2.java b/testdata-java8/src/main/java/basic/LambdaJ2.java new file mode 100644 index 0000000..ac64d7c --- /dev/null +++ b/testdata-java8/src/main/java/basic/LambdaJ2.java @@ -0,0 +1,24 @@ +package basic; + +public class LambdaJ2 { + + public interface Foo { String m(String s, String t); } + + public String getFoo(String s, String t) { + return "foo"+s+t; + } + + public static void main(String[] args) { + run(); + } + + public static String run() { + return new LambdaJ().run2(); + } + + public String run2() { + Foo f = this::getFoo; + return f.m("a","b"); + } + +}