use of org.elasticsearch.painless.Definition.Method in project elasticsearch by elastic.
the class Def method lookupReferenceInternal.
/** Returns a method handle to an implementation of clazz, given method reference signature. */
private static MethodHandle lookupReferenceInternal(Lookup lookup, Definition.Type clazz, String type, String call, Class<?>... captures) throws Throwable {
final FunctionRef ref;
if ("this".equals(type)) {
// user written method
Method interfaceMethod = clazz.struct.getFunctionalMethod();
if (interfaceMethod == null) {
throw new IllegalArgumentException("Cannot convert function reference [" + type + "::" + call + "] " + "to [" + clazz.name + "], not a functional interface");
}
int arity = interfaceMethod.arguments.size() + captures.length;
final MethodHandle handle;
try {
MethodHandle accessor = lookup.findStaticGetter(lookup.lookupClass(), getUserFunctionHandleFieldName(call, arity), MethodHandle.class);
handle = (MethodHandle) accessor.invokeExact();
} catch (NoSuchFieldException | IllegalAccessException e) {
// because the arity does not match the expected interface type.
if (call.contains("$")) {
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name + "] in [" + clazz.clazz + "]");
}
throw new IllegalArgumentException("Unknown call [" + call + "] with [" + arity + "] arguments.");
}
ref = new FunctionRef(clazz, interfaceMethod, handle, captures.length);
} else {
// whitelist lookup
ref = new FunctionRef(clazz, type, call, captures.length);
}
final CallSite callSite;
if (ref.needsBridges()) {
callSite = LambdaMetafactory.altMetafactory(lookup, ref.invokedName, ref.invokedType, ref.samMethodType, ref.implMethod, ref.samMethodType, LambdaMetafactory.FLAG_BRIDGES, 1, ref.interfaceMethodType);
} else {
callSite = LambdaMetafactory.altMetafactory(lookup, ref.invokedName, ref.invokedType, ref.samMethodType, ref.implMethod, ref.samMethodType, 0);
}
return callSite.dynamicInvoker().asType(MethodType.methodType(clazz.clazz, captures));
}
use of org.elasticsearch.painless.Definition.Method in project elasticsearch by elastic.
the class Def method lookupReference.
/**
* Returns an implementation of interfaceClass that calls receiverClass.name
* <p>
* This is just like LambdaMetaFactory, only with a dynamic type. The interface type is known,
* so we simply need to lookup the matching implementation method based on receiver type.
*/
static MethodHandle lookupReference(Lookup lookup, String interfaceClass, Class<?> receiverClass, String name) throws Throwable {
Definition.Type interfaceType = Definition.getType(interfaceClass);
Method interfaceMethod = interfaceType.struct.getFunctionalMethod();
if (interfaceMethod == null) {
throw new IllegalArgumentException("Class [" + interfaceClass + "] is not a functional interface");
}
int arity = interfaceMethod.arguments.size();
Method implMethod = lookupMethodInternal(receiverClass, name, arity);
return lookupReferenceInternal(lookup, interfaceType, implMethod.owner.name, implMethod.name, receiverClass);
}
use of org.elasticsearch.painless.Definition.Method in project elasticsearch by elastic.
the class Def method lookupMethod.
/**
* Looks up handle for a dynamic method call, with lambda replacement
* <p>
* A dynamic method call for variable {@code x} of type {@code def} looks like:
* {@code x.method(args...)}
* <p>
* This method traverses {@code recieverClass}'s class hierarchy (including interfaces)
* until it finds a matching whitelisted method. If one is not found, it throws an exception.
* Otherwise it returns a handle to the matching method.
* <p>
* @param lookup caller's lookup
* @param callSiteType callsite's type
* @param receiverClass Class of the object to invoke the method on.
* @param name Name of the method.
* @param args bootstrap args passed to callsite
* @return pointer to matching method to invoke. never returns null.
* @throws IllegalArgumentException if no matching whitelisted method was found.
* @throws Throwable if a method reference cannot be converted to an functional interface
*/
static MethodHandle lookupMethod(Lookup lookup, MethodType callSiteType, Class<?> receiverClass, String name, Object[] args) throws Throwable {
String recipeString = (String) args[0];
int numArguments = callSiteType.parameterCount();
// simple case: no lambdas
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 = 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;
}
}
// lookup the method with the proper arity, then we know everything (e.g. interface types of parameters).
// based on these we can finally link any remaining lambdas that were deferred.
Method method = lookupMethodInternal(receiverClass, name, arity);
MethodHandle handle = method.handle;
int replaced = 0;
upTo = 1;
for (int i = 1; i < numArguments; i++) {
// its a functional reference, replace the argument with an impl
if (lambdaArgs.get(i - 1)) {
// decode signature of form 'type.call,2'
String signature = (String) args[upTo++];
int separator = signature.lastIndexOf('.');
int separator2 = signature.indexOf(',');
String type = signature.substring(1, separator);
String call = signature.substring(separator + 1, separator2);
int numCaptures = Integer.parseInt(signature.substring(separator2 + 1));
Class<?>[] captures = new Class<?>[numCaptures];
for (int capture = 0; capture < captures.length; capture++) {
captures[capture] = callSiteType.parameterType(i + 1 + capture);
}
MethodHandle filter;
Definition.Type interfaceType = method.arguments.get(i - 1 - replaced);
if (signature.charAt(0) == 'S') {
// the implementation is strongly typed, now that we know the interface type,
// we have everything.
filter = lookupReferenceInternal(lookup, interfaceType, type, call, captures);
} else if (signature.charAt(0) == 'D') {
// the interface type is now known, but we need to get the implementation.
// this is dynamically based on the receiver type (and cached separately, underneath
// this cache). It won't blow up since we never nest here (just references)
MethodType nestedType = MethodType.methodType(interfaceType.clazz, captures);
CallSite nested = DefBootstrap.bootstrap(lookup, call, nestedType, 0, DefBootstrap.REFERENCE, interfaceType.name);
filter = nested.dynamicInvoker();
} else {
throw new AssertionError();
}
// the filter now ignores the signature (placeholder) on the stack
filter = MethodHandles.dropArguments(filter, 0, String.class);
handle = MethodHandles.collectArguments(handle, i, filter);
i += numCaptures;
replaced += numCaptures;
}
}
return handle;
}
use of org.elasticsearch.painless.Definition.Method in project elasticsearch by elastic.
the class SSource method analyze.
public void analyze() {
Map<MethodKey, Method> methods = new HashMap<>();
for (SFunction function : functions) {
function.generateSignature();
MethodKey key = new MethodKey(function.name, function.parameters.size());
if (methods.put(key, function.method) != null) {
throw createError(new IllegalArgumentException("Duplicate functions with name [" + function.name + "]."));
}
}
analyze(Locals.newProgramScope(methods.values()));
}
use of org.elasticsearch.painless.Definition.Method in project elasticsearch by elastic.
the class ELambda method analyze.
@Override
void analyze(Locals locals) {
final Type returnType;
final List<String> actualParamTypeStrs;
Method interfaceMethod;
// inspect the target first, set interface method if we know it.
if (expected == null) {
interfaceMethod = null;
// we don't know anything: treat as def
returnType = Definition.DEF_TYPE;
// don't infer any types
actualParamTypeStrs = paramTypeStrs;
} else {
// we know the method statically, infer return type and any unknown/def types
interfaceMethod = expected.struct.getFunctionalMethod();
if (interfaceMethod == null) {
throw createError(new IllegalArgumentException("Cannot pass lambda to [" + expected.name + "], not a functional interface"));
}
// check arity before we manipulate parameters
if (interfaceMethod.arguments.size() != paramTypeStrs.size())
throw new IllegalArgumentException("Incorrect number of parameters for [" + interfaceMethod.name + "] in [" + expected.clazz + "]");
// for method invocation, its allowed to ignore the return value
if (interfaceMethod.rtn == Definition.VOID_TYPE) {
returnType = Definition.DEF_TYPE;
} else {
returnType = interfaceMethod.rtn;
}
// replace any def types with the actual type (which could still be def)
actualParamTypeStrs = new ArrayList<String>();
for (int i = 0; i < paramTypeStrs.size(); i++) {
String paramType = paramTypeStrs.get(i);
if (paramType.equals(Definition.DEF_TYPE.name)) {
actualParamTypeStrs.add(interfaceMethod.arguments.get(i).name);
} else {
actualParamTypeStrs.add(paramType);
}
}
}
// gather any variables used by the lambda body first.
Set<String> variables = new HashSet<>();
for (AStatement statement : statements) {
statement.extractVariables(variables);
}
// any of those variables defined in our scope need to be captured
captures = new ArrayList<>();
for (String variable : variables) {
if (locals.hasVariable(variable)) {
captures.add(locals.getVariable(location, variable));
}
}
// prepend capture list to lambda's arguments
List<String> paramTypes = new ArrayList<>();
List<String> paramNames = new ArrayList<>();
for (Variable var : captures) {
paramTypes.add(var.type.name);
paramNames.add(var.name);
}
paramTypes.addAll(actualParamTypeStrs);
paramNames.addAll(paramNameStrs);
// desugar lambda body into a synthetic method
desugared = new SFunction(reserved, location, returnType.name, name, paramTypes, paramNames, statements, true);
desugared.generateSignature();
desugared.analyze(Locals.newLambdaScope(locals.getProgramScope(), returnType, desugared.parameters, captures.size(), reserved.getMaxLoopCounter()));
// setup method reference to synthetic method
if (expected == null) {
ref = null;
actual = Definition.getType("String");
defPointer = "Sthis." + name + "," + captures.size();
} else {
defPointer = null;
try {
ref = new FunctionRef(expected, interfaceMethod, desugared.method, captures.size());
} catch (IllegalArgumentException e) {
throw createError(e);
}
actual = expected;
}
}
Aggregations