blob: b33a7c470055b145bdc607973ac1f90d2b1383b3 [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.tajo.engine.codegen;
import com.google.common.base.Preconditions;
import org.apache.tajo.common.TajoDataTypes;
import org.apache.tajo.function.FunctionUtil;
import org.apache.tajo.function.StaticMethodInvocationDesc;
import org.apache.tajo.org.objectweb.asm.Label;
import org.apache.tajo.org.objectweb.asm.Opcodes;
import org.apache.tajo.plan.expr.EvalNode;
import org.apache.tajo.plan.expr.FunctionEval;
import java.util.Stack;
/**
* It generates the scalar function binding code for StaticMethodInvocationDesc.
*
* @see org.apache.tajo.function.StaticMethodInvocationDesc
*/
public class ScalarFunctionBindingEmitter {
public static void emit(EvalCodeGenerator generator, EvalCodeGenContext context, FunctionEval func,
Stack<EvalNode> stack) {
EvalNode [] params = func.getArgs();
StaticMethodInvocationDesc method = func.getFuncDesc().getInvocation().getScalar();
// check if there are at least one nullable field.
int notNullParamNum = 0;
for (Class paramClass : method.getParamClasses()) {
notNullParamNum += !FunctionUtil.isNullableParam(paramClass) ? 1 : 0;
}
int nullParamFlag = -1;
if (notNullParamNum > 0) {
// initialize the base null flag
context.methodvisitor.visitInsn(Opcodes.ICONST_1);
nullParamFlag = context.istore();
}
stack.push(func);
for (int paramIdx = 0; paramIdx < func.getArgs().length; paramIdx++) {
Class clazz = method.getParamClasses()[paramIdx];
generator.visit(context, params[paramIdx], stack);
if (FunctionUtil.isNullableParam(clazz)) {
emitBoxedParameter(context, func.getArgs()[paramIdx].getValueType());
} else {
updateNullFlag(context, clazz, nullParamFlag);
}
}
stack.pop();
if (notNullParamNum > 0) {
Label ifNull = new Label();
Label afterAll = new Label();
Preconditions.checkArgument(nullParamFlag >= 0);
context.iload(nullParamFlag);
context.methodvisitor.visitJumpInsn(Opcodes.IFEQ, ifNull);
// -- If all parameters are NOT NULL
context.invokeStatic(
method.getBaseClassName(),
method.getMethodName(),
method.getReturnClass(),
method.getParamClasses());
emitFunctionReturnValue(context, func.getValueType(), method);
context.gotoLabel(afterAll);
// -- If at least parameter is NULL
context.methodvisitor.visitLabel(ifNull);
for (int paramIdx = 0; paramIdx < func.getArgs().length; paramIdx++) {
Class clazz = method.getParamClasses()[paramIdx];
if (FunctionUtil.isNullableParam(clazz)) {
context.pop();
} else {
context.pop(func.getArgs()[paramIdx].getValueType());
}
}
context.pushDummyValue(func.getValueType());
context.pushNullFlag(false);
// -- After All
context.methodvisitor.visitLabel(afterAll);
} else {
context.invokeStatic(
method.getBaseClassName(),
method.getMethodName(),
method.getReturnClass(),
method.getParamClasses());
emitFunctionReturnValue(context, func.getValueType(), method);
}
}
private static void emitFunctionReturnValue(EvalCodeGenContext context, TajoDataTypes.DataType returnType,
StaticMethodInvocationDesc method) {
if (FunctionUtil.isNullableParam(method.getReturnClass())) {
Label ifNull = new Label();
Label afterAll = new Label();
context.dup();
context.methodvisitor.visitJumpInsn(Opcodes.IFNULL, ifNull);
context.emitUnboxing(context, returnType);
context.pushNullFlag(true); // push null flag
context.gotoLabel(afterAll);
context.markLabel(ifNull);
context.pop(); // remove null reference
context.pushDummyValue(returnType); // push dummy value for stack balance
context.pushNullFlag(false); // push null flag
context.markLabel(afterAll);
} else {
context.pushNullFlag(true);
}
}
private static void updateNullFlag(EvalCodeGenContext context, Class clazz, int nullFlagId) {
Preconditions.checkArgument(!FunctionUtil.isNullableParam(clazz));
context.iload(nullFlagId);
context.methodvisitor.visitInsn(Opcodes.IAND);
context.istore(nullFlagId);
}
private static void emitBoxedParameter(EvalCodeGenContext context, TajoDataTypes.DataType dataType) {
Label ifNull = new Label();
Label afterAll = new Label();
context.emitNullityCheck(ifNull);
context.emitBoxing(context, dataType);
context.gotoLabel(afterAll);
context.markLabel(ifNull);
context.pop(dataType); // pop dummy value
context.methodvisitor.visitInsn(Opcodes.ACONST_NULL);
context.methodvisitor.visitLabel(afterAll);
}
}