alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

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.

Java - Groovy tags/keywords

asmclassgenerator, binaryexpression, binaryexpressionwriter, classnode, classnode, expression, expression, groovybugerror, methodcaller, methodcaller, operandstack, override, override, util, variableexpression

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:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.