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

Groovy example source code file (BinaryExpressionHelper.java)

This example Groovy source code file (BinaryExpressionHelper.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, binaryexpression, classnode, expression, expression, label, methodcaller, methodcaller, operandstack, operandstack, string, variableexpression, variableslotloader

The Groovy BinaryExpressionHelper.java source code

/*
 * Copyright 2003-2009 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 org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.ClassExpression;
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.EmptyExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.FieldExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.PostfixExpression;
import org.codehaus.groovy.ast.expr.PrefixExpression;
import org.codehaus.groovy.ast.expr.PropertyExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.expr.VariableExpression;
import org.codehaus.groovy.classgen.AsmClassGenerator;
import org.codehaus.groovy.classgen.BytecodeExpression;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.syntax.Types;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import static org.codehaus.groovy.syntax.Types.*;
import static org.objectweb.asm.Opcodes.*;

public class BinaryExpressionHelper {
    //compare
    private static final MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
    private static final MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
    private static final MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
    private static final MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
    private static final MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
    private static final MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
    private static final MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
    //regexpr
    private static final MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
    private static final MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
    // isCase
    private static final MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");

    private WriterController controller;
    
    public BinaryExpressionHelper(WriterController wc) {
        this.controller = wc;
    }
    
    public WriterController getController(){
        return controller;
    }
    
    public void eval(BinaryExpression expression) {
        switch (expression.getOperation().getType()) {
        case EQUAL: // = assignment
            evaluateEqual(expression, false);
            break;

        case COMPARE_EQUAL: // ==
            evaluateCompareExpression(compareEqualMethod, expression);
            break;

        case COMPARE_NOT_EQUAL:
            evaluateCompareExpression(compareNotEqualMethod, expression);
            break;

        case COMPARE_TO:
            evaluateCompareTo(expression);
            break;

        case COMPARE_GREATER_THAN:
            evaluateCompareExpression(compareGreaterThanMethod, expression);
            break;

        case COMPARE_GREATER_THAN_EQUAL:
            evaluateCompareExpression(compareGreaterThanEqualMethod, expression);
            break;

        case COMPARE_LESS_THAN:
            evaluateCompareExpression(compareLessThanMethod, expression);
            break;

        case COMPARE_LESS_THAN_EQUAL:
            evaluateCompareExpression(compareLessThanEqualMethod, expression);
            break;

        case LOGICAL_AND:
            evaluateLogicalAndExpression(expression);
            break;

        case LOGICAL_OR:
            evaluateLogicalOrExpression(expression);
            break;

        case BITWISE_AND:
            evaluateBinaryExpression("and", expression);
            break;

        case BITWISE_AND_EQUAL:
            evaluateBinaryExpressionWithAssignment("and", expression);
            break;

        case BITWISE_OR:
            evaluateBinaryExpression("or", expression);
            break;

        case BITWISE_OR_EQUAL:
            evaluateBinaryExpressionWithAssignment("or", expression);
            break;

        case BITWISE_XOR:
            evaluateBinaryExpression("xor", expression);
            break;

        case BITWISE_XOR_EQUAL:
            evaluateBinaryExpressionWithAssignment("xor", expression);
            break;

        case PLUS:
            evaluateBinaryExpression("plus", expression);
            break;

        case PLUS_EQUAL:
            evaluateBinaryExpressionWithAssignment("plus", expression);
            break;

        case MINUS:
            evaluateBinaryExpression("minus", expression);
            break;

        case MINUS_EQUAL:
            evaluateBinaryExpressionWithAssignment("minus", expression);
            break;

        case MULTIPLY:
            evaluateBinaryExpression("multiply", expression);
            break;

        case MULTIPLY_EQUAL:
            evaluateBinaryExpressionWithAssignment("multiply", expression);
            break;

        case DIVIDE:
            evaluateBinaryExpression("div", expression);
            break;

        case DIVIDE_EQUAL:
            //SPG don't use divide since BigInteger implements directly
            //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
            evaluateBinaryExpressionWithAssignment("div", expression);
            break;

        case INTDIV:
            evaluateBinaryExpression("intdiv", expression);
            break;

        case INTDIV_EQUAL:
            evaluateBinaryExpressionWithAssignment("intdiv", expression);
            break;

        case MOD:
            evaluateBinaryExpression("mod", expression);
            break;

        case MOD_EQUAL:
            evaluateBinaryExpressionWithAssignment("mod", expression);
            break;

        case POWER:
            evaluateBinaryExpression("power", expression);
            break;

        case POWER_EQUAL:
            evaluateBinaryExpressionWithAssignment("power", expression);
            break;

        case LEFT_SHIFT:
            evaluateBinaryExpression("leftShift", expression);
            break;

        case LEFT_SHIFT_EQUAL:
            evaluateBinaryExpressionWithAssignment("leftShift", expression);
            break;

        case RIGHT_SHIFT:
            evaluateBinaryExpression("rightShift", expression);
            break;

        case RIGHT_SHIFT_EQUAL:
            evaluateBinaryExpressionWithAssignment("rightShift", expression);
            break;

        case RIGHT_SHIFT_UNSIGNED:
            evaluateBinaryExpression("rightShiftUnsigned", expression);
            break;

        case RIGHT_SHIFT_UNSIGNED_EQUAL:
            evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
            break;

        case KEYWORD_INSTANCEOF:
            evaluateInstanceof(expression);
            break;

        case FIND_REGEX:
            evaluateCompareExpression(findRegexMethod, expression);
            break;

        case MATCH_REGEX:
            evaluateCompareExpression(matchRegexMethod, expression);
            break;

        case LEFT_SQUARE_BRACKET:
            if (controller.getCompileStack().isLHS()) {
                evaluateEqual(expression, false);
            } else {
                evaluateBinaryExpression("getAt", expression);
            }
            break;

        case KEYWORD_IN:
            evaluateCompareExpression(isCaseMethod, expression);
            break;

        default:
            throw new GroovyBugError("Operation: " + expression.getOperation() + " not supported");
        }
    }
    
    protected void assignToArray(Expression parrent, Expression receiver, Expression index, Expression rhsValueLoader) {
        // let's replace this assignment to a subscript operator with a
        // method call
        // e.g. x[5] = 10
        // -> (x, [], 5), =, 10
        // -> methodCall(x, "putAt", [5, 10])
        ArgumentListExpression ae = new ArgumentListExpression(index,rhsValueLoader);
        controller.getCallSiteWriter().makeCallSite(receiver, "putAt", ae, false, false, false, false);
        controller.getOperandStack().pop();
        // return value of assignment
        rhsValueLoader.visit(controller.getAcg());
    }
    

    public void evaluateEqual(BinaryExpression expression, boolean defineVariable) {
        AsmClassGenerator acg = controller.getAcg();
        CompileStack compileStack = controller.getCompileStack();
        OperandStack operandStack = controller.getOperandStack();
        Expression rightExpression = expression.getRightExpression();
        Expression leftExpression = expression.getLeftExpression();
        
        if (    defineVariable && 
                rightExpression instanceof EmptyExpression && 
                !(leftExpression instanceof TupleExpression) )
        {
            VariableExpression ve = (VariableExpression) leftExpression;
            BytecodeVariable var = compileStack.defineVariable(ve, false);
            operandStack.loadOrStoreVariable(var, false);
            return;
        }
        
        // let's evaluate the RHS and store the result
        ClassNode rhsType;
        if (rightExpression instanceof EmptyExpression) {
            rhsType = leftExpression.getType();
            loadInitValue(rhsType);
        } else {
            rightExpression.visit(acg);
            //rhsType = getCastType(rightExpression);
            rhsType = controller.getOperandStack().getTopOperand();
        }
        int rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
        //TODO: if rhs is VariableSlotLoader already, then skip crating a new one
        BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType,rhsValueId,operandStack); 
        
        // assignment for subscript
        if (leftExpression instanceof BinaryExpression) {
            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
                assignToArray(expression, leftBinExpr.getLeftExpression(), leftBinExpr.getRightExpression(), rhsValueLoader);
            }
            compileStack.removeVar(rhsValueId);
            return;
        }
        
        compileStack.pushLHS(true);

        // multiple declaration
        if (leftExpression instanceof TupleExpression) {
            TupleExpression tuple = (TupleExpression) leftExpression;
            int i = 0;
            for (Expression e : tuple.getExpressions()) {
                VariableExpression var = (VariableExpression) e;
                MethodCallExpression call = new MethodCallExpression(
                        rhsValueLoader, "getAt",
                        new ArgumentListExpression(new ConstantExpression(i)));
                call.visit(acg);
                i++;
                if (defineVariable) {
                    operandStack.doGroovyCast(var);
                    compileStack.defineVariable(var, true);
                    operandStack.remove(1);
                } else {
                    acg.visitVariableExpression(var);
                }
            }
        } 
        // single declaration
        else if (defineVariable) {
            VariableExpression var = (VariableExpression) leftExpression;
            rhsValueLoader.visit(acg);
            operandStack.doGroovyCast(var);
            compileStack.defineVariable(var, true);
            operandStack.remove(1);
        } 
        // normal assignment
        else {
            int mark = operandStack.getStackLength();
            // to leave a copy of the rightExpression value on the stack after the assignment.
            rhsValueLoader.visit(acg);
            leftExpression.visit(acg);
            operandStack.remove(operandStack.getStackLength()-mark);
        }
        compileStack.popLHS();
        
        // return value of assignment
        rhsValueLoader.visit(acg);
        compileStack.removeVar(rhsValueId);
    }

    private void loadInitValue(ClassNode type) {
        MethodVisitor mv = controller.getMethodVisitor();
        if (ClassHelper.isPrimitiveType(type)) {
            mv.visitLdcInsn(0);
        } else {
            mv.visitInsn(ACONST_NULL);
        }
        controller.getOperandStack().push(type);
    }

    protected void evaluateCompareExpression(MethodCaller compareMethod, BinaryExpression expression) {
        Expression leftExp = expression.getLeftExpression();
        Expression rightExp = expression.getRightExpression();
        AsmClassGenerator acg = controller.getAcg();
        OperandStack operandStack = controller.getOperandStack();
        
        leftExp.visit(acg);
        operandStack.box();
        rightExp.visit(acg);
        operandStack.box();

        compareMethod.call(controller.getMethodVisitor());
        ClassNode resType = ClassHelper.boolean_TYPE;
        if (compareMethod==findRegexMethod) {
            resType = ClassHelper.OBJECT_TYPE;
        } 
        operandStack.replace(resType,2);
    }
    
    private void evaluateCompareTo(BinaryExpression expression) {
        Expression leftExpression = expression.getLeftExpression();
        AsmClassGenerator acg = controller.getAcg();
        OperandStack operandStack = controller.getOperandStack();
        
        leftExpression.visit(acg);
        operandStack.box();

        // if the right hand side is a boolean expression, we need to autobox
        Expression rightExpression = expression.getRightExpression();
        rightExpression.visit(acg);
        operandStack.box();

        compareToMethod.call(controller.getMethodVisitor());
        operandStack.replace(ClassHelper.Integer_TYPE,2);
    }

    private void evaluateLogicalAndExpression(BinaryExpression expression) {
        MethodVisitor mv = controller.getMethodVisitor();
        AsmClassGenerator acg = controller.getAcg();
        OperandStack operandStack = controller.getOperandStack();

        expression.getLeftExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label falseCase = operandStack.jump(IFEQ);

        expression.getRightExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        operandStack.jump(IFEQ,falseCase);

        ConstantExpression.PRIM_TRUE.visit(acg);
        Label trueCase = new Label();
        mv.visitJumpInsn(GOTO, trueCase);

        mv.visitLabel(falseCase);
        ConstantExpression.PRIM_FALSE.visit(acg);

        mv.visitLabel(trueCase);
        operandStack.remove(1); // have to remove 1 because of the GOTO
    }
    
    private void evaluateLogicalOrExpression(BinaryExpression expression) {
        MethodVisitor mv = controller.getMethodVisitor();
        AsmClassGenerator acg = controller.getAcg();
        OperandStack operandStack = controller.getOperandStack();

        Label end = new Label();

        expression.getLeftExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label trueCase = operandStack.jump(IFNE);
        
        expression.getRightExpression().visit(acg);
        operandStack.doGroovyCast(ClassHelper.boolean_TYPE);
        Label falseCase = operandStack.jump(IFEQ);
        
        mv.visitLabel(trueCase);
        ConstantExpression.PRIM_TRUE.visit(acg);
        operandStack.jump(GOTO, end);

        mv.visitLabel(falseCase);
        ConstantExpression.PRIM_FALSE.visit(acg);
        
        mv.visitLabel(end);
    }
    
    protected void evaluateBinaryExpression(String message, BinaryExpression binExp) {
        CompileStack compileStack = controller.getCompileStack();

        Expression receiver = binExp.getLeftExpression();
        Expression arguments = binExp.getRightExpression();

        // ensure VariableArguments are read, not stored
        compileStack.pushLHS(false);
        controller.getCallSiteWriter().makeInvocation(receiver, message, arguments);
        compileStack.popLHS();        
    }

    protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression expression) {
        Expression leftExpression = expression.getLeftExpression();
        MethodVisitor mv  = controller.getMethodVisitor();
        AsmClassGenerator acg = controller.getAcg();
        OperandStack operandStack = controller.getOperandStack();
        CompileStack compileStack = controller.getCompileStack();
        
        if (leftExpression instanceof BinaryExpression) {
            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
                // e.g. x[a] += b
                // -> subscript=a, x[subscript], =, x[subscript] + b
                // -> subscript=a, methodCall_3(x, "putAt", [subscript, methodCall_2(methodCall_1(x, "getAt", [subscript]), "plus", b)])
                
                Expression subscriptExpression = leftBinExpr.getRightExpression(); 
                subscriptExpression.visit(acg); // value(subscript)
                operandStack.box();
                int subscriptValueId = compileStack.defineTemporaryVariable("$subscript", ClassHelper.OBJECT_TYPE, true);

                // method calls from outer to inner (most inner will be called first):
                controller.getCallSiteWriter().prepareCallSite("putAt");
                controller.getCallSiteWriter().prepareCallSite(method);
                controller.getCallSiteWriter().prepareCallSite("getAt");
                
                // getAt call
                //x = receiver
                leftBinExpr.getLeftExpression().visit(acg);  
                operandStack.box();
                // we save that value for later
                operandStack.dup();
                int xValueId = compileStack.defineTemporaryVariable("$xValue", ClassHelper.OBJECT_TYPE, true);
                // subscript = argument to getAt call
                operandStack.load(ClassHelper.OBJECT_TYPE, subscriptValueId);
                // invoke getAt
                mv.visitMethodInsn(INVOKEINTERFACE, "org/codehaus/groovy/runtime/callsite/CallSite", "call","(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
                operandStack.replace(ClassHelper.OBJECT_TYPE, 2); 
                //x[subscript] with type Object now on stack

                // call with method (e.g. "plus")
                // receiver is the result from the getAt before
                // load b
                expression.getRightExpression().visit(acg);
                operandStack.box();
                // invoke "method"
                mv.visitMethodInsn(INVOKEINTERFACE, "org/codehaus/groovy/runtime/callsite/CallSite", "call","(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
                operandStack.replace(ClassHelper.OBJECT_TYPE, 2); 
                //RHS with type Object now on stack
                
                // let us save that value for the return
                int resultValueId = compileStack.defineTemporaryVariable("$result", ClassHelper.OBJECT_TYPE, true);               
                
                // call for putAt
                // receiver for putAt is x, the arguments will be the subscript
                // value and the value of the RHS.
                // load receiver x
                operandStack.load(ClassHelper.OBJECT_TYPE, xValueId);
                operandStack.load(ClassHelper.OBJECT_TYPE, subscriptValueId);
                operandStack.load(ClassHelper.OBJECT_TYPE, resultValueId);
                // invoke putAt
                mv.visitMethodInsn(INVOKEINTERFACE, "org/codehaus/groovy/runtime/callsite/CallSite", "call","(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
                operandStack.replace(ClassHelper.OBJECT_TYPE, 3);

                // remove result of putAt and keep the result on stack
                operandStack.pop();
                operandStack.load(ClassHelper.OBJECT_TYPE, resultValueId);
                
                compileStack.removeVar(resultValueId);
                compileStack.removeVar(xValueId);
                compileStack.removeVar(subscriptValueId);
                return;
            }
        } 

        evaluateBinaryExpression(method, expression);

        // br to leave a copy of rvalue on the stack. see also isPopRequired()
        operandStack.dup();
        
        controller.getCompileStack().pushLHS(true);
        leftExpression.visit(acg);
        controller.getCompileStack().popLHS();
    }
    
    private void evaluateInstanceof(BinaryExpression expression) {
        OperandStack operandStack = controller.getOperandStack();
        
        expression.getLeftExpression().visit(controller.getAcg());
        operandStack.box();
        Expression rightExp = expression.getRightExpression();
        ClassNode classType;
        if (rightExp instanceof ClassExpression) {
            ClassExpression classExp = (ClassExpression) rightExp;
            classType = classExp.getType();
        } else {
            throw new RuntimeException(
                    "Right hand side of the instanceof keyword must be a class name, not: " + rightExp);
        }
        String classInternalName = BytecodeHelper.getClassInternalName(classType);
        controller.getMethodVisitor().visitTypeInsn(INSTANCEOF, classInternalName);
        operandStack.replace(ClassHelper.boolean_TYPE);
    }

    public MethodCaller getIsCaseMethod() {
        return isCaseMethod;
    }

    private void evaluatePostfixMethod(int op, String method, Expression expression, Expression orig) {
        CompileStack compileStack = controller.getCompileStack();
        final OperandStack operandStack = controller.getOperandStack();
        
        // load Expressions
        VariableSlotLoader usesSubscript = loadWithSubscript(expression);

        // save copy for later
        operandStack.dup();
        ClassNode expressionType = operandStack.getTopOperand(); 
        int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, expressionType, true);
        
        // execute Method
        execMethodAndStoreForSubscriptOperator(op,method,expression,usesSubscript,orig);
        
        // remove the result of the method call
        operandStack.pop();        
        
        //reload saved value
        operandStack.load(expressionType, tempIdx);
        compileStack.removeVar(tempIdx);
        if (usesSubscript!=null) compileStack.removeVar(usesSubscript.getIndex());
    }

    public void evaluatePostfixMethod(PostfixExpression expression) {
        int op = expression.getOperation().getType();
        switch (op) {
            case Types.PLUS_PLUS:
                evaluatePostfixMethod(op, "next", expression.getExpression(), expression);
                break;
            case Types.MINUS_MINUS:
                evaluatePostfixMethod(op, "previous", expression.getExpression(), expression);
                break;
        }
    }

    public void evaluatePrefixMethod(PrefixExpression expression) {
        int type = expression.getOperation().getType();
        switch (type) {
            case Types.PLUS_PLUS:
                evaluatePrefixMethod(type, "next", expression.getExpression(), expression);
                break;
            case Types.MINUS_MINUS:
                evaluatePrefixMethod(type, "previous", expression.getExpression(), expression);
                break;
        }
    }
    
    private void evaluatePrefixMethod(int op, String method, Expression expression, Expression orig) {
        // load Expressions
        VariableSlotLoader usesSubscript = loadWithSubscript(expression);
        
        // execute Method
        execMethodAndStoreForSubscriptOperator(op,method,expression,usesSubscript,orig);

        // new value is already on stack, so nothing to do here
        if (usesSubscript!=null) controller.getCompileStack().removeVar(usesSubscript.getIndex());
    }
    
    private VariableSlotLoader loadWithSubscript(Expression expression) {
        final OperandStack operandStack = controller.getOperandStack();
        // if we have a BinaryExpression, let us check if it is with
        // subscription
        if (expression instanceof BinaryExpression) {
            BinaryExpression be = (BinaryExpression) expression;
            if (be.getOperation().getType()==Types.LEFT_SQUARE_BRACKET) {
                // right expression is the subscript expression
                // we store the result of the subscription on the stack
                Expression subscript = be.getRightExpression();
                subscript.visit(controller.getAcg());
                operandStack.box(); //TODO: maybe not box here anymore, but need subscript type then
                int id = controller.getCompileStack().defineTemporaryVariable("$subscript", true);
                VariableSlotLoader subscriptExpression = new VariableSlotLoader(id,operandStack);
                // do modified visit
                BinaryExpression newBe = new BinaryExpression(be.getLeftExpression(), be.getOperation(), subscriptExpression);
                newBe.setSourcePosition(be);
                newBe.visit(controller.getAcg());
                return subscriptExpression;
            } 
        } 
        
        // normal loading of expression
        expression.visit(controller.getAcg());
        return null;
    }
    
    private void execMethodAndStoreForSubscriptOperator(int op, String method, Expression expression, VariableSlotLoader usesSubscript, Expression orig) {
        final OperandStack operandStack = controller.getOperandStack();
        writePostOrPrefixMethod(op,method,expression,orig);

        // we need special code for arrays to store the result (like for a[1]++)
        if (usesSubscript!=null) {
            CompileStack compileStack = controller.getCompileStack();
            BinaryExpression be = (BinaryExpression) expression;
            
            ClassNode methodResultType = operandStack.getTopOperand();
            final int resultIdx = compileStack.defineTemporaryVariable("postfix_" + method, methodResultType, true);
            BytecodeExpression methodResultLoader = new VariableSlotLoader(methodResultType, resultIdx, operandStack);
            
            // execute the assignment, this will leave the right side 
            // (here the method call result) on the stack
            assignToArray(be, be.getLeftExpression(), usesSubscript, methodResultLoader);

            compileStack.removeVar(resultIdx);
        } 
        // here we handle a.b++ and a++
        else if (expression instanceof VariableExpression ||
            expression instanceof FieldExpression || 
            expression instanceof PropertyExpression)
        {
            operandStack.dup();
            controller.getCompileStack().pushLHS(true);
            expression.visit(controller.getAcg());
            controller.getCompileStack().popLHS();
        }
        // other cases don't need storing, so nothing to be done for them
    }

    protected void writePostOrPrefixMethod(int op, String method, Expression expression, Expression orig) {
        final OperandStack operandStack = controller.getOperandStack();
        // at this point the receiver will be already on the stack.
        // in a[1]++ the method will be "++" aka "next" and the receiver a[1]
        
        ClassNode BEType = BinaryExpressionMultiTypeDispatcher.getType(expression,controller.getClassNode());
        Expression callSiteReceiverSwap = new BytecodeExpression(BEType) {
            @Override
            public void visit(MethodVisitor mv) {
                // CallSite is normally not showing up on the 
                // operandStack, so we place a dummy here with same
                // slot length.
                operandStack.push(ClassHelper.OBJECT_TYPE);
                // change (receiver,callsite) to (callsite,receiver)
                operandStack.swap();
                setType(operandStack.getTopOperand());
                
                // no need to keep any of those on the operand stack
                // after this expression is processed, the operand stack
                // will contain callSiteReceiverSwap.getType()
                operandStack.remove(2);
            }
        };
        // execute method
        // this will load the callsite and the receiver normally in the wrong
        // order since the receiver is already present, but before the callsite
        // Therefore we use callSiteReceiverSwap to correct the order. 
        // After this call the JVM operand stack will contain the the result of
        // the method call... usually simply Object in operandStack
        controller.getCallSiteWriter().makeCallSite(
                callSiteReceiverSwap,
                method,
                MethodCallExpression.NO_ARGUMENTS,
                false, false, false, false);
        // now rhs is completely done and we need only to store. In a[1]++ this 
        // would be a.getAt(1).next() for the rhs, "lhs" code is a.putAt(1, rhs)
         
    }
}

Other Groovy examples (source code examples)

Here is a short list of links related to this Groovy BinaryExpressionHelper.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.