External variable references from Java lambdas are given as constructor arguments. Java also allows up to 255 arguments to be given to a method.
For example, compiling this code:
public class Main {
public static void main(String[] args) throws InterruptedException {
var message = "Hello world!";
var thread = new Thread(() -> System.out.println(message));
thread.run();
thread.join();
}
}
And, javac generates this code:
final class Main$$Lambda$1 implements java.lang.Runnable {
private final String arg$1;
private Main$$Lambda$1(String arg1) {
super();
this.arg$1 = arg1;
}
public void run() {
Main.lambda$main$0(this.arg$1);
}
}
Then, I tried to use 255 external local variables to be referenced from the lambda.
But when I ran it, it threw a BootstrapMethodError(caused by IllegalArgumentException: bad parameter count 256)
I understood that the reason this error was thrown was because the constructor was automatically given its own object as an argument, and consequently 256 arguments were given to the constructor.
Exception in thread "main" java.lang.BootstrapMethodError: java.lang.IllegalArgumentException: bad parameter count 256
at Main.main(Main.java:260)
Caused by: java.lang.IllegalArgumentException: bad parameter count 256
at java.base/java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:167)
at java.base/java.lang.invoke.MethodType.checkSlotCount(MethodType.java:223)
at java.base/java.lang.invoke.MethodType.insertParameterTypes(MethodType.java:437)
at java.base/java.lang.invoke.MethodType.appendParameterTypes(MethodType.java:461)
at java.base/java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:233)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:218)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:227)
at java.base/java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:108)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:4004)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(MethodHandles.java:3960)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectMethodForConstant(MethodHandles.java:4204)
at java.base/java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:4152)
at java.base/java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:615)
... 1 more
Next, I created a lambda that references 254 external local variables. When I ran this, the same error was thrown again. Butthe number of arguments given to the constructor is 254 + 1, which should be 255.
Exception in thread "main" java.lang.BootstrapMethodError: bootstrap method initialization exception
at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:188)
at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:315)
at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:281)
at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:271)
at Main.main(Main.java:259)
Caused by: java.lang.IllegalArgumentException: bad parameter count 256
at java.base/java.lang.invoke.MethodHandleStatics.newIllegalArgumentException(MethodHandleStatics.java:167)
at java.base/java.lang.invoke.MethodType.checkSlotCount(MethodType.java:223)
at java.base/java.lang.invoke.MethodType.insertParameterTypes(MethodType.java:437)
at java.base/java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:259)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:233)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:218)
at java.base/java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:227)
at java.base/java.lang.invoke.DirectMethodHandle.makeAllocator(DirectMethodHandle.java:142)
at java.base/java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:133)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectConstructorCommon(MethodHandles.java:4122)
at java.base/java.lang.invoke.MethodHandles$Lookup.getDirectConstructor(MethodHandles.java:4106)
at java.base/java.lang.invoke.MethodHandles$Lookup.findConstructor(MethodHandles.java:2751)
at java.base/java.lang.invoke.InnerClassLambdaMetafactory.buildCallSite(InnerClassLambdaMetafactory.java:269)
at java.base/java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:341)
at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:134)
... 4 more
I also rewrote var to the proper type and tried running it in java8, but got the same error.
Why did I get an error saying that 256 arguments were given?
This question has been translated from Japanese using a translator and I have based on this article.
Thanks.
If the lambda references outside local variables it needs to capture those at lambda creation time. The JVM dynamically creates a for this and also dynamically creates a constructor for this class. The constructor has as arguments the instance of the lambda, the captured local variables and an extra
MemberName
parameter (https://github.com/openjdk/jdk17u/blob/master/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java#L256).This hidden extra parameter is the reason for the exception.
Actually, this
MemberName
parameter is added first to the parameter list. This is the reason that your first example fails on the lineDirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:256)
if you try to use a lambda with 255 captured local variables.In the case with 254 captured local variables adding this
MemberName
parameter works and the code fails when it tries to insert the lambda instance at position 0 in the lineDirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:259)