| /** |
| * 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.tajo.engine.codegen; |
| |
| import com.google.common.base.Preconditions; |
| import com.google.common.collect.Maps; |
| import org.apache.tajo.common.TajoDataTypes; |
| import org.apache.tajo.datum.*; |
| import org.apache.tajo.exception.InvalidValueForCastException; |
| import org.apache.tajo.exception.TajoRuntimeException; |
| import org.apache.tajo.exception.UnsupportedException; |
| import org.apache.tajo.org.objectweb.asm.Label; |
| import org.apache.tajo.org.objectweb.asm.MethodVisitor; |
| import org.apache.tajo.org.objectweb.asm.Opcodes; |
| import org.apache.tajo.org.objectweb.asm.Type; |
| import org.apache.tajo.org.objectweb.asm.commons.GeneratorAdapter; |
| import org.apache.tajo.org.objectweb.asm.commons.TableSwitchGenerator; |
| import org.apache.tajo.plan.expr.EvalNode; |
| import org.apache.tajo.plan.expr.EvalType; |
| import org.apache.tajo.type.Char; |
| import org.apache.tajo.util.TUtil; |
| import org.apache.tajo.util.datetime.DateTimeUtil; |
| |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| import static org.apache.tajo.common.TajoDataTypes.Type.*; |
| |
| class TajoGeneratorAdapter { |
| |
| public static final Map<EvalType, Map<TajoDataTypes.Type, Integer>> OpCodesMap = Maps.newHashMap(); |
| |
| static { |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT1, Opcodes.IADD); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT2, Opcodes.IADD); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT4, Opcodes.IADD); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, INT8, Opcodes.LADD); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, FLOAT4, Opcodes.FADD); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.PLUS, FLOAT8, Opcodes.DADD); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT1, Opcodes.ISUB); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT2, Opcodes.ISUB); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT4, Opcodes.ISUB); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, INT8, Opcodes.LSUB); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, FLOAT4, Opcodes.FSUB); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MINUS, FLOAT8, Opcodes.DSUB); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT1, Opcodes.IMUL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT2, Opcodes.IMUL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT4, Opcodes.IMUL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, INT8, Opcodes.LMUL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, FLOAT4, Opcodes.FMUL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MULTIPLY, FLOAT8, Opcodes.DMUL); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT1, Opcodes.IDIV); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT2, Opcodes.IDIV); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT4, Opcodes.IDIV); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, INT8, Opcodes.LDIV); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, FLOAT4, Opcodes.FDIV); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.DIVIDE, FLOAT8, Opcodes.DDIV); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT1, Opcodes.IREM); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT2, Opcodes.IREM); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT4, Opcodes.IREM); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, INT8, Opcodes.LREM); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, FLOAT4, Opcodes.FREM); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.MODULAR, FLOAT8, Opcodes.DREM); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT1, Opcodes.IAND); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT2, Opcodes.IAND); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT4, Opcodes.IAND); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_AND, INT8, Opcodes.LAND); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT1, Opcodes.IOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT2, Opcodes.IOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT4, Opcodes.IOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_OR, INT8, Opcodes.LOR); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT1, Opcodes.IXOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT2, Opcodes.IXOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT4, Opcodes.IXOR); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.BIT_XOR, INT8, Opcodes.LXOR); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT1, Opcodes.IF_ICMPEQ); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT2, Opcodes.IF_ICMPEQ); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT4, Opcodes.IF_ICMPEQ); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, FLOAT8, Opcodes.DCMPG); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.EQUAL, TEXT, Opcodes.IF_ACMPNE); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT1, Opcodes.IF_ICMPNE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT2, Opcodes.IF_ICMPNE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT4, Opcodes.IF_ICMPNE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, FLOAT8, Opcodes.DCMPG); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.NOT_EQUAL, TEXT, Opcodes.IF_ACMPNE); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT1, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT2, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT4, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT8, Opcodes.DCMPG); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT1, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT2, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT4, Opcodes.IF_ICMPLT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LTH, FLOAT8, Opcodes.DCMPG); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT1, Opcodes.IF_ICMPLE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT2, Opcodes.IF_ICMPLE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT4, Opcodes.IF_ICMPLE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.LEQ, FLOAT8, Opcodes.DCMPG); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT1, Opcodes.IF_ICMPGT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT2, Opcodes.IF_ICMPGT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT4, Opcodes.IF_ICMPGT); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GTH, FLOAT8, Opcodes.DCMPG); |
| |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT1, Opcodes.IF_ICMPGE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT2, Opcodes.IF_ICMPGE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT4, Opcodes.IF_ICMPGE); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, INT8, Opcodes.LCMP); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, FLOAT4, Opcodes.FCMPL); |
| TUtil.putToNestedMap(OpCodesMap, EvalType.GEQ, FLOAT8, Opcodes.DCMPG); |
| } |
| |
| protected int access; |
| protected MethodVisitor methodvisitor; |
| protected GeneratorAdapter generatorAdapter; |
| |
| public TajoGeneratorAdapter() {} |
| |
| public TajoGeneratorAdapter(int access, MethodVisitor methodVisitor, String name, String desc) { |
| this.access = access; |
| this.methodvisitor = methodVisitor; |
| generatorAdapter = new GeneratorAdapter(methodVisitor, access, name, desc); |
| } |
| |
| public static boolean isJVMInternalInt(org.apache.tajo.type.Type type) { |
| final TajoDataTypes.Type baseType = type.kind(); |
| return baseType == BOOLEAN || baseType == INT1 || baseType == INT2 || baseType == INT4 || baseType== INET4; |
| } |
| |
| public static int getWordSize(org.apache.tajo.type.Type type) { |
| final TajoDataTypes.Type baseType = type.kind(); |
| if (baseType == INT8 || baseType == FLOAT8 || baseType == TIMESTAMP || baseType == TIME) { |
| return 2; |
| } else { |
| return 1; |
| } |
| } |
| |
| public void push(final boolean value) { |
| methodvisitor.visitInsn(value ? Opcodes.ICONST_1 : Opcodes.ICONST_0); |
| } |
| |
| public void push(final int value) { |
| if (value >= -1 && value <= 5) { |
| methodvisitor.visitInsn(Opcodes.ICONST_0 + value); |
| } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { |
| methodvisitor.visitIntInsn(Opcodes.BIPUSH, value); |
| } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { |
| methodvisitor.visitIntInsn(Opcodes.SIPUSH, value); |
| } else { |
| methodvisitor.visitLdcInsn(Integer.valueOf(value)); |
| } |
| } |
| |
| public void push(final long value) { |
| if (value == 0L || value == 1L) { |
| methodvisitor.visitInsn(Opcodes.LCONST_0 + (int) value); |
| } else { |
| methodvisitor.visitLdcInsn(Long.valueOf(value)); |
| } |
| } |
| |
| public void push(final float value) { |
| int bits = Float.floatToIntBits(value); |
| if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 |
| methodvisitor.visitInsn(Opcodes.FCONST_0 + (int) value); |
| } else { |
| methodvisitor.visitLdcInsn(Float.valueOf(value)); |
| } |
| } |
| |
| public void push(final double value) { |
| long bits = Double.doubleToLongBits(value); |
| if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d |
| methodvisitor.visitInsn(Opcodes.DCONST_0 + (int) value); |
| } else { |
| methodvisitor.visitLdcInsn(new Double(value)); |
| } |
| } |
| |
| public void push(final String value) { |
| Preconditions.checkNotNull(value); |
| methodvisitor.visitLdcInsn(value); |
| } |
| |
| public void ifCmp(org.apache.tajo.type.Type type, EvalType evalType, Label elseLabel) { |
| |
| if (isJVMInternalInt(type)) { |
| switch (evalType) { |
| case EQUAL: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPNE, elseLabel); |
| break; |
| case NOT_EQUAL: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPEQ, elseLabel); |
| break; |
| case LTH: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPGE, elseLabel); |
| break; |
| case LEQ: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPGT, elseLabel); |
| break; |
| case GTH: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPLE, elseLabel); |
| break; |
| case GEQ: |
| methodvisitor.visitJumpInsn(Opcodes.IF_ICMPLT, elseLabel); |
| break; |
| default: |
| throw new CompilationError("Unknown comparison operator: " + evalType.name()); |
| } |
| } else { |
| |
| if (type.kind() == TEXT) { |
| invokeVirtual(String.class, "compareTo", int.class, new Class[]{String.class}); |
| } else { |
| int opCode = TajoGeneratorAdapter.getOpCode(evalType, type); |
| methodvisitor.visitInsn(opCode); |
| } |
| |
| switch (evalType) { |
| case EQUAL: |
| methodvisitor.visitJumpInsn(Opcodes.IFNE, elseLabel); |
| break; |
| case NOT_EQUAL: |
| methodvisitor.visitJumpInsn(Opcodes.IFEQ, elseLabel); |
| break; |
| case LTH: |
| methodvisitor.visitJumpInsn(Opcodes.IFGE, elseLabel); |
| break; |
| case LEQ: |
| methodvisitor.visitJumpInsn(Opcodes.IFGT, elseLabel); |
| break; |
| case GTH: |
| methodvisitor.visitJumpInsn(Opcodes.IFLE, elseLabel); |
| break; |
| case GEQ: |
| methodvisitor.visitJumpInsn(Opcodes.IFLT, elseLabel); |
| break; |
| default: |
| throw new CompilationError("Unknown comparison operator: " + evalType.name()); |
| } |
| } |
| } |
| |
| public void load(org.apache.tajo.type.Type type, int idx) { |
| switch (type.kind()) { |
| case NULL_TYPE: |
| case BOOLEAN: |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: |
| case INET4: |
| methodvisitor.visitVarInsn(Opcodes.ILOAD, idx); |
| break; |
| case INT8: |
| methodvisitor.visitVarInsn(Opcodes.LLOAD, idx); |
| break; |
| case FLOAT4: |
| methodvisitor.visitVarInsn(Opcodes.FLOAD, idx); |
| break; |
| case FLOAT8: |
| methodvisitor.visitVarInsn(Opcodes.DLOAD, idx); |
| break; |
| case TEXT: |
| case INTERVAL: |
| case PROTOBUF: |
| methodvisitor.visitVarInsn(Opcodes.ALOAD, idx); |
| break; |
| default: |
| throw new CompilationError("Unknown data type: " + type); |
| } |
| } |
| |
| public static String getDescription(Class clazz) { |
| if (clazz == null) { |
| return ""; |
| } else if (clazz == void.class) { |
| return "V"; |
| } else if (clazz == boolean.class) { |
| return "Z"; |
| } else if (clazz == char.class) { |
| return "C"; |
| } else if (clazz == byte.class) { |
| return "B"; |
| } else if (clazz == short.class) { |
| return "S"; |
| } else if (clazz == int.class) { |
| return "I"; |
| } else if (clazz == long.class) { |
| return "J"; |
| } else if (clazz == float.class) { |
| return "F"; |
| } else if (clazz == double.class) { |
| return "D"; |
| } else if (clazz.isArray()) { |
| return "[" + getDescription(clazz.getComponentType()); |
| } else { |
| return "L" + getInternalName(clazz) + ";"; |
| } |
| } |
| |
| public static String getMethodDescription(Class returnType, Class [] argumentTypes) { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("("); |
| if (argumentTypes != null) { |
| for (Class argType : argumentTypes) { |
| builder.append(getDescription(argType)); |
| } |
| } |
| builder.append(")"); |
| |
| builder.append(getDescription(returnType)); |
| return builder.toString(); |
| } |
| |
| public Label newLabel() { |
| return new Label(); |
| } |
| |
| public void markLabel(Label label) { |
| methodvisitor.visitLabel(label); |
| } |
| |
| public void gotoLabel(Label label) { |
| methodvisitor.visitJumpInsn(Opcodes.GOTO, label); |
| } |
| |
| public void pushBooleanOfThreeValuedLogic(boolean value) { |
| push(value ? 1 : 2); // TRUE or FALSE |
| } |
| |
| public void pushNullOfThreeValuedLogic() { |
| push(0); // NULL of three valued logic |
| } |
| |
| public void pushNullFlag(boolean trueIfNotNull) { |
| push(trueIfNotNull ? true : false); |
| } |
| |
| public void emitNullityCheck(Label ifNull) { |
| methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNull); |
| } |
| |
| /** |
| * If at least one of all local variables corresponding to <code>varIds</code> is null, jump the <code>label</code>. |
| * |
| * @param ifNull The label to jump |
| * @param varIds A list of variable Ids. |
| */ |
| public void emitNullityCheck(Label ifNull, int ... varIds) { |
| // TODO - ANDing can be reduced if we interleave IAND into a sequence of ILOAD instructions. |
| for (int varId : varIds) { |
| methodvisitor.visitVarInsn(Opcodes.ILOAD, varId); |
| } |
| if (varIds.length > 1) { |
| for (int i = 0; i < varIds.length - 1; i++) { |
| methodvisitor.visitInsn(Opcodes.IAND); |
| } |
| } |
| emitNullityCheck(ifNull); |
| } |
| |
| public void pushDummyValue(org.apache.tajo.type.Type type) { |
| TajoDataTypes.Type baseType = type.kind(); |
| |
| if (type.isNull()) { |
| pushNullOfThreeValuedLogic(); |
| } else if (isJVMInternalInt(type) || baseType == DATE) { |
| push(0); |
| } else if (baseType == INT8 || baseType == TIMESTAMP || baseType == TIME) { |
| push(0L); |
| } else if (baseType == FLOAT8) { |
| push(0.0d); |
| } else if (baseType == FLOAT4) { |
| push(0.0f); |
| } else if (baseType == CHAR || baseType == TEXT) { |
| push(""); |
| } else if (baseType == INTERVAL || baseType == PROTOBUF) { |
| invokeStatic(NullDatum.class, "get", NullDatum.class, new Class[]{}); |
| } else { |
| assert false; |
| } |
| } |
| |
| public void newInstance(Class owner, Class [] paramTypes) { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, getInternalName(owner), "<init>", |
| getMethodDescription(void.class, paramTypes)); |
| } |
| |
| public void invokeSpecial(Class owner, String methodName, Class returnType, Class [] paramTypes) { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESPECIAL, getInternalName(owner), methodName, |
| getMethodDescription(returnType, paramTypes)); |
| } |
| |
| public void invokeStatic(Class owner, String methodName, Class returnType, Class [] paramTypes) { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, getInternalName(owner), methodName, |
| getMethodDescription(returnType, paramTypes)); |
| } |
| |
| public void invokeVirtual(Class owner, String methodName, Class returnType, Class [] paramTypes) { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, Type.getInternalName(owner), methodName, |
| getMethodDescription(returnType, paramTypes)); |
| } |
| |
| public void invokeInterface(Class owner, String methodName, Class returnType, Class [] paramTypes) { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKEINTERFACE, Type.getInternalName(owner), methodName, |
| getMethodDescription(returnType, paramTypes)); |
| } |
| |
| public static boolean isPrimitiveOpCode(EvalType evalType, org.apache.tajo.type.Type returnType) { |
| return TUtil.containsInNestedMap(OpCodesMap, evalType, returnType.kind()); |
| } |
| |
| public static int getOpCode(EvalType evalType, org.apache.tajo.type.Type returnType) { |
| if (!isPrimitiveOpCode(evalType, returnType)) { |
| throw new CompilationError("No Such OpCode for " + evalType + " returning " + returnType); |
| } |
| return TUtil.getFromNestedMap(OpCodesMap, evalType, returnType.kind()); |
| } |
| |
| public void castInsn(org.apache.tajo.type.Type srcType, org.apache.tajo.type.Type targetType) { |
| TajoDataTypes.Type srcBaseType = srcType.kind(); |
| TajoDataTypes.Type targetBaseType = targetType.kind(); |
| switch(srcBaseType) { |
| case BOOLEAN: |
| case CHAR: { |
| Char srcCharType = (Char) srcType; |
| if (srcCharType.length() == 1) { |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: break; |
| case INT8: methodvisitor.visitInsn(Opcodes.I2L); break; |
| case FLOAT4: methodvisitor.visitInsn(Opcodes.I2F); break; |
| case FLOAT8: methodvisitor.visitInsn(Opcodes.I2D); break; |
| case TEXT: emitStringValueOfChar(); break; |
| default: |
| throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| } else { |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: emitParseInt4(); break; |
| case INT8: emitParseInt8(); break; |
| case FLOAT4: emitParseFloat4(); break; |
| case FLOAT8: emitParseFloat8(); break; |
| case TEXT: break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| } |
| break; |
| } |
| case INT1: |
| case INT2: |
| case INT4: |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: methodvisitor.visitInsn(Opcodes.I2C); break; |
| case INT2: methodvisitor.visitInsn(Opcodes.I2S); break; |
| case INT4: return; |
| case INT8: methodvisitor.visitInsn(Opcodes.I2L); break; |
| case FLOAT4: methodvisitor.visitInsn(Opcodes.I2F); break; |
| case FLOAT8: methodvisitor.visitInsn(Opcodes.I2D); break; |
| case TEXT: emitStringValueOfInt4(); break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| break; |
| case INT8: |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: methodvisitor.visitInsn(Opcodes.L2I); break; |
| case INT8: return; |
| case FLOAT4: methodvisitor.visitInsn(Opcodes.L2F); break; |
| case FLOAT8: methodvisitor.visitInsn(Opcodes.L2D); break; |
| case TEXT: emitStringValueOfInt8(); break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| break; |
| case FLOAT4: |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: methodvisitor.visitInsn(Opcodes.F2I); break; |
| case INT8: methodvisitor.visitInsn(Opcodes.F2L); break; |
| case FLOAT4: return; |
| case FLOAT8: methodvisitor.visitInsn(Opcodes.F2D); break; |
| case TEXT: emitStringValueOfFloat4(); break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| break; |
| case FLOAT8: |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: methodvisitor.visitInsn(Opcodes.D2I); break; |
| case INT8: methodvisitor.visitInsn(Opcodes.D2L); break; |
| case FLOAT4: methodvisitor.visitInsn(Opcodes.D2F); break; |
| case FLOAT8: return; |
| case TEXT: emitStringValueOfFloat8(); break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| break; |
| case TEXT: |
| switch (targetBaseType) { |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: emitParseInt4(); break; |
| case INT8: emitParseInt8(); break; |
| case FLOAT4: emitParseFloat4(); break; |
| case FLOAT8: emitParseFloat8(); break; |
| case TEXT: break; |
| case TIMESTAMP: { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), |
| "toJulianTimestampWithTZ", "(L" + Type.getInternalName(String.class) + ";)J"); |
| break; |
| } |
| case DATE: { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), |
| "toJulianDate", "(L" + Type.getInternalName(String.class) + ";)I"); |
| break; |
| } |
| case TIME: { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(DateTimeUtil.class), |
| "toJulianTime", "(L" + Type.getInternalName(String.class) + ";)J"); |
| break; |
| } |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| break; |
| default: throw new TajoRuntimeException(new InvalidValueForCastException(srcType, targetType)); |
| } |
| } |
| |
| public static String getInternalName(String className) { |
| return className.replace('.', '/'); |
| } |
| |
| public static String getInternalName(Class clazz) { |
| return clazz.getName().replace('.', '/'); |
| } |
| |
| public void convertToPrimitive(org.apache.tajo.type.Type type) { |
| |
| Label ifNull = new Label(); |
| Label afterAll = new Label(); |
| |
| // datum |
| int datum = astore(); |
| |
| aload(datum); |
| invokeVirtual(Datum.class, "isNotNull", boolean.class, new Class [] {}); |
| methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNull); // datum |
| |
| aload(datum); |
| switch (type.kind()) { |
| case BOOLEAN: |
| case INT1: |
| case INT2: |
| invokeVirtual(Datum.class, "asInt2", short.class, new Class[] {}); |
| break; |
| case INT4: |
| case DATE: |
| invokeVirtual(Datum.class, "asInt4", int.class, new Class[] {}); |
| break; |
| case INT8: |
| case TIMESTAMP: |
| case TIME: |
| invokeVirtual(Datum.class, "asInt8", long.class, new Class[] {}); |
| break; |
| case FLOAT4: |
| invokeVirtual(Datum.class, "asFloat4", float.class, new Class[] {}); |
| break; |
| case FLOAT8: |
| invokeVirtual(Datum.class, "asFloat8", double.class, new Class[] {}); |
| break; |
| case CHAR: |
| case TEXT: |
| invokeVirtual(Datum.class, "asChars", String.class, new Class[]{}); |
| break; |
| default: |
| throw new TajoRuntimeException(new UnsupportedException("data type '" + type + "'")); |
| } |
| |
| pushNullFlag(true); |
| gotoLabel(afterAll); |
| |
| methodvisitor.visitLabel(ifNull); |
| pushDummyValue(type); |
| pushNullFlag(false); |
| |
| methodvisitor.visitLabel(afterAll); |
| } |
| |
| public void convertToDatum(org.apache.tajo.type.Type type, boolean castToDatum) { |
| String convertMethod; |
| Class returnType; |
| Class [] paramTypes; |
| switch (type.kind()) { |
| case NULL_TYPE: |
| pop(); // pop null flag |
| pop(type); // pop null datum |
| invokeStatic(NullDatum.class, "get", NullDatum.class, new Class[] {}); |
| if (castToDatum) { |
| methodvisitor.visitTypeInsn(Opcodes.CHECKCAST, getInternalName(Datum.class)); |
| } |
| return; |
| |
| case BOOLEAN: |
| convertMethod = "createBool"; |
| returnType = Datum.class; |
| paramTypes = new Class[] {int.class}; |
| break; |
| case CHAR: |
| convertMethod = "createChar"; |
| returnType = CharDatum.class; |
| paramTypes = new Class[] {String.class}; |
| break; |
| case INT1: |
| case INT2: |
| convertMethod = "createInt2"; |
| returnType = Int2Datum.class; |
| paramTypes = new Class[] {short.class}; |
| break; |
| case INT4: |
| convertMethod = "createInt4"; |
| returnType = Int4Datum.class; |
| paramTypes = new Class[] {int.class}; |
| break; |
| case INT8: |
| convertMethod = "createInt8"; |
| returnType = Int8Datum.class; |
| paramTypes = new Class[] {long.class}; |
| break; |
| case FLOAT4: |
| convertMethod = "createFloat4"; |
| returnType = Float4Datum.class; |
| paramTypes = new Class[] {float.class}; |
| break; |
| case FLOAT8: |
| convertMethod = "createFloat8"; |
| returnType = Float8Datum.class; |
| paramTypes = new Class[] {double.class}; |
| break; |
| case TEXT: |
| convertMethod = "createText"; |
| returnType = TextDatum.class; |
| paramTypes = new Class[] {String.class}; |
| break; |
| case TIMESTAMP: |
| convertMethod = "createTimestamp"; |
| returnType = TimestampDatum.class; |
| paramTypes = new Class[] {long.class}; |
| break; |
| case DATE: |
| convertMethod = "createDate"; |
| returnType = DateDatum.class; |
| paramTypes = new Class[] {int.class}; |
| break; |
| case TIME: |
| convertMethod = "createTime"; |
| returnType = TimeDatum.class; |
| paramTypes = new Class[] {long.class}; |
| break; |
| case INTERVAL: |
| case PROTOBUF: |
| convertMethod = null; |
| returnType = null; |
| paramTypes = null; |
| break; |
| case INET4: |
| convertMethod = "createInet4"; |
| returnType = Inet4Datum.class; |
| paramTypes = new Class[] {int.class}; |
| break; |
| default: |
| throw new RuntimeException("Unsupported type: " + type); |
| } |
| |
| Label ifNull = new Label(); |
| Label afterAll = new Label(); |
| |
| emitNullityCheck(ifNull); |
| if (convertMethod != null) { |
| invokeStatic(DatumFactory.class, convertMethod, returnType, paramTypes); |
| } |
| methodvisitor.visitJumpInsn(Opcodes.GOTO, afterAll); |
| |
| methodvisitor.visitLabel(ifNull); |
| pop(type); |
| invokeStatic(NullDatum.class, "get", NullDatum.class, null); |
| |
| methodvisitor.visitLabel(afterAll); |
| if (castToDatum) { |
| methodvisitor.visitTypeInsn(Opcodes.CHECKCAST, TajoGeneratorAdapter.getInternalName(Datum.class)); |
| } |
| } |
| |
| public void pop(org.apache.tajo.type.Type type) { |
| if (getWordSize(type) == 2) { |
| methodvisitor.visitInsn(Opcodes.POP2); |
| } else { |
| methodvisitor.visitInsn(Opcodes.POP); |
| } |
| } |
| |
| public void emitStringValueOfChar() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), |
| "valueOf", "(C)L" + Type.getInternalName(String.class) + ";"); |
| } |
| |
| public void emitStringValueOfInt4() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), |
| "valueOf", "(I)L" + Type.getInternalName(String.class) + ";"); |
| } |
| |
| public void emitStringValueOfInt8() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), |
| "valueOf", "(J)L" + Type.getInternalName(String.class) + ";"); |
| } |
| |
| public void emitStringValueOfFloat4() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), |
| "valueOf", "(F)L" + Type.getInternalName(String.class) + ";"); |
| } |
| |
| public void emitStringValueOfFloat8() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(String.class), |
| "valueOf", "(D)L" + Type.getInternalName(String.class) + ";"); |
| } |
| |
| public void emitParseInt4() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Integer.class), |
| "parseInt", "(L" + Type.getInternalName(String.class) + ";)I"); |
| } |
| |
| public void emitParseInt8() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Long.class), |
| "parseLong", "(L" + Type.getInternalName(String.class) + ";)J"); |
| } |
| |
| public void emitParseFloat4() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Float.class), |
| "parseFloat", "(L" + Type.getInternalName(String.class) + ";)F"); |
| } |
| |
| public void emitParseFloat8() { |
| methodvisitor.visitMethodInsn(Opcodes.INVOKESTATIC, Type.getInternalName(Double.class), |
| "parseDouble", "(L" + Type.getInternalName(String.class) + ";)D"); |
| } |
| |
| public void newArray(final Class clazz) { |
| int typeCode; |
| if (clazz == boolean.class) { |
| typeCode = Opcodes.T_BOOLEAN; |
| } else if (clazz == char.class) { |
| typeCode = Opcodes.T_CHAR; |
| } else if (clazz == byte.class) { |
| typeCode = Opcodes.T_BYTE; |
| } else if (clazz == short.class) { |
| typeCode = Opcodes.T_SHORT; |
| } else if (clazz == int.class) { |
| typeCode = Opcodes.T_INT; |
| } else if (clazz == long.class) { |
| typeCode = Opcodes.T_LONG; |
| } else if (clazz == float.class) { |
| typeCode = Opcodes.T_FLOAT; |
| } else if (clazz == double.class) { |
| typeCode = Opcodes.T_DOUBLE; |
| } else { |
| methodvisitor.visitTypeInsn(Opcodes.ANEWARRAY, getInternalName(clazz)); |
| return; |
| } |
| |
| methodvisitor.visitIntInsn(Opcodes.NEWARRAY, typeCode); |
| } |
| |
| private int nextVarId = 3; |
| |
| private Map<String, Integer> localVariablesMap = new HashMap<>(); |
| |
| public void astore(String name) { |
| if (localVariablesMap.containsKey(name)) { |
| int varId = localVariablesMap.get(name); |
| methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); |
| } else { |
| int varId = nextVarId++; |
| methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); |
| localVariablesMap.put(name, varId); |
| } |
| } |
| |
| public int astore() { |
| int varId = getCurVarIdAndIncrease(); |
| methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); |
| return varId; |
| } |
| |
| public void astore(int varId) { |
| methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); |
| } |
| |
| public void aload(String name) { |
| if (localVariablesMap.containsKey(name)) { |
| int varId = localVariablesMap.get(name); |
| methodvisitor.visitVarInsn(Opcodes.ALOAD, varId); |
| } else { |
| throw new RuntimeException("No such variable name: " + name); |
| } |
| } |
| |
| public void aload(int varId) { |
| methodvisitor.visitVarInsn(Opcodes.ALOAD, varId); |
| } |
| |
| public void dup() { |
| methodvisitor.visitInsn(Opcodes.DUP); |
| } |
| |
| public void pop() { |
| methodvisitor.visitInsn(Opcodes.POP); |
| } |
| |
| public void pop2() { |
| methodvisitor.visitInsn(Opcodes.POP2); |
| } |
| |
| public int istore() { |
| int varId = getCurVarIdAndIncrease(); |
| return istore(varId); |
| } |
| |
| public int istore(int varId) { |
| methodvisitor.visitVarInsn(Opcodes.ISTORE, varId); |
| return varId; |
| } |
| |
| public void iload(int varId) { |
| methodvisitor.visitVarInsn(Opcodes.ILOAD, varId); |
| } |
| |
| private int getCurVarIdAndIncrease() { |
| int varId = nextVarId++; |
| return varId; |
| } |
| |
| public int store(org.apache.tajo.type.Type type) { |
| int varId = nextVarId; |
| nextVarId += TajoGeneratorAdapter.getWordSize(type); |
| |
| switch (type.kind()) { |
| case NULL_TYPE: |
| case BOOLEAN: |
| case CHAR: |
| case INT1: |
| case INT2: |
| case INT4: |
| case INET4: |
| methodvisitor.visitVarInsn(Opcodes.ISTORE, varId); |
| break; |
| case TIME: |
| case TIMESTAMP: |
| case INT8: |
| methodvisitor.visitVarInsn(Opcodes.LSTORE, varId); |
| break; |
| case FLOAT4: |
| methodvisitor.visitVarInsn(Opcodes.FSTORE, varId); |
| break; |
| case FLOAT8: |
| methodvisitor.visitVarInsn(Opcodes.DSTORE, varId); |
| break; |
| case INTERVAL: |
| case TEXT: |
| methodvisitor.visitVarInsn(Opcodes.ASTORE, varId); |
| break; |
| default: |
| throw new CompilationError("Unknown data type: " + type); |
| } |
| |
| return varId; |
| } |
| |
| public void emitBoxing(EvalCodeGenContext context, org.apache.tajo.type.Type type) { |
| switch (type.kind()) { |
| case CHAR: |
| case TEXT: |
| |
| case INT2: |
| context.invokeStatic(Short.class, "valueOf", Short.class, new Class[]{short.class}); |
| break; |
| case INT4: |
| context.invokeStatic(Integer.class, "valueOf", Integer.class, new Class[]{int.class}); |
| break; |
| case INT8: |
| context.invokeStatic(Long.class, "valueOf", Long.class, new Class[]{long.class}); |
| break; |
| case FLOAT4: |
| context.invokeStatic(Float.class, "valueOf", Float.class, new Class[]{float.class}); |
| break; |
| case FLOAT8: |
| context.invokeStatic(Double.class, "valueOf", Double.class, new Class[]{double.class}); |
| break; |
| |
| default: |
| throw new RuntimeException(type + " is not supported yet"); |
| } |
| } |
| |
| public void emitUnboxing(EvalCodeGenContext context, org.apache.tajo.type.Type type) { |
| switch (type.kind()) { |
| case CHAR: |
| case TEXT: |
| |
| case INT2: |
| context.invokeVirtual(Short.class, "shortValue", short.class, new Class[]{}); |
| break; |
| case INT4: |
| context.invokeVirtual(Integer.class, "intValue", int.class, new Class[]{}); |
| break; |
| case INT8: |
| context.invokeVirtual(Long.class, "longValue", long.class, new Class[]{}); |
| break; |
| case FLOAT4: |
| context.invokeVirtual(Float.class, "floatValue", float.class, new Class[]{}); |
| break; |
| case FLOAT8: |
| context.invokeVirtual(Double.class, "doubleValue", double.class, new Class[]{}); |
| break; |
| |
| default: |
| throw new RuntimeException(type + " is not supported yet"); |
| } |
| } |
| |
| public static interface SwitchCaseGenerator extends TableSwitchGenerator { |
| int size(); |
| int min(); |
| int max(); |
| int key(int index); |
| void generateCase(int index, Label end); |
| void generateDefault(); |
| } |
| |
| public static class SwitchCase implements Comparable<SwitchCase> { |
| private final int index; |
| private final EvalNode thanResult; |
| |
| public SwitchCase(int index, EvalNode thanResult) { |
| this.index = index; |
| this.thanResult = thanResult; |
| } |
| |
| public int key() { |
| return index; |
| } |
| |
| public EvalNode result() { |
| return thanResult; |
| } |
| |
| @Override |
| public int compareTo(SwitchCase o) { |
| return index - o.index; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (this == o) return true; |
| if (o == null || getClass() != o.getClass()) return false; |
| |
| SwitchCase that = (SwitchCase) o; |
| |
| if (index != that.index) return false; |
| if (thanResult != null ? !thanResult.equals(that.thanResult) : that.thanResult != null) return false; |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = index; |
| result = 31 * result + (thanResult != null ? thanResult.hashCode() : 0); |
| return result; |
| } |
| } |
| } |