blob: de949a89d4c696e172f97ab263874327d9f8cba8 [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.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);
}