From 4d78be5b9effff0abc5190ff89562a8e25456eeb Mon Sep 17 00:00:00 2001 From: Robert Muir Date: Mon, 20 Jun 2016 05:37:31 -0400 Subject: [PATCH] remove arity restriction (as def call incorporates all lambdas and all their captures) --- .../java/org/elasticsearch/painless/Def.java | 17 ++++++++++++----- .../elasticsearch/painless/DefBootstrap.java | 6 +++--- .../elasticsearch/painless/node/LDefCall.java | 17 ++++++----------- .../painless/DefBootstrapTests.java | 8 ++++---- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java index a4033255b77..e575ed32183 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/Def.java @@ -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('.'); diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java index a60985eec67..9174911b79c 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/DefBootstrap.java @@ -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"); } diff --git a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java index d216eacae19..4b04cfbf7a5 100644 --- a/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java +++ b/modules/lang-painless/src/main/java/org/elasticsearch/painless/node/LDefCall.java @@ -41,7 +41,7 @@ final class LDefCall extends ALink implements IDefLink { final String name; final List arguments; - long recipe; + StringBuilder recipe; List pointers = new ArrayList<>(); LDefCall(Location location, String name, List 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 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()); } diff --git a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java index 6529bed3a48..8cb065c2611 100644 --- a/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java +++ b/modules/lang-painless/src/test/java/org/elasticsearch/painless/DefBootstrapTests.java @@ -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")));