Skip to content

Commit 9df8ed4

Browse files
committed
Fix method references to private instance methods being invoked with invokespecial instead of invokevirtual
Really fixes #17
1 parent 313613e commit 9df8ed4

File tree

5 files changed

+29
-29
lines changed

5 files changed

+29
-29
lines changed

retrolambda/src/main/java/net/orfjackal/retrolambda/LambdaClassBackporter.java

+14-6
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ private static class LambdaClassVisitor extends ClassVisitor {
2323
private final int targetVersion;
2424
private String lambdaClass;
2525
private Type constructor;
26+
private Handle implMethod;
2627
private LambdaFactoryMethod factoryMethod;
2728

2829
public LambdaClassVisitor(ClassWriter cw, int targetVersion) {
@@ -34,6 +35,7 @@ public LambdaClassVisitor(ClassWriter cw, int targetVersion) {
3435
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
3536
lambdaClass = name;
3637
LambdaReifier.setLambdaClass(lambdaClass);
38+
implMethod = LambdaReifier.getLambdaImplMethod();
3739
factoryMethod = LambdaReifier.getLambdaFactoryMethod();
3840

3941
if (version > targetVersion) {
@@ -52,7 +54,7 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
5254
}
5355
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
5456
mv = new MagicLambdaRemovingMethodVisitor(mv);
55-
mv = new PrivateMethodInvocationFixingMethodVisitor(mv, factoryMethod.getInvoker());
57+
mv = new PrivateMethodInvocationFixingMethodVisitor(mv, implMethod);
5658
return mv;
5759
}
5860

@@ -135,18 +137,24 @@ public void visitMethodInsn(int opcode, String owner, String name, String desc)
135137

136138
private static class PrivateMethodInvocationFixingMethodVisitor extends MethodVisitor {
137139

138-
private final String invoker;
140+
private final Handle implMethod;
139141

140-
public PrivateMethodInvocationFixingMethodVisitor(MethodVisitor mv, Class<?> invoker) {
142+
public PrivateMethodInvocationFixingMethodVisitor(MethodVisitor mv, Handle implMethod) {
141143
super(ASM4, mv);
142-
this.invoker = Type.getInternalName(invoker);
144+
this.implMethod = implMethod;
143145
}
144146

145147
@Override
146148
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
149+
// Java 8's lambda classes get away with calling private virtual methods
150+
// by using invokespecial because the JVM relaxes the bytecode validation
151+
// of the lambda classes it generates. We must however use invokevirtual
152+
// for them (which is possible because we also make them non-private).
147153
if (opcode == INVOKESPECIAL
148-
&& owner.equals(invoker)
149-
&& LambdaNaming.LAMBDA_IMPL_METHOD.matcher(name).matches()) {
154+
&& !name.equals("<init>")
155+
&& owner.equals(implMethod.getOwner())
156+
&& name.equals(implMethod.getName())
157+
&& desc.equals(implMethod.getDesc())) {
150158
opcode = INVOKEVIRTUAL;
151159
}
152160
super.visitMethodInsn(opcode, owner, name, desc);

retrolambda/src/main/java/net/orfjackal/retrolambda/LambdaFactoryMethod.java

+1-7
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,10 @@ public class LambdaFactoryMethod {
1010

1111
private final String owner;
1212
private final String desc;
13-
private final Class<?> invoker;
1413

15-
public LambdaFactoryMethod(String lambdaClass, Type invokedType, Class<?> invoker) {
14+
public LambdaFactoryMethod(String lambdaClass, Type invokedType) {
1615
owner = lambdaClass;
1716
desc = invokedType.getDescriptor();
18-
this.invoker = invoker;
1917
}
2018

2119
public String getOwner() {
@@ -29,8 +27,4 @@ public String getName() {
2927
public String getDesc() {
3028
return desc;
3129
}
32-
33-
public Class<?> getInvoker() {
34-
return invoker;
35-
}
3630
}

retrolambda/src/main/java/net/orfjackal/retrolambda/LambdaNaming.java

-6
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,4 @@ public class LambdaNaming {
1515
* Java 8 produces at runtime classes named {@code EnclosingClass$$Lambda$1}
1616
*/
1717
public static final Pattern LAMBDA_CLASS = Pattern.compile("^.+\\$\\$Lambda\\$\\d+$");
18-
19-
/**
20-
* Oracle JDK 8 compiler names the methods {@code lambda$methodName$0} but the
21-
* Eclipse JDT compiler names them {@code lambda$0} (similar to older JDK 8 EA builds).
22-
*/
23-
public static final Pattern LAMBDA_IMPL_METHOD = Pattern.compile("^lambda(\\$.*)?\\$\\d+$");
2418
}

retrolambda/src/main/java/net/orfjackal/retrolambda/LambdaReifier.java

+11-8
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ public class LambdaReifier {
1919
// is spying on the LambdaMetafactory's dynamically generated bytecode.
2020
// We expect only one class being processed at a time, so it should
2121
// be an error if these collections contain more than one element.
22-
private static final BlockingDeque<Class<?>> currentInvoker = new LinkedBlockingDeque<>(1);
22+
private static final BlockingDeque<Handle> currentLambdaImplMethod = new LinkedBlockingDeque<>(1);
2323
private static final BlockingDeque<Type> currentInvokedType = new LinkedBlockingDeque<>(1);
2424
private static final BlockingDeque<String> currentLambdaClass = new LinkedBlockingDeque<>(1);
2525

26-
public static LambdaFactoryMethod reifyLambdaClass(Class<?> invoker, String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
26+
public static LambdaFactoryMethod reifyLambdaClass(Handle lambdaImplMethod, Class<?> invoker, String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
2727
try {
28-
setInvoker(invoker);
28+
setLambdaImplMethod(lambdaImplMethod);
2929
setInvokedType(invokedType);
3030

3131
// Causes the lambda class to be loaded. Retrolambda's Java agent
@@ -42,8 +42,8 @@ public static LambdaFactoryMethod reifyLambdaClass(Class<?> invoker, String invo
4242
}
4343
}
4444

45-
public static void setInvoker(Class<?> invoker) {
46-
currentInvoker.push(invoker);
45+
private static void setLambdaImplMethod(Handle lambdaImplMethod) {
46+
currentLambdaImplMethod.push(lambdaImplMethod);
4747
}
4848

4949
private static void setInvokedType(Type invokedType) {
@@ -54,15 +54,18 @@ public static void setLambdaClass(String lambdaClass) {
5454
currentLambdaClass.push(lambdaClass);
5555
}
5656

57+
public static Handle getLambdaImplMethod() {
58+
return currentLambdaImplMethod.getFirst();
59+
}
60+
5761
public static LambdaFactoryMethod getLambdaFactoryMethod() {
5862
String lambdaClass = currentLambdaClass.getFirst();
5963
Type invokedType = currentInvokedType.getFirst();
60-
Class<?> invoker = currentInvoker.getFirst();
61-
return new LambdaFactoryMethod(lambdaClass, invokedType, invoker);
64+
return new LambdaFactoryMethod(lambdaClass, invokedType);
6265
}
6366

6467
private static void resetGlobals() {
65-
currentInvoker.clear();
68+
currentLambdaImplMethod.clear();
6669
currentInvokedType.clear();
6770
currentLambdaClass.clear();
6871
}

retrolambda/src/main/java/net/orfjackal/retrolambda/LambdaUsageBackporter.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ public InvokeDynamicInsnConvertingMethodVisitor(int api, MethodVisitor mv, Strin
118118
@Override
119119
public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) {
120120
if (bsm.getOwner().equals(LambdaNaming.LAMBDA_METAFACTORY)) {
121-
lambdaImplMethods.add((Handle) bsmArgs[1]);
122121
backportLambda(name, Type.getType(desc), bsm, bsmArgs);
123122
} else {
124123
super.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
@@ -127,7 +126,9 @@ public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object.
127126

128127
private void backportLambda(String invokedName, Type invokedType, Handle bsm, Object[] bsmArgs) {
129128
Class<?> invoker = loadClass(myClassName);
130-
LambdaFactoryMethod factory = LambdaReifier.reifyLambdaClass(invoker, invokedName, invokedType, bsm, bsmArgs);
129+
Handle lambdaImplMethod = (Handle) bsmArgs[1];
130+
lambdaImplMethods.add(lambdaImplMethod);
131+
LambdaFactoryMethod factory = LambdaReifier.reifyLambdaClass(lambdaImplMethod, invoker, invokedName, invokedType, bsm, bsmArgs);
131132
super.visitMethodInsn(INVOKESTATIC, factory.getOwner(), factory.getName(), factory.getDesc());
132133
}
133134

0 commit comments

Comments
 (0)