blob: d133a885d075fd24eabc2a385220e74e12d47787 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.exec.expr;
import static org.apache.drill.exec.compile.sig.GeneratorMapping.GM;
import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import io.netty.buffer.DrillBuf;
import org.apache.calcite.util.Pair;
import org.apache.drill.common.exceptions.DrillRuntimeException;
import org.apache.drill.common.expression.LogicalExpression;
import org.apache.drill.common.types.TypeProtos;
import org.apache.drill.common.types.TypeProtos.DataMode;
import org.apache.drill.common.types.TypeProtos.MajorType;
import org.apache.drill.exec.ExecConstants;
import org.apache.drill.exec.compile.sig.CodeGeneratorArgument;
import org.apache.drill.exec.compile.sig.CodeGeneratorMethod;
import org.apache.drill.exec.compile.sig.GeneratorMapping;
import org.apache.drill.exec.compile.sig.MappingSet;
import org.apache.drill.exec.compile.sig.SignatureHolder;
import org.apache.drill.exec.exception.SchemaChangeException;
import org.apache.drill.exec.expr.fn.WorkspaceReference;
import org.apache.drill.exec.expr.holders.ValueHolder;
import org.apache.drill.exec.record.TypedFieldId;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCatchBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldRef;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JLabel;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JTryBlock;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;
import org.apache.drill.exec.server.options.OptionSet;
public class ClassGenerator<T> {
public static final GeneratorMapping DEFAULT_SCALAR_MAP = GM("doSetup", "doEval", null, null);
public static final GeneratorMapping DEFAULT_CONSTANT_MAP = GM("doSetup", "doSetup", null, null);
public static final String INNER_CLASS_FIELD_NAME = "innerClassField";
public enum BlockType {SETUP, EVAL, RESET, CLEANUP}
private final SignatureHolder sig;
private final EvaluationVisitor evaluationVisitor;
private final Map<ValueVectorSetup, JVar> vvDeclaration = Maps.newHashMap();
private final Map<String, ClassGenerator<T>> innerClasses = Maps.newHashMap();
private final List<TypedFieldId> workspaceTypes = Lists.newArrayList();
private final Map<WorkspaceReference, JVar> workspaceVectors = Maps.newHashMap();
private final Map<Pair<Integer, JVar>, Function<DrillBuf, ? extends ValueHolder>> constantVars;
private final CodeGenerator<T> codeGenerator;
public final JDefinedClass clazz;
private final JCodeModel model;
private final OptionSet optionManager;
private ClassGenerator<T> innerClassGenerator;
private LinkedList<SizedJBlock>[] blocks;
private LinkedList<SizedJBlock>[] oldBlocks;
private JVar innerClassField;
/**
* Assume that field has 3 indexes within the constant pull: index of the<br>
* CONSTANT_Fieldref_info + CONSTANT_Fieldref_info.name_and_type_index + CONSTANT_NameAndType_info.name_index.
* CONSTANT_NameAndType_info.descriptor_index has limited range of values,
* CONSTANT_Fieldref_info.class_index is the same for a single class, they will
* be taken into account later.
* <p>
* Local variable has 1 index within the constant pool.
* {@link org.objectweb.asm.MethodWriter#visitLocalVariable(String, String, String, Label, Label, int)}
* <p>
* For upper estimation of max index value, suppose that each field and local
* variable uses different literal values that have two indexes, then the
* number of occupied indexes within the constant pull is<br>
* fieldCount * 3 + fieldCount * 2 + (index - fieldCount) * 3 => fieldCount * 2 + index * 3
* <p>
* Assumed that method has 3 indexes within the constant pull: index of the
* CONSTANT_Methodref_info + CONSTANT_Methodref_info.name_and_type_index +
* CONSTANT_NameAndType_info.name_index.
* <p>
* For the upper estimation of number of split methods suppose that each
* expression in the method uses single variable. Suppose that the max number
* of indexes within the constant pull occupied by fields and local variables
* is M, the number of split methods is N, number of abstract methods in the
* template is A, then splitted methods count is<br>
* N = (M - A * N * 3) / 50 => N = M / (50 + A * 3)
* <p>
* Additionally should be taken into account class references; fields and
* methods from the template, so reserves 1000 for them.
* <p>
* Then the size of the occupied part in the constant pull is<br>
* (fieldCount * 2 + index * 3 + 1000) * (1 + 3 / (50 + A * 3))
*/
private long maxIndex;
private int index;
private int labelIndex;
private MappingSet mappings;
public static MappingSet getDefaultMapping() {
return new MappingSet("inIndex", "outIndex", DEFAULT_CONSTANT_MAP, DEFAULT_SCALAR_MAP);
}
@SuppressWarnings("unchecked")
ClassGenerator(CodeGenerator<T> codeGenerator, MappingSet mappingSet, SignatureHolder signature,
EvaluationVisitor eval, JDefinedClass clazz, JCodeModel model,
OptionSet optionManager) throws JClassAlreadyExistsException {
this.codeGenerator = codeGenerator;
this.clazz = clazz;
this.mappings = mappingSet;
this.sig = signature;
this.evaluationVisitor = eval;
this.model = model;
this.optionManager = optionManager;
constantVars = new HashMap<>();
blocks = new LinkedList[sig.size()];
for (int i =0; i < sig.size(); i++) {
blocks[i] = Lists.newLinkedList();
}
rotateBlock();
for (SignatureHolder child : signature.getChildHolders()) {
Class<?> innerClass = child.getSignatureClass();
String innerClassName = innerClass.getSimpleName();
// Create the inner class as private final. If the template (super) class
// is static, then make the subclass static as well. Note the conversion
// from the JDK Modifier values to the JCodeModel JMod values: the
// values are different.
int mods = JMod.FINAL;
if ((innerClass.getModifiers() & Modifier.STATIC) != 0) {
mods += JMod.STATIC;
}
JDefinedClass innerClazz = clazz._class(mods, innerClassName);
innerClasses.put(innerClassName, new ClassGenerator<>(codeGenerator, mappingSet, child, eval, innerClazz, model, optionManager));
}
long maxExprsNumber = optionManager != null ? optionManager.getOption(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE_VALIDATOR) : 50;
maxIndex = Math.round((0xFFFF / (1 + 3. / (3L * sig.size() + maxExprsNumber)) - 1000) / 3);
}
public ClassGenerator<T> getInnerGenerator(String name) {
ClassGenerator<T> inner = innerClasses.get(name);
Preconditions.checkNotNull(inner);
return inner;
}
public MappingSet getMappingSet() {
return mappings;
}
public void setMappingSet(MappingSet mappings) {
if (innerClassGenerator != null) {
innerClassGenerator.setMappingSet(mappings);
}
this.mappings = mappings;
}
public CodeGenerator<T> getCodeGenerator() {
return codeGenerator;
}
private GeneratorMapping getCurrentMapping() {
return mappings.getCurrentMapping();
}
public JBlock getBlock(String methodName) {
JBlock blk = this.blocks[sig.get(methodName)].getLast().getBlock();
Preconditions.checkNotNull(blk, "Requested method name of %s was not available for signature %s.", methodName, this.sig);
return blk;
}
public JBlock getBlock(BlockType type) {
return getBlock(getCurrentMapping().getMethodName(type));
}
public JBlock getSetupBlock() {
return getBlock(getCurrentMapping().getMethodName(BlockType.SETUP));
}
public JBlock getEvalBlock() {
return getBlock(getCurrentMapping().getMethodName(BlockType.EVAL));
}
public JBlock getResetBlock() {
return getBlock(getCurrentMapping().getMethodName(BlockType.RESET));
}
public JBlock getCleanupBlock() {
return getBlock(getCurrentMapping().getMethodName(BlockType.CLEANUP));
}
public void nestEvalBlock(JBlock block) {
String methodName = getCurrentMapping().getMethodName(BlockType.EVAL);
evaluationVisitor.newScope();
this.blocks[sig.get(methodName)].addLast(new SizedJBlock(block));
}
public void unNestEvalBlock() {
String methodName = getCurrentMapping().getMethodName(BlockType.EVAL);
evaluationVisitor.leaveScope();
this.blocks[sig.get(methodName)].removeLast();
}
public JLabel getEvalBlockLabel (String prefix) {
return getEvalBlock().label(prefix + labelIndex++);
}
/**
* Creates an inner braced and indented block
* @param type type of the created block
* @return a newly created inner block
*/
private JBlock createInnerBlock(BlockType type) {
final JBlock currBlock = getBlock(type);
final JBlock innerBlock = new JBlock();
currBlock.add(innerBlock);
return innerBlock;
}
/**
* Creates an inner braced and indented block for evaluation of the expression.
* @return a newly created inner eval block
*/
protected JBlock createInnerEvalBlock() {
return createInnerBlock(BlockType.EVAL);
}
public JVar declareVectorValueSetupAndMember(String batchName, TypedFieldId fieldId) {
return declareVectorValueSetupAndMember(DirectExpression.direct(batchName), fieldId);
}
/**
* Creates class variable for the value vector using metadata from {@code fieldId}
* and initializes it using setup blocks.
*
* @param batchName expression for invoking {@code getValueAccessorById} method
* @param fieldId metadata of the field that should be declared
* @return a newly generated class field
*/
public JVar declareVectorValueSetupAndMember(DirectExpression batchName, TypedFieldId fieldId) {
// declares field in the inner class if innerClassGenerator has been created
if (innerClassGenerator != null) {
return innerClassGenerator.declareVectorValueSetupAndMember(batchName, fieldId);
}
final ValueVectorSetup setup = new ValueVectorSetup(batchName, fieldId);
// JVar var = this.vvDeclaration.get(setup);
// if(var != null) return var;
Class<?> valueVectorClass = fieldId.getIntermediateClass();
JClass vvClass = model.ref(valueVectorClass);
JClass retClass = vvClass;
String vectorAccess = "getValueVector";
if (fieldId.isHyperReader()) {
retClass = retClass.array();
vectorAccess = "getValueVectors";
}
JVar vv = declareClassField("vv", retClass);
JClass t = model.ref(SchemaChangeException.class);
JType objClass = model.ref(Object.class);
JBlock b = getSetupBlock();
JVar fieldArr = b.decl(model.INT.array(), "fieldIds" + index++, JExpr.newArray(model.INT, fieldId.getFieldIds().length));
int[] fieldIndices = fieldId.getFieldIds();
for (int i = 0; i < fieldIndices.length; i++) {
b.assign(fieldArr.component(JExpr.lit(i)), JExpr.lit(fieldIndices[i]));
}
JInvocation invoke = batchName
.invoke("getValueAccessorById")
.arg(vvClass.dotclass())
.arg(fieldArr);
JVar obj = b.decl(
objClass,
getNextVar("tmp"),
invoke.invoke(vectorAccess));
b._if(obj.eq(JExpr._null()))._then()._throw(JExpr._new(t).arg(JExpr.lit(
String.format("Failure while loading vector %s with id: %s.", vv.name(), fieldId.toString()))));
//b.assign(vv, JExpr.cast(retClass, ((JExpression) JExpr.cast(wrapperClass, obj)).invoke(vectorAccess)));
b.assign(vv, JExpr.cast(retClass, obj));
vvDeclaration.put(setup, vv);
return vv;
}
public enum BlkCreateMode {
/** Create new block */
TRUE,
/** Do not create block; put into existing block. */
FALSE,
/** Create new block only if # of expressions added hit upper-bound
* ({@link ExecConstants#CODE_GEN_EXP_IN_METHOD_SIZE}). */
TRUE_IF_BOUND
}
public HoldingContainer addExpr(LogicalExpression ex) {
// default behavior is always to put expression into new block.
return addExpr(ex, BlkCreateMode.TRUE);
}
public HoldingContainer addExpr(LogicalExpression ex, BlkCreateMode mode) {
if (mode == BlkCreateMode.TRUE || mode == BlkCreateMode.TRUE_IF_BOUND) {
rotateBlock(mode);
}
for (LinkedList<SizedJBlock> b : blocks) {
b.getLast().incCounter();
}
return evaluationVisitor.addExpr(ex, this);
}
public void rotateBlock() {
// default behavior is always to create new block.
rotateBlock(BlkCreateMode.TRUE);
}
/**
* Assigns {@link #blocks} from the last nested {@link #innerClassGenerator} to {@link this#blocks}
* recursively if {@link #innerClassGenerator} has been created.
*/
private void setupValidBlocks() {
if (createNestedClass()) {
// blocks from the last inner class should be used
setupInnerClassBlocks();
}
}
/**
* Creates {@link #innerClassGenerator} with inner class
* if {@link #hasMaxIndexValue()} returns {@code true}.
*
* @return true if splitting happened.
*/
private boolean createNestedClass() {
if (hasMaxIndexValue()) {
// all new fields will be declared in the class from innerClassGenerator
if (innerClassGenerator == null) {
try {
JDefinedClass innerClazz = clazz._class(JMod.NONE, clazz.name() + "0");
innerClassGenerator = new ClassGenerator<>(codeGenerator, mappings, sig, evaluationVisitor, innerClazz, model, optionManager);
} catch (JClassAlreadyExistsException e) {
throw new DrillRuntimeException(e);
}
oldBlocks = blocks;
innerClassGenerator.index = index;
innerClassGenerator.maxIndex += index;
// blocks from the inner class should be used
setupInnerClassBlocks();
innerClassField = clazz.field(JMod.NONE, model.ref(innerClassGenerator.clazz.name()), INNER_CLASS_FIELD_NAME);
return true;
}
return innerClassGenerator.createNestedClass();
}
return false;
}
/**
* Checks that {@link #index} has reached its max value.
*
* @return true if {@code index + clazz.fields().size() * 2 / 3} is greater than {@code maxIndex}
*/
private boolean hasMaxIndexValue() {
return index + clazz.fields().size() * 2 / 3 > maxIndex;
}
/**
* Gets blocks from the last inner {@link ClassGenerator innerClassGenerator}
* and assigns it to the {@link this#blocks} recursively.
*/
private void setupInnerClassBlocks() {
if (innerClassGenerator != null) {
innerClassGenerator.setupInnerClassBlocks();
blocks = innerClassGenerator.blocks;
}
}
/**
* Create a new code block, closing the current block.
*
* @param mode the {@link BlkCreateMode block create mode}
* for the new block.
*/
private void rotateBlock(BlkCreateMode mode) {
boolean blockRotated = false;
for (LinkedList<SizedJBlock> b : blocks) {
if (mode == BlkCreateMode.TRUE ||
(mode == BlkCreateMode.TRUE_IF_BOUND &&
optionManager != null &&
b.getLast().getCount() > optionManager.getOption(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE_VALIDATOR))) {
b.add(new SizedJBlock(new JBlock(true, true)));
blockRotated = true;
}
}
if (blockRotated) {
evaluationVisitor.previousExpressions.clear();
setupValidBlocks();
}
}
/**
* Creates methods from the signature {@code sig} with body from the
* appropriate {@code blocks}.
*/
void flushCode() {
if (innerClassGenerator != null) {
blocks = oldBlocks;
innerClassGenerator.flushCode();
}
int i = 0;
for (CodeGeneratorMethod method : sig) {
JMethod outer = clazz.method(JMod.PUBLIC, model._ref(method.getReturnType()), method.getMethodName());
for (CodeGeneratorArgument arg : method) {
outer.param(arg.getType(), arg.getName());
}
for (Class<?> c : method.getThrowsIterable()) {
outer._throws(model.ref(c));
}
outer._throws(SchemaChangeException.class);
int methodIndex = 0;
int exprsInMethod = 0;
boolean isVoidMethod = method.getReturnType() == void.class;
for (SizedJBlock sb : blocks[i++]) {
JBlock b = sb.getBlock();
if (!b.isEmpty()) {
if (optionManager != null &&
exprsInMethod > optionManager.getOption(ExecConstants.CODE_GEN_EXP_IN_METHOD_SIZE_VALIDATOR)) {
JMethod inner = clazz.method(JMod.PRIVATE, model._ref(method.getReturnType()), method.getMethodName() + methodIndex);
JInvocation methodCall = JExpr.invoke(inner);
for (CodeGeneratorArgument arg : method) {
inner.param(arg.getType(), arg.getName());
methodCall.arg(JExpr.direct(arg.getName()));
}
for (Class<?> c : method.getThrowsIterable()) {
inner._throws(model.ref(c));
}
inner._throws(SchemaChangeException.class);
if (isVoidMethod) {
outer.body().add(methodCall);
} else {
outer.body()._return(methodCall);
}
outer = inner;
exprsInMethod = 0;
++methodIndex;
}
outer.body().add(b);
exprsInMethod += sb.getCount();
}
}
if (innerClassField != null) {
// creates inner class instance and initializes innerClassField
if (method.getMethodName().equals("__DRILL_INIT__")) {
JInvocation rhs = JExpr._new(innerClassGenerator.clazz);
JBlock block = new JBlock().assign(innerClassField, rhs);
outer.body().add(block);
}
List<JType> argTypes = new ArrayList<>();
for (CodeGeneratorArgument arg : method) {
argTypes.add(model._ref(arg.getType()));
}
JMethod inner = innerClassGenerator.clazz.getMethod(method.getMethodName(), argTypes.toArray(new JType[0]));
if (inner != null) {
// removes empty method from the inner class
if (inner.body().isEmpty()) {
innerClassGenerator.clazz.methods().remove(inner);
continue;
}
JInvocation methodCall = innerClassField.invoke(inner);
for (CodeGeneratorArgument arg : method) {
methodCall.arg(JExpr.direct(arg.getName()));
}
if (isVoidMethod) {
outer.body().add(methodCall);
} else {
outer.body()._return(methodCall);
}
}
}
}
for(ClassGenerator<T> child : innerClasses.values()) {
child.flushCode();
}
}
public JCodeModel getModel() {
return model;
}
public String getNextVar() {
return "v" + index++;
}
public String getNextVar(String prefix) {
return prefix + index++;
}
public JVar declareClassField(String prefix, JType t) {
return declareClassField(prefix, t, null);
}
public JVar declareClassField(String prefix, JType t, JExpression init) {
if (innerClassGenerator != null && hasMaxIndexValue()) {
return innerClassGenerator.declareClassField(prefix, t, init);
}
return clazz.field(JMod.NONE, t, getNextVar(prefix), init);
}
public Pair<Integer, JVar> declareClassConstField(String prefix, JType t,
Function<DrillBuf, ? extends ValueHolder> function) {
return declareClassConstField(prefix, t, null, function);
}
/**
* Declare a constant field for the class.
* The class field innerClassField will be created if innerClassGenerator exists.
*
* @param prefix the prefix name of class field
* @param t the type of class field
* @param init init expression
* @param function holds the constant value which
* returns a value holder must be set to the class field when the class instance created.
* @return the depth of nested class, class field
*/
public Pair<Integer, JVar> declareClassConstField(String prefix, JType t, JExpression init,
Function<DrillBuf, ? extends ValueHolder> function) {
JVar var;
int depth = 1;
if (innerClassGenerator != null) {
Pair<Integer, JVar> nested = innerClassGenerator.declareClassConstField(prefix, t, init, function);
depth = nested.getKey() + 1;
var = nested.getValue();
} else {
var = clazz.field(JMod.NONE, t, getNextVar(prefix), init);
}
Pair<Integer, JVar> depthVar = Pair.of(depth, var);
constantVars.put(depthVar, function);
return depthVar;
}
public Map<Pair<Integer, JVar>, Function<DrillBuf, ? extends ValueHolder>> getConstantVars() {
return constantVars;
}
public HoldingContainer declare(MajorType t) {
return declare(t, true);
}
public HoldingContainer declare(MajorType t, boolean includeNewInstance) {
return declare(t, "out", includeNewInstance);
}
/**
* Adds a local variable declaration based on given name and type.
*
* @param t major type
* @param name variable name
* @param includeNewInstance whether to create new instance
* @return holder instance
*/
public HoldingContainer declare(MajorType t, String name, boolean includeNewInstance) {
JType holderType = getHolderType(t);
JVar var;
if (includeNewInstance) {
var = getEvalBlock().decl(holderType, getNextVar(name), JExpr._new(holderType));
} else {
var = getEvalBlock().decl(holderType, getNextVar(name));
}
JFieldRef outputSet = null;
if (t.getMode() == DataMode.OPTIONAL) {
outputSet = var.ref("isSet");
}
return new HoldingContainer(t, var, var.ref("value"), outputSet);
}
public List<TypedFieldId> getWorkspaceTypes() {
return workspaceTypes;
}
public Map<WorkspaceReference, JVar> getWorkspaceVectors() {
return workspaceVectors;
}
/**
* Prepare the generated class for use as a plain-old Java class
* (to be compiled by a compiler and directly loaded without a
* byte-code merge. Three additions are necessary:
* <ul>
* <li>The class must extend its template as we won't merge byte
* codes.</li>
* <li>A constructor is required to call the <tt>__DRILL_INIT__</tt>
* method. If this is a nested class, then the constructor must
* include parameters defined by the base class.</li>
* <li>For each nested class, create a method that creates an
* instance of that nested class using a well-defined name. This
* method overrides the base class method defined for this purpose.</li>
*/
public void preparePlainJava() {
// If this generated class uses the "straight Java" technique
// (no byte code manipulation), then the class must extend the
// template so it plays by normal Java rules for finding the
// template methods via inheritance rather than via code injection.
Class<?> baseClass = sig.getSignatureClass();
clazz._extends(baseClass);
// Create a constuctor for the class: either a default one,
// or (for nested classes) one that passes along arguments to
// the super class constructor.
Constructor<?>[] ctors = baseClass.getConstructors();
for (Constructor<?> ctor : ctors) {
addCtor(ctor.getParameterTypes());
}
// Some classes have no declared constructor, but we need to generate one
// anyway.
if ( ctors.length == 0 ) {
addCtor( new Class<?>[] {} );
}
// Repeat for inner classes.
for(ClassGenerator<T> child : innerClasses.values()) {
child.preparePlainJava();
// If there are inner classes, then we need to generate a "shim" method
// to instantiate that class.
//
// protected TemplateClass.TemplateInnerClass newTemplateInnerClass( args... ) {
// return new GeneratedClass.GeneratedInnerClass( args... );
// }
//
// The name is special, it is "new" + inner class name. The template must
// provide a method of this name that creates the inner class instance.
String innerClassName = child.clazz.name();
JMethod shim = clazz.method(JMod.PROTECTED, child.sig.getSignatureClass(), "new" + innerClassName);
JInvocation childNew = JExpr._new(child.clazz);
Constructor<?>[] childCtors = child.sig.getSignatureClass().getConstructors();
Class<?>[] params;
if (childCtors.length==0) {
params = new Class<?>[0];
} else {
params = childCtors[0].getParameterTypes();
}
for (int i = 1; i < params.length; i++) {
Class<?> p = params[i];
childNew.arg(shim.param(model._ref(p), "arg" + i));
}
shim.body()._return(JExpr._this().invoke("injectMembers").arg(childNew));
}
}
/**
* The code generator creates a method called __DRILL_INIT__ which takes the
* place of the constructor when the code goes though the byte code merge.
* For Plain-old Java, we call the method from a constructor created for
* that purpose. (Generated code, fortunately, never includes a constructor,
* so we can create one.) Since the init block throws an exception (which
* should never occur), the generated constructor converts the checked
* exception into an unchecked one so as to not require changes to the
* various places that create instances of the generated classes.
*
* Example:<code><pre>
* public StreamingAggregatorGen1() {
* try {
* __DRILL_INIT__();
* } catch (SchemaChangeException e) {
* throw new UnsupportedOperationException(e);
* }
* }</pre></code>
*
* Note: in Java 8 we'd use the <tt>Parameter</tt> class defined in Java's
* introspection package. But, Drill prefers Java 7 which only provides
* parameter types.
*/
private void addCtor(Class<?>[] parameters) {
JMethod ctor = clazz.constructor(JMod.PUBLIC);
JBlock body = ctor.body();
// If there are parameters, need to pass them to the super class.
if (parameters.length > 0) {
JInvocation superCall = JExpr.invoke("super");
// This case only occurs for nested classes, and all nested classes
// in Drill are inner classes. Don't pass along the (hidden)
// this$0 field.
for (int i = 1; i < parameters.length; i++) {
Class<?> p = parameters[i];
superCall.arg(ctor.param(model._ref(p), "arg" + i));
}
body.add(superCall);
}
JTryBlock tryBlock = body._try();
tryBlock.body().invoke(SignatureHolder.DRILL_INIT_METHOD);
JCatchBlock catchBlock = tryBlock._catch(model.ref(SchemaChangeException.class));
catchBlock.body()._throw(JExpr._new(model.ref(UnsupportedOperationException.class)).arg(catchBlock.param("e")));
}
private static class ValueVectorSetup {
final DirectExpression batch;
final TypedFieldId fieldId;
public ValueVectorSetup(DirectExpression batch, TypedFieldId fieldId) {
super();
this.batch = batch;
this.fieldId = fieldId;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((batch == null) ? 0 : batch.hashCode());
result = prime * result + ((fieldId == null) ? 0 : fieldId.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ValueVectorSetup other = (ValueVectorSetup) obj;
if (batch == null) {
if (other.batch != null) {
return false;
}
} else if (!batch.equals(other.batch)) {
return false;
}
if (fieldId == null) {
if (other.fieldId != null) {
return false;
}
} else if (!fieldId.equals(other.fieldId)) {
return false;
}
return true;
}
}
/**
* Represents a (Nullable)?(Type)Holder instance. Allows code
* gen to declare, set, work with and retrieve values from a holder.
* Holders exploit scalar replacement in Drill (or in Java): that
* the holder can, via code rewrites, be replaced by scalars that
* do the same job.
*/
// TODO: Might be better modeled as a set of classes for the special
// kinds of holders rather than a generic class and many if-statements.
public static class HoldingContainer {
private final JVar holder;
private final JFieldRef value;
private final JFieldRef isSet;
private final MajorType type;
private boolean isConstant;
private final boolean singularRepeated;
private final boolean isReader;
public HoldingContainer(MajorType t, JVar holder, JFieldRef value, JFieldRef isSet) {
this(t, holder, value, isSet, false, false);
}
public HoldingContainer(MajorType t, JVar holder, JFieldRef value, JFieldRef isSet, boolean singularRepeated, boolean isReader) {
this.holder = holder;
this.value = value;
this.isSet = isSet;
this.type = t;
this.isConstant = false;
this.singularRepeated = singularRepeated;
this.isReader = isReader;
}
public HoldingContainer(HoldingContainer from) {
this.holder = from.holder;
this.value =from. value;
this.isSet = from.isSet;
this.type = from.type;
this.isConstant = from.isConstant;
this.singularRepeated = from.singularRepeated;
this.isReader = from.isReader;
}
public boolean isReader() {
return isReader;
}
public boolean isSingularRepeated() {
return singularRepeated;
}
public HoldingContainer setConstant(boolean isConstant) {
this.isConstant = isConstant;
return this;
}
public JFieldRef f(String name) {
return holder.ref(name);
}
public boolean isConstant() {
return isConstant;
}
public JVar getHolder() {
return holder;
}
public JFieldRef getValue() {
return value;
}
public MajorType getMajorType() {
return type;
}
public JFieldRef getIsSet() {
Preconditions.checkNotNull(isSet,
"You cannot access the isSet variable when operating on a non-nullable output value.");
return isSet;
}
public boolean isOptional() {
return type.getMode() == DataMode.OPTIONAL;
}
public boolean isRepeated() {
return type.getMode() == DataMode.REPEATED;
}
public TypeProtos.MinorType getMinorType() {
return type.getMinorType();
}
/**
* Convert holder to a string for debugging use.
*/
@Override
public String toString() {
DebugStringBuilder buf = new DebugStringBuilder(this);
if (isConstant()) {
buf.append("const ");
}
buf.append(holder.type().name())
.append(" ")
.append(holder.name())
.append(", ")
.append(type.getMode().name())
.append(" ")
.append(type.getMinorType().name())
.append(", ");
holder.generate(buf.formatter());
if (value != null) {
buf.append(", ");
value.generate(buf.formatter());
}
return buf.toString();
}
}
public JType getHolderType(MajorType t) {
return TypeHelper.getHolderType(model, t.getMinorType(), t.getMode());
}
}