/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.max.vm.bytecode.graft;
import static com.sun.cri.bytecode.Bytecodes.*;
import static com.sun.max.vm.classfile.ErrorContext.*;
import static com.sun.max.vm.classfile.constant.PoolConstantFactory.*;
import java.util.*;
import com.sun.cri.bytecode.*;
import com.sun.max.program.*;
import com.sun.max.vm.classfile.constant.*;
import com.sun.max.vm.classfile.constant.ConstantPool.Tag;
import com.sun.max.vm.type.*;
/**
* A bytecode assembler. The bytecode instructions supported
* by this assembler are a subset of the full JVM bytecode instruction
* set. As extra instructions are needed, they will be added.
*
* There is support for assembling forward branches (i.e.
* branches whose target address is not known at the time the branch
* instruction is assembled). Any use of such branches, requires
* {@link #fixup()} to be called before the assembled code is
* used.
*/
public abstract class BytecodeAssembler {
/**
* A label is a place holder for the target address of a branch.
*
* If labels are used while assembling a method, then {@link BytecodeAssembler#fixup()}
* must be called before the assembled code is used.
*/
public class Label {
private int address = -1;
/**
* Binds this label to the current address in the instruction stream.
*/
public void bind() {
assert address == -1 : "cannot rebind label";
address = currentAddress();
}
/**
* Binds this label to a given address.
*/
public void bind(int address) {
assert this.address == -1 : "cannot rebind label";
this.address = address;
}
public int address() {
assert address != -1 : "label not yet bound to an address";
return address;
}
public boolean isBound() {
return address != -1;
}
@Override
public String toString() {
return isBound() ? Integer.toString(address) : "?";
}
}
/**
* A bookmark for a branch instruction whose target address(es) is (are) not yet known.
*/
abstract class LabelInstruction {
final int opcodeAddress;
final int size;
public abstract void assemble(BytecodeAssembler assembler);
/**
* Creates a placeholder for a forward branch instruction.
*
* @param opcodeAddress
* the address at which the instruction's opcode is located in the instruction stream
* @param size
* the size of the encoded instruction
*/
public LabelInstruction(int opcodeAddress, int size) {
this.opcodeAddress = opcodeAddress;
this.size = size;
}
public void fixup() {
final int originalAddress = currentAddress();
setCurrentAddress(opcodeAddress);
assemble(BytecodeAssembler.this);
ProgramError.check(currentAddress() - opcodeAddress == size, "patched forward branch instruction changed size");
setCurrentAddress(originalAddress);
}
}
private final ConstantPoolEditor constantPoolEditor;
private final int startAddress;
private int currentAddress;
private int highestAddress;
private int maxLocals;
private int maxStack;
private int stack;
/**
* Creates local variable slots based on the signature of a method and adjusts the {@linkplain #maxLocals() number of locals variables}.
*/
public void allocateParameters(boolean isStatic, SignatureDescriptor signature) {
for (int i = 0; i < signature.numberOfParameters(); i++) {
final Kind parameterKind = signature.parameterDescriptorAt(i).toKind();
maxLocals += parameterKind.isCategory1 ? 1 : 2;
}
if (!isStatic) {
++maxLocals;
}
}
/**
* Creates a new local variable slot and adjusts the {@linkplain #maxLocals() number of locals variables}.
*/
public int allocateLocal(Kind kind) {
final int local = maxLocals;
if (!kind.isCategory1) {
maxLocals += 2;
} else {
++maxLocals;
}
return local;
}
public int stack() {
return stack;
}
/**
* Gets the maximum height of the operand stack in the generated method. This value
* may change as more instructions are emitted.
*/
public int maxStack() {
return maxStack;
}
/**
* Adjusts the current stack height. Clients should call this to re-adjust the stack depth tracking at
* the beginning of basic blocks as this assembler only accurately tracks the stack depth within
* a basic block.
*
* @param depth the new stack depth
*/
public void setStack(int depth) {
if (depth > maxStack) {
maxStack = depth;
} else if (depth < 0) {
throw new IllegalArgumentException("stack underflow in bytecode assembler");
}
stack = depth;
}
public void incStack() {
setStack(stack + 1);
}
public void incStack2() {
setStack(stack + 2);
}
public void decStack() {
setStack(stack - 1);
}
public void decStack2() {
setStack(stack - 2);
}
public void adjustStack(int delta) {
setStack(stack + delta);
}
/**
* Models the stack effect of an invoke.
*/
public void invoke(Kind[] parameterKinds, Kind resultKind, boolean isStatic) {
// Pop parameters
for (Kind parameterKind : parameterKinds) {
pop(parameterKind);
}
if (!isStatic) {
// Pop receiver of non-static method
pop(Kind.REFERENCE);
}
if (resultKind != Kind.VOID) {
push(resultKind);
}
}
// Expensive - should only be called from within an assertion clause
private boolean adjustmentMatchesSignature(int methodRefIndex, boolean isStatic, boolean isInterface, int numArgSlots, int numReturnValueSlots) {
final SignatureDescriptor signatureDescriptor = isInterface ?
constantPool().interfaceMethodAt(methodRefIndex).signature(constantPool()) :
constantPool().classMethodAt(methodRefIndex).signature(constantPool());
return adjustmentMatchesSignature(signatureDescriptor, isStatic, numArgSlots, numReturnValueSlots);
}
// Expensive - should only be called from within an assertion clause
private boolean adjustmentMatchesSignature(SignatureDescriptor signatureDescriptor, boolean isStatic, int numArgSlots, int numReturnValueSlots) {
// Pop parameters
int computedArgSlots = 0;
for (int i = 0; i < signatureDescriptor.numberOfParameters(); i++) {
computedArgSlots += signatureDescriptor.parameterDescriptorAt(i).toKind().stackSlots;
}
if (!isStatic) {
++computedArgSlots;
}
final boolean result = numArgSlots == computedArgSlots && numReturnValueSlots == signatureDescriptor.resultKind().stackSlots;
assert result;
return result;
}
private void adjustStackForInvoke(int methodRefIndex, boolean isStatic, boolean isInterface, int numArgSlots, int numReturnValueSlots) {
assert adjustmentMatchesSignature(methodRefIndex, isStatic, isInterface, numArgSlots, numReturnValueSlots);
setStack(stack - numArgSlots + numReturnValueSlots);
}
/**
* Gets the total number of variable slots used in the generated method. This value
* may change as more instructions are emitted.
*/
public int maxLocals() {
return maxLocals;
}
public ConstantPool constantPool() {
return constantPoolEditor.pool();
}
public ConstantPoolEditor constantPoolEditor() {
return constantPoolEditor;
}
/**
* Fixes up any unbound labels and returns the assembled code in a byte array.
*/
public abstract byte[] code();
private List<LabelInstruction> unboundInstructions = new LinkedList<LabelInstruction>();
/**
* Constructor for assembling code for a new method.
*/
protected BytecodeAssembler(ConstantPoolEditor constantPoolEditor) {
this.constantPoolEditor = constantPoolEditor;
this.startAddress = 0;
}
/**
* Constructor for assembling code that will be appended to the end of an existing method.
*/
protected BytecodeAssembler(ConstantPoolEditor constantPoolEditor, int startAddress, int initialMaxStack, int initialMaxLocals) {
this.constantPoolEditor = constantPoolEditor;
this.startAddress = startAddress;
this.currentAddress = startAddress;
this.highestAddress = startAddress;
this.maxStack = initialMaxStack;
this.maxLocals = initialMaxLocals;
}
void setCurrentAddress(int address) {
assert address <= highestAddress;
currentAddress = address;
setWriteBCI(address - startAddress);
}
public int currentAddress() {
return currentAddress;
}
public int numberOfEmittedBytes() {
return highestAddress - startAddress;
}
protected abstract void writeByte(byte b);
/**
* Sets the bci at which the next byte will be {@link #writeByte(byte) written}. This is
* used when {@link #fixup() fixing} up forward branches and {@code bci} will never
* be greater than {@link #numberOfEmittedBytes()}.
*/
protected abstract void setWriteBCI(int bci);
public final void emitByte(int b) {
writeByte((byte) (b & 0xff));
currentAddress++;
if (currentAddress > highestAddress) {
highestAddress = currentAddress;
}
}
public final void emitShort(int s) {
emitByte(s >> 8);
emitByte(s);
}
public final void emitOpcode(int opcode) {
emitByte(opcode);
}
public final void emitOffset2(int offset) {
emitByte(offset >> 8);
emitByte(offset);
}
public final void emitOffset4(int offset) {
emitByte(offset >> 24);
emitByte(offset >> 16);
emitByte(offset >> 8);
emitByte(offset);
}
/**
* Emits a 1-byte index into the constant pool.
*/
private void emitCPIndex1(int index) {
assert (index & 0xff) == index;
emitByte(index);
}
/**
* Emits a 2-byte index into the constant pool.
*/
private void emitCPIndex2(int index) {
assert (index & 0xffff) == index;
emitOffset2(index);
}
public void branch(int opcode, int address) {
final int offset = address - currentAddress;
if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
throw classFormatError("Offset (" + offset + ") cannot be represented as signed 16-bit value");
}
emitOpcode(opcode);
emitOffset2(offset);
}
public void branch(final int opcode, final Label label) {
if (label.isBound()) {
branch(opcode, label.address());
} else {
final int opcodeAddress = currentAddress;
branch(opcode, currentAddress);
unboundInstructions.add(new LabelInstruction(opcodeAddress, 3){
@Override
public void assemble(BytecodeAssembler assembler) {
branch(opcode, label.address());
}
});
}
}
/**
* Creates a label to be used as a placeholder for the target address of a forward branch instruction.
*/
public Label newLabel() {
return new Label();
}
public boolean needsFixup() {
return !unboundInstructions.isEmpty();
}
/**
* Fixes up any forward branch instructions that still have placeholders for their target addresses.
*/
public final void fixup() {
if (needsFixup()) {
for (LabelInstruction unboundInstruction : unboundInstructions) {
unboundInstruction.fixup();
}
unboundInstructions = new LinkedList<LabelInstruction>();
}
}
public void push(Kind kind) {
setStack(stack + (kind.isCategory1 ? 1 : 2));
}
public void pop(Kind kind) {
setStack(stack - (kind.isCategory1 ? 1 : 2));
}
public void appendByte(int b) {
emitByte(b);
}
// Instructions
public void athrow() {
emitOpcode(ATHROW);
decStack();
}
public void dup() {
emitOpcode(DUP);
incStack();
}
public void pop() {
emitOpcode(POP);
decStack();
}
public void aconst_null() {
emitOpcode(ACONST_NULL);
incStack();
}
public void jsr(int address) {
branch(JSR, address);
}
public void jsr(Label label) {
branch(JSR, label);
}
public void ret(int index) {
ProgramError.check(index >= 0 && index < maxLocals && index < 0xff);
emitOpcode(RET);
emitByte(index);
}
public void goto_(int address) {
branch(GOTO, address);
}
public void goto_(Label label) {
branch(GOTO, label);
}
public void ifne(int address) {
branch(IFNE, address);
decStack();
}
public void ifne(Label label) {
branch(IFNE, label);
decStack();
}
public void ifgt(Label label) {
branch(IFGT, label);
decStack();
}
public void iflt(Label label) {
branch(IFLT, label);
decStack();
}
public void ifnull(int address) {
branch(IFNULL, address);
decStack();
}
public void ifnull(Label label) {
branch(IFNULL, label);
decStack();
}
public void ifeq(Label label) {
branch(IFEQ, label);
decStack();
}
public void ifnonnull(int address) {
branch(IFNONNULL, address);
decStack();
}
public void ifnonnull(Label label) {
branch(IFNONNULL, label);
decStack();
}
public void if_icmpeq(Label label) {
branch(IF_ICMPEQ, label);
decStack2();
}
public void if_icmpne(Label label) {
branch(IF_ICMPNE, label);
decStack2();
}
public void if_acmpeq(Label label) {
branch(IF_ACMPEQ, label);
decStack2();
}
public void anewarray(int typeRefIndex) {
emitOpcode(ANEWARRAY);
emitCPIndex2(typeRefIndex);
}
public void anewarray(ClassConstant type) {
anewarray(constantPoolEditor.indexOf(type, true));
}
public void invokestatic(int methodRefIndex, int numArgSlots, int numReturnValueSlots) {
emitOpcode(INVOKESTATIC);
emitCPIndex2(methodRefIndex);
adjustStackForInvoke(methodRefIndex, true, false, numArgSlots, numReturnValueSlots);
}
public void invokestatic(ClassMethodRefConstant method, int numArgSlots, int numReturnValueSlots) {
invokestatic(constantPoolEditor.indexOf(method, true), numArgSlots, numReturnValueSlots);
}
public void invokevirtual(int methodRefIndex, int numArgSlots, int numReturnValueSlots) {
emitOpcode(INVOKEVIRTUAL);
emitCPIndex2(methodRefIndex);
adjustStackForInvoke(methodRefIndex, false, false, numArgSlots, numReturnValueSlots);
}
public void invokevirtual(ClassMethodRefConstant method, int numArgSlots, int numReturnValueSlots) {
invokevirtual(constantPoolEditor.indexOf(method, true), numArgSlots, numReturnValueSlots);
}
public void invokespecial(int methodRefIndex, int argSlots, int numReturnValueSlots) {
emitOpcode(INVOKESPECIAL);
emitCPIndex2(methodRefIndex);
adjustStackForInvoke(methodRefIndex, false, false, argSlots, numReturnValueSlots);
}
public void invokespecial(ClassMethodRefConstant method, int argSlots, int numReturnValueSlots) {
invokespecial(constantPoolEditor.indexOf(method, true), argSlots, numReturnValueSlots);
}
public void invokeinterface(int methodRefIndex, int argSlots, int count, int numReturnValueSlots) {
emitOpcode(INVOKEINTERFACE);
emitCPIndex2(methodRefIndex);
emitByte(count);
emitByte(0);
adjustStackForInvoke(methodRefIndex, false, true, argSlots, numReturnValueSlots);
}
public void invokeinterface(InterfaceMethodRefConstant method, int argSlots, int count, int numReturnValueSlots) {
invokeinterface(constantPoolEditor.indexOf(method, true), argSlots, count, numReturnValueSlots);
}
/**
* @see Bytecodes#JNICALL
* @param nativeFunctionDescriptor
* the parameters and result type of the native function linked for the Java native method
*/
public void callnative(SignatureDescriptor nativeFunctionDescriptor, int argSlots, int returnValueSlots) {
final int nativeFunctionDescriptorIndex = constantPoolEditor.indexOf(makeUtf8Constant(nativeFunctionDescriptor.toString()));
emitOpcode(JNICALL);
emitCPIndex2(nativeFunctionDescriptorIndex);
assert adjustmentMatchesSignature(nativeFunctionDescriptor, true, argSlots, returnValueSlots);
setStack(stack - argSlots + returnValueSlots);
}
private int fieldSizeInSlots(int fieldRefIndex) {
return constantPool().fieldAt(fieldRefIndex).type(constantPool()).toKind().stackSlots;
}
public void getstatic(int fieldRefIndex) {
emitOpcode(GETSTATIC);
emitCPIndex2(fieldRefIndex);
adjustStack(fieldSizeInSlots(fieldRefIndex));
}
public void getstatic(FieldRefConstant field) {
getstatic(constantPoolEditor.indexOf(field, true));
}
public void putstatic(int fieldRefIndex) {
emitOpcode(PUTSTATIC);
emitCPIndex2(fieldRefIndex);
adjustStack(-fieldSizeInSlots(fieldRefIndex));
}
public void putstatic(FieldRefConstant field) {
putstatic(constantPoolEditor.indexOf(field, true));
}
public void getfield(int fieldRefIndex) {
emitOpcode(GETFIELD);
emitCPIndex2(fieldRefIndex);
adjustStack(fieldSizeInSlots(fieldRefIndex) - 1);
}
public void getfield(FieldRefConstant field) {
getfield(constantPoolEditor.indexOf(field, true));
}
public void putfield(int fieldRefIndex) {
emitOpcode(PUTFIELD);
emitCPIndex2(fieldRefIndex);
adjustStack(-(1 + fieldSizeInSlots(fieldRefIndex)));
}
public void putfield(FieldRefConstant field) {
putfield(constantPoolEditor.indexOf(field, true));
}
public void ldc(int index) {
if (index <= 0xff) {
emitOpcode(LDC);
emitByte(index);
} else {
emitOpcode(LDC_W);
emitCPIndex2(index);
}
final Tag tag = constantPool().tagAt(index);
switch (tag) {
case CLASS: {
break;
}
case INTEGER: {
break;
}
case FLOAT: {
break;
}
case STRING: {
break;
}
case OBJECT: {
break;
}
default: {
throw verifyError("Invalid index in LDC to " + tag);
}
}
incStack();
}
public void ldc(PoolConstant constant) {
ldc(constantPoolEditor.indexOf(constant, true));
}
/**
* Emit a local variable load or store instruction.
*/
private void accessLocalVariable(int immediate0Opcode, int standardOpcode, int index, Kind kind, boolean isLoad) {
ProgramError.check(index >= 0 && index < maxLocals);
assert nameOf(immediate0Opcode).endsWith("_0");
if (index >= 0 && index <= 3) {
emitOpcode(immediate0Opcode + index);
} else if (index <= 0xff) {
emitOpcode(standardOpcode);
emitByte(index);
} else {
emitOpcode(WIDE);
emitOpcode(standardOpcode);
emitOffset2(index);
}
}
public void iload(int index) {
accessLocalVariable(ILOAD_0, ILOAD, index, Kind.INT, true);
incStack();
}
public void istore(int index) {
accessLocalVariable(ISTORE_0, ISTORE, index, Kind.INT, false);
decStack();
}
public void lload(int index) {
accessLocalVariable(LLOAD_0, LLOAD, index, Kind.LONG, true);
adjustStack(2);
}
public void fload(int index) {
accessLocalVariable(FLOAD_0, FLOAD, index, Kind.FLOAT, true);
incStack();
}
public void dload(int index) {
accessLocalVariable(DLOAD_0, DLOAD, index, Kind.DOUBLE, true);
adjustStack(2);
}
public void aload(int index) {
accessLocalVariable(ALOAD_0, ALOAD, index, Kind.REFERENCE, true);
incStack();
}
public void astore(int index) {
accessLocalVariable(ASTORE_0, ASTORE, index, Kind.REFERENCE, false);
decStack();
}
public void return_(Kind kind) {
switch (kind.asEnum) {
case BYTE:
case BOOLEAN:
case SHORT:
case CHAR:
case INT: {
ireturn();
break;
}
case FLOAT: {
freturn();
break;
}
case LONG: {
lreturn();
break;
}
case DOUBLE: {
dreturn();
break;
}
case WORD: {
areturn();
break;
}
case REFERENCE: {
areturn();
break;
}
case VOID: {
vreturn();
break;
}
}
}
public void ireturn() {
emitOpcode(IRETURN);
decStack();
}
public void lreturn() {
emitOpcode(LRETURN);
decStack2();
}
public void freturn() {
emitOpcode(FRETURN);
decStack();
}
public void dreturn() {
emitOpcode(DRETURN);
decStack2();
}
public void areturn() {
emitOpcode(ARETURN);
decStack();
}
public void vreturn() {
emitOpcode(RETURN);
}
public void iconst(int value) {
if (value == -1) {
emitOpcode(ICONST_M1);
} else if (value >= 0 && value <= 5) {
emitOpcode(ICONST_0 + value);
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
emitOpcode(BIPUSH);
emitByte(value);
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
emitOpcode(SIPUSH);
emitShort(value);
} else {
final int offset = constantPoolEditor.indexOf(createIntegerConstant(value));
if (offset <= 0xff) {
emitOpcode(LDC);
emitCPIndex1(offset);
} else {
emitOpcode(LDC_W);
emitCPIndex2(offset);
}
}
incStack();
}
public void lconst(long value) {
if (value == 0L) {
emitOpcode(LCONST_0);
} else if (value == 1L) {
emitOpcode(LCONST_1);
} else {
final int offset = constantPoolEditor.indexOf(createLongConstant(value));
emitOpcode(LDC2_W);
emitCPIndex2(offset);
}
incStack2();
}
public void fconst(float value) {
if (value == 0f) {
emitOpcode(FCONST_0);
} else if (value == 1f) {
emitOpcode(FCONST_1);
} else if (value == 2f) {
emitOpcode(FCONST_2);
} else {
final int offset = constantPoolEditor.indexOf(createFloatConstant(value));
if (offset <= 0xff) {
emitOpcode(LDC);
emitCPIndex1(offset);
} else {
emitOpcode(LDC_W);
emitCPIndex2(offset);
}
}
incStack();
}
public void dconst(double value) {
if (value == 0d) {
emitOpcode(DCONST_0);
} else if (value == 1d) {
emitOpcode(DCONST_1);
} else {
final int offset = constantPoolEditor.indexOf(createDoubleConstant(value));
emitOpcode(LDC2_W);
emitCPIndex2(offset);
}
incStack2();
}
public void new_(int index) {
emitOpcode(NEW);
emitCPIndex2(index);
incStack();
}
public void new_(ClassConstant type) {
new_(constantPoolEditor.indexOf(type, true));
}
public void checkcast(int index) {
emitOpcode(CHECKCAST);
emitCPIndex2(index);
}
public void checkcast(ClassConstant type) {
checkcast(constantPoolEditor.indexOf(type, true));
}
public void instanceof_(int classIndex) {
emitOpcode(INSTANCEOF);
emitCPIndex2(classIndex);
}
public void instanceof_(ClassConstant type) {
instanceof_(constantPoolEditor.indexOf(type, true));
}
public void arraylength() {
emitOpcode(ARRAYLENGTH);
}
public void aaload() {
emitOpcode(AALOAD);
decStack();
}
public void aastore() {
emitOpcode(AASTORE);
adjustStack(-3);
}
public void iadd() {
emitOpcode(IADD);
decStack();
}
public void imul() {
emitOpcode(IMUL);
decStack();
}
public void i2l() {
emitOpcode(I2L);
incStack();
}
public void i2f() {
emitOpcode(I2F);
}
public void i2d() {
emitOpcode(I2D);
incStack();
}
public void l2f() {
emitOpcode(L2F);
decStack();
}
public void l2d() {
emitOpcode(L2D);
}
public void f2d() {
emitOpcode(F2D);
incStack();
}
public void dup_x1() {
emitOpcode(DUP_X1);
incStack();
}
public void swap() {
emitOpcode(SWAP);
}
public void ishl() {
emitOpcode(ISHL);
decStack();
}
public void ishr() {
emitOpcode(ISHR);
decStack();
}
public void monitorenter() {
emitOpcode(MONITORENTER);
decStack();
}
public void monitorexit() {
emitOpcode(MONITOREXIT);
decStack();
}
private void tableswitch0(int defaultTarget, int lowMatch, int highMatch, int[] targets) {
final int opcodeAddress = currentAddress;
emitOpcode(TABLESWITCH);
final int padding = 3 - opcodeAddress % 4; // number of pad bytes
for (int i = 0; i < padding; i++) {
emitByte(0);
}
emitOffset4(defaultTarget - opcodeAddress);
emitOffset4(lowMatch);
emitOffset4(highMatch);
final int nTargets = highMatch - lowMatch + 1;
if (targets != null) {
ProgramError.check(targets.length == nTargets);
for (int target : targets) {
emitOffset4(target - opcodeAddress);
}
} else {
for (int i = 0; i != nTargets; ++i) {
emitOffset4(0);
}
}
}
public void tableswitch(int defaultTarget, int lowMatch, int highMatch, int[] targets) {
ProgramError.check(targets != null);
decStack();
tableswitch0(defaultTarget, lowMatch, highMatch, targets);
}
public void tableswitch(final Label defaultTarget, final int lowMatch, final int highMatch, final Label[] targets) {
final int opcodeAddress = currentAddress;
decStack();
tableswitch0(0, lowMatch, highMatch, null);
final int size = currentAddress - opcodeAddress;
unboundInstructions.add(new LabelInstruction(opcodeAddress, size){
@Override
public void assemble(BytecodeAssembler assembler) {
final int[] boundTargets = new int[targets.length];
for (int i = 0; i != boundTargets.length; ++i) {
boundTargets[i] = targets[i].address();
}
tableswitch0(defaultTarget.address(), lowMatch, highMatch, boundTargets);
}
});
}
private void lookupswitch0(int defaultTarget, int npairs, int[] matches, int[] targets) {
final int opcodeAddress = currentAddress;
emitOpcode(LOOKUPSWITCH);
final int padding = 3 - opcodeAddress % 4; // number of pad bytes
for (int i = 0; i < padding; i++) {
emitByte(0);
}
emitOffset4(defaultTarget - opcodeAddress);
emitOffset4(npairs);
if (matches != null) {
ProgramError.check(matches.length == npairs);
ProgramError.check(targets.length == npairs);
for (int i = 0; i != npairs; ++i) {
emitOffset4(matches[i]);
emitOffset4(targets[i] - opcodeAddress);
}
} else {
for (int i = 0; i != npairs; ++i) {
emitOffset4(0);
emitOffset4(0);
}
}
}
public void lookupswitch(int defaultTarget, int[] matches, int[] targets) {
ProgramError.check(matches != null);
ProgramError.check(targets != null);
decStack();
lookupswitch0(defaultTarget, matches.length, matches, targets);
}
public void lookupswitch(final Label defaultTarget, final int[] matches, final Label[] targets) {
final int opcodeAddress = currentAddress;
decStack();
lookupswitch0(0, matches.length, null, null);
final int size = currentAddress - opcodeAddress;
unboundInstructions.add(new LabelInstruction(opcodeAddress, size){
@Override
public void assemble(BytecodeAssembler assembler) {
final int[] boundTargets = new int[targets.length];
for (int i = 0; i != boundTargets.length; ++i) {
boundTargets[i] = targets[i].address();
}
lookupswitch0(defaultTarget.address(), matches.length, matches, boundTargets);
}
});
}
public void caload() {
emitOpcode(CALOAD);
decStack();
}
public void castore() {
emitOpcode(CASTORE);
adjustStack(-3);
}
public void dup2() {
emitOpcode(DUP2);
adjustStack(2);
}
public void i2c() {
emitOpcode(I2C);
}
public void iinc(int index, int cnst) {
emitOpcode(IINC);
// UNSAFE for wide instructions - wide is currently not supported anyway
emitCPIndex1(index);
emitByte(cnst);
}
public void isub() {
emitOpcode(ISUB);
decStack();
}
public static final int T_BOOLEAN = 4;
public static final int T_CHAR = 5;
public static final int T_FLOAT = 6;
public static final int T_DOUBLE = 7;
public static final int T_BYTE = 8;
public static final int T_SHORT = 9;
public static final int T_INT = 10;
public static final int T_LONG = 11;
public void newarray(int type) {
emitOpcode(NEWARRAY);
emitByte(type);
}
}