Lambda targets are not remapped
Hi, I have encountered an issue with the remapping of InvokeDynamics, specifically when dealing with Javac compiled invokedynamics.
To explain I made a simple example:
import org.objectweb.asm.*;
import org.objectweb.asm.commons.*;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.util.*;
import java.io.PrintWriter;
public class Test {
public static void main(String[] args) throws Throwable {
ClassNode classNode = new ClassNode();
new ClassReader("Example").accept(classNode, ClassReader.SKIP_DEBUG);
print(classNode);
ClassNode newNode = new ClassNode();
Remapper remapper = new SimpleRemapper(
"Example.something()V",
"different"
);
classNode.accept(new ClassRemapper(newNode, remapper));
print(newNode);
}
public static void print(ClassNode cn) {
Textifier textifier = new Textifier();
PrintWriter writer = new PrintWriter(System.out, true);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(null, textifier, writer);
cn.accept(traceClassVisitor);
}
}
interface Example {
static void test() {
((Example) () -> System.out.println("Hello, World!")).something();
}
void something();
}
Example
is a functional interface with the method something
. A method implements the functional interface and the method something
as a lambda.
Test
attempts to remap the name of something
to nothing
. The expected behaviour would be for the method's name to change and all references to be updated.
By printing the class before and after the remapper is applied to it we can however see that this is not the case.
Before remapping:
// class version 52.0 (52)
// access flags 0x600
abstract interface Example {
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x9
public static test()V
INVOKEDYNAMIC something()LExample; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
Example.lambda$test$0()V itf,
()V
]
INVOKEINTERFACE Example.something ()V (itf)
RETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x401
public abstract something()V
// access flags 0x100A
private static synthetic lambda$test$0()V
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
After remapping:
// class version 52.0 (52)
// access flags 0x600
abstract interface Example {
// access flags 0x19
public final static INNERCLASS java/lang/invoke/MethodHandles$Lookup java/lang/invoke/MethodHandles Lookup
// access flags 0x9
public static test()V
INVOKEDYNAMIC something()LExample; [
// handle kind 0x6 : 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;
// arguments:
()V,
// handle kind 0x6 : INVOKESTATIC
Example.lambda$test$0()V itf,
()V
]
INVOKEINTERFACE Example.different ()V (itf)
RETURN
MAXSTACK = 1
MAXLOCALS = 0
// access flags 0x401
public abstract different()V
// access flags 0x100A
private static synthetic lambda$test$0()V
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
LDC "Hello, World!"
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
RETURN
MAXSTACK = 2
MAXLOCALS = 0
}
As you can see the arguments of the invokedynamic instruction have not been remapped, it remains as something()LExample;
which will result in a runtime error.
This can be fixed by providing special behaviour when remapping LambdaMetafactory invokedynamics and there is a patch implementing this here: https://github.com/MinecraftForge/MinecraftForge/pull/3532.
Thank You