/* * 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 static org.codehaus.groovy.syntax.Types.*; import static org.objectweb.asm.Opcodes.*; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; /** * Base class for writing primitive typed operations * @author <a href="mailto:blackdrag@gmx.org">Jochen "blackdrag" Theodorou</a> */ 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); }