private static void generateField( ClassDefinition definition, Block constructor, StateField stateField) { FieldDefinition field = definition.declareField( a(PRIVATE), UPPER_CAMEL.to(LOWER_CAMEL, stateField.getName()) + "Value", stateField.getType()); // Generate getter definition .declareMethod(a(PUBLIC), stateField.getGetterName(), type(stateField.getType())) .getBody() .pushThis() .getField(field) .ret(stateField.getType()); // Generate setter definition .declareMethod( a(PUBLIC), stateField.getSetterName(), type(void.class), arg("value", stateField.getType())) .getBody() .pushThis() .getVariable("value") .putField(field) .ret(); constructor.pushThis(); pushInitialValue(constructor, stateField); constructor.putField(field); }
private static Map<String, Class<?>> defineClasses( List<ClassDefinition> classDefinitions, DynamicClassLoader classLoader) { ClassInfoLoader classInfoLoader = ClassInfoLoader.createClassInfoLoader(classDefinitions, classLoader); if (DUMP_BYTE_CODE_TREE) { ByteArrayOutputStream out = new ByteArrayOutputStream(); DumpByteCodeVisitor dumpByteCode = new DumpByteCodeVisitor(new PrintStream(out)); for (ClassDefinition classDefinition : classDefinitions) { dumpByteCode.visitClass(classDefinition); } System.out.println(new String(out.toByteArray(), StandardCharsets.UTF_8)); } Map<String, byte[]> byteCodes = new LinkedHashMap<>(); for (ClassDefinition classDefinition : classDefinitions) { ClassWriter cw = new SmartClassWriter(classInfoLoader); classDefinition.visit(cw); byte[] byteCode = cw.toByteArray(); if (RUN_ASM_VERIFIER) { ClassReader reader = new ClassReader(byteCode); CheckClassAdapter.verify(reader, classLoader, true, new PrintWriter(System.out)); } byteCodes.put(classDefinition.getType().getJavaClassName(), byteCode); } String dumpClassPath = DUMP_CLASS_FILES_TO.get(); if (dumpClassPath != null) { for (Map.Entry<String, byte[]> entry : byteCodes.entrySet()) { File file = new File( dumpClassPath, ParameterizedType.typeFromJavaClassName(entry.getKey()).getClassName() + ".class"); try { log.debug("ClassFile: " + file.getAbsolutePath()); Files.createParentDirs(file); Files.write(entry.getValue(), file); } catch (IOException e) { log.error(e, "Failed to write generated class file to: %s" + file.getAbsolutePath()); } } } if (DUMP_BYTE_CODE_RAW) { for (byte[] byteCode : byteCodes.values()) { ClassReader classReader = new ClassReader(byteCode); classReader.accept( new TraceClassVisitor(new PrintWriter(System.err)), ClassReader.SKIP_FRAMES); } } Map<String, Class<?>> classes = classLoader.defineClasses(byteCodes); try { for (Class<?> clazz : classes.values()) { Reflection.initialize(clazz); } } catch (VerifyError e) { throw new RuntimeException(e); } return classes; }
private void generateFilterMethod( CallSiteBinder callSiteBinder, ClassDefinition classDefinition, RowExpression filter, boolean sourceIsCursor) { MethodDefinition filterMethod; if (sourceIsCursor) { filterMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), "filter", type(boolean.class), arg("cursor", RecordCursor.class)); } else { filterMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), "filter", type(boolean.class), ImmutableList.<NamedParameterDefinition>builder() .add(arg("position", int.class)) .addAll(toBlockParameters(getInputChannels(filter))) .build()); } filterMethod.comment("Filter: %s", filter.toString()); filterMethod.getCompilerContext().declareVariable(type(boolean.class), "wasNull"); Block getSessionByteCode = new Block(filterMethod.getCompilerContext()) .pushThis() .getField(classDefinition.getType(), "session", type(ConnectorSession.class)); ByteCodeNode body = compileExpression( callSiteBinder, filter, sourceIsCursor, filterMethod.getCompilerContext(), getSessionByteCode); LabelNode end = new LabelNode("end"); filterMethod .getBody() .comment("boolean wasNull = false;") .putVariable("wasNull", false) .append(body) .getVariable("wasNull") .ifFalseGoto(end) .pop(boolean.class) .push(false) .visitLabel(end) .retBoolean(); }
private static <T> Class<? extends T> generateGroupedStateClass( Class<T> clazz, DynamicClassLoader classLoader) { ClassDefinition definition = new ClassDefinition( a(PUBLIC, FINAL), makeClassName("Grouped" + clazz.getSimpleName()), type(AbstractGroupedAccumulatorState.class), type(clazz), type(GroupedAccumulator.class)); List<StateField> fields = enumerateFields(clazz); // Create constructor Block constructor = definition .declareConstructor(a(PUBLIC)) .getBody() .pushThis() .invokeConstructor(AbstractGroupedAccumulatorState.class); // Create ensureCapacity Block ensureCapacity = definition .declareMethod(a(PUBLIC), "ensureCapacity", type(void.class), arg("size", long.class)) .getBody(); // Generate fields, constructor, and ensureCapacity List<FieldDefinition> fieldDefinitions = new ArrayList<>(); for (StateField field : fields) { fieldDefinitions.add(generateGroupedField(definition, constructor, ensureCapacity, field)); } constructor.ret(); ensureCapacity.ret(); // Generate getEstimatedSize Block getEstimatedSize = definition .declareMethod(a(PUBLIC), "getEstimatedSize", type(long.class)) .getBody() .comment("long size = 0;") .push(0L); for (FieldDefinition field : fieldDefinitions) { getEstimatedSize .comment("size += %s.sizeOf();", field.getName()) .pushThis() .getField(field) .invokeVirtual(field.getType(), "sizeOf", type(long.class)) .longAdd(); } getEstimatedSize.comment("return size;"); getEstimatedSize.retLong(); return defineClass(definition, clazz, classLoader); }
private static FieldDefinition generateGroupedField( ClassDefinition definition, Block constructor, Block ensureCapacity, StateField stateField) { Class<?> bigArrayType = getBigArrayType(stateField.getType()); FieldDefinition field = definition.declareField( a(PRIVATE), UPPER_CAMEL.to(LOWER_CAMEL, stateField.getName()) + "Values", bigArrayType); // Generate getter definition .declareMethod(a(PUBLIC), stateField.getGetterName(), type(stateField.getType())) .getBody() .comment("return field.get(getGroupId());") .pushThis() .getField(field) .pushThis() .invokeVirtual(AbstractGroupedAccumulatorState.class, "getGroupId", long.class) .invokeVirtual(bigArrayType, "get", stateField.getType(), long.class) .ret(stateField.getType()); // Generate setter definition .declareMethod( a(PUBLIC), stateField.getSetterName(), type(void.class), arg("value", stateField.getType())) .getBody() .comment("return field.set(getGroupId(), value);") .pushThis() .getField(field) .pushThis() .invokeVirtual(AbstractGroupedAccumulatorState.class, "getGroupId", long.class) .getVariable("value") .invokeVirtual(bigArrayType, "set", void.class, long.class, stateField.getType()) .ret(); ensureCapacity .pushThis() .getField(field) .getVariable("size") .invokeVirtual(field.getType(), "ensureCapacity", type(void.class), type(long.class)); // Initialize field in constructor constructor.pushThis().newObject(field.getType()).dup(); pushInitialValue(constructor, stateField); constructor.invokeConstructor(field.getType(), type(stateField.getType())); constructor.putField(field); return field; }
private static void generateGetSerializedType( ClassDefinition definition, List<StateField> fields, CallSiteBinder callSiteBinder) { CompilerContext compilerContext = new CompilerContext(); Block body = definition .declareMethod(compilerContext, a(PUBLIC), "getSerializedType", type(Type.class)) .getBody(); Type type; if (fields.size() > 1) { type = VARCHAR; } else { Class<?> stackType = fields.get(0).getType(); if (stackType == long.class) { type = BIGINT; } else if (stackType == double.class) { type = DOUBLE; } else if (stackType == boolean.class) { type = BOOLEAN; } else if (stackType == byte.class) { type = BIGINT; } else if (stackType == Slice.class) { type = VARCHAR; } else { throw new IllegalArgumentException("Unsupported type: " + stackType); } } body.comment("return %s", type.getName()) .append(constantType(new CompilerContext(BOOTSTRAP_METHOD), callSiteBinder, type)) .retObject(); }
private static void generateProcessColumnarMethod( ClassDefinition classDefinition, List<RowExpression> projections, List<MethodDefinition> projectColumnarMethods) { Parameter session = arg("session", ConnectorSession.class); Parameter page = arg("page", Page.class); Parameter types = arg("types", List.class); MethodDefinition method = classDefinition.declareMethod( a(PUBLIC), "processColumnar", type(Page.class), session, page, types); Scope scope = method.getScope(); BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); Variable selectedPositions = scope.declareVariable( "selectedPositions", body, thisVariable.invoke("filterPage", int[].class, session, page)); Variable cardinality = scope.declareVariable("cardinality", body, selectedPositions.length()); body.comment("if no rows selected return null") .append( new IfStatement() .condition(equal(cardinality, constantInt(0))) .ifTrue(constantNull(Page.class).ret())); if (projections.isEmpty()) { // if no projections, return new page with selected rows body.append(newInstance(Page.class, cardinality, newArray(type(Block[].class), 0)).ret()); return; } Variable pageBuilder = scope.declareVariable( "pageBuilder", body, newInstance(PageBuilder.class, cardinality, types)); Variable outputBlocks = scope.declareVariable( "outputBlocks", body, newArray(type(Block[].class), projections.size())); for (int projectionIndex = 0; projectionIndex < projections.size(); projectionIndex++) { List<BytecodeExpression> params = ImmutableList.<BytecodeExpression>builder() .add(session) .add(page) .add(selectedPositions) .add(pageBuilder) .add(constantInt(projectionIndex)) .build(); body.append( outputBlocks.setElement( projectionIndex, thisVariable.invoke(projectColumnarMethods.get(projectionIndex), params))); } // create new page from outputBlocks body.append(newInstance(Page.class, cardinality, outputBlocks).ret()); }
private static void generateGetNonLazyPageMethod( ClassDefinition classDefinition, RowExpression filter, List<RowExpression> projections) { Parameter page = arg("page", Page.class); MethodDefinition method = classDefinition.declareMethod(a(PRIVATE), "getNonLazyPage", type(Page.class), page); Scope scope = method.getScope(); BytecodeBlock body = method.getBody(); List<Integer> allInputChannels = getInputChannels(concat(projections, ImmutableList.of(filter))); if (allInputChannels.isEmpty()) { body.append(page.ret()); return; } Variable index = scope.declareVariable(int.class, "index"); Variable channelCount = scope.declareVariable("channelCount", body, page.invoke("getChannelCount", int.class)); Variable blocks = scope.declareVariable("blocks", body, newArray(type(Block[].class), channelCount)); Variable inputBlock = scope.declareVariable(Block.class, "inputBlock"); Variable positionCount = scope.declareVariable("positionCount", body, page.invoke("getPositionCount", int.class)); Variable createNewPage = scope.declareVariable("createNewPage", body, constantFalse()); ForLoop forLoop = new ForLoop() .initialize(index.set(constantInt(0))) .condition(lessThan(index, channelCount)) .update(index.increment()) .body( new BytecodeBlock() .append(inputBlock.set(page.invoke("getBlock", Block.class, index))) .append( new IfStatement() .condition(inputBlock.instanceOf(LazyBlock.class)) .ifTrue( new BytecodeBlock() .append( blocks.setElement( index, inputBlock .cast(LazyBlock.class) .invoke("getBlock", Block.class))) .append(createNewPage.set(constantTrue()))) .ifFalse(blocks.setElement(index, inputBlock)))); body.append(forLoop); body.append( new IfStatement() .condition(createNewPage) .ifTrue(page.set(newInstance(Page.class, positionCount, blocks)))); body.append(page.ret()); }
public <T> AccumulatorStateSerializer<T> generateStateSerializer( Class<T> clazz, DynamicClassLoader classLoader) { AccumulatorStateMetadata metadata = getMetadataAnnotation(clazz); if (metadata != null && metadata.stateSerializerClass() != void.class) { try { return (AccumulatorStateSerializer<T>) metadata.stateSerializerClass().getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw Throwables.propagate(e); } } ClassDefinition definition = new ClassDefinition( a(PUBLIC, FINAL), makeClassName(clazz.getSimpleName() + "Serializer"), type(Object.class), type(AccumulatorStateSerializer.class)); CallSiteBinder callSiteBinder = new CallSiteBinder(); // Generate constructor definition.declareDefaultConstructor(a(PUBLIC)); List<StateField> fields = enumerateFields(clazz); generateGetSerializedType(definition, fields, callSiteBinder); generateSerialize(definition, clazz, fields); generateDeserialize(definition, clazz, fields); Class<? extends AccumulatorStateSerializer> serializerClass = defineClass( definition, AccumulatorStateSerializer.class, callSiteBinder.getBindings(), classLoader); try { return (AccumulatorStateSerializer<T>) serializerClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw Throwables.propagate(e); } }
private void generateToString(ClassDefinition classDefinition, String string) { // Constant strings can't be too large or the bytecode becomes invalid if (string.length() > 100) { string = string.substring(0, 100) + "..."; } classDefinition .declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), "toString", type(String.class)) .getBody() .push(string) .retObject(); }
private static void generateConstructor( ClassDefinition classDefinition, CachedInstanceBinder cachedInstanceBinder, int projectionCount) { MethodDefinition constructorDefinition = classDefinition.declareConstructor(a(PUBLIC)); FieldDefinition inputDictionaries = classDefinition.declareField(a(PRIVATE, FINAL), "inputDictionaries", Block[].class); FieldDefinition outputDictionaries = classDefinition.declareField(a(PRIVATE, FINAL), "outputDictionaries", Block[].class); BytecodeBlock body = constructorDefinition.getBody(); Variable thisVariable = constructorDefinition.getThis(); body.comment("super();").append(thisVariable).invokeConstructor(Object.class); body.append( thisVariable.setField(inputDictionaries, newArray(type(Block[].class), projectionCount))); body.append( thisVariable.setField(outputDictionaries, newArray(type(Block[].class), projectionCount))); cachedInstanceBinder.generateInitializations(thisVariable, body); body.ret(); }
private static void generateFilterPageMethod( ClassDefinition classDefinition, RowExpression filter) { Parameter session = arg("session", ConnectorSession.class); Parameter page = arg("page", Page.class); MethodDefinition method = classDefinition.declareMethod(a(PUBLIC), "filterPage", type(int[].class), session, page); method.comment("Filter: %s rows in the page", filter.toString()); Scope scope = method.getScope(); Variable thisVariable = method.getThis(); BytecodeBlock body = method.getBody(); Variable positionCount = scope.declareVariable("positionCount", body, page.invoke("getPositionCount", int.class)); Variable selectedPositions = scope.declareVariable( "selectedPositions", body, newArray(type(int[].class), positionCount)); List<Integer> filterChannels = getInputChannels(filter); // extract block variables ImmutableList.Builder<Variable> blockVariablesBuilder = ImmutableList.<Variable>builder(); for (int channel : filterChannels) { Variable blockVariable = scope.declareVariable( "block_" + channel, body, page.invoke("getBlock", Block.class, constantInt(channel))); blockVariablesBuilder.add(blockVariable); } List<Variable> blockVariables = blockVariablesBuilder.build(); Variable selectedCount = scope.declareVariable("selectedCount", body, constantInt(0)); Variable position = scope.declareVariable(int.class, "position"); IfStatement ifStatement = new IfStatement(); ifStatement .condition(invokeFilter(thisVariable, session, blockVariables, position)) .ifTrue() .append(selectedPositions.setElement(selectedCount, position)) .append(selectedCount.increment()); body.append( new ForLoop() .initialize(position.set(constantInt(0))) .condition(lessThan(position, positionCount)) .update(position.increment()) .body(ifStatement)); body.append( invokeStatic(Arrays.class, "copyOf", int[].class, selectedPositions, selectedCount).ret()); }
private static <T> void generateSerialize( ClassDefinition definition, Class<T> clazz, List<StateField> fields) { CompilerContext compilerContext = new CompilerContext(); Block serializerBody = definition .declareMethod( compilerContext, a(PUBLIC), "serialize", type(void.class), arg("state", Object.class), arg("out", BlockBuilder.class)) .getBody(); if (fields.size() == 1) { generatePrimitiveSerializer(serializerBody, getGetter(clazz, fields.get(0))); } else { Variable slice = compilerContext.declareVariable(Slice.class, "slice"); int size = serializedSizeOf(clazz); serializerBody .comment("Slice slice = Slices.allocate(%d);", size) .push(size) .invokeStatic(Slices.class, "allocate", Slice.class, int.class) .putVariable(slice); for (StateField field : fields) { generateSerializeFieldToSlice( serializerBody, slice, getGetter(clazz, field), offsetOfField(field, fields)); } serializerBody .comment("out.appendSlice(slice);") .getVariable("out") .getVariable(slice) .push(0) .push(size) .invokeInterface( BlockBuilder.class, "writeBytes", BlockBuilder.class, Slice.class, int.class, int.class) .invokeInterface(BlockBuilder.class, "closeEntry", BlockBuilder.class) .pop(); } serializerBody.ret(); }
private static <T> Class<? extends T> generateSingleStateClass( Class<T> clazz, DynamicClassLoader classLoader) { ClassDefinition definition = new ClassDefinition( a(PUBLIC, FINAL), makeClassName("Single" + clazz.getSimpleName()), type(Object.class), type(clazz)); // Store class size in static field FieldDefinition classSize = definition.declareField(a(PRIVATE, STATIC, FINAL), "CLASS_SIZE", long.class); definition .getClassInitializer() .getBody() .comment( "CLASS_SIZE = ClassLayout.parseClass(%s.class).instanceSize()", definition.getName()) .push(definition.getType()) .invokeStatic(ClassLayout.class, "parseClass", ClassLayout.class, Class.class) .invokeVirtual(ClassLayout.class, "instanceSize", int.class) .intToLong() .putStaticField(classSize); // Add getter for class size definition .declareMethod(new CompilerContext(null), a(PUBLIC), "getEstimatedSize", type(long.class)) .getBody() .getStaticField(classSize) .retLong(); // Generate constructor Block constructor = definition .declareConstructor(a(PUBLIC)) .getBody() .pushThis() .invokeConstructor(Object.class); // Generate fields List<StateField> fields = enumerateFields(clazz); for (StateField field : fields) { generateField(definition, constructor, field); } constructor.ret(); return defineClass(definition, clazz, classLoader); }
private static <T> void generateDeserialize( ClassDefinition definition, Class<T> clazz, List<StateField> fields) { CompilerContext compilerContext = new CompilerContext(); Block deserializerBody = definition .declareMethod( compilerContext, a(PUBLIC), "deserialize", type(void.class), arg("block", com.facebook.presto.spi.block.Block.class), arg("index", int.class), arg("state", Object.class)) .getBody(); if (fields.size() == 1) { generatePrimitiveDeserializer(deserializerBody, getSetter(clazz, fields.get(0))); } else { Variable slice = compilerContext.declareVariable(Slice.class, "slice"); deserializerBody .comment("Slice slice = block.getSlice(index, 0, block.getLength(index));") .getVariable("block") .getVariable("index") .push(0) .getVariable("block") .getVariable("index") .invokeInterface( com.facebook.presto.spi.block.Block.class, "getLength", int.class, int.class) .invokeInterface( com.facebook.presto.spi.block.Block.class, "getSlice", Slice.class, int.class, int.class, int.class) .putVariable(slice); for (StateField field : fields) { generateDeserializeFromSlice( deserializerBody, slice, getSetter(clazz, field), offsetOfField(field, fields)); } } deserializerBody.ret(); }
private MethodDefinition generateProjectMethod( ClassDefinition classDefinition, CallSiteBinder callSiteBinder, CachedInstanceBinder cachedInstanceBinder, String methodName, RowExpression projection) { Parameter session = arg("session", ConnectorSession.class); List<Parameter> inputs = toBlockParameters(getInputChannels(projection)); Parameter position = arg("position", int.class); Parameter output = arg("output", BlockBuilder.class); MethodDefinition method = classDefinition.declareMethod( a(PUBLIC), methodName, type(void.class), ImmutableList.<Parameter>builder() .add(session) .addAll(inputs) .add(position) .add(output) .build()); method.comment("Projection: %s", projection.toString()); Scope scope = method.getScope(); BytecodeBlock body = method.getBody(); Variable wasNullVariable = scope.declareVariable("wasNull", body, constantFalse()); BytecodeExpressionVisitor visitor = new BytecodeExpressionVisitor( callSiteBinder, cachedInstanceBinder, fieldReferenceCompiler(callSiteBinder, position, wasNullVariable), metadata.getFunctionRegistry()); body.getVariable(output) .comment("evaluate projection: " + projection.toString()) .append(projection.accept(visitor, scope)) .append(generateWrite(callSiteBinder, scope, wasNullVariable, projection.getType())) .ret(); return method; }
private void generateFilterMethod( ClassDefinition classDefinition, CallSiteBinder callSiteBinder, CachedInstanceBinder cachedInstanceBinder, RowExpression filter) { Parameter session = arg("session", ConnectorSession.class); Parameter position = arg("position", int.class); List<Parameter> blocks = toBlockParameters(getInputChannels(filter)); MethodDefinition method = classDefinition.declareMethod( a(PUBLIC), "filter", type(boolean.class), ImmutableList.<Parameter>builder().add(session).addAll(blocks).add(position).build()); method.comment("Filter: %s", filter.toString()); BytecodeBlock body = method.getBody(); Scope scope = method.getScope(); Variable wasNullVariable = scope.declareVariable("wasNull", body, constantFalse()); BytecodeExpressionVisitor visitor = new BytecodeExpressionVisitor( callSiteBinder, cachedInstanceBinder, fieldReferenceCompiler(callSiteBinder, position, wasNullVariable), metadata.getFunctionRegistry()); BytecodeNode visitorBody = filter.accept(visitor, scope); Variable result = scope.declareVariable(boolean.class, "result"); body.append(visitorBody) .putVariable(result) .append( new IfStatement() .condition(wasNullVariable) .ifTrue(constantFalse().ret()) .ifFalse(result.ret())); }
private static Class<?> generateArrayConstructor(List<Class<?>> stackTypes, Type elementType) { List<String> stackTypeNames = stackTypes.stream().map(Class::getSimpleName).collect(toImmutableList()); ClassDefinition definition = new ClassDefinition( a(PUBLIC, FINAL), CompilerUtils.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(Slice.class), parameters.build()); Scope scope = method.getScope(); Block body = method.getBody(); Variable elementTypeVariable = scope.declareVariable(Type.class, "elementTypeVariable"); CallSiteBinder binder = new CallSiteBinder(); body.comment("elementTypeVariable = elementType;") .append(constantType(binder, elementType)) .putVariable(elementTypeVariable); Variable valuesVariable = scope.declareVariable(List.class, "values"); body.comment("List<Object> values = new ArrayList();") .newObject(ArrayList.class) .dup() .invokeConstructor(ArrayList.class) .putVariable(valuesVariable); for (int i = 0; i < stackTypes.size(); i++) { body.comment("values.add(arg%d);", i) .getVariable(valuesVariable) .append(scope.getVariable("arg" + i)); Class<?> stackType = stackTypes.get(i); if (stackType.isPrimitive()) { body.append(ByteCodeUtils.boxPrimitiveIfNecessary(scope, stackType)); } body.invokeInterface(List.class, "add", boolean.class, Object.class); } body.comment("return toStackRepresentation(values, elementType);") .getVariable(valuesVariable) .getVariable(elementTypeVariable) .invokeStatic(ArrayType.class, "toStackRepresentation", Slice.class, List.class, Type.class) .retObject(); return defineClass( definition, Object.class, binder.getBindings(), new DynamicClassLoader(ArrayConstructor.class.getClassLoader())); }
private static MethodDefinition generateProjectDictionaryMethod( ClassDefinition classDefinition, String methodName, RowExpression projection, MethodDefinition project, MethodDefinition projectColumnar) { Parameter session = arg("session", ConnectorSession.class); Parameter page = arg("page", Page.class); Parameter selectedPositions = arg("selectedPositions", int[].class); Parameter pageBuilder = arg("pageBuilder", PageBuilder.class); Parameter projectionIndex = arg("projectionIndex", int.class); List<Parameter> params = ImmutableList.<Parameter>builder() .add(session) .add(page) .add(selectedPositions) .add(pageBuilder) .add(projectionIndex) .build(); MethodDefinition method = classDefinition.declareMethod(a(PRIVATE), methodName, type(Block.class), params); BytecodeBlock body = method.getBody(); Scope scope = method.getScope(); Variable thisVariable = method.getThis(); List<Integer> inputChannels = getInputChannels(projection); if (inputChannels.size() != 1) { body.append(thisVariable.invoke(projectColumnar, params).ret()); return method; } Variable inputBlock = scope.declareVariable( "inputBlock", body, page.invoke( "getBlock", Block.class, constantInt(Iterables.getOnlyElement(inputChannels)))); IfStatement ifStatement = new IfStatement() .condition(inputBlock.instanceOf(DictionaryBlock.class)) .ifFalse(thisVariable.invoke(projectColumnar, params).ret()); body.append(ifStatement); Variable blockBuilder = scope.declareVariable( "blockBuilder", body, pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, projectionIndex)); Variable cardinality = scope.declareVariable("cardinality", body, selectedPositions.length()); Variable dictionary = scope.declareVariable(Block.class, "dictionary"); Variable ids = scope.declareVariable(Slice.class, "ids"); Variable dictionaryCount = scope.declareVariable(int.class, "dictionaryCount"); Variable outputDictionary = scope.declareVariable(Block.class, "outputDictionary"); Variable outputIds = scope.declareVariable(int[].class, "outputIds"); BytecodeExpression inputDictionaries = thisVariable.getField("inputDictionaries", Block[].class); BytecodeExpression outputDictionaries = thisVariable.getField("outputDictionaries", Block[].class); Variable position = scope.declareVariable("position", body, constantInt(0)); body.comment("Extract dictionary and ids") .append( dictionary.set( inputBlock.cast(DictionaryBlock.class).invoke("getDictionary", Block.class))) .append(ids.set(inputBlock.cast(DictionaryBlock.class).invoke("getIds", Slice.class))) .append(dictionaryCount.set(dictionary.invoke("getPositionCount", int.class))); BytecodeBlock projectDictionary = new BytecodeBlock() .comment("Project dictionary") .append( new ForLoop() .initialize(position.set(constantInt(0))) .condition(lessThan(position, dictionaryCount)) .update(position.increment()) .body( invokeProject( thisVariable, session, ImmutableList.of(dictionary), position, pageBuilder, projectionIndex, project))) .append(outputDictionary.set(blockBuilder.invoke("build", Block.class))) .append(inputDictionaries.setElement(projectionIndex, dictionary)) .append(outputDictionaries.setElement(projectionIndex, outputDictionary)); body.comment("Use processed dictionary, if available, else project it") .append( new IfStatement() .condition(equal(inputDictionaries.getElement(projectionIndex), dictionary)) .ifTrue(outputDictionary.set(outputDictionaries.getElement(projectionIndex))) .ifFalse(projectDictionary)); body.comment("Filter ids") .append(outputIds.set(newArray(type(int[].class), cardinality))) .append( new ForLoop() .initialize(position.set(constantInt(0))) .condition(lessThan(position, cardinality)) .update(position.increment()) .body( outputIds.setElement( position, ids.invoke( "getInt", int.class, multiply( selectedPositions.getElement(position), constantInt(SIZE_OF_INT)))))); body.append( newInstance( DictionaryBlock.class, cardinality, outputDictionary, invokeStatic(Slices.class, "wrappedIntArray", Slice.class, outputIds)) .cast(Block.class) .ret()); return method; }
private static void generateProcessMethod( ClassDefinition classDefinition, RowExpression filter, List<RowExpression> projections, List<MethodDefinition> projectionMethods) { Parameter session = arg("session", ConnectorSession.class); Parameter page = arg("page", Page.class); Parameter start = arg("start", int.class); Parameter end = arg("end", int.class); Parameter pageBuilder = arg("pageBuilder", PageBuilder.class); MethodDefinition method = classDefinition.declareMethod( a(PUBLIC), "process", type(int.class), session, page, start, end, pageBuilder); Scope scope = method.getScope(); BytecodeBlock body = method.getBody(); Variable thisVariable = method.getThis(); // extract blocks List<Integer> allInputChannels = getInputChannels(concat(projections, ImmutableList.of(filter))); ImmutableMap.Builder<Integer, Variable> builder = ImmutableMap.builder(); for (int channel : allInputChannels) { Variable blockVariable = scope.declareVariable( "block_" + channel, body, page.invoke("getBlock", Block.class, constantInt(channel))); builder.put(channel, blockVariable); } Map<Integer, Variable> channelBlocks = builder.build(); Map<RowExpression, List<Variable>> expressionInputBlocks = getExpressionInputBlocks(projections, filter, channelBlocks); // projection body Variable position = scope.declareVariable(int.class, "position"); BytecodeBlock project = new BytecodeBlock().append(pageBuilder.invoke("declarePosition", void.class)); for (int projectionIndex = 0; projectionIndex < projections.size(); projectionIndex++) { RowExpression projection = projections.get(projectionIndex); project.append( invokeProject( thisVariable, session, expressionInputBlocks.get(projection), position, pageBuilder, constantInt(projectionIndex), projectionMethods.get(projectionIndex))); } LabelNode done = new LabelNode("done"); // for loop loop body ForLoop loop = new ForLoop() .initialize(position.set(start)) .condition(lessThan(position, end)) .update(position.set(add(position, constantInt(1)))) .body( new BytecodeBlock() .append( new IfStatement() .condition(pageBuilder.invoke("isFull", boolean.class)) .ifTrue(jump(done))) .append( new IfStatement() .condition( invokeFilter( thisVariable, session, expressionInputBlocks.get(filter), position)) .ifTrue(project))); body.append(loop).visitLabel(done).append(position.ret()); }
private Class<?> generateProjectMethod( CallSiteBinder callSiteBinder, ClassDefinition classDefinition, String methodName, RowExpression projection, boolean sourceIsCursor) { MethodDefinition projectionMethod; if (sourceIsCursor) { projectionMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), methodName, type(void.class), arg("cursor", RecordCursor.class), arg("output", BlockBuilder.class)); } else { ImmutableList.Builder<NamedParameterDefinition> parameters = ImmutableList.builder(); parameters.add(arg("position", int.class)); parameters.addAll(toBlockParameters(getInputChannels(projection))); parameters.add(arg("output", BlockBuilder.class)); projectionMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), methodName, type(void.class), parameters.build()); } projectionMethod.comment("Projection: %s", projection.toString()); // generate body code CompilerContext context = projectionMethod.getCompilerContext(); context.declareVariable(type(boolean.class), "wasNull"); Block getSessionByteCode = new Block(context) .pushThis() .getField(classDefinition.getType(), "session", type(ConnectorSession.class)); ByteCodeNode body = compileExpression(callSiteBinder, projection, sourceIsCursor, context, getSessionByteCode); projectionMethod .getBody() .comment("boolean wasNull = false;") .putVariable("wasNull", false) .getVariable("output") .append(body); Type projectionType = projection.getType(); Block notNullBlock = new Block(context); if (projectionType.getJavaType() == boolean.class) { notNullBlock .comment("output.append(<booleanStackValue>);") .invokeInterface(BlockBuilder.class, "appendBoolean", BlockBuilder.class, boolean.class) .pop(); } else if (projectionType.getJavaType() == long.class) { notNullBlock .comment("output.append(<longStackValue>);") .invokeInterface(BlockBuilder.class, "appendLong", BlockBuilder.class, long.class) .pop(); } else if (projectionType.getJavaType() == double.class) { notNullBlock .comment("output.append(<doubleStackValue>);") .invokeInterface(BlockBuilder.class, "appendDouble", BlockBuilder.class, double.class) .pop(); } else if (projectionType.getJavaType() == Slice.class) { notNullBlock .comment("output.append(<sliceStackValue>);") .invokeInterface(BlockBuilder.class, "appendSlice", BlockBuilder.class, Slice.class) .pop(); } else { throw new UnsupportedOperationException("Type " + projectionType + " can not be output yet"); } Block nullBlock = new Block(context) .comment("output.appendNull();") .pop(projectionType.getJavaType()) .invokeInterface(BlockBuilder.class, "appendNull", BlockBuilder.class) .pop(); projectionMethod .getBody() .comment("if the result was null, appendNull; otherwise append the value") .append( new IfStatement( context, new Block(context).getVariable("wasNull"), nullBlock, notNullBlock)) .ret(); return projectionType.getJavaType(); }
private void generateFilterAndProjectCursorMethod( ClassDefinition classDefinition, List<RowExpression> projections) { MethodDefinition filterAndProjectMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), "filterAndProjectRowOriented", type(int.class), arg("cursor", RecordCursor.class), arg("pageBuilder", PageBuilder.class)); CompilerContext compilerContext = filterAndProjectMethod.getCompilerContext(); LocalVariableDefinition completedPositionsVariable = compilerContext.declareVariable(int.class, "completedPositions"); filterAndProjectMethod .getBody() .comment("int completedPositions = 0;") .putVariable(completedPositionsVariable, 0); // // for loop loop body // LabelNode done = new LabelNode("done"); ForLoopBuilder forLoop = ForLoop.forLoopBuilder(compilerContext) .initialize(NOP) .condition( new Block(compilerContext) .comment("completedPositions < 16384") .getVariable(completedPositionsVariable) .push(16384) .invokeStatic( CompilerOperations.class, "lessThan", boolean.class, int.class, int.class)) .update( new Block(compilerContext) .comment("completedPositions++") .incrementVariable(completedPositionsVariable, (byte) 1)); Block forLoopBody = new Block(compilerContext); forLoop.body(forLoopBody); forLoopBody .comment("if (pageBuilder.isFull()) break;") .append( new Block(compilerContext) .getVariable("pageBuilder") .invokeVirtual(PageBuilder.class, "isFull", boolean.class) .ifTrueGoto(done)); forLoopBody .comment("if (!cursor.advanceNextPosition()) break;") .append( new Block(compilerContext) .getVariable("cursor") .invokeInterface(RecordCursor.class, "advanceNextPosition", boolean.class) .ifFalseGoto(done)); // if (filter(cursor)) IfStatementBuilder ifStatement = new IfStatementBuilder(compilerContext); ifStatement.condition( new Block(compilerContext) .pushThis() .getVariable("cursor") .invokeVirtual( classDefinition.getType(), "filter", type(boolean.class), type(RecordCursor.class))); Block trueBlock = new Block(compilerContext); ifStatement.ifTrue(trueBlock); if (projections.isEmpty()) { // pageBuilder.declarePosition(); trueBlock .getVariable("pageBuilder") .invokeVirtual(PageBuilder.class, "declarePosition", void.class); } else { // project_43(block..., pageBuilder.getBlockBuilder(42))); for (int projectionIndex = 0; projectionIndex < projections.size(); projectionIndex++) { trueBlock.pushThis(); trueBlock.getVariable("cursor"); // pageBuilder.getBlockBuilder(0) trueBlock .getVariable("pageBuilder") .push(projectionIndex) .invokeVirtual(PageBuilder.class, "getBlockBuilder", BlockBuilder.class, int.class); // project(block..., blockBuilder) trueBlock.invokeVirtual( classDefinition.getType(), "project_" + projectionIndex, type(void.class), type(RecordCursor.class), type(BlockBuilder.class)); } } forLoopBody.append(ifStatement.build()); filterAndProjectMethod .getBody() .append(forLoop.build()) .visitLabel(done) .comment("return completedPositions;") .getVariable("completedPositions") .retInt(); }
private void generateFilterAndProjectRowOriented( ClassDefinition classDefinition, RowExpression filter, List<RowExpression> projections) { MethodDefinition filterAndProjectMethod = classDefinition.declareMethod( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), "filterAndProjectRowOriented", type(void.class), arg("page", com.facebook.presto.operator.Page.class), arg("pageBuilder", PageBuilder.class)); CompilerContext compilerContext = filterAndProjectMethod.getCompilerContext(); LocalVariableDefinition positionVariable = compilerContext.declareVariable(int.class, "position"); LocalVariableDefinition rowsVariable = compilerContext.declareVariable(int.class, "rows"); filterAndProjectMethod .getBody() .comment("int rows = page.getPositionCount();") .getVariable("page") .invokeVirtual(com.facebook.presto.operator.Page.class, "getPositionCount", int.class) .putVariable(rowsVariable); List<Integer> allInputChannels = getInputChannels(Iterables.concat(projections, ImmutableList.of(filter))); for (int channel : allInputChannels) { LocalVariableDefinition blockVariable = compilerContext.declareVariable( com.facebook.presto.spi.block.Block.class, "block_" + channel); filterAndProjectMethod .getBody() .comment("Block %s = page.getBlock(%s);", blockVariable.getName(), channel) .getVariable("page") .push(channel) .invokeVirtual( com.facebook.presto.operator.Page.class, "getBlock", com.facebook.presto.spi.block.Block.class, int.class) .putVariable(blockVariable); } // // for loop body // // for (position = 0; position < rows; position++) ForLoopBuilder forLoop = forLoopBuilder(compilerContext) .comment("for (position = 0; position < rows; position++)") .initialize(new Block(compilerContext).putVariable(positionVariable, 0)) .condition( new Block(compilerContext) .getVariable(positionVariable) .getVariable(rowsVariable) .invokeStatic( CompilerOperations.class, "lessThan", boolean.class, int.class, int.class)) .update(new Block(compilerContext).incrementVariable(positionVariable, (byte) 1)); Block forLoopBody = new Block(compilerContext); IfStatementBuilder ifStatement = new IfStatementBuilder(compilerContext).comment("if (filter(position, blocks...)"); Block condition = new Block(compilerContext); condition.pushThis(); condition.getVariable(positionVariable); List<Integer> filterInputChannels = getInputChannels(filter); for (int channel : filterInputChannels) { condition.getVariable("block_" + channel); } condition.invokeVirtual( classDefinition.getType(), "filter", type(boolean.class), ImmutableList.<ParameterizedType>builder() .add(type(int.class)) .addAll( nCopies( filterInputChannels.size(), type(com.facebook.presto.spi.block.Block.class))) .build()); ifStatement.condition(condition); Block trueBlock = new Block(compilerContext); if (projections.isEmpty()) { trueBlock .comment("pageBuilder.declarePosition()") .getVariable("pageBuilder") .invokeVirtual(PageBuilder.class, "declarePosition", void.class); } else { // pageBuilder.getBlockBuilder(0).append(block.getDouble(0); for (int projectionIndex = 0; projectionIndex < projections.size(); projectionIndex++) { trueBlock.comment( "project_%s(position, blocks..., pageBuilder.getBlockBuilder(%s))", projectionIndex, projectionIndex); trueBlock.pushThis(); List<Integer> projectionInputs = getInputChannels(projections.get(projectionIndex)); trueBlock.getVariable(positionVariable); for (int channel : projectionInputs) { trueBlock.getVariable("block_" + channel); } // pageBuilder.getBlockBuilder(0) trueBlock .getVariable("pageBuilder") .push(projectionIndex) .invokeVirtual(PageBuilder.class, "getBlockBuilder", BlockBuilder.class, int.class); // project(position, block_0, block_1, blockBuilder) trueBlock.invokeVirtual( classDefinition.getType(), "project_" + projectionIndex, type(void.class), ImmutableList.<ParameterizedType>builder() .add(type(int.class)) .addAll( nCopies( projectionInputs.size(), type(com.facebook.presto.spi.block.Block.class))) .add(type(BlockBuilder.class)) .build()); } } ifStatement.ifTrue(trueBlock); forLoopBody.append(ifStatement.build()); filterAndProjectMethod.getBody().append(forLoop.body(forLoopBody).build()); filterAndProjectMethod.getBody().ret(); }
private static MethodDefinition generateProjectColumnarMethod( ClassDefinition classDefinition, CallSiteBinder callSiteBinder, String methodName, RowExpression projection, MethodDefinition projectionMethod) { Parameter session = arg("session", ConnectorSession.class); Parameter page = arg("page", Page.class); Parameter selectedPositions = arg("selectedPositions", int[].class); Parameter pageBuilder = arg("pageBuilder", PageBuilder.class); Parameter projectionIndex = arg("projectionIndex", int.class); List<Parameter> params = ImmutableList.<Parameter>builder() .add(session) .add(page) .add(selectedPositions) .add(pageBuilder) .add(projectionIndex) .build(); MethodDefinition method = classDefinition.declareMethod(a(PRIVATE), methodName, type(Block.class), params); BytecodeBlock body = method.getBody(); Scope scope = method.getScope(); Variable thisVariable = method.getThis(); ImmutableList.Builder<Variable> builder = ImmutableList.<Variable>builder(); for (int channel : getInputChannels(projection)) { Variable blockVariable = scope.declareVariable( "block_" + channel, body, page.invoke("getBlock", Block.class, constantInt(channel))); builder.add(blockVariable); } List<Variable> inputs = builder.build(); Variable positionCount = scope.declareVariable("positionCount", body, page.invoke("getPositionCount", int.class)); Variable position = scope.declareVariable("position", body, constantInt(0)); Variable cardinality = scope.declareVariable("cardinality", body, selectedPositions.length()); Variable outputBlock = scope.declareVariable(Block.class, "outputBlock"); Variable blockBuilder = scope.declareVariable( "blockBuilder", body, pageBuilder.invoke("getBlockBuilder", BlockBuilder.class, projectionIndex)); Variable type = scope.declareVariable( "type", body, pageBuilder.invoke("getType", Type.class, projectionIndex)); BytecodeBlock projectBlock = new BytecodeBlock() .append( new ForLoop() .initialize(position.set(constantInt(0))) .condition(lessThan(position, cardinality)) .update(position.increment()) .body( invokeProject( thisVariable, session, inputs, selectedPositions.getElement(position), pageBuilder, projectionIndex, projectionMethod))) .append(outputBlock.set(blockBuilder.invoke("build", Block.class))); if (isIdentityExpression(projection)) { // if nothing is filtered out, copy the entire block, else project it body.append( new IfStatement() .condition(equal(cardinality, positionCount)) .ifTrue(outputBlock.set(inputs.get(0))) .ifFalse(projectBlock)); } else if (isConstantExpression(projection)) { // if projection is a constant, create RLE block of constant expression with cardinality // positions ConstantExpression constantExpression = (ConstantExpression) projection; verify(getInputChannels(projection).isEmpty()); BytecodeExpression value = loadConstant(callSiteBinder, constantExpression.getValue(), Object.class); body.append( outputBlock.set( invokeStatic( RunLengthEncodedBlock.class, "create", Block.class, type, value, cardinality))); } else { body.append(projectBlock); } body.append(outputBlock.ret()); return method; }
private TypedOperatorClass compileScanFilterAndProjectOperator( RowExpression filter, List<RowExpression> projections, DynamicClassLoader classLoader) { CallSiteBinder callSiteBinder = new CallSiteBinder(); ClassDefinition classDefinition = new ClassDefinition( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC, FINAL), typeFromPathName("ScanFilterAndProjectOperator_" + CLASS_ID.incrementAndGet()), type(AbstractScanFilterAndProjectOperator.class)); // declare fields FieldDefinition sessionField = classDefinition.declareField(a(PRIVATE, FINAL), "session", ConnectorSession.class); classDefinition.declareField(a(PRIVATE, VOLATILE, STATIC), "callSites", Map.class); // constructor classDefinition .declareConstructor( new CompilerContext(BOOTSTRAP_METHOD), a(PUBLIC), arg("operatorContext", OperatorContext.class), arg("sourceId", PlanNodeId.class), arg("dataStreamProvider", DataStreamProvider.class), arg("columns", type(Iterable.class, ColumnHandle.class)), arg("types", type(Iterable.class, Type.class))) .getBody() .comment("super(operatorContext, sourceId, dataStreamProvider, columns, types);") .pushThis() .getVariable("operatorContext") .getVariable("sourceId") .getVariable("dataStreamProvider") .getVariable("columns") .getVariable("types") .invokeConstructor( AbstractScanFilterAndProjectOperator.class, OperatorContext.class, PlanNodeId.class, DataStreamProvider.class, Iterable.class, Iterable.class) .comment("this.session = operatorContext.getSession();") .pushThis() .getVariable("operatorContext") .invokeVirtual(OperatorContext.class, "getSession", ConnectorSession.class) .putField(sessionField) .ret(); generateFilterAndProjectRowOriented(classDefinition, filter, projections); generateFilterAndProjectCursorMethod(classDefinition, projections); // // filter method // generateFilterMethod(callSiteBinder, classDefinition, filter, true); generateFilterMethod(callSiteBinder, classDefinition, filter, false); // // project methods // List<Type> types = new ArrayList<>(); int projectionIndex = 0; for (RowExpression projection : projections) { generateProjectMethod( callSiteBinder, classDefinition, "project_" + projectionIndex, projection, true); generateProjectMethod( callSiteBinder, classDefinition, "project_" + projectionIndex, projection, false); types.add(projection.getType()); projectionIndex++; } // // toString method // generateToString( classDefinition, toStringHelper(classDefinition.getType().getJavaClassName()) .add("filter", filter) .add("projections", projections) .toString()); Class<? extends SourceOperator> filterAndProjectClass = defineClass(classDefinition, SourceOperator.class, classLoader); setCallSitesField(filterAndProjectClass, callSiteBinder.getBindings()); return new TypedOperatorClass(filterAndProjectClass, types); }
public <T> AccumulatorStateFactory<T> generateStateFactory( Class<T> clazz, DynamicClassLoader classLoader) { AccumulatorStateMetadata metadata = getMetadataAnnotation(clazz); if (metadata != null && metadata.stateFactoryClass() != void.class) { try { return (AccumulatorStateFactory<T>) metadata.stateFactoryClass().getConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { throw Throwables.propagate(e); } } Class<? extends T> singleStateClass = generateSingleStateClass(clazz, classLoader); Class<? extends T> groupedStateClass = generateGroupedStateClass(clazz, classLoader); ClassDefinition definition = new ClassDefinition( a(PUBLIC, FINAL), makeClassName(clazz.getSimpleName() + "Factory"), type(Object.class), type(AccumulatorStateFactory.class)); // Generate constructor definition.declareDefaultConstructor(a(PUBLIC)); // Generate single state creation method definition .declareMethod(a(PUBLIC), "createSingleState", type(Object.class)) .getBody() .newObject(singleStateClass) .dup() .invokeConstructor(singleStateClass) .retObject(); // Generate grouped state creation method definition .declareMethod(a(PUBLIC), "createGroupedState", type(Object.class)) .getBody() .newObject(groupedStateClass) .dup() .invokeConstructor(groupedStateClass) .retObject(); // Generate getters for state class definition .declareMethod(a(PUBLIC), "getSingleStateClass", type(Class.class, singleStateClass)) .getBody() .push(singleStateClass) .retObject(); definition .declareMethod(a(PUBLIC), "getGroupedStateClass", type(Class.class, groupedStateClass)) .getBody() .push(groupedStateClass) .retObject(); Class<? extends AccumulatorStateFactory> factoryClass = defineClass(definition, AccumulatorStateFactory.class, classLoader); try { return (AccumulatorStateFactory<T>) factoryClass.newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw Throwables.propagate(e); } }