org.codehaus.groovy.classgen.asm.BinaryExpressionWriter.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.groovy.classgen.asm.BinaryExpressionWriter.java

Source

/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
package org.codehaus.groovy.classgen.asm;

import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;

import static org.codehaus.groovy.syntax.Types.BITWISE_OR;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
import static org.codehaus.groovy.syntax.Types.LEFT_SHIFT;
import static org.codehaus.groovy.syntax.Types.LEFT_SQUARE_BRACKET;
import static org.codehaus.groovy.syntax.Types.MINUS_MINUS;
import static org.codehaus.groovy.syntax.Types.PLUS;
import static org.codehaus.groovy.syntax.Types.PLUS_PLUS;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.ICONST_0;
import static org.objectweb.asm.Opcodes.ICONST_1;
import static org.objectweb.asm.Opcodes.ICONST_M1;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.IFGE;
import static org.objectweb.asm.Opcodes.IFGT;
import static org.objectweb.asm.Opcodes.IFLE;
import static org.objectweb.asm.Opcodes.IFLT;
import static org.objectweb.asm.Opcodes.IFNE;

/**
 * Base class for writing primitive typed operations
 */
public abstract class BinaryExpressionWriter {

    private final WriterController controller;
    private MethodCaller arraySet, arrayGet;

    public BinaryExpressionWriter(WriterController controller, MethodCaller arraySet, MethodCaller arrayGet) {
        this.controller = controller;
        this.arraySet = arraySet;
        this.arrayGet = arrayGet;
    }

    /**
     * return writer controller
     * @since 2.5.0
     */
    public WriterController getController() {
        return controller;
    }

    protected static final int[] stdCompareCodes = { IFEQ, // COMPARE_NOT_EQUAL            120
            IFNE, // COMPARE_IDENTICAL            121 
            IFEQ, // COMPARE_NOT_IDENTICAL        122
            IFNE, // COMPARE_EQUAL                123
            IFGE, // COMPARE_LESS_THAN            124
            IFGT, // COMPARE_LESS_THAN_EQUAL      125
            IFLE, // COMPARE_GREATER_THAN         126
            IFLT, // COMPARE_GREATER_THAN_EQUAL   127
    };

    protected abstract int getCompareCode();

    /**
     * writes some int standard operations for compares
     * @param type the token type
     * @return true if a successful std operator write
     */
    protected boolean writeStdCompare(int type, boolean simulate) {
        type = type - COMPARE_NOT_EQUAL;
        // look if really compare
        if (type < 0 || type > 7)
            return false;

        if (!simulate) {
            MethodVisitor mv = controller.getMethodVisitor();
            OperandStack operandStack = controller.getOperandStack();
            // operands are on the stack already
            int bytecode = stdCompareCodes[type];
            mv.visitInsn(getCompareCode());
            Label l1 = new Label();
            mv.visitJumpInsn(bytecode, l1);
            mv.visitInsn(ICONST_1);
            Label l2 = new Label();
            mv.visitJumpInsn(GOTO, l2);
            mv.visitLabel(l1);
            mv.visitInsn(ICONST_0);
            mv.visitLabel(l2);
            operandStack.replace(ClassHelper.boolean_TYPE, 2);
        }
        return true;
    }

    protected abstract void doubleTwoOperands(MethodVisitor mv);

    protected abstract void removeTwoOperands(MethodVisitor mv);

    protected boolean writeSpaceship(int type, boolean simulate) {
        if (type != COMPARE_TO)
            return false;
        /*  
           we will actually do
             
          (x < y) ? -1 : ((x == y) ? 0 : 1)
          which is the essence of what the call with Number would do
          this compiles to something along
              
          <x>
          <y>
          LCMP
          IFGE L1
          ICONST_M1
          GOTO L2
          L1
          <x>
          <y>
          LCMP
          IFNE L3
          ICONST_0
          GOTO L2
          L3
          ICONST_1
          L2
              
          since the operators are already on the stack and we don't want
          to load them again, we will instead duplicate them. This will
          require some pop actions in the branches!
              
          DUP4          (operands: L1L2L1L2)
          LCMP          
          IFGE L1       (operands: L1L2)
          ICONST_M1     (operands: L1L2I)
          GOTO L2
          L1
          -----         (operands: L1L2)
          LCMP
          IFNE L3       (operands: -)
          ICONST_0      (operands: I)
          GOTO L2
          L3
          - jump from L1 branch to here (operands: -)
          ICONST_1      (operands: I)
          L2  
          - if jump from GOTO L2 we have LLI, but need only I
          - if from L3 branch we get only I
              
          this means we have to pop of LL before loading -1
              
          since there is no DUP4 we have to do this:
        DUP2_X1
        POP2
        DUP2_X1
        DUP2_X1
        POP2
        DUP2_X1          
        */
        if (!simulate) {
            MethodVisitor mv = controller.getMethodVisitor();
            // duplicate arguments
            doubleTwoOperands(mv);

            Label l1 = new Label();
            mv.visitInsn(getCompareCode());
            mv.visitJumpInsn(IFGE, l1);
            // no jump, so -1, need to pop off surplus LL
            removeTwoOperands(mv);
            mv.visitInsn(ICONST_M1);
            Label l2 = new Label();
            mv.visitJumpInsn(GOTO, l2);

            mv.visitLabel(l1);
            Label l3 = new Label();
            mv.visitInsn(getCompareCode());
            mv.visitJumpInsn(IFNE, l3);
            mv.visitInsn(ICONST_0);
            mv.visitJumpInsn(GOTO, l2);

            mv.visitLabel(l3);
            mv.visitInsn(ICONST_1);

            controller.getOperandStack().replace(ClassHelper.int_TYPE, 2);
        }
        return true;
    }

    protected abstract ClassNode getNormalOpResultType();

    protected abstract int getStandardOperationBytecode(int type);

    protected boolean writeStdOperators(int type, boolean simulate) {
        type = type - PLUS;
        if (type < 0 || type > 5 || type == 3 /*DIV*/)
            return false;

        if (!simulate) {
            int bytecode = getStandardOperationBytecode(type);
            controller.getMethodVisitor().visitInsn(bytecode);
            controller.getOperandStack().replace(getNormalOpResultType(), 2);
        }
        return true;
    }

    protected boolean writeDivision(boolean simulate) {
        if (!supportsDivision())
            return false;
        if (!simulate) {
            int bytecode = getStandardOperationBytecode(3 /*DIV*/);
            controller.getMethodVisitor().visitInsn(bytecode);
            controller.getOperandStack().replace(getDevisionOpResultType(), 2);
        }
        return true;
    }

    protected boolean supportsDivision() {
        return false;
    }

    protected abstract ClassNode getDevisionOpResultType();

    protected abstract int getBitwiseOperationBytecode(int type);

    /**
     * writes some the bitwise operations. type is one of BITWISE_OR, 
     * BITWISE_AND, BITWISE_XOR
     * @param type the token type
     * @return true if a successful bitwise operation write
     */
    protected boolean writeBitwiseOp(int type, boolean simulate) {
        type = type - BITWISE_OR;
        if (type < 0 || type > 2)
            return false;

        if (!simulate) {
            int bytecode = getBitwiseOperationBytecode(type);
            controller.getMethodVisitor().visitInsn(bytecode);
            controller.getOperandStack().replace(getNormalOpResultType(), 2);
        }
        return true;
    }

    protected abstract int getShiftOperationBytecode(int type);

    /**
     * Write shifting operations.
     * Type is one of LEFT_SHIFT, RIGHT_SHIFT, or RIGHT_SHIFT_UNSIGNED
     *
     * @param type the token type
     * @return true on a successful shift operation write
     */
    protected boolean writeShiftOp(int type, boolean simulate) {
        type = type - LEFT_SHIFT;
        if (type < 0 || type > 2)
            return false;

        if (!simulate) {
            int bytecode = getShiftOperationBytecode(type);
            controller.getMethodVisitor().visitInsn(bytecode);
            controller.getOperandStack().replace(getNormalOpResultType(), 2);
        }
        return true;
    }

    public boolean write(int operation, boolean simulate) {
        return writeStdCompare(operation, simulate) || writeSpaceship(operation, simulate)
                || writeStdOperators(operation, simulate) || writeBitwiseOp(operation, simulate)
                || writeShiftOp(operation, simulate);
    }

    protected MethodCaller getArrayGetCaller() {
        return arrayGet;
    }

    protected ClassNode getArrayGetResultType() {
        return getNormalOpResultType();
    }

    protected MethodCaller getArraySetCaller() {
        return arraySet;
    }

    public void setArraySetAndGet(MethodCaller arraySet, MethodCaller arrayGet) {
        this.arraySet = arraySet;
        this.arrayGet = arrayGet;
    }

    public boolean arrayGet(int operation, boolean simulate) {
        if (operation != LEFT_SQUARE_BRACKET)
            return false;

        if (!simulate) {
            getArrayGetCaller().call(controller.getMethodVisitor());
        }
        return true;
    }

    public boolean arraySet(boolean simulate) {
        if (!simulate) {
            getArraySetCaller().call(controller.getMethodVisitor());
        }
        return true;
    }

    public boolean writePostOrPrefixMethod(int operation, boolean simulate) {
        if (operation != PLUS_PLUS && operation != MINUS_MINUS)
            return false;
        if (!simulate) {
            MethodVisitor mv = controller.getMethodVisitor();
            if (operation == PLUS_PLUS) {
                writePlusPlus(mv);
            } else {
                writeMinusMinus(mv);
            }
        }
        return true;
    }

    protected abstract void writePlusPlus(MethodVisitor mv);

    protected abstract void writeMinusMinus(MethodVisitor mv);
}