use of com.facebook.presto.bytecode.CallSiteBinder in project presto by prestodb.
the class ExpressionCompiler method compileProcessor.
private <T> Class<? extends T> compileProcessor(SqlFunctionProperties sqlFunctionProperties, RowExpression filter, List<RowExpression> projections, BodyCompiler bodyCompiler, Class<? extends T> superType) {
ClassDefinition classDefinition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName(superType.getSimpleName()), type(Object.class), type(superType));
CallSiteBinder callSiteBinder = new CallSiteBinder();
bodyCompiler.generateMethods(sqlFunctionProperties, classDefinition, callSiteBinder, filter, projections);
//
// toString method
//
generateToString(classDefinition, callSiteBinder, toStringHelper(classDefinition.getType().getJavaClassName()).add("filter", filter).add("projections", projections).toString());
return defineClass(classDefinition, superType, callSiteBinder.getBindings(), getClass().getClassLoader());
}
use of com.facebook.presto.bytecode.CallSiteBinder in project presto by prestodb.
the class DereferenceCodeGenerator method generateExpression.
@Override
public BytecodeNode generateExpression(BytecodeGeneratorContext generator, Type returnType, List<RowExpression> arguments, Optional<Variable> outputBlockVariable) {
checkArgument(arguments.size() == 2);
CallSiteBinder callSiteBinder = generator.getCallSiteBinder();
// Collect all nested intermediateDerefs
ImmutableList.Builder<RowExpression> nestedDerefernces = ImmutableList.builder();
RowExpression nestedObject = arguments.get(0);
int leafFieldIndex = ((Number) ((ConstantExpression) arguments.get(1)).getValue()).intValue();
// Find all the intermediate nestedDerefernces.
while (nestedObject instanceof SpecialFormExpression && ((SpecialFormExpression) nestedObject).getForm() == DEREFERENCE) {
nestedDerefernces.add(nestedObject);
nestedObject = ((SpecialFormExpression) nestedObject).getArguments().get(0);
}
// Here nestedObject is the inner-most expression (so the toplevel object)
// Just generate a loop
BytecodeBlock block = new BytecodeBlock().comment("DEREFERENCE").setDescription("DEREFERENCE");
Variable rowBlock = generator.getScope().createTempVariable(Block.class);
Variable wasNull = generator.wasNull();
// Labels for control-flow
LabelNode end = new LabelNode("end");
LabelNode returnNull = new LabelNode("returnNull");
// clear the wasNull flag before evaluating the row value and evaluate the root (innermost) object
block.putVariable(wasNull, false).append(generator.generate(nestedObject, Optional.empty())).putVariable(rowBlock).comment("If the object is null return null").append(wasNull).ifTrueGoto(returnNull);
for (RowExpression rowExpression : nestedDerefernces.build().reverse()) {
SpecialFormExpression nestedDerefernce = (SpecialFormExpression) rowExpression;
int fieldIndex = ((Number) ((ConstantExpression) nestedDerefernce.getArguments().get(1)).getValue()).intValue();
block.append(rowBlock).push(fieldIndex).invokeInterface(Block.class, "isNull", boolean.class, int.class).comment("If the deref result is null return null").ifTrueGoto(returnNull).append(constantType(callSiteBinder, nestedDerefernce.getType()).getValue(rowBlock, constantInt(fieldIndex))).putVariable(rowBlock);
}
block.append(rowBlock).push(((Number) ((ConstantExpression) arguments.get(1)).getValue()).intValue()).invokeInterface(Block.class, "isNull", boolean.class, int.class).ifTrueGoto(returnNull);
BytecodeExpression value = constantType(callSiteBinder, returnType).getValue(rowBlock, constantInt(leafFieldIndex));
block.append(wasNull).ifTrueGoto(returnNull).append(value).gotoLabel(end);
// Here one of the enclosing objects is null
Class<?> javaType = returnType.getJavaType();
block.visitLabel(returnNull).pushJavaDefault(javaType).comment("if the field is null, push null to stack").putVariable(wasNull, true);
block.visitLabel(end);
outputBlockVariable.ifPresent(output -> block.append(generateWrite(generator, returnType, output)));
return block;
}
use of com.facebook.presto.bytecode.CallSiteBinder in project presto by prestodb.
the class MapTransformValueFunction method generateTransform.
private static MethodHandle generateTransform(Type keyType, Type valueType, Type transformedValueType, Type resultMapType) {
CallSiteBinder binder = new CallSiteBinder();
Class<?> keyJavaType = Primitives.wrap(keyType.getJavaType());
Class<?> valueJavaType = Primitives.wrap(valueType.getJavaType());
Class<?> transformedValueJavaType = Primitives.wrap(transformedValueType.getJavaType());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("MapTransformValue"), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
// define transform method
Parameter state = arg("state", Object.class);
Parameter block = arg("block", Block.class);
Parameter function = arg("function", BinaryFunctionInterface.class);
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "transform", type(Block.class), ImmutableList.of(state, block, function));
BytecodeBlock body = method.getBody();
Scope scope = method.getScope();
Variable positionCount = scope.declareVariable(int.class, "positionCount");
Variable position = scope.declareVariable(int.class, "position");
Variable pageBuilder = scope.declareVariable(PageBuilder.class, "pageBuilder");
Variable mapBlockBuilder = scope.declareVariable(BlockBuilder.class, "mapBlockBuilder");
Variable blockBuilder = scope.declareVariable(BlockBuilder.class, "blockBuilder");
Variable keyElement = scope.declareVariable(keyJavaType, "keyElement");
Variable valueElement = scope.declareVariable(valueJavaType, "valueElement");
Variable transformedValueElement = scope.declareVariable(transformedValueJavaType, "transformedValueElement");
// invoke block.getPositionCount()
body.append(positionCount.set(block.invoke("getPositionCount", int.class)));
// prepare the single map block builder
body.append(pageBuilder.set(state.cast(PageBuilder.class)));
body.append(new IfStatement().condition(pageBuilder.invoke("isFull", boolean.class)).ifTrue(pageBuilder.invoke("reset", void.class)));
body.append(mapBlockBuilder.set(pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, constantInt(0))));
body.append(blockBuilder.set(mapBlockBuilder.invoke("beginBlockEntry", BlockBuilder.class)));
// throw null key exception block
BytecodeNode throwNullKeyException = new BytecodeBlock().append(newInstance(PrestoException.class, getStatic(INVALID_FUNCTION_ARGUMENT.getDeclaringClass(), "INVALID_FUNCTION_ARGUMENT").cast(ErrorCodeSupplier.class), constantString("map key cannot be null"))).throwObject();
SqlTypeBytecodeExpression keySqlType = constantType(binder, keyType);
BytecodeNode loadKeyElement;
if (!keyType.equals(UNKNOWN)) {
loadKeyElement = new BytecodeBlock().append(keyElement.set(keySqlType.getValue(block, position).cast(keyJavaType)));
} else {
// make sure invokeExact will not take uninitialized keys during compile time
// but if we reach this point during runtime, it is an exception
// also close the block builder before throwing as we may be in a TRY() call
// so that subsequent calls do not find it in an inconsistent state
loadKeyElement = new BytecodeBlock().append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop()).append(keyElement.set(constantNull(keyJavaType))).append(throwNullKeyException);
}
SqlTypeBytecodeExpression valueSqlType = constantType(binder, valueType);
BytecodeNode loadValueElement;
if (!valueType.equals(UNKNOWN)) {
loadValueElement = new IfStatement().condition(block.invoke("isNull", boolean.class, add(position, constantInt(1)))).ifTrue(valueElement.set(constantNull(valueJavaType))).ifFalse(valueElement.set(valueSqlType.getValue(block, add(position, constantInt(1))).cast(valueJavaType)));
} else {
loadValueElement = new BytecodeBlock().append(valueElement.set(constantNull(valueJavaType)));
}
BytecodeNode writeTransformedValueElement;
if (!transformedValueType.equals(UNKNOWN)) {
writeTransformedValueElement = new IfStatement().condition(equal(transformedValueElement, constantNull(transformedValueJavaType))).ifTrue(blockBuilder.invoke("appendNull", BlockBuilder.class).pop()).ifFalse(constantType(binder, transformedValueType).writeValue(blockBuilder, transformedValueElement.cast(transformedValueType.getJavaType())));
} else {
writeTransformedValueElement = new BytecodeBlock().append(blockBuilder.invoke("appendNull", BlockBuilder.class).pop());
}
Variable transformationException = scope.declareVariable(Throwable.class, "transformationException");
body.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(incrementVariable(position, (byte) 2)).body(new BytecodeBlock().append(loadKeyElement).append(loadValueElement).append(new TryCatch("Close builder before throwing to avoid subsequent calls finding it in an inconsistent state if we are in in a TRY() call.", transformedValueElement.set(function.invoke("apply", Object.class, keyElement.cast(Object.class), valueElement.cast(Object.class)).cast(transformedValueJavaType)), new BytecodeBlock().append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop()).append(pageBuilder.invoke("declarePosition", void.class)).putVariable(transformationException).append(invokeStatic(Throwables.class, "throwIfUnchecked", void.class, transformationException)).append(newInstance(RuntimeException.class, transformationException)).throwObject(), type(Throwable.class))).append(keySqlType.invoke("appendTo", void.class, block, position, blockBuilder)).append(writeTransformedValueElement)));
body.append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop());
body.append(pageBuilder.invoke("declarePosition", void.class));
body.append(constantType(binder, resultMapType).invoke("getObject", Object.class, mapBlockBuilder.cast(Block.class), subtract(mapBlockBuilder.invoke("getPositionCount", int.class), constantInt(1))).ret());
Class<?> generatedClass = defineClass(definition, Object.class, binder.getBindings(), MapTransformValueFunction.class.getClassLoader());
return methodHandle(generatedClass, "transform", Object.class, Block.class, BinaryFunctionInterface.class);
}
use of com.facebook.presto.bytecode.CallSiteBinder in project presto by prestodb.
the class MapFilterFunction method mapFilter.
@CodegenScalarFunction(value = "map_filter", deterministic = false)
@Description("return map containing entries that match the given predicate")
@TypeParameter("K")
@TypeParameter("V")
@SqlType("map(K,V)")
public static MethodHandle mapFilter(@SqlType("map(K,V)") Type inputType, @SqlType("function(K,V,boolean)") Type filter) {
MapType mapType = (MapType) inputType;
CallSiteBinder binder = new CallSiteBinder();
Type keyType = mapType.getKeyType();
Type valueType = mapType.getValueType();
Class<?> keyJavaType = Primitives.wrap(keyType.getJavaType());
Class<?> valueJavaType = Primitives.wrap(valueType.getJavaType());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("MapFilter"), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
Parameter block = arg("block", Block.class);
Parameter function = arg("function", BinaryFunctionInterface.class);
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "filter", type(Block.class), ImmutableList.of(block, function));
BytecodeBlock body = method.getBody();
Scope scope = method.getScope();
Variable positionCount = scope.declareVariable(int.class, "positionCount");
Variable position = scope.declareVariable(int.class, "position");
Variable mapBlockBuilder = scope.declareVariable(BlockBuilder.class, "mapBlockBuilder");
Variable singleMapBlockWriter = scope.declareVariable(BlockBuilder.class, "singleMapBlockWriter");
Variable keyElement = scope.declareVariable(keyJavaType, "keyElement");
Variable valueElement = scope.declareVariable(valueJavaType, "valueElement");
Variable keep = scope.declareVariable(Boolean.class, "keep");
// invoke block.getPositionCount()
body.append(positionCount.set(block.invoke("getPositionCount", int.class)));
// prepare the single map block builder
body.append(mapBlockBuilder.set(constantType(binder, mapType).invoke("createBlockBuilder", BlockBuilder.class, constantNull(BlockBuilderStatus.class), constantInt(0))));
body.append(singleMapBlockWriter.set(mapBlockBuilder.invoke("beginBlockEntry", BlockBuilder.class)));
SqlTypeBytecodeExpression keySqlType = constantType(binder, keyType);
BytecodeNode loadKeyElement;
if (!keyType.equals(UNKNOWN)) {
// key element must be non-null
loadKeyElement = new BytecodeBlock().append(keyElement.set(keySqlType.getValue(block, position).cast(keyJavaType)));
} else {
loadKeyElement = new BytecodeBlock().append(keyElement.set(constantNull(keyJavaType)));
}
SqlTypeBytecodeExpression valueSqlType = constantType(binder, valueType);
BytecodeNode loadValueElement;
if (!valueType.equals(UNKNOWN)) {
loadValueElement = new IfStatement().condition(block.invoke("isNull", boolean.class, add(position, constantInt(1)))).ifTrue(valueElement.set(constantNull(valueJavaType))).ifFalse(valueElement.set(valueSqlType.getValue(block, add(position, constantInt(1))).cast(valueJavaType)));
} else {
loadValueElement = new BytecodeBlock().append(valueElement.set(constantNull(valueJavaType)));
}
body.append(new ForLoop().initialize(position.set(constantInt(0))).condition(lessThan(position, positionCount)).update(incrementVariable(position, (byte) 2)).body(new BytecodeBlock().append(loadKeyElement).append(loadValueElement).append(keep.set(function.invoke("apply", Object.class, keyElement.cast(Object.class), valueElement.cast(Object.class)).cast(Boolean.class))).append(new IfStatement("if (keep != null && keep) ...").condition(and(notEqual(keep, constantNull(Boolean.class)), keep.cast(boolean.class))).ifTrue(new BytecodeBlock().append(keySqlType.invoke("appendTo", void.class, block, position, singleMapBlockWriter)).append(valueSqlType.invoke("appendTo", void.class, block, add(position, constantInt(1)), singleMapBlockWriter))))));
body.append(mapBlockBuilder.invoke("closeEntry", BlockBuilder.class).pop());
body.append(constantType(binder, mapType).invoke("getObject", Object.class, mapBlockBuilder.cast(Block.class), subtract(mapBlockBuilder.invoke("getPositionCount", int.class), constantInt(1))).ret());
Class<?> generatedClass = defineClass(definition, Object.class, binder.getBindings(), MapFilterFunction.class.getClassLoader());
return methodHandle(generatedClass, "filter", Block.class, BinaryFunctionInterface.class);
}
use of com.facebook.presto.bytecode.CallSiteBinder in project presto by prestodb.
the class ArrayToArrayCast method generateArrayCast.
private static Class<?> generateArrayCast(TypeManager typeManager, FunctionMetadata elementCastFunctionMetadata, JavaScalarFunctionImplementation elementCast) {
CallSiteBinder binder = new CallSiteBinder();
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName(Joiner.on("$").join("ArrayCast", elementCastFunctionMetadata.getArgumentTypes().get(0), elementCastFunctionMetadata.getReturnType())), type(Object.class));
Parameter properties = arg("properties", SqlFunctionProperties.class);
Parameter value = arg("value", Block.class);
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "castArray", type(Block.class), properties, value);
Scope scope = method.getScope();
BytecodeBlock body = method.getBody();
Variable wasNull = scope.declareVariable(boolean.class, "wasNull");
body.append(wasNull.set(constantBoolean(false)));
// cast map elements
Type fromElementType = typeManager.getType(elementCastFunctionMetadata.getArgumentTypes().get(0));
Type toElementType = typeManager.getType(elementCastFunctionMetadata.getReturnType());
CachedInstanceBinder cachedInstanceBinder = new CachedInstanceBinder(definition, binder);
ArrayMapBytecodeExpression newArray = ArrayGeneratorUtils.map(scope, cachedInstanceBinder, fromElementType, toElementType, value, elementCastFunctionMetadata.getName().getObjectName(), elementCast);
// return the block
body.append(newArray.ret());
MethodDefinition constructorDefinition = definition.declareConstructor(a(PUBLIC));
BytecodeBlock constructorBody = constructorDefinition.getBody();
Variable thisVariable = constructorDefinition.getThis();
constructorBody.comment("super();").append(thisVariable).invokeConstructor(Object.class);
cachedInstanceBinder.generateInitializations(thisVariable, constructorBody);
constructorBody.ret();
return defineClass(definition, Object.class, binder.getBindings(), ArrayToArrayCast.class.getClassLoader());
}
Aggregations