remove arity restriction (as def call incorporates all lambdas and all their captures)

This commit is contained in:
Robert Muir 2016-06-20 05:37:31 -04:00
parent b53d735602
commit 4d78be5b9e
4 changed files with 25 additions and 23 deletions

View File

@ -28,6 +28,7 @@ import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.util.BitSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@ -222,19 +223,25 @@ public final class Def {
*/
static MethodHandle lookupMethod(Lookup lookup, MethodType callSiteType,
Class<?> receiverClass, String name, Object args[]) throws Throwable {
long recipe = (Long) args[0];
String recipeString = (String) args[0];
int numArguments = callSiteType.parameterCount();
// simple case: no lambdas
if (recipe == 0) {
if (recipeString.isEmpty()) {
return lookupMethodInternal(receiverClass, name, numArguments - 1).handle;
}
// convert recipe string to a bitset for convenience (the code below should be refactored...)
BitSet lambdaArgs = new BitSet();
for (int i = 0; i < recipeString.length(); i++) {
lambdaArgs.set(recipeString.charAt(i));
}
// otherwise: first we have to compute the "real" arity. This is because we have extra arguments:
// e.g. f(a, g(x), b, h(y), i()) looks like f(a, g, x, b, h, y, i).
int arity = callSiteType.parameterCount() - 1;
int upTo = 1;
for (int i = 0; i < numArguments; i++) {
if ((recipe & (1L << (i - 1))) != 0) {
for (int i = 1; i < numArguments; i++) {
if (lambdaArgs.get(i - 1)) {
String signature = (String) args[upTo++];
int numCaptures = Integer.parseInt(signature.substring(signature.indexOf(',')+1));
arity -= numCaptures;
@ -250,7 +257,7 @@ public final class Def {
upTo = 1;
for (int i = 1; i < numArguments; i++) {
// its a functional reference, replace the argument with an impl
if ((recipe & (1L << (i - 1))) != 0) {
if (lambdaArgs.get(i - 1)) {
// decode signature of form 'type.call,2'
String signature = (String) args[upTo++];
int separator = signature.indexOf('.');

View File

@ -397,11 +397,11 @@ public final class DefBootstrap {
if (args.length == 0) {
throw new BootstrapMethodError("Invalid number of parameters for method call");
}
if (args[0] instanceof Long == false) {
if (args[0] instanceof String == false) {
throw new BootstrapMethodError("Illegal parameter for method call: " + args[0]);
}
long recipe = (Long) args[0];
int numLambdas = Long.bitCount(recipe);
String recipe = (String) args[0];
int numLambdas = recipe.length();
if (numLambdas > type.parameterCount()) {
throw new BootstrapMethodError("Illegal recipe for method call: too many bits");
}

View File

@ -41,7 +41,7 @@ final class LDefCall extends ALink implements IDefLink {
final String name;
final List<AExpression> arguments;
long recipe;
StringBuilder recipe;
List<String> pointers = new ArrayList<>();
LDefCall(Location location, String name, List<AExpression> arguments) {
@ -60,14 +60,7 @@ final class LDefCall extends ALink implements IDefLink {
@Override
ALink analyze(Locals locals) {
if (arguments.size() > 63) {
// technically, the limitation is just methods with > 63 params, containing method references.
// this is because we are lazy and use a long as a bitset. we can always change to a "string" if need be.
// but NEED NOT BE. nothing with this many parameters is in the whitelist and we do not support varargs.
throw new UnsupportedOperationException("methods with > 63 arguments are currently not supported");
}
recipe = 0;
recipe = new StringBuilder();
int totalCaptures = 0;
for (int argument = 0; argument < arguments.size(); ++argument) {
AExpression expression = arguments.get(argument);
@ -78,7 +71,9 @@ final class LDefCall extends ALink implements IDefLink {
if (expression instanceof ILambda) {
ILambda lambda = (ILambda) expression;
pointers.add(lambda.getPointer());
recipe |= (1L << (argument + totalCaptures)); // mark argument as deferred reference
// encode this parameter as a deferred reference
char ch = (char) (argument + totalCaptures);
recipe.append(ch);
totalCaptures += lambda.getCaptureCount();
}
@ -124,7 +119,7 @@ final class LDefCall extends ALink implements IDefLink {
List<Object> args = new ArrayList<>();
args.add(DefBootstrap.METHOD_CALL);
args.add(recipe);
args.add(recipe.toString());
args.addAll(pointers);
writer.invokeDynamic(name, signature.toString(), DEF_BOOTSTRAP_HANDLE, args.toArray());
}

View File

@ -36,7 +36,7 @@ public class DefBootstrapTests extends ESTestCase {
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
"toString",
MethodType.methodType(String.class, Object.class),
DefBootstrap.METHOD_CALL, 0L);
DefBootstrap.METHOD_CALL, "");
MethodHandle handle = site.dynamicInvoker();
assertDepthEquals(site, 0);
@ -53,7 +53,7 @@ public class DefBootstrapTests extends ESTestCase {
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
"toString",
MethodType.methodType(String.class, Object.class),
DefBootstrap.METHOD_CALL, 0L);
DefBootstrap.METHOD_CALL, "");
MethodHandle handle = site.dynamicInvoker();
assertDepthEquals(site, 0);
@ -75,7 +75,7 @@ public class DefBootstrapTests extends ESTestCase {
CallSite site = DefBootstrap.bootstrap(MethodHandles.publicLookup(),
"toString",
MethodType.methodType(String.class, Object.class),
DefBootstrap.METHOD_CALL, 0L);
DefBootstrap.METHOD_CALL, "");
MethodHandle handle = site.dynamicInvoker();
assertDepthEquals(site, 0);
@ -98,7 +98,7 @@ public class DefBootstrapTests extends ESTestCase {
DefBootstrap.PIC site = (DefBootstrap.PIC) DefBootstrap.bootstrap(MethodHandles.publicLookup(),
"size",
MethodType.methodType(int.class, Object.class),
DefBootstrap.METHOD_CALL, 0L);
DefBootstrap.METHOD_CALL, "");
site.depth = DefBootstrap.PIC.MAX_DEPTH; // mark megamorphic
MethodHandle handle = site.dynamicInvoker();
assertEquals(2, (int)handle.invokeExact((Object) Arrays.asList("1", "2")));