|
Groovy example source code file (BinaryExpressionMultiTypeDispatcher.java)
This example Groovy source code file (BinaryExpressionMultiTypeDispatcher.java) is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.
The Groovy BinaryExpressionMultiTypeDispatcher.java source code
/*
* Copyright 2003-2010 the original author or authors.
*
* 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 org.codehaus.groovy.classgen.asm;
import java.util.HashMap;
import java.util.Map;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.Variable;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.asm.OptimizingStatementWriter.StatementMeta;
import org.codehaus.groovy.runtime.BytecodeInterface8;
import org.objectweb.asm.MethodVisitor;
import static org.codehaus.groovy.ast.ClassHelper.*;
import static org.codehaus.groovy.syntax.Types.*;
/**
* This class is for internal use only!
* This class will dispatch to the right type adapters according to the
* kind of binary expression that is provided.
* @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou
*/
public class BinaryExpressionMultiTypeDispatcher extends BinaryExpressionHelper {
private static class DummyHelper extends BinaryExpressionWriter {
public DummyHelper(WriterController controller) {
super(controller);
}
public boolean writePostOrPrefixMethod(int operation, boolean simulate) {
if (simulate) return false;
throw new GroovyBugError("should not reach here");
}
public boolean write(int operation, boolean simulate) {
if (simulate) return false;
throw new GroovyBugError("should not reach here");
}
public boolean arrayGet(int operation, boolean simulate) {
if (simulate) return false;
throw new GroovyBugError("should not reach here");
}
public boolean arraySet(boolean simulate) {
if (simulate) return false;
throw new GroovyBugError("should not reach here");
}
protected void doubleTwoOperands(MethodVisitor mv) {}
protected MethodCaller getArrayGetCaller() {
return null;
}
protected MethodCaller getArraySetCaller() {
return null;
}
protected int getBitwiseOperationBytecode(int type) {
return -1;
}
protected int getCompareCode() {
return -1;
}
protected ClassNode getNormalOpResultType() {
return null;
}
protected int getShiftOperationBytecode(int type) {
return -1;
}
protected int getStandardOperationBytecode(int type) {
return -1;
}
protected void removeTwoOperands(MethodVisitor mv) {}
protected void writePlusPlus(MethodVisitor mv) {}
protected void writeMinusMinus(MethodVisitor mv) {}
}
private static class BinaryCharExpressionHelper extends BinaryIntExpressionHelper {
public BinaryCharExpressionHelper(WriterController wc) {
super(wc);
}
private static final MethodCaller
charArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "cArrayGet"),
charArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "cArraySet");
@Override protected MethodCaller getArrayGetCaller() { return charArrayGet; }
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.char_TYPE; }
@Override protected MethodCaller getArraySetCaller() { return charArraySet; }
}
private static class BinaryByteExpressionHelper extends BinaryIntExpressionHelper {
public BinaryByteExpressionHelper(WriterController wc) {
super(wc);
}
private static final MethodCaller
byteArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "bArrayGet"),
byteArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "bArraySet");
@Override protected MethodCaller getArrayGetCaller() { return byteArrayGet; }
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.byte_TYPE; }
@Override protected MethodCaller getArraySetCaller() { return byteArraySet; }
}
private static class BinaryShortExpressionHelper extends BinaryIntExpressionHelper {
public BinaryShortExpressionHelper(WriterController wc) {
super(wc);
}
private static final MethodCaller
shortArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "sArrayGet"),
shortArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "sArraySet");
@Override protected MethodCaller getArrayGetCaller() { return shortArrayGet; }
@Override protected ClassNode getArrayGetResultType() { return ClassHelper.short_TYPE; }
@Override protected MethodCaller getArraySetCaller() { return shortArraySet; }
}
private BinaryExpressionWriter[] binExpWriter = {
/* 0: dummy */ new DummyHelper(getController()),
/* 1: int */ new BinaryIntExpressionHelper(getController()),
/* 2: long */ new BinaryLongExpressionHelper(getController()),
/* 3: double */ new BinaryDoubleExpressionHelper(getController()),
/* 4: char */ new BinaryCharExpressionHelper(getController()),
/* 5: byte */ new BinaryByteExpressionHelper(getController()),
/* 6: short */ new BinaryShortExpressionHelper(getController()),
/* 7: float */ new BinaryFloatExpressionHelper(getController()),
};
protected static Map<ClassNode,Integer> typeMap = new HashMap(14);
static {
typeMap.put(int_TYPE, 1); typeMap.put(long_TYPE, 2);
typeMap.put(double_TYPE, 3); typeMap.put(char_TYPE, 4);
typeMap.put(byte_TYPE, 5); typeMap.put(short_TYPE, 6);
typeMap.put(float_TYPE, 7);
}
protected final static String[] typeMapKeyNames = {"dummy", "int", "long", "double", "char", "byte", "short", "float"};
public BinaryExpressionMultiTypeDispatcher(WriterController wc) {
super(wc);
}
/**
* return the type of an expression, taking meta data into account
*/
protected static ClassNode getType(Expression exp, ClassNode current) {
StatementMeta meta = (StatementMeta) exp.getNodeMetaData(StatementMeta.class);
ClassNode type = null;
if (meta!=null) type = meta.type;
if (type!=null) return type;
if (exp instanceof VariableExpression) {
VariableExpression ve = (VariableExpression) exp;
if (ve.isClosureSharedVariable()) return ve.getType();
type = ve.getOriginType();
if (ve.getAccessedVariable() instanceof FieldNode) {
FieldNode fn = (FieldNode) ve.getAccessedVariable();
if (!fn.getDeclaringClass().equals(current)) return OBJECT_TYPE;
}
} else if (exp instanceof Variable) {
Variable v = (Variable) exp;
type = v.getOriginType();
} else {
type = exp.getType();
}
return type.redirect();
}
protected static boolean isIntCategory(ClassNode type) {
return type == int_TYPE || type == char_TYPE ||
type == byte_TYPE || type == short_TYPE;
}
protected static boolean isLongCategory(ClassNode type) {
return type == long_TYPE || isIntCategory(type);
}
protected static boolean isDoubleCategory(ClassNode type) {
return type == float_TYPE || type == double_TYPE ||
isLongCategory(type);
}
private int getOperandConversionType(ClassNode leftType, ClassNode rightType) {
if (isIntCategory(leftType) && isIntCategory(rightType)) return 1;
if (isLongCategory(leftType) && isLongCategory(rightType)) return 2;
if (isDoubleCategory(leftType) && isDoubleCategory(rightType)) return 3;
return 0;
}
private int getOperandType(ClassNode type) {
Integer ret = typeMap.get(type);
if (ret==null) return 0;
return ret;
}
@Override
protected void evaluateCompareExpression(final MethodCaller compareMethod, BinaryExpression binExp) {
ClassNode current = getController().getClassNode();
int operation = binExp.getOperation().getType();
Expression leftExp = binExp.getLeftExpression();
ClassNode leftType = getType(leftExp, current);
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = getType(rightExp, current);
int operationType = getOperandConversionType(leftType,rightType);
BinaryExpressionWriter bew = binExpWriter[operationType];
if (bew.write(operation, true)) {
AsmClassGenerator acg = getController().getAcg();
OperandStack os = getController().getOperandStack();
leftExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
rightExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
bew.write(operation, false);
} else {
super.evaluateCompareExpression(compareMethod, binExp);
}
}
@Override
protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) {
int operation = binExp.getOperation().getType();
ClassNode current = getController().getClassNode();
Expression leftExp = binExp.getLeftExpression();
ClassNode leftTypeOrig = getType(leftExp, current);
ClassNode leftType = leftTypeOrig;
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = getType(rightExp, current);
if (operation==LEFT_SQUARE_BRACKET) {
leftType = leftTypeOrig.getComponentType();
}
int operationType = getOperandConversionType(leftType,rightType);
BinaryExpressionWriter bew = binExpWriter[operationType];
AsmClassGenerator acg = getController().getAcg();
OperandStack os = getController().getOperandStack();
if (bew.arrayGet(operation, true)) {
leftExp.visit(acg);
os.doGroovyCast(leftTypeOrig);
rightExp.visit(acg);
os.doGroovyCast(int_TYPE);
bew.arrayGet(operation, false);
os.doGroovyCast(bew.getArrayGetResultType());
} else if (bew.write(operation, true)) {
leftExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
rightExp.visit(acg);
os.doGroovyCast(bew.getNormalOpResultType());
bew.write(operation, false);
} else {
super.evaluateBinaryExpression(message, binExp);
}
}
private boolean isAssignmentToArray(BinaryExpression binExp) {
Expression leftExpression = binExp.getLeftExpression();
if (!(leftExpression instanceof BinaryExpression)) return false;
BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
if (leftBinExpr.getOperation().getType() != LEFT_SQUARE_BRACKET) return false;
return true;
}
private int removeAssignment(int op) {
switch (op) {
case PLUS_EQUAL: return PLUS;
case MINUS_EQUAL: return MINUS;
case MULTIPLY_EQUAL: return MULTIPLY;
default: return op;
}
}
@Override
protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression binExp) {
if (!isAssignmentToArray(binExp)) {
super.evaluateBinaryExpressionWithAssignment(method, binExp);
return;
}
// we need to handle only assignment to arrays combined with an operation
// special here. e.g x[a] += b
ClassNode current = getController().getClassNode();
int operation = removeAssignment(binExp.getOperation().getType());
Expression leftExp = binExp.getLeftExpression();
ClassNode leftType = getType(leftExp, current);
Expression rightExp = binExp.getRightExpression();
ClassNode rightType = getType(rightExp, current);
int operationType = getOperandConversionType(leftType,rightType);
BinaryExpressionWriter bew = binExpWriter[operationType];
boolean simulationSuccess = bew.arrayGet(LEFT_SQUARE_BRACKET, true);
simulationSuccess = simulationSuccess && bew.write(operation, true);
simulationSuccess = simulationSuccess && bew.arraySet(true);
if (!simulationSuccess) {
super.evaluateBinaryExpressionWithAssignment(method, binExp);
return;
}
AsmClassGenerator acg = getController().getAcg();
OperandStack operandStack = getController().getOperandStack();
CompileStack compileStack = getController().getCompileStack();
// for x[a] += b we have the structure:
// x = left(left(binExp))), b = right(binExp), a = right(left(binExp)))
// for array set we need these values on stack: array, index, right
// for array get we need these values on stack: array, index
// to eval the expression we need x[a] = x[a]+b
// -> arraySet(x,a, x[a]+b)
// -> arraySet(x,a, arrayGet(x,a,b))
// --> x,a, x,a, b as operands
// --> load x, load a, DUP2, call arrayGet, load b, call operation,call arraySet
// since we cannot DUP2 here easily we will save the subscript and DUP x
// --> sub=a, load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet
BinaryExpression arrayWithSubscript = (BinaryExpression) leftExp;
Expression subscript = arrayWithSubscript.getRightExpression();
// load array index: sub=a [load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call arraySet]
subscript.visit(acg);
operandStack.doGroovyCast(int_TYPE);
int subscriptValueId = compileStack.defineTemporaryVariable("$sub", ClassHelper.int_TYPE, true);
// load array: load x and DUP [load sub, call arrayGet, load b, call operation, load sub, call arraySet]
arrayWithSubscript.getLeftExpression().visit(acg);
operandStack.dup();
// array get: load sub, call arrayGet [load b, call operation, load sub, call arraySet]
operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
bew.arrayGet(LEFT_SQUARE_BRACKET, false);
operandStack.replace(leftType, 2);
// complete rhs: load b, call operation [load sub, call arraySet]
binExp.getRightExpression().visit(acg);
bew.write(operation, false);
// let us save that value for the return
operandStack.dup();
int resultValueId = compileStack.defineTemporaryVariable("$result", rightType, true);
// array set: load sub, call arraySet []
operandStack.load(ClassHelper.int_TYPE, subscriptValueId);
operandStack.swap();
bew.arraySet(false);
operandStack.remove(2);
// load return value
operandStack.load(rightType, resultValueId);
// cleanup
compileStack.removeVar(resultValueId);
compileStack.removeVar(subscriptValueId);
}
@Override
protected void assignToArray(Expression orig, Expression receiver, Expression index, Expression rhsValueLoader) {
ClassNode current = getController().getClassNode();
ClassNode arrayType = getType(receiver, current);
ClassNode arrayComponentType = arrayType.getComponentType();
int operationType = getOperandType(arrayComponentType);
BinaryExpressionWriter bew = binExpWriter[operationType];
AsmClassGenerator acg = getController().getAcg();
if (bew.arraySet(true)) {
OperandStack operandStack = getController().getOperandStack();
// load the array
receiver.visit(acg);
operandStack.doGroovyCast(arrayType);
// load index
index.visit(acg);
operandStack.doGroovyCast(int_TYPE);
// load rhs
rhsValueLoader.visit(acg);
operandStack.doGroovyCast(arrayComponentType);
// store value in array
bew.arraySet(false);
// load return value && correct operand stack stack
operandStack.remove(3);
rhsValueLoader.visit(acg);
} else {
super.assignToArray(orig, receiver, index, rhsValueLoader);
}
}
@Override
protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
ClassNode type = getType(orig,getController().getClassNode());
int operationType = getOperandType(type);
BinaryExpressionWriter bew = binExpWriter[operationType];
if (bew.writePostOrPrefixMethod(op,true)) {
OperandStack operandStack = getController().getOperandStack();
// at this point the receiver will be already on the stack
operandStack.doGroovyCast(type);
bew.writePostOrPrefixMethod(op,false);
operandStack.replace(bew.getNormalOpResultType());
} else {
super.writePostOrPrefixMethod(op, method, expression, orig);
}
}
}
Other Groovy examples (source code examples)
Here is a short list of links related to this Groovy BinaryExpressionMultiTypeDispatcher.java source code file:
|