use of com.facebook.presto.bytecode.ClassDefinition in project presto by prestodb.
the class ScalarMethodHandles method generateUnbound.
/**
* Generates an unbound MethodHandle by {@code signature}.
* <p>
* For example, if {@code signature} describes a method like
* <pre>
* {@code
* IntegerType func(
* IntegerType i,
* VarcharType j)
* }
* </pre>,
* a method is generated as
* <pre>
* {@code
* static Long callEvaluate(
* ScalarFunctionInvoker invoker,
* Long input_0,
* Slice input_1) {
* return (Long) Invoker.evaluate(input_0, input_1);
* }
* }
* </pre>,
* and its MethodHandle returns.
*/
public static MethodHandle generateUnbound(Signature signature, TypeManager typeManager) {
Class<?> returnType = Primitives.wrap(typeManager.getType(signature.getReturnType()).getJavaType());
List<TypeSignature> argumentTypes = signature.getArgumentTypes();
List<Class<?>> argumentJavaTypes = argumentTypes.stream().map(t -> typeManager.getType(t).getJavaType()).map(Primitives::wrap).collect(toImmutableList());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName(CLASS_NAME), ParameterizedType.type(Object.class));
// Step 1: Declare default constructor
definition.declareDefaultConstructor(a(PRIVATE));
// Step 2: Declare method
Parameter[] declareParameters = new Parameter[argumentTypes.size() + 1];
declareParameters[0] = arg("invoker", ScalarFunctionInvoker.class);
for (int i = 0; i < argumentTypes.size(); i++) {
declareParameters[i + 1] = arg("input_" + i, argumentJavaTypes.get(i));
}
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), METHOD_NAME, ParameterizedType.type(returnType), declareParameters);
// Step 3: Implement method body
BytecodeExpression[] evaluateInputs = new BytecodeExpression[argumentTypes.size()];
for (int i = 0; i < argumentTypes.size(); i++) {
evaluateInputs[i] = declareParameters[i + 1];
}
method.getBody().append(declareParameters[0].invoke("evaluate", Object.class, newArray(type(Object[].class), evaluateInputs)).cast(returnType).ret());
// Step 4: Generate class
Class<?> generatedClass = defineClass(definition, Object.class, Collections.emptyMap(), ScalarMethodHandles.class.getClassLoader());
// Step 5: Lookup MethodHandle
Class<?>[] lookupClasses = new Class[argumentTypes.size() + 1];
lookupClasses[0] = ScalarFunctionInvoker.class;
for (int i = 0; i < argumentTypes.size(); i++) {
lookupClasses[i + 1] = Primitives.wrap(typeManager.getType(argumentTypes.get(i)).getJavaType());
}
return methodHandle(generatedClass, METHOD_NAME, lookupClasses);
}
use of com.facebook.presto.bytecode.ClassDefinition in project presto by prestodb.
the class AbstractGreatestLeast method generate.
private Class<?> generate(List<Class<?>> javaTypes, Type type, MethodHandle compareMethod) {
checkCondition(javaTypes.size() <= 127, NOT_SUPPORTED, "Too many arguments for function call %s()", getSignature().getNameSuffix());
String javaTypeName = javaTypes.stream().map(Class::getSimpleName).collect(joining());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName(javaTypeName + "$" + getSignature().getNameSuffix()), type(Object.class));
definition.declareDefaultConstructor(a(PRIVATE));
List<Parameter> parameters = IntStream.range(0, javaTypes.size()).mapToObj(i -> arg("arg" + i, javaTypes.get(i))).collect(toImmutableList());
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), getSignature().getNameSuffix(), type(javaTypes.get(0)), parameters);
Scope scope = method.getScope();
BytecodeBlock body = method.getBody();
CallSiteBinder binder = new CallSiteBinder();
if (type.getTypeSignature().getBase().equals(StandardTypes.DOUBLE)) {
for (Parameter parameter : parameters) {
body.append(parameter);
body.append(invoke(binder.bind(CHECK_NOT_NAN.bindTo(getSignature().getNameSuffix())), "checkNotNaN"));
}
}
Variable value = scope.declareVariable(javaTypes.get(0), "value");
body.append(value.set(parameters.get(0)));
for (int i = 1; i < javaTypes.size(); i++) {
body.append(new IfStatement().condition(new BytecodeBlock().append(parameters.get(i)).append(value).append(invoke(binder.bind(compareMethod), "compare"))).ifTrue(value.set(parameters.get(i))));
}
body.append(value.ret());
return defineClass(definition, Object.class, binder.getBindings(), new DynamicClassLoader(getClass().getClassLoader()));
}
use of com.facebook.presto.bytecode.ClassDefinition in project presto by prestodb.
the class ArrayConstructor method generateArrayConstructor.
private static Class<?> generateArrayConstructor(List<Class<?>> stackTypes, Type elementType) {
checkCondition(stackTypes.size() <= 254, NOT_SUPPORTED, "Too many arguments for array constructor");
List<String> stackTypeNames = stackTypes.stream().map(Class::getSimpleName).collect(toImmutableList());
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName(Joiner.on("").join(stackTypeNames) + "ArrayConstructor"), type(Object.class));
// Generate constructor
definition.declareDefaultConstructor(a(PRIVATE));
// Generate arrayConstructor()
ImmutableList.Builder<Parameter> parameters = ImmutableList.builder();
for (int i = 0; i < stackTypes.size(); i++) {
Class<?> stackType = stackTypes.get(i);
parameters.add(arg("arg" + i, stackType));
}
MethodDefinition method = definition.declareMethod(a(PUBLIC, STATIC), "arrayConstructor", type(Block.class), parameters.build());
Scope scope = method.getScope();
BytecodeBlock body = method.getBody();
Variable blockBuilderVariable = scope.declareVariable(BlockBuilder.class, "blockBuilder");
CallSiteBinder binder = new CallSiteBinder();
BytecodeExpression createBlockBuilder = blockBuilderVariable.set(constantType(binder, elementType).invoke("createBlockBuilder", BlockBuilder.class, constantNull(BlockBuilderStatus.class), constantInt(stackTypes.size())));
body.append(createBlockBuilder);
for (int i = 0; i < stackTypes.size(); i++) {
Variable argument = scope.getVariable("arg" + i);
IfStatement ifStatement = new IfStatement().condition(equal(argument, constantNull(stackTypes.get(i)))).ifTrue(blockBuilderVariable.invoke("appendNull", BlockBuilder.class).pop()).ifFalse(constantType(binder, elementType).writeValue(blockBuilderVariable, argument.cast(elementType.getJavaType())));
body.append(ifStatement);
}
body.append(blockBuilderVariable.invoke("build", Block.class).ret());
return defineClass(definition, Object.class, binder.getBindings(), new DynamicClassLoader(ArrayConstructor.class.getClassLoader()));
}
use of com.facebook.presto.bytecode.ClassDefinition in project presto by prestodb.
the class StateCompiler method generateGroupedStateClass.
private static <T> Class<? extends T> generateGroupedStateClass(Class<T> clazz, Map<String, Type> fieldTypes, DynamicClassLoader classLoader) {
ClassDefinition definition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("Grouped" + clazz.getSimpleName()), type(AbstractGroupedAccumulatorState.class), type(clazz), type(GroupedAccumulator.class));
FieldDefinition instanceSize = generateInstanceSize(definition);
List<StateField> fields = enumerateFields(clazz, fieldTypes);
// Create constructor
MethodDefinition constructor = definition.declareConstructor(a(PUBLIC));
constructor.getBody().append(constructor.getThis()).invokeConstructor(AbstractGroupedAccumulatorState.class);
// Create ensureCapacity
MethodDefinition ensureCapacity = definition.declareMethod(a(PUBLIC), "ensureCapacity", type(void.class), arg("size", long.class));
// Generate fields, constructor, and ensureCapacity
List<FieldDefinition> fieldDefinitions = new ArrayList<>();
for (StateField field : fields) {
fieldDefinitions.add(generateGroupedField(definition, constructor, ensureCapacity, field));
}
constructor.getBody().ret();
ensureCapacity.getBody().ret();
// Generate getEstimatedSize
MethodDefinition getEstimatedSize = definition.declareMethod(a(PUBLIC), "getEstimatedSize", type(long.class));
BytecodeBlock body = getEstimatedSize.getBody();
Variable size = getEstimatedSize.getScope().declareVariable(long.class, "size");
// initialize size to the size of the instance
body.append(size.set(getStatic(instanceSize)));
// add field to size
for (FieldDefinition field : fieldDefinitions) {
body.append(size.set(add(size, getEstimatedSize.getThis().getField(field).invoke("sizeOf", long.class))));
}
// return size
body.append(size.ret());
return defineClass(definition, clazz, classLoader);
}
use of com.facebook.presto.bytecode.ClassDefinition in project presto by prestodb.
the class MapFilterFunction method generateFilter.
private static MethodHandle generateFilter(Type keyType, Type valueType) {
CallSiteBinder binder = new CallSiteBinder();
MapType mapType = new MapType(keyType, valueType);
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", MethodHandle.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 blockBuilder = scope.declareVariable(BlockBuilder.class, "blockBuilder");
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)));
// create the interleaved block builder
body.append(blockBuilder.set(newInstance(InterleavedBlockBuilder.class, constantType(binder, mapType).invoke("getTypeParameters", List.class), newInstance(BlockBuilderStatus.class), positionCount)));
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("invokeExact", Boolean.class, keyElement, valueElement))).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, blockBuilder)).append(valueSqlType.invoke("appendTo", void.class, block, add(position, constantInt(1)), blockBuilder))))));
body.append(blockBuilder.invoke("build", Block.class).ret());
Class<?> generatedClass = defineClass(definition, Object.class, binder.getBindings(), MapFilterFunction.class.getClassLoader());
return methodHandle(generatedClass, "filter", Block.class, MethodHandle.class);
}
Aggregations