use of java.lang.invoke.MethodType in project es6draft by anba.
the class Properties method convertArguments.
private static MethodHandle convertArguments(MethodHandle handle, int fixedArguments, boolean varargs, Converter converter) {
MethodType type = handle.type();
int pcount = type.parameterCount();
int actual = pcount - fixedArguments - (varargs ? 1 : 0);
Class<?>[] params = type.parameterArray();
MethodHandle[] filters = new MethodHandle[pcount];
for (int p = 0; p < actual; ++p) {
filters[fixedArguments + p] = converter.filterFor(params[fixedArguments + p]);
}
if (varargs) {
filters[pcount - 1] = converter.arrayFilterFor(params[pcount - 1]);
}
handle = MethodHandles.filterArguments(handle, 0, filters);
return handle;
}
use of java.lang.invoke.MethodType in project presto by prestodb.
the class VarArgsToArrayAdapterGenerator method generateVarArgsToArrayAdapter.
public static MethodHandleAndConstructor generateVarArgsToArrayAdapter(Class<?> returnType, Class<?> javaType, int argsLength, MethodHandle function, MethodHandle userStateFactory) {
requireNonNull(returnType, "returnType is null");
requireNonNull(javaType, "javaType is null");
requireNonNull(function, "function is null");
requireNonNull(userStateFactory, "userStateFactory is null");
MethodType methodType = function.type();
Class<?> javaArrayType = toArrayClass(javaType);
checkArgument(methodType.returnType() == returnType, "returnType does not match");
checkArgument(methodType.parameterList().equals(ImmutableList.of(Object.class, javaArrayType)), "parameter types do not match");
CallSiteBinder callSiteBinder = new CallSiteBinder();
ClassDefinition classDefinition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("VarArgsToListAdapter"), type(Object.class));
classDefinition.declareDefaultConstructor(a(PRIVATE));
// generate userState constructor
MethodDefinition stateFactoryDefinition = classDefinition.declareMethod(a(PUBLIC, STATIC), "createState", type(VarArgsToArrayAdapterState.class));
stateFactoryDefinition.getBody().comment("create userState for current instance").append(newInstance(VarArgsToArrayAdapterState.class, loadConstant(callSiteBinder, userStateFactory, MethodHandle.class).invoke("invokeExact", Object.class), newArray(type(javaArrayType), argsLength).cast(Object.class)).ret());
// generate adapter method
ImmutableList.Builder<Parameter> parameterListBuilder = ImmutableList.builder();
parameterListBuilder.add(arg("userState", VarArgsToArrayAdapterState.class));
for (int i = 0; i < argsLength; i++) {
parameterListBuilder.add(arg("input_" + i, javaType));
}
ImmutableList<Parameter> parameterList = parameterListBuilder.build();
MethodDefinition methodDefinition = classDefinition.declareMethod(a(PUBLIC, STATIC), "varArgsToArray", type(returnType), parameterList);
BytecodeBlock body = methodDefinition.getBody();
BytecodeExpression userState = parameterList.get(0).getField("userState", Object.class);
BytecodeExpression args = parameterList.get(0).getField("args", Object.class).cast(javaArrayType);
for (int i = 0; i < argsLength; i++) {
body.append(args.setElement(i, parameterList.get(i + 1)));
}
body.append(loadConstant(callSiteBinder, function, MethodHandle.class).invoke("invokeExact", returnType, userState, args).ret());
// define class
Class<?> generatedClass = defineClass(classDefinition, Object.class, callSiteBinder.getBindings(), VarArgsToArrayAdapterGenerator.class.getClassLoader());
return new MethodHandleAndConstructor(Reflection.methodHandle(generatedClass, "varArgsToArray", ImmutableList.builder().add(VarArgsToArrayAdapterState.class).addAll(nCopies(argsLength, javaType)).build().toArray(new Class<?>[argsLength])), Reflection.methodHandle(generatedClass, "createState"));
}
use of java.lang.invoke.MethodType in project spring-loaded by spring-projects.
the class Java8 method callLambdaMetaFactory.
// TODO [perf] How about a table of CallSites indexed by invokedynamic number through the class file. Computed on first reference but cleared on reload. Possibly extend this to all invoke types!
// TODO [lambda] Need to handle altMetaFactory which is used when the lambdas are more 'complex' (e.g. Serializable)
public static CallSite callLambdaMetaFactory(ReloadableType rtype, Object[] bsmArgs, Object lookup, String indyNameAndDescriptor, Class<?> executorClass) throws Exception {
MethodHandles.Lookup caller = (MethodHandles.Lookup) lookup;
ClassLoader callerLoader = caller.lookupClass().getClassLoader();
int descriptorStart = indyNameAndDescriptor.indexOf('(');
String invokedName = indyNameAndDescriptor.substring(0, descriptorStart);
MethodType invokedType = MethodType.fromMethodDescriptorString(indyNameAndDescriptor.substring(descriptorStart), callerLoader);
// Use bsmArgs to build the parameters
MethodType samMethodType = MethodType.fromMethodDescriptorString((((Type) bsmArgs[0]).getDescriptor()), callerLoader);
Handle bsmArgsHandle = (Handle) bsmArgs[1];
String owner = bsmArgsHandle.getOwner();
String name = bsmArgsHandle.getName();
String descriptor = bsmArgsHandle.getDesc();
MethodType implMethodType = MethodType.fromMethodDescriptorString(descriptor, callerLoader);
// Looking up the lambda$run method in the caller class (note the caller class is the executor, which gets us around the
// problem of having to hack into LambdaMetafactory to intercept reflection)
MethodHandle implMethod = null;
switch(bsmArgsHandle.getTag()) {
case Opcodes.H_INVOKESTATIC:
implMethod = caller.findStatic(caller.lookupClass(), name, implMethodType);
break;
case Opcodes.H_INVOKESPECIAL:
// will be static with a new leading parameter.
if (executorClass == null) {
// TODO is final parameter here correct?
implMethod = caller.findSpecial(caller.lookupClass(), name, implMethodType, caller.lookupClass());
} else {
implMethod = caller.findStatic(caller.lookupClass(), name, MethodType.fromMethodDescriptorString("(L" + owner + ";" + descriptor.substring(1), callerLoader));
}
break;
case Opcodes.H_INVOKEVIRTUAL:
// There is a possibility to 'shortcut' here. Basically we are trying to resolve a callsite reference
// to the method that satisfies it. The easiest option is to just find the method on the originally
// loaded version of the target class and return that. A more optimal shortcut could return the
// method on the executor class if the target has been reloaded (effectively bypassing the method
// on the originally loaded version since we know that it will be acting as a pass through). But this
// opens up a can of worms related to visibility. The executor is loaded into the child classloader,
// and if the caller has not been reloaded it will not be able to 'see' the executor (since it is in
// a child classloader). So, basically keep this dumb (but reliable) for now.
TypeRegistry typeRegistry = rtype.getTypeRegistry();
ReloadableType ownerRType = typeRegistry.getReloadableType(owner);
if (null == ownerRType || !ownerRType.hasBeenReloaded()) {
// target containing the reference/lambdaMethod has not been reloaded, no need to get over
// complicated.
Class<?> clazz = callerLoader.loadClass(owner.replace("/", "."));
implMethod = caller.findVirtual(clazz, name, implMethodType);
} else {
MethodMember targetReferenceMethodMember = ownerRType.getCurrentMethod(name, descriptor);
String targetReferenceDescriptor = targetReferenceMethodMember.getDescriptor();
MethodType targetReferenceMethodType = MethodType.fromMethodDescriptorString(targetReferenceDescriptor, callerLoader);
Class<?> targetReferenceClass = ownerRType.getClazz();
MethodMember currentMethod = ownerRType.getCurrentMethod(name, descriptor);
if (currentMethod.original == null) {
// caller and reloaded target are in the same child classloader (no visibility problem).
if (!rtype.hasBeenReloaded()) {
throw new IllegalStateException("Assertion violated: When a method added on reload is being referenced" + "in target type '" + ownerRType.getName() + "', expected the caller to also have been reloaded: '" + rtype.getName() + "'");
}
CurrentLiveVersion ownerLiveVersion = ownerRType.getLiveVersion();
Class<?> ownerExecutorClass = ownerLiveVersion.getExecutorClass();
Method executorMethod = ownerLiveVersion.getExecutorMethod(currentMethod);
String methodDescriptor = Type.getType(executorMethod).getDescriptor();
MethodType type = MethodType.fromMethodDescriptorString(methodDescriptor, callerLoader);
implMethod = caller.findStatic(ownerExecutorClass, name, type);
} else {
// This finds the reference method on the originally loaded class. It will pass through
// to the actual code on the reloaded version.
implMethod = caller.findVirtual(targetReferenceClass, name, targetReferenceMethodType);
}
}
break;
case Opcodes.H_NEWINVOKESPECIAL:
Class<?> clazz = callerLoader.loadClass(owner.replace("/", "."));
implMethod = caller.findConstructor(clazz, implMethodType);
break;
case Opcodes.H_INVOKEINTERFACE:
Handle h = (Handle) bsmArgs[1];
String interfaceOwner = h.getOwner();
// TODO Should there not be a more direct way to this than classloading?
// TODO What about when this is a method added to the interface on a reload? It won't really exist, should we point
// to the executor? or something else? (maybe just directly the real method that will satisfy the interface - if it can be worked out)
// interface type, eg StreamB$Foo
Class<?> interfaceClass = callerLoader.loadClass(interfaceOwner.replace('/', '.'));
implMethod = caller.findVirtual(interfaceClass, name, implMethodType);
break;
default:
throw new IllegalStateException("nyi " + bsmArgsHandle.getTag());
}
MethodType instantiatedMethodType = MethodType.fromMethodDescriptorString((((Type) bsmArgs[2]).getDescriptor()), callerLoader);
return LambdaMetafactory.metafactory(caller, invokedName, invokedType, samMethodType, implMethod, instantiatedMethodType);
}
use of java.lang.invoke.MethodType in project presto by prestodb.
the class CallTask method execute.
@Override
public ListenableFuture<?> execute(Call call, TransactionManager transactionManager, Metadata metadata, AccessControl accessControl, QueryStateMachine stateMachine, List<Expression> parameters) {
if (!stateMachine.isAutoCommit()) {
throw new PrestoException(NOT_SUPPORTED, "Procedures cannot be called within a transaction (use autocommit mode)");
}
Session session = stateMachine.getSession();
QualifiedObjectName procedureName = createQualifiedObjectName(session, call, call.getName());
ConnectorId connectorId = metadata.getCatalogHandle(stateMachine.getSession(), procedureName.getCatalogName()).orElseThrow(() -> new SemanticException(MISSING_CATALOG, call, "Catalog %s does not exist", procedureName.getCatalogName()));
Procedure procedure = metadata.getProcedureRegistry().resolve(connectorId, procedureName.asSchemaTableName());
// map declared argument names to positions
Map<String, Integer> positions = new HashMap<>();
for (int i = 0; i < procedure.getArguments().size(); i++) {
positions.put(procedure.getArguments().get(i).getName(), i);
}
// per specification, do not allow mixing argument types
Predicate<CallArgument> hasName = argument -> argument.getName().isPresent();
boolean anyNamed = call.getArguments().stream().anyMatch(hasName);
boolean allNamed = call.getArguments().stream().allMatch(hasName);
if (anyNamed && !allNamed) {
throw new SemanticException(INVALID_PROCEDURE_ARGUMENTS, call, "Named and positional arguments cannot be mixed");
}
// get the argument names in call order
Map<String, CallArgument> names = new LinkedHashMap<>();
for (int i = 0; i < call.getArguments().size(); i++) {
CallArgument argument = call.getArguments().get(i);
if (argument.getName().isPresent()) {
String name = argument.getName().get();
if (names.put(name, argument) != null) {
throw new SemanticException(INVALID_PROCEDURE_ARGUMENTS, argument, "Duplicate procedure argument: %s", name);
}
if (!positions.containsKey(name)) {
throw new SemanticException(INVALID_PROCEDURE_ARGUMENTS, argument, "Unknown argument name: %s", name);
}
} else if (i < procedure.getArguments().size()) {
names.put(procedure.getArguments().get(i).getName(), argument);
} else {
throw new SemanticException(INVALID_PROCEDURE_ARGUMENTS, call, "Too many arguments for procedure");
}
}
// verify argument count
if (names.size() < positions.size()) {
throw new SemanticException(INVALID_PROCEDURE_ARGUMENTS, call, "Too few arguments for procedure");
}
// get argument values
Object[] values = new Object[procedure.getArguments().size()];
for (Entry<String, CallArgument> entry : names.entrySet()) {
CallArgument callArgument = entry.getValue();
int index = positions.get(entry.getKey());
Argument argument = procedure.getArguments().get(index);
Expression expression = ExpressionTreeRewriter.rewriteWith(new ParameterRewriter(parameters), callArgument.getValue());
Type type = metadata.getType(argument.getType());
checkCondition(type != null, INVALID_PROCEDURE_DEFINITION, "Unknown procedure argument type: %s", argument.getType());
Object value = evaluateConstantExpression(expression, type, metadata, session, parameters);
values[index] = toTypeObjectValue(session, type, value);
}
// validate arguments
MethodType methodType = procedure.getMethodHandle().type();
for (int i = 0; i < procedure.getArguments().size(); i++) {
if ((values[i] == null) && methodType.parameterType(i).isPrimitive()) {
String name = procedure.getArguments().get(i).getName();
throw new PrestoException(INVALID_PROCEDURE_ARGUMENT, "Procedure argument cannot be null: " + name);
}
}
// insert session argument
List<Object> arguments = new ArrayList<>();
Iterator<Object> valuesIterator = asList(values).iterator();
for (Class<?> type : methodType.parameterList()) {
if (ConnectorSession.class.isAssignableFrom(type)) {
arguments.add(session.toConnectorSession(connectorId));
} else {
arguments.add(valuesIterator.next());
}
}
try {
procedure.getMethodHandle().invokeWithArguments(arguments);
} catch (Throwable t) {
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
propagateIfInstanceOf(t, PrestoException.class);
throw new PrestoException(PROCEDURE_CALL_FAILED, t);
}
return immediateFuture(null);
}
use of java.lang.invoke.MethodType in project presto by prestodb.
the class BytecodeUtils method generateInvocation.
public static BytecodeNode generateInvocation(Scope scope, String name, ScalarFunctionImplementation function, Optional<BytecodeNode> instance, List<BytecodeNode> arguments, Binding binding) {
MethodType methodType = binding.getType();
Class<?> returnType = methodType.returnType();
Class<?> unboxedReturnType = Primitives.unwrap(returnType);
LabelNode end = new LabelNode("end");
BytecodeBlock block = new BytecodeBlock().setDescription("invoke " + name);
List<Class<?>> stackTypes = new ArrayList<>();
if (function.getInstanceFactory().isPresent()) {
checkArgument(instance.isPresent());
}
// Index of current parameter in the MethodHandle
int currentParameterIndex = 0;
// Index of parameter (without @IsNull) in Presto function
int realParameterIndex = 0;
boolean boundInstance = false;
while (currentParameterIndex < methodType.parameterArray().length) {
Class<?> type = methodType.parameterArray()[currentParameterIndex];
stackTypes.add(type);
if (function.getInstanceFactory().isPresent() && !boundInstance) {
checkState(type.equals(function.getInstanceFactory().get().type().returnType()), "Mismatched type for instance parameter");
block.append(instance.get());
boundInstance = true;
} else if (type == ConnectorSession.class) {
block.append(scope.getVariable("session"));
} else {
block.append(arguments.get(realParameterIndex));
if (!function.getNullableArguments().get(realParameterIndex)) {
checkArgument(!Primitives.isWrapperType(type), "Non-nullable argument must not be primitive wrapper type");
block.append(ifWasNullPopAndGoto(scope, end, unboxedReturnType, Lists.reverse(stackTypes)));
} else {
if (function.getNullFlags().get(realParameterIndex)) {
if (type == Void.class) {
block.append(boxPrimitiveIfNecessary(scope, type));
}
block.append(scope.getVariable("wasNull"));
stackTypes.add(boolean.class);
currentParameterIndex++;
} else {
block.append(boxPrimitiveIfNecessary(scope, type));
}
block.append(scope.getVariable("wasNull").set(constantFalse()));
}
realParameterIndex++;
}
currentParameterIndex++;
}
block.append(invoke(binding, name));
if (function.isNullable()) {
block.append(unboxPrimitiveIfNecessary(scope, returnType));
}
block.visitLabel(end);
return block;
}
Aggregations