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