Package org.codehaus.groovy.classgen.asm

Source Code of org.codehaus.groovy.classgen.asm.BinaryExpressionHelper

/*
* 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)
        
    }
}
TOP

Related Classes of org.codehaus.groovy.classgen.asm.BinaryExpressionHelper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.