/* * Licensed 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 com.facebook.presto.sql.gen; import com.facebook.presto.bytecode.BytecodeBlock; import com.facebook.presto.bytecode.BytecodeNode; import com.facebook.presto.bytecode.Variable; import com.facebook.presto.bytecode.control.IfStatement; import com.facebook.presto.bytecode.expression.BytecodeExpression; import com.facebook.presto.bytecode.instruction.LabelNode; import com.facebook.presto.metadata.Signature; import com.facebook.presto.spi.block.Block; import com.facebook.presto.spi.type.Type; import com.facebook.presto.sql.relational.ConstantExpression; import com.facebook.presto.sql.relational.RowExpression; import java.util.List; import static com.facebook.presto.bytecode.expression.BytecodeExpressions.constantInt; import static com.facebook.presto.sql.gen.SqlTypeBytecodeExpression.constantType; import static com.google.common.base.Preconditions.checkArgument; public class DereferenceCodeGenerator implements BytecodeGenerator { @Override public BytecodeNode generateExpression(Signature signature, BytecodeGeneratorContext generator, Type returnType, List<RowExpression> arguments) { checkArgument(arguments.size() == 2); CallSiteBinder callSiteBinder = generator.getCallSiteBinder(); BytecodeBlock block = new BytecodeBlock().comment("DEREFERENCE").setDescription("DEREFERENCE"); Variable wasNull = generator.wasNull(); Variable rowBlock = generator.getScope().createTempVariable(Block.class); int index = (int) ((ConstantExpression) arguments.get(1)).getValue(); // clear the wasNull flag before evaluating the row value block.putVariable(wasNull, false); block.append(generator.generate(arguments.get(0))).putVariable(rowBlock); IfStatement ifRowBlockIsNull = new IfStatement("if row block is null...") .condition(wasNull); Class<?> javaType = returnType.getJavaType(); LabelNode end = new LabelNode("end"); ifRowBlockIsNull.ifTrue() .comment("if row block is null, push null to the stack and goto 'end' label (return)") .putVariable(wasNull, true) .pushJavaDefault(javaType) .gotoLabel(end); block.append(ifRowBlockIsNull); IfStatement ifFieldIsNull = new IfStatement("if row field is null..."); ifFieldIsNull.condition() .comment("call rowBlock.isNull(index)") .append(rowBlock) .push(index) .invokeInterface(Block.class, "isNull", boolean.class, int.class); ifFieldIsNull.ifTrue() .comment("if the field is null, push null to stack") .putVariable(wasNull, true) .pushJavaDefault(javaType); BytecodeExpression value = constantType(callSiteBinder, returnType).getValue(rowBlock, constantInt(index)); ifFieldIsNull.ifFalse() .comment("otherwise call type.getTYPE(rowBlock, index)") .append(value) .putVariable(wasNull, false); block.append(ifFieldIsNull) .visitLabel(end); return block; } }