use of io.airlift.bytecode.BytecodeBlock in project hetu-core by openlookeng.
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");
checkCondition(argsLength <= 253, NOT_SUPPORTED, "Too many arguments for vararg function");
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 io.airlift.bytecode.BytecodeBlock in project hetu-core by openlookeng.
the class VarArgsToMapAdapterGenerator method generateVarArgsToMapAdapter.
/**
* Generate byte code that
* <p><ul>
* <li>takes a specified number of variables as arguments (types of the arguments are provided in {@code javaTypes})
* <li>put the variables in a map (keys of the map are provided in {@code names})
* <li>invoke the provided {@code function} with the map
* <li>return with the result of the function call (type must match {@code returnType})
* </ul></p>
*/
public static MethodHandle generateVarArgsToMapAdapter(Class<?> returnType, List<Class<?>> javaTypes, List<String> names, Function<Map<String, Object>, Object> function) {
checkCondition(javaTypes.size() <= 254, NOT_SUPPORTED, "Too many arguments for vararg function");
CallSiteBinder callSiteBinder = new CallSiteBinder();
ClassDefinition classDefinition = new ClassDefinition(a(PUBLIC, FINAL), makeClassName("VarArgsToMapAdapter"), type(Object.class));
ImmutableList.Builder<Parameter> parameterListBuilder = ImmutableList.builder();
for (int i = 0; i < javaTypes.size(); i++) {
Class<?> javaType = javaTypes.get(i);
parameterListBuilder.add(arg("input_" + i, javaType));
}
ImmutableList<Parameter> parameterList = parameterListBuilder.build();
MethodDefinition methodDefinition = classDefinition.declareMethod(a(PUBLIC, STATIC), "varArgsToMap", type(returnType), parameterList);
BytecodeBlock body = methodDefinition.getBody();
// ImmutableMap.Builder can not be used here because it doesn't allow nulls.
Variable map = methodDefinition.getScope().declareVariable("map", methodDefinition.getBody(), invokeStatic(Maps.class, "newHashMapWithExpectedSize", HashMap.class, constantInt(javaTypes.size())));
for (int i = 0; i < javaTypes.size(); i++) {
body.append(map.invoke("put", Object.class, constantString(names.get(i)).cast(Object.class), parameterList.get(i).cast(Object.class)));
}
body.append(loadConstant(callSiteBinder, function, Function.class).invoke("apply", Object.class, map.cast(Object.class)).cast(returnType).ret());
Class<?> generatedClass = defineClass(classDefinition, Object.class, callSiteBinder.getBindings(), new DynamicClassLoader(VarArgsToMapAdapterGenerator.class.getClassLoader()));
return Reflection.methodHandle(generatedClass, "varArgsToMap", javaTypes.toArray(new Class<?>[javaTypes.size()]));
}
use of io.airlift.bytecode.BytecodeBlock in project hetu-core by openlookeng.
the class BytecodeUtils method unboxPrimitiveIfNecessary.
public static BytecodeBlock unboxPrimitiveIfNecessary(Scope scope, Class<?> boxedType) {
BytecodeBlock block = new BytecodeBlock();
LabelNode end = new LabelNode("end");
Class<?> unboxedType = Primitives.unwrap(boxedType);
Variable wasNull = scope.getVariable("wasNull");
if (unboxedType.isPrimitive()) {
LabelNode notNull = new LabelNode("notNull");
block.dup(boxedType).ifNotNullGoto(notNull).append(wasNull.set(constantTrue())).comment("swap boxed null with unboxed default").pop(boxedType).pushJavaDefault(unboxedType).gotoLabel(end).visitLabel(notNull).append(unboxPrimitive(unboxedType));
} else {
block.dup(boxedType).ifNotNullGoto(end).append(wasNull.set(constantTrue()));
}
block.visitLabel(end);
return block;
}
use of io.airlift.bytecode.BytecodeBlock in project hetu-core by openlookeng.
the class BytecodeUtils method generateInvocation.
public static BytecodeNode generateInvocation(Scope scope, String name, BuiltInScalarFunctionImplementation function, Optional<BytecodeNode> instance, List<BytecodeNode> arguments, CallSiteBinder binder) {
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;
// Go through all the choices in the function and then pick the best one
List<ScalarImplementationChoice> choices = function.getAllChoices();
ScalarImplementationChoice bestChoice = null;
for (ScalarImplementationChoice currentChoice : choices) {
boolean isValid = true;
for (int i = 0; i < arguments.size(); i++) {
if (currentChoice.getArgumentProperty(i).getArgumentType() != VALUE_TYPE) {
continue;
}
if (!(arguments.get(i) instanceof InputReferenceNode) && currentChoice.getArgumentProperty(i).getNullConvention() == NullConvention.BLOCK_AND_POSITION) {
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 == ConnectorSession.class) {
block.append(scope.getVariable("session"));
} 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");
block.append(ifWasNullPopAndGoto(scope, end, unboxedReturnType, Lists.reverse(stackTypes)));
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 (function.isNullable()) {
block.append(unboxPrimitiveIfNecessary(scope, returnType));
}
block.visitLabel(end);
return block;
}
use of io.airlift.bytecode.BytecodeBlock in project hetu-core by openlookeng.
the class BytecodeUtils method boxPrimitiveIfNecessary.
public static BytecodeNode boxPrimitiveIfNecessary(Scope scope, Class<?> type) {
checkArgument(!type.isPrimitive(), "cannot box into primitive type");
if (!Primitives.isWrapperType(type)) {
return NOP;
}
BytecodeBlock notNull = new BytecodeBlock().comment("box primitive");
Class<?> expectedCurrentStackType;
if (type == Long.class) {
notNull.invokeStatic(Long.class, "valueOf", Long.class, long.class);
expectedCurrentStackType = long.class;
} else if (type == Double.class) {
notNull.invokeStatic(Double.class, "valueOf", Double.class, double.class);
expectedCurrentStackType = double.class;
} else if (type == Boolean.class) {
notNull.invokeStatic(Boolean.class, "valueOf", Boolean.class, boolean.class);
expectedCurrentStackType = boolean.class;
} else {
throw new UnsupportedOperationException("not yet implemented: " + type);
}
BytecodeBlock condition = new BytecodeBlock().append(scope.getVariable("wasNull"));
BytecodeBlock wasNull = new BytecodeBlock().pop(expectedCurrentStackType).pushNull().checkCast(type);
return new IfStatement().condition(condition).ifTrue(wasNull).ifFalse(notNull);
}
Aggregations