/* * 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.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.asm.OptimizingStatementWriter.StatementMeta; 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 BinaryIntExpressionHelper extends BinaryExpressionHelper { private WriterController controller; /* from org.codehaus.groovy.syntax.Types public static final int COMPARE_NOT_EQUAL = 120; // != public static final int COMPARE_IDENTICAL = 121; // === public static final int COMPARE_NOT_IDENTICAL = 122; // !== public static final int COMPARE_EQUAL = 123; // == public static final int COMPARE_LESS_THAN = 124; // < public static final int COMPARE_LESS_THAN_EQUAL = 125; // <= public static final int COMPARE_GREATER_THAN = 126; // > public static final int COMPARE_GREATER_THAN_EQUAL = 127; // >= */ private static final int[] stdCompareCodes = { IF_ICMPEQ, // COMPARE_NOT_EQUAL IF_ICMPNE, // COMPARE_IDENTICAL IF_ICMPEQ, // COMPARE_NOT_IDENTICAL IF_ICMPNE, // COMPARE_EQUAL IF_ICMPGE, // COMPARE_LESS_THAN IF_ICMPGT, // COMPARE_LESS_THAN_EQUAL IF_ICMPLE, // COMPARE_GREATER_THAN IF_ICMPLT, // COMPARE_GREATER_THAN_EQUAL }; /* from org.codehaus.groovy.syntax.Types public static final int PLUS = 200; // + public static final int MINUS = 201; // - public static final int MULTIPLY = 202; // * public static final int INTDIV = 204; // \ public static final int MOD = 205; // % */ private static final int[] stdOperations = { IADD, // PLUS ISUB, // MINUS IMUL, // MULTIPLY 0, // DIV, but we don't want that one IDIV, // INTDIV IREM, // MOD }; /* from org.codehaus.groovy.syntax.Types public static final int PIPE = 340; // | public static final int BITWISE_OR = PIPE; // | public static final int BITWISE_AND = 341; // & public static final int BITWISE_XOR = 342; // ^ */ private static final int[] bitOp = { IOR, // BITWISE_OR / PIPE IAND, // BITWISE_AND IXOR, // BIWISE_XOR }; /* unhandled types from from org.codehaus.groovy.syntax.Types public static final int LOGICAL_OR = 162; // || public static final int LOGICAL_AND = 164; // && public static final int DIVIDE = 203; // / public static final int STAR_STAR = 206; // ** public static final int POWER = STAR_STAR; // public static final int PLUS_EQUAL = 210; // += public static final int MINUS_EQUAL = 211; // -= public static final int MULTIPLY_EQUAL = 212; // *= public static final int DIVIDE_EQUAL = 213; // /= public static final int INTDIV_EQUAL = 214; // \= public static final int MOD_EQUAL = 215; // %= public static final int POWER_EQUAL = 216; // **= public static final int PLUS_PLUS = 250; // ++ public static final int PREFIX_PLUS_PLUS = 251; // ++ public static final int POSTFIX_PLUS_PLUS = 252; // ++ public static final int PREFIX_PLUS = 253; // + public static final int MINUS_MINUS = 260; // -- public static final int PREFIX_MINUS_MINUS = 261; // -- public static final int POSTFIX_MINUS_MINUS = 262; // -- public static final int PREFIX_MINUS = 263; // - (negation) */ public static final int LEFT_SHIFT = 280; // << public static final int RIGHT_SHIFT = 281; // >> public static final int RIGHT_SHIFT_UNSIGNED = 282; // >>> private static final int[] shiftOp = { ISHL, // LEFT_SHIFT ISHR, // RIGHT_SHIFT IUSHR // RIGHT_SHIFT_UNSIGNED }; /* public static final int LEFT_SHIFT_EQUAL = 285; // <<= public static final int RIGHT_SHIFT_EQUAL = 286; // >>= public static final int RIGHT_SHIFT_UNSIGNED_EQUAL = 287; // >>>= public static final int BITWISE_OR_EQUAL = 350; // |= public static final int BITWISE_AND_EQUAL = 351; // &= public static final int BITWISE_XOR_EQUAL = 352; // ^= public static final int BITWISE_NEGATION = REGEX_PATTERN; // ~ */ public BinaryIntExpressionHelper(WriterController wc) { super(wc); controller = 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); if (meta!=null) return meta.type; ClassNode type = null; if (exp instanceof VariableExpression) { VariableExpression ve = (VariableExpression) exp; type = ve.getOriginType(); if (ve.getAccessedVariable() instanceof FieldNode) { FieldNode fn = (FieldNode) ve.getAccessedVariable(); if (!fn.getDeclaringClass().equals(current)) return ClassHelper.OBJECT_TYPE; } } else if (exp instanceof Variable) { Variable v = (Variable) exp; type = v.getOriginType(); } else { type = exp.getType(); } return type.redirect(); } /** * @return true if expression is an evals to an int */ protected static boolean isIntOperand(Expression exp, ClassNode current) { return getType(exp,current) == ClassHelper.int_TYPE; } @Override protected void evaluateCompareExpression(final MethodCaller compareMethod, BinaryExpression binExp) { int type = binExp.getOperation().getType(); Expression left = binExp.getLeftExpression(); boolean leftIsInt = isIntOperand(left, controller.getClassNode()); Expression right = binExp.getRightExpression(); boolean rightIsInt = isIntOperand(right, controller.getClassNode()); if (leftIsInt && rightIsInt && writeIntXInt(type, true)) { left.visit(controller.getAcg()); right.visit(controller.getAcg()); writeIntXInt(type, false); } else { super.evaluateCompareExpression(compareMethod, binExp); } } @Override protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) { int type = binExp.getOperation().getType(); Expression left = binExp.getLeftExpression(); boolean leftIsInt = isIntOperand(left, controller.getClassNode()); Expression right = binExp.getRightExpression(); boolean rightIsInt = isIntOperand(right, controller.getClassNode()); if (leftIsInt && rightIsInt && writeIntXInt(type, true)) { left.visit(controller.getAcg()); controller.getOperandStack().doGroovyCast(ClassHelper.int_TYPE); right.visit(controller.getAcg()); controller.getOperandStack().doGroovyCast(ClassHelper.int_TYPE); writeIntXInt(type, false); return; } else if ( rightIsInt && type==LEFT_SQUARE_BRACKET && getType(left,controller.getClassNode()).getComponentType()==ClassHelper.int_TYPE) { left.visit(controller.getAcg()); controller.getOperandStack().doGroovyCast(getType(left,controller.getClassNode())); right.visit(controller.getAcg()); controller.getOperandStack().doGroovyCast(ClassHelper.int_TYPE); MethodVisitor mv = controller.getMethodVisitor(); mv.visitInsn(IALOAD); controller.getOperandStack().replace(ClassHelper.int_TYPE,2); } else { super.evaluateBinaryExpression(message, binExp); } } @Override protected void assignToArray(Expression orig, Expression receiver, Expression index, Expression rhsValueLoader) { if (OptimizingStatementWriter.shouldOptimize(orig)) { OperandStack operandStack = controller.getOperandStack(); // load the array receiver.visit(controller.getAcg()); //operandStack.doGroovyCast(ClassHelper.int_TYPE.makeArray()); // load index index.visit(controller.getAcg()); operandStack.doGroovyCast(ClassHelper.int_TYPE); // load rhs rhsValueLoader.visit(controller.getAcg()); operandStack.doGroovyCast(ClassHelper.int_TYPE); // store value in array controller.getMethodVisitor().visitInsn(IASTORE); // load return value && correct operand stack stack operandStack.remove(3); rhsValueLoader.visit(controller.getAcg()); } else { super.assignToArray(orig, receiver, index, rhsValueLoader); } } /** * writes a std compare. This involves the tokens IF_ICMPEQ, IF_ICMPNE, * IF_ICMPEQ, IF_ICMPNE, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE and IF_ICMPLT * @param type the token type * @return true if a successful std compare write */ private 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]; 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; } /** * writes the spaceship operator, type should be COMPARE_TO * @param type the token type * @return true if a successful spaceship operator write */ private 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 Integers would do this compiles to something along <x> <y> IF_ICMPGE L1 ICONST_M1 GOTO L2 L1 <x> <y> IF_ICMPNE 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! DUP2 (operands: IIII) IF_ICMPGE L1 (operands: II) ICONST_M1 (operands: III) GOTO L2 L1 ----- (operands: II) IF_ICMPNE 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 III, but need only I - if from L3 branch we get only I this means we have to pop of II before loading -1 */ if (!simulate) { MethodVisitor mv = controller.getMethodVisitor(); // duplicate int arguments mv.visitInsn(DUP2); Label l1 = new Label(); mv.visitJumpInsn(IF_ICMPGE,l1); // no jump, so -1, need to pop off surplus II mv.visitInsn(POP2); mv.visitInsn(ICONST_M1); Label l2 = new Label();; mv.visitJumpInsn(GOTO, l2); mv.visitLabel(l1); Label l3 = new Label(); mv.visitJumpInsn(IF_ICMPNE,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; } /** * writes some int standard operations. type is one of IADD, ISUB, IMUL, * IDIV or IREM * @param type the token type * @return true if a successful std operator write */ private boolean writeStdOperators(int type, boolean simulate) { type = type-PLUS; if (type<0 || type>5 || type == 3 /*DIV*/) return false; if (!simulate) { int bytecode = stdOperations[type]; controller.getMethodVisitor().visitInsn(bytecode); controller.getOperandStack().replace(ClassHelper.int_TYPE, 2); } return true; } /** * writes some the bitwise operations. type is one of BITWISE_OR, * BITWISE_AND, BIWISE_XOR * @param type the token type * @return true if a successful bitwise operation write */ private boolean writeBitwiseOp(int type, boolean simulate) { type = type-BITWISE_OR; if (type<0 || type>2) return false; if (!simulate) { int bytecode = bitOp[type]; controller.getMethodVisitor().visitInsn(bytecode); controller.getOperandStack().replace(ClassHelper.int_TYPE, 2); } return true; } /** * 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 */ private boolean writeShiftOp(int type, boolean simulate) { type = type - LEFT_SHIFT; if (type < 0 || type > 2) return false; if (!simulate) { int bytecode = shiftOp[type]; controller.getMethodVisitor().visitInsn(bytecode); controller.getOperandStack().replace(ClassHelper.int_TYPE, 2); } return true; } private boolean writeIntXInt(int type, boolean simulate) { return writeStdCompare(type, simulate) || writeSpaceship(type, simulate) || writeStdOperators(type, simulate) || writeBitwiseOp(type, simulate) || writeShiftOp(type, simulate); } }