use of com.facebook.presto.operator.scalar.ScalarFunctionImplementationChoice in project presto by prestodb.
the class InterpretedFunctionInvoker method invoke.
/**
* Arguments must be the native container type for the corresponding SQL types.
* <p>
* Returns a value in the native container type corresponding to the declared SQL return type
*/
private Object invoke(JavaScalarFunctionImplementation function, SqlFunctionProperties properties, List<Object> arguments) {
ScalarFunctionImplementationChoice choice = getAllScalarFunctionImplementationChoices(function).get(0);
MethodHandle method = choice.getMethodHandle();
// handle function on instance method, to allow use of fields
method = bindInstanceFactory(method, function);
if (method.type().parameterCount() > 0 && method.type().parameterType(0) == SqlFunctionProperties.class) {
method = method.bindTo(properties);
}
List<Object> actualArguments = new ArrayList<>();
for (int i = 0; i < arguments.size(); i++) {
Object argument = arguments.get(i);
ArgumentProperty argumentProperty = choice.getArgumentProperty(i);
if (argumentProperty.getArgumentType() == VALUE_TYPE) {
if (choice.getArgumentProperty(i).getNullConvention() == RETURN_NULL_ON_NULL) {
if (argument == null) {
return null;
}
actualArguments.add(argument);
} else if (choice.getArgumentProperty(i).getNullConvention() == USE_NULL_FLAG) {
boolean isNull = argument == null;
if (isNull) {
argument = Defaults.defaultValue(method.type().parameterType(actualArguments.size()));
}
actualArguments.add(argument);
actualArguments.add(isNull);
} else {
actualArguments.add(argument);
}
} else {
argument = asInterfaceInstance(argumentProperty.getLambdaInterface(), (MethodHandle) argument);
actualArguments.add(argument);
}
}
try {
return method.invokeWithArguments(actualArguments);
} catch (Throwable throwable) {
throw propagate(throwable);
}
}
use of com.facebook.presto.operator.scalar.ScalarFunctionImplementationChoice in project presto by prestodb.
the class ParametricScalarImplementation method specialize.
public Optional<BuiltInScalarFunctionImplementation> specialize(Signature boundSignature, BoundVariables boundVariables, FunctionAndTypeManager functionAndTypeManager) {
List<ScalarFunctionImplementationChoice> implementationChoices = new ArrayList<>();
for (Map.Entry<String, Class<?>> entry : specializedTypeParameters.entrySet()) {
if (entry.getValue() != boundVariables.getTypeVariable(entry.getKey()).getJavaType()) {
return Optional.empty();
}
}
if (returnNativeContainerType != Object.class && returnNativeContainerType != functionAndTypeManager.getType(boundSignature.getReturnType()).getJavaType()) {
return Optional.empty();
}
for (int i = 0; i < boundSignature.getArgumentTypes().size(); i++) {
if (boundSignature.getArgumentTypes().get(i).getBase().equals(FunctionType.NAME)) {
if (argumentNativeContainerTypes.get(i).isPresent()) {
return Optional.empty();
}
} else {
if (!argumentNativeContainerTypes.get(i).isPresent()) {
return Optional.empty();
}
Class<?> argumentType = functionAndTypeManager.getType(boundSignature.getArgumentTypes().get(i)).getJavaType();
Class<?> argumentNativeContainerType = argumentNativeContainerTypes.get(i).get();
if (argumentNativeContainerType != Object.class && argumentNativeContainerType != argumentType) {
return Optional.empty();
}
}
}
for (ParametricScalarImplementationChoice choice : choices) {
MethodHandle boundMethodHandle = bindDependencies(choice.getMethodHandle(), choice.getDependencies(), boundVariables, functionAndTypeManager);
Optional<MethodHandle> boundConstructor = choice.getConstructor().map(constructor -> {
MethodHandle result = bindDependencies(constructor, choice.getConstructorDependencies(), boundVariables, functionAndTypeManager);
checkCondition(result.type().parameterList().isEmpty(), FUNCTION_IMPLEMENTATION_ERROR, "All parameters of a constructor in a function definition class must be Dependencies. Signature: %s", boundSignature);
return result;
});
implementationChoices.add(new ScalarFunctionImplementationChoice(choice.nullable, choice.argumentProperties, choice.returnPlaceConvention, boundMethodHandle.asType(javaMethodType(choice, boundSignature, functionAndTypeManager)), boundConstructor));
}
return Optional.of(new BuiltInScalarFunctionImplementation(implementationChoices));
}
use of com.facebook.presto.operator.scalar.ScalarFunctionImplementationChoice in project presto by prestodb.
the class FunctionCallCodeGenerator method generateCall.
public BytecodeNode generateCall(FunctionHandle functionHandle, BytecodeGeneratorContext context, Type returnType, List<RowExpression> arguments, Optional<Variable> outputBlockVariable) {
FunctionAndTypeManager functionAndTypeManager = context.getFunctionManager();
ScalarFunctionImplementation function = functionAndTypeManager.getScalarFunctionImplementation(functionHandle);
checkArgument(function instanceof JavaScalarFunctionImplementation, format("FunctionCallCodeGenerator only handles JavaScalarFunctionImplementation, get %s", function.getClass().getName()));
JavaScalarFunctionImplementation javaFunction = (JavaScalarFunctionImplementation) function;
List<BytecodeNode> argumentsBytecode = new ArrayList<>();
ScalarFunctionImplementationChoice choice = getAllScalarFunctionImplementationChoices(javaFunction).get(0);
for (int i = 0; i < arguments.size(); i++) {
RowExpression argument = arguments.get(i);
ArgumentProperty argumentProperty = choice.getArgumentProperty(i);
if (argumentProperty.getArgumentType() == VALUE_TYPE) {
argumentsBytecode.add(context.generate(argument, Optional.empty()));
} else {
argumentsBytecode.add(context.generate(argument, Optional.empty(), Optional.of(argumentProperty.getLambdaInterface())));
}
}
return context.generateCall(functionAndTypeManager.getFunctionMetadata(functionHandle).getName().getObjectName(), javaFunction, argumentsBytecode, outputBlockVariable.map(variable -> new OutputBlockVariableAndType(variable, returnType)));
}
use of com.facebook.presto.operator.scalar.ScalarFunctionImplementationChoice in project presto by prestodb.
the class BytecodeUtils method generateInvocation.
public static BytecodeNode generateInvocation(Scope scope, String name, JavaScalarFunctionImplementation function, Optional<BytecodeNode> instance, List<BytecodeNode> arguments, CallSiteBinder binder, Optional<OutputBlockVariableAndType> outputBlockVariableAndType) {
LabelNode end = new LabelNode("end");
BytecodeBlock block = new BytecodeBlock().setDescription("invoke " + name);
List<Class<?>> stackTypes = new ArrayList<>();
if (function instanceof BuiltInScalarFunctionImplementation && ((BuiltInScalarFunctionImplementation) 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;
// Go through all the choices in the function and then pick the best one
List<ScalarFunctionImplementationChoice> choices = getAllScalarFunctionImplementationChoices(function);
ScalarFunctionImplementationChoice bestChoice = null;
for (ScalarFunctionImplementationChoice currentChoice : choices) {
boolean isValid = true;
for (int i = 0; i < arguments.size(); i++) {
if (currentChoice.getArgumentProperty(i).getArgumentType() != VALUE_TYPE) {
continue;
}
if (currentChoice.getArgumentProperty(i).getNullConvention() == BLOCK_AND_POSITION && !(arguments.get(i) instanceof InputReferenceNode) || currentChoice.getReturnPlaceConvention() == PROVIDED_BLOCKBUILDER && (!outputBlockVariableAndType.isPresent())) {
isValid = false;
break;
}
}
if (isValid) {
bestChoice = currentChoice;
}
}
checkState(bestChoice != null, "None of the scalar function implementation choices are valid");
Binding binding = binder.bind(bestChoice.getMethodHandle());
MethodType methodType = binding.getType();
Class<?> returnType = methodType.returnType();
Class<?> unboxedReturnType = Primitives.unwrap(returnType);
boolean boundInstance = false;
while (currentParameterIndex < methodType.parameterArray().length) {
Class<?> type = methodType.parameterArray()[currentParameterIndex];
stackTypes.add(type);
if (bestChoice.getInstanceFactory().isPresent() && !boundInstance) {
checkState(type.equals(bestChoice.getInstanceFactory().get().type().returnType()), "Mismatched type for instance parameter");
block.append(instance.get());
boundInstance = true;
} else if (type == SqlFunctionProperties.class) {
block.append(scope.getVariable("properties"));
} else if (type == BlockBuilder.class) {
block.append(outputBlockVariableAndType.get().getOutputBlockVariable());
} else {
ArgumentProperty argumentProperty = bestChoice.getArgumentProperty(realParameterIndex);
switch(argumentProperty.getArgumentType()) {
case VALUE_TYPE:
// Apply null convention for value type argument
switch(argumentProperty.getNullConvention()) {
case RETURN_NULL_ON_NULL:
block.append(arguments.get(realParameterIndex));
checkArgument(!Primitives.isWrapperType(type), "Non-nullable argument must not be primitive wrapper type");
switch(bestChoice.getReturnPlaceConvention()) {
case STACK:
block.append(ifWasNullPopAndGoto(scope, end, unboxedReturnType, Lists.reverse(stackTypes)));
break;
case PROVIDED_BLOCKBUILDER:
checkArgument(unboxedReturnType == void.class);
block.append(ifWasNullClearPopAppendAndGoto(scope, end, unboxedReturnType, outputBlockVariableAndType.get().getOutputBlockVariable(), Lists.reverse(stackTypes)));
break;
default:
throw new UnsupportedOperationException(format("Unsupported return place convention: %s", bestChoice.getReturnPlaceConvention()));
}
break;
case USE_NULL_FLAG:
block.append(arguments.get(realParameterIndex));
block.append(scope.getVariable("wasNull"));
block.append(scope.getVariable("wasNull").set(constantFalse()));
stackTypes.add(boolean.class);
currentParameterIndex++;
break;
case USE_BOXED_TYPE:
block.append(arguments.get(realParameterIndex));
block.append(boxPrimitiveIfNecessary(scope, type));
block.append(scope.getVariable("wasNull").set(constantFalse()));
break;
case BLOCK_AND_POSITION:
InputReferenceNode inputReferenceNode = (InputReferenceNode) arguments.get(realParameterIndex);
block.append(inputReferenceNode.produceBlockAndPosition());
stackTypes.add(int.class);
currentParameterIndex++;
break;
default:
throw new UnsupportedOperationException(format("Unsupported null convention: %s", argumentProperty.getNullConvention()));
}
break;
case FUNCTION_TYPE:
block.append(arguments.get(realParameterIndex));
break;
default:
throw new UnsupportedOperationException(format("Unsupported argument type: %s", argumentProperty.getArgumentType()));
}
realParameterIndex++;
}
currentParameterIndex++;
}
block.append(invoke(binding, name));
if (bestChoice.isNullable()) {
switch(bestChoice.getReturnPlaceConvention()) {
case STACK:
block.append(unboxPrimitiveIfNecessary(scope, returnType));
break;
case PROVIDED_BLOCKBUILDER:
// no-op
break;
default:
throw new UnsupportedOperationException(format("Unsupported return place convention: %s", bestChoice.getReturnPlaceConvention()));
}
}
block.visitLabel(end);
if (outputBlockVariableAndType.isPresent()) {
switch(bestChoice.getReturnPlaceConvention()) {
case STACK:
block.append(generateWrite(binder, scope, scope.getVariable("wasNull"), outputBlockVariableAndType.get().getType(), outputBlockVariableAndType.get().getOutputBlockVariable()));
break;
case PROVIDED_BLOCKBUILDER:
// no-op
break;
default:
throw new UnsupportedOperationException(format("Unsupported return place convention: %s", bestChoice.getReturnPlaceConvention()));
}
}
return block;
}
use of com.facebook.presto.operator.scalar.ScalarFunctionImplementationChoice in project presto by prestodb.
the class PolymorphicScalarFunction method getScalarFunctionImplementationChoice.
private ScalarFunctionImplementationChoice getScalarFunctionImplementationChoice(BoundVariables boundVariables, FunctionAndTypeManager functionAndTypeManager, PolymorphicScalarFunctionChoice choice) {
List<Type> resolvedParameterTypes = applyBoundVariables(functionAndTypeManager, getSignature().getArgumentTypes(), boundVariables);
Type resolvedReturnType = applyBoundVariables(functionAndTypeManager, getSignature().getReturnType(), boundVariables);
SpecializeContext context = new SpecializeContext(boundVariables, resolvedParameterTypes, resolvedReturnType, functionAndTypeManager);
Optional<MethodAndNativeContainerTypes> matchingMethod = Optional.empty();
Optional<MethodsGroup> matchingMethodsGroup = Optional.empty();
for (MethodsGroup candidateMethodsGroup : choice.getMethodsGroups()) {
for (MethodAndNativeContainerTypes candidateMethod : candidateMethodsGroup.getMethods()) {
if (matchesParameterAndReturnTypes(candidateMethod, resolvedParameterTypes, resolvedReturnType, choice.getArgumentProperties(), choice.isNullableResult())) {
if (matchingMethod.isPresent()) {
throw new IllegalStateException("two matching methods (" + matchingMethod.get().getMethod().getName() + " and " + candidateMethod.getMethod().getName() + ") for parameter types " + resolvedParameterTypes);
}
matchingMethod = Optional.of(candidateMethod);
matchingMethodsGroup = Optional.of(candidateMethodsGroup);
}
}
}
checkState(matchingMethod.isPresent(), "no matching method for parameter types %s", resolvedParameterTypes);
List<Object> extraParameters = computeExtraParameters(matchingMethodsGroup.get(), context);
MethodHandle methodHandle = applyExtraParameters(matchingMethod.get().getMethod(), extraParameters, choice.getArgumentProperties());
return new ScalarFunctionImplementationChoice(choice.isNullableResult(), choice.getArgumentProperties(), choice.getReturnPlaceConvention(), methodHandle, Optional.empty());
}
Aggregations