blob: dfa124510a90a7206c613cbc4f7d32da5827f4de [file] [log] [blame]
/*
* 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.codehaus.groovy.runtime.BytecodeInterface8;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import static org.codehaus.groovy.syntax.Types.COMPARE_NOT_EQUAL;
import static org.codehaus.groovy.syntax.Types.COMPARE_TO;
import static org.objectweb.asm.Opcodes.DUP2;
import static org.objectweb.asm.Opcodes.GOTO;
import static org.objectweb.asm.Opcodes.IADD;
import static org.objectweb.asm.Opcodes.IAND;
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.IDIV;
import static org.objectweb.asm.Opcodes.IF_ICMPEQ;
import static org.objectweb.asm.Opcodes.IF_ICMPGE;
import static org.objectweb.asm.Opcodes.IF_ICMPGT;
import static org.objectweb.asm.Opcodes.IF_ICMPLE;
import static org.objectweb.asm.Opcodes.IF_ICMPLT;
import static org.objectweb.asm.Opcodes.IF_ICMPNE;
import static org.objectweb.asm.Opcodes.IMUL;
import static org.objectweb.asm.Opcodes.IOR;
import static org.objectweb.asm.Opcodes.IREM;
import static org.objectweb.asm.Opcodes.ISHL;
import static org.objectweb.asm.Opcodes.ISHR;
import static org.objectweb.asm.Opcodes.ISUB;
import static org.objectweb.asm.Opcodes.IUSHR;
import static org.objectweb.asm.Opcodes.IXOR;
import static org.objectweb.asm.Opcodes.POP2;
public class BinaryIntExpressionHelper extends BinaryExpressionWriter {
private static final MethodCaller intArrayGet = MethodCaller.newStatic(BytecodeInterface8.class, "intArrayGet");
private static final MethodCaller intArraySet = MethodCaller.newStatic(BytecodeInterface8.class, "intArraySet");
private static final int[] stdCompareCodes = {
IF_ICMPEQ, // COMPARE_NOT_EQUAL 120
IF_ICMPNE, // COMPARE_IDENTICAL 121
IF_ICMPEQ, // COMPARE_NOT_IDENTICAL 122
IF_ICMPNE, // COMPARE_EQUAL 123
IF_ICMPGE, // COMPARE_LESS_THAN 124
IF_ICMPGT, // COMPARE_LESS_THAN_EQUAL 125
IF_ICMPLE, // COMPARE_GREATER_THAN 126
IF_ICMPLT, // COMPARE_GREATER_THAN_EQUAL 127
};
private static final int[] stdOperations = {
IADD, // PLUS 200
ISUB, // MINUS 201
IMUL, // MULTIPLY 202
IDIV, // DIV 203
IDIV, // INTDIV 204
IREM, // MOD 203
};
private static final int[] bitOp = {
IOR, // BITWISE_OR / PIPE 340
IAND, // BITWISE_AND 341
IXOR, // BIWISE_XOR 342
};
/* 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)
*/
private static final int[] shiftOp = {
ISHL, // LEFT_SHIFT 280
ISHR, // RIGHT_SHIFT 281
IUSHR // RIGHT_SHIFT_UNSIGNED 282
};
/*
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) {
this(wc, intArraySet, intArrayGet);
}
/**
* @since 2.5.0
*/
public BinaryIntExpressionHelper(WriterController wc, MethodCaller arraySet, MethodCaller arrayGet) {
super(wc, arraySet, arrayGet);
}
/**
* 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
*/
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 = getController().getMethodVisitor();
OperandStack operandStack = getController().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
*/
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 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 = getController().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);
getController().getOperandStack().replace(ClassHelper.int_TYPE, 2);
}
return true;
}
protected void doubleTwoOperands(MethodVisitor mv) {
mv.visitInsn(DUP2);
}
protected int getBitwiseOperationBytecode(int type) {
return bitOp[type];
}
protected int getCompareCode() {
return -1;
}
protected ClassNode getNormalOpResultType() {
return ClassHelper.int_TYPE;
}
protected int getShiftOperationBytecode(int type) {
return shiftOp[type];
}
protected int getStandardOperationBytecode(int type) {
return stdOperations[type];
}
protected void removeTwoOperands(MethodVisitor mv) {
mv.visitInsn(POP2);
}
protected void writeMinusMinus(MethodVisitor mv) {
mv.visitInsn(ICONST_1);
mv.visitInsn(ISUB);
}
protected void writePlusPlus(MethodVisitor mv) {
mv.visitInsn(ICONST_1);
mv.visitInsn(IADD);
}
protected ClassNode getDevisionOpResultType() {
return ClassHelper.int_TYPE;
}
@Override
protected boolean supportsDivision() {
return true;
}
}