/*
* Copyright 2004-2010 Brian S O'Neill
*
* 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.cojen.classfile;
import org.cojen.classfile.attribute.CodeAttr;
import org.cojen.classfile.constant.ConstantClassInfo;
import org.cojen.classfile.constant.ConstantFieldInfo;
/**
* This class is used as an aid in generating code for a method.
* It controls the max stack, local variable allocation, labels and bytecode.
*
* @author Brian S O'Neill
*/
public class CodeBuilder extends AbstractCodeAssembler implements CodeBuffer, CodeAssembler {
private final CodeAttr mCodeAttr;
private final ClassFile mClassFile;
private final ConstantPool mCp;
private final InstructionList mInstructions;
private final LocalVariable mThisReference;
private final LocalVariable[] mParameters;
private final int mTarget;
private final boolean mSaveLineNumberInfo;
private final boolean mSaveLocalVariableInfo;
/**
* Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
* CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
*/
public CodeBuilder(MethodInfo info) {
this(info, true, false);
}
/**
* Construct a CodeBuilder for the CodeAttr of the given MethodInfo. The
* CodeBuffer for the CodeAttr is automatically set to this CodeBuilder.
*
* @param saveLineNumberInfo When set false, all calls to mapLineNumber
* are ignored. By default, this value is true.
* @param saveLocalVariableInfo When set true, all local variable
* usage information is saved in the ClassFile. By default, this value
* is false.
* @see #mapLineNumber
*/
public CodeBuilder(MethodInfo info, boolean saveLineNumberInfo,
boolean saveLocalVariableInfo) {
String target = info.getClassFile().getTarget();
if ("1.0".equals(target)) {
mTarget = 0x00010000;
} else if ("1.1".equals(target)) {
mTarget = 0x00010001;
} else if ("1.2".equals(target)) {
mTarget = 0x00010002;
} else if ("1.3".equals(target)) {
mTarget = 0x00010003;
} else if ("1.4".equals(target)) {
mTarget = 0x00010004;
} else if ("1.5".equals(target)) {
mTarget = 0x00010005;
} else if ("1.6".equals(target)) {
mTarget = 0x00010006;
} else {
mTarget = 0x00010000;
}
mCodeAttr = info.getCodeAttr();
mClassFile = info.getClassFile();
mCp = mClassFile.getConstantPool();
mInstructions = new InstructionList(saveLocalVariableInfo);
mCodeAttr.setCodeBuffer(this);
mSaveLineNumberInfo = saveLineNumberInfo;
mSaveLocalVariableInfo = saveLocalVariableInfo;
// Create LocalVariable references for "this" reference and other
// passed in parameters.
LocalVariable localVar;
if (info.getModifiers().isStatic()) {
mThisReference = null;
} else {
localVar = mInstructions.createLocalParameter("this", mClassFile.getType());
mThisReference = localVar;
if (saveLocalVariableInfo) {
mCodeAttr.localVariableUse(localVar);
}
}
TypeDesc[] paramTypes = info.getMethodDescriptor().getParameterTypes();
int paramSize = paramTypes.length;
mParameters = new LocalVariable[paramSize];
for (int i = 0; i<paramTypes.length; i++) {
localVar = mInstructions.createLocalParameter(null, paramTypes[i]);
mParameters[i] = localVar;
if (saveLocalVariableInfo) {
mCodeAttr.localVariableUse(localVar);
}
}
}
public int getMaxStackDepth() {
return mInstructions.getMaxStackDepth();
}
public int getMaxLocals() {
return mInstructions.getMaxLocals();
}
public byte[] getByteCodes() {
return mInstructions.getByteCodes();
}
public ExceptionHandler[] getExceptionHandlers() {
return mInstructions.getExceptionHandlers();
}
/**
* @param pushed type of argument pushed to operand stack after instruction
* executes; pass TypeDesc.VOID if nothing
*/
private void addInstruction(int stackAdjust, TypeDesc pushed, byte opcode) {
mInstructions.new SimpleInstruction(stackAdjust, pushed, new byte[] {opcode});
}
/**
* @param pushed type of argument pushed to operand stack after instruction
* executes; pass TypeDesc.VOID if nothing
*/
private void addInstruction(int stackAdjust, TypeDesc pushed, byte opcode, byte operand) {
mInstructions.new SimpleInstruction(stackAdjust, pushed, new byte[] {opcode, operand});
}
/**
* @param pushed type of argument pushed to operand stack after instruction
* executes; pass TypeDesc.VOID if nothing
*/
private void addInstruction(int stackAdjust, TypeDesc pushed, byte opcode, short operand) {
mInstructions.new SimpleInstruction
(stackAdjust, pushed,
new byte[] {opcode, (byte)(operand >> 8), (byte)operand});
}
/**
* @param pushed type of argument pushed to operand stack after instruction
* executes; pass TypeDesc.VOID if nothing
*/
private void addInstruction(int stackAdjust, TypeDesc pushed, byte opcode,
ConstantInfo operand) {
mInstructions.new ConstantOperandInstruction
(stackAdjust, pushed,
// The zeros get filled in later, when the ConstantInfo index is
// resolved.
new byte[] {opcode, (byte)0, (byte)0}, operand);
}
private String getClassName(TypeDesc classDesc) throws IllegalArgumentException {
if (classDesc.isPrimitive()) {
throw new IllegalArgumentException("Primitive type not allowed");
}
if (classDesc.isArray()) {
throw new IllegalArgumentException("Array type not allowed");
}
return classDesc.getRootName();
}
public int getParameterCount() {
return mParameters.length;
}
public LocalVariable getParameter(int index) {
return mParameters[index];
}
public LocalVariable createLocalVariable(String name, TypeDesc type) {
LocalVariable localVar = mInstructions.createLocalVariable(name, type);
if (mSaveLocalVariableInfo) {
mCodeAttr.localVariableUse(localVar);
}
return localVar;
}
public Label createLabel() {
return mInstructions.new LabelInstruction();
}
public void exceptionHandler(Location startLocation,
Location endLocation,
String catchClassName)
{
if (!(startLocation instanceof InstructionList.LabelInstruction)) {
throw new IllegalArgumentException("Start location is not a label instruction");
}
if (!(endLocation instanceof InstructionList.LabelInstruction)) {
throw new IllegalArgumentException("End location is not a label instruction");
}
Location catchLocation = createLabel().setLocation();
ConstantClassInfo catchClass;
if (catchClassName == null) {
catchClass = null;
} else {
catchClass = mCp.addConstantClass(catchClassName);
}
ExceptionHandler<InstructionList.LabelInstruction> handler =
new ExceptionHandler<InstructionList.LabelInstruction>
((InstructionList.LabelInstruction)startLocation,
(InstructionList.LabelInstruction)endLocation,
(InstructionList.LabelInstruction)catchLocation,
catchClass);
mInstructions.addExceptionHandler(handler);
}
public void mapLineNumber(int lineNumber) {
if (mSaveLineNumberInfo) {
mCodeAttr.mapLineNumber(createLabel().setLocation(), lineNumber);
}
}
// load-constant-to-stack style instructions
public void loadNull() {
addInstruction(1, null, Opcode.ACONST_NULL);
}
public void loadConstant(String value) {
if (value == null) {
loadNull();
return;
}
int strlen = value.length();
if (strlen <= (65535 / 3)) {
// Guaranteed to fit in a Java UTF encoded string.
ConstantInfo info = mCp.addConstantString(value);
mInstructions.new LoadConstantInstruction(1, TypeDesc.STRING, info);
return;
}
// Compute actual UTF length.
int utflen = 0;
for (int i=0; i<strlen; i++) {
int c = value.charAt(i);
if ((c >= 0x0001) && (c <= 0x007F)) {
utflen++;
} else if (c > 0x07FF) {
utflen += 3;
} else {
utflen += 2;
}
}
if (utflen <= 65535) {
ConstantInfo info = mCp.addConstantString(value);
mInstructions.new LoadConstantInstruction(1, TypeDesc.STRING, info);
return;
}
// Break string up into chunks and construct in a StringBuffer.
TypeDesc stringBufferDesc;
if (mTarget >= 0x00010005) {
stringBufferDesc = TypeDesc.forClass("java.lang.StringBuilder");
} else {
stringBufferDesc = TypeDesc.forClass(StringBuffer.class);
}
TypeDesc intDesc = TypeDesc.INT;
TypeDesc stringDesc = TypeDesc.STRING;
TypeDesc[] stringParam = new TypeDesc[] {stringDesc};
newObject(stringBufferDesc);
dup();
loadConstant(strlen);
invokeConstructor(stringBufferDesc, new TypeDesc[] {intDesc});
int beginIndex;
int endIndex = 0;
while (endIndex < strlen) {
beginIndex = endIndex;
// Make each chunk as large as possible.
utflen = 0;
for (; endIndex < strlen; endIndex++) {
int c = value.charAt(endIndex);
int size;
if ((c >= 0x0001) && (c <= 0x007F)) {
size = 1;
} else if (c > 0x07FF) {
size = 3;
} else {
size = 2;
}
if ((utflen + size) > 65535) {
break;
} else {
utflen += size;
}
}
String substr = value.substring(beginIndex, endIndex);
ConstantInfo info = mCp.addConstantString(substr);
mInstructions.new LoadConstantInstruction(1, TypeDesc.STRING, info);
invokeVirtual(stringBufferDesc, "append",
stringBufferDesc, stringParam);
}
invokeVirtual(stringBufferDesc, "toString", stringDesc, null);
}
public void loadConstant(TypeDesc type) throws IllegalStateException {
if (type == null) {
loadNull();
return;
}
if (type.isPrimitive()) {
if (mTarget < 0x00010001) {
throw new IllegalStateException
("Loading constant primitive classes not supported below target version 1.1");
}
loadStaticField(type.toObjectType(), "TYPE", TypeDesc.forClass(Class.class));
} else {
if (mTarget < 0x00010005) {
throw new IllegalStateException
("Loading constant object classes not supported below target version 1.5");
}
ConstantInfo info = mCp.addConstantClass(type);
mInstructions.new LoadConstantInstruction(1, TypeDesc.forClass(Class.class), info);
}
}
public void loadConstant(boolean value) {
loadConstant(value ? 1 : 0);
}
public void loadConstant(int value) {
if (-1 <= value && value <= 5) {
byte op;
switch(value) {
case -1:
op = Opcode.ICONST_M1;
break;
case 0:
op = Opcode.ICONST_0;
break;
case 1:
op = Opcode.ICONST_1;
break;
case 2:
op = Opcode.ICONST_2;
break;
case 3:
op = Opcode.ICONST_3;
break;
case 4:
op = Opcode.ICONST_4;
break;
case 5:
op = Opcode.ICONST_5;
break;
default:
op = Opcode.NOP;
}
addInstruction(1, TypeDesc.INT, op);
} else if (-128 <= value && value <= 127) {
addInstruction(1, TypeDesc.INT, Opcode.BIPUSH, (byte)value);
} else if (-32768 <= value && value <= 32767) {
addInstruction(1, TypeDesc.INT, Opcode.SIPUSH, (short)value);
} else {
ConstantInfo info = mCp.addConstantInteger(value);
mInstructions.new LoadConstantInstruction(1, TypeDesc.INT, info);
}
}
public void loadConstant(long value) {
if (value == 0) {
addInstruction(2, TypeDesc.LONG, Opcode.LCONST_0);
} else if (value == 1) {
addInstruction(2, TypeDesc.LONG, Opcode.LCONST_1);
} else {
ConstantInfo info = mCp.addConstantLong(value);
mInstructions.new LoadConstantInstruction(2, TypeDesc.LONG, info, true);
}
}
public void loadConstant(float value) {
if (value == 0) {
addInstruction(1, TypeDesc.FLOAT, Opcode.FCONST_0);
} else if (value == 1) {
addInstruction(1, TypeDesc.FLOAT, Opcode.FCONST_1);
} else if (value == 2) {
addInstruction(1, TypeDesc.FLOAT, Opcode.FCONST_2);
} else {
ConstantInfo info = mCp.addConstantFloat(value);
mInstructions.new LoadConstantInstruction(1, TypeDesc.FLOAT, info);
}
}
public void loadConstant(double value) {
if (value == 0) {
addInstruction(2, TypeDesc.DOUBLE, Opcode.DCONST_0);
} else if (value == 1) {
addInstruction(2, TypeDesc.DOUBLE, Opcode.DCONST_1);
} else {
ConstantInfo info = mCp.addConstantDouble(value);
mInstructions.new LoadConstantInstruction(2, TypeDesc.DOUBLE, info, true);
}
}
// load-local-to-stack style instructions
public void loadLocal(LocalVariable local) {
if (local == null) {
throw new IllegalArgumentException("No local variable specified");
}
int stackAdjust = local.getType().isDoubleWord() ? 2 : 1;
mInstructions.new LoadLocalInstruction(stackAdjust, local);
}
public void loadThis() {
if (mThisReference != null) {
loadLocal(mThisReference);
} else {
throw new IllegalStateException
("Attempt to load \"this\" reference in a static method");
}
}
// store-from-stack-to-local style instructions
public void storeLocal(LocalVariable local) {
if (local == null) {
throw new IllegalArgumentException("No local variable specified");
}
int stackAdjust = local.getType().isDoubleWord() ? -2 : -1;
mInstructions.new StoreLocalInstruction(stackAdjust, local);
}
// load-to-stack-from-array style instructions
public void loadFromArray(TypeDesc type) {
byte op;
int stackAdjust = -1;
switch (type.getTypeCode()) {
case TypeDesc.INT_CODE:
op = Opcode.IALOAD;
break;
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
op = Opcode.BALOAD;
break;
case TypeDesc.SHORT_CODE:
op = Opcode.SALOAD;
break;
case TypeDesc.CHAR_CODE:
op = Opcode.CALOAD;
break;
case TypeDesc.FLOAT_CODE:
op = Opcode.FALOAD;
break;
case TypeDesc.LONG_CODE:
stackAdjust = 0;
op = Opcode.LALOAD;
break;
case TypeDesc.DOUBLE_CODE:
stackAdjust = 0;
op = Opcode.DALOAD;
break;
default:
op = Opcode.AALOAD;
break;
}
addInstruction(stackAdjust, type, op);
}
// store-to-array-from-stack style instructions
public void storeToArray(TypeDesc type) {
byte op;
int stackAdjust = -3;
switch (type.getTypeCode()) {
case TypeDesc.INT_CODE:
op = Opcode.IASTORE;
break;
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
op = Opcode.BASTORE;
break;
case TypeDesc.SHORT_CODE:
op = Opcode.SASTORE;
break;
case TypeDesc.CHAR_CODE:
op = Opcode.CASTORE;
break;
case TypeDesc.FLOAT_CODE:
op = Opcode.FASTORE;
break;
case TypeDesc.LONG_CODE:
stackAdjust = -4;
op = Opcode.LASTORE;
break;
case TypeDesc.DOUBLE_CODE:
stackAdjust = -4;
op = Opcode.DASTORE;
break;
default:
op = Opcode.AASTORE;
break;
}
addInstruction(stackAdjust, TypeDesc.VOID, op);
}
// load-field-to-stack style instructions
public void loadField(String fieldName,
TypeDesc type) {
getfield(0, Opcode.GETFIELD, constantField(fieldName, type), type);
}
public void loadField(String className,
String fieldName,
TypeDesc type) {
getfield(0, Opcode.GETFIELD,
mCp.addConstantField(className, fieldName, type),
type);
}
public void loadField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
loadField(getClassName(classDesc), fieldName, type);
}
public void loadStaticField(String fieldName,
TypeDesc type) {
getfield(1, Opcode.GETSTATIC, constantField(fieldName, type), type);
}
public void loadStaticField(String className,
String fieldName,
TypeDesc type) {
getfield(1, Opcode.GETSTATIC,
mCp.addConstantField(className, fieldName, type),
type);
}
public void loadStaticField(TypeDesc classDesc,
String fieldName,
TypeDesc type) {
loadStaticField(getClassName(classDesc), fieldName, type);
}
private void getfield(int stackAdjust, byte opcode, ConstantInfo info, TypeDesc type) {
if (type.isDoubleWord()) {
stackAdjust++;
}
addInstruction(stackAdjust, type, opcode, info);
}
private ConstantFieldInfo constantField(String fieldName, TypeDesc type) {
return mCp.addConstantField(mClassFile.getClassName(), fieldName, type);
}
// store-to-field-from-stack style instructions
public void storeField(String fieldName, TypeDesc type) {
putfield(-1, Opcode.PUTFIELD, constantField(fieldName, type), type);
}
public void storeField(String className, String fieldName, TypeDesc type) {
putfield(-1, Opcode.PUTFIELD,
mCp.addConstantField(className, fieldName, type),
type);
}
public void storeField(TypeDesc classDesc, String fieldName, TypeDesc type) {
storeField(getClassName(classDesc), fieldName, type);
}
public void storeStaticField(String fieldName, TypeDesc type) {
putfield(0, Opcode.PUTSTATIC, constantField(fieldName, type), type);
}
public void storeStaticField(String className, String fieldName, TypeDesc type) {
putfield(0, Opcode.PUTSTATIC,
mCp.addConstantField(className, fieldName, type),
type);
}
public void storeStaticField(TypeDesc classDesc, String fieldName, TypeDesc type) {
storeStaticField(getClassName(classDesc), fieldName, type);
}
private void putfield(int stackAdjust, byte opcode, ConstantInfo info, TypeDesc type) {
if (type.isDoubleWord()) {
stackAdjust -= 2;
} else {
stackAdjust--;
}
addInstruction(stackAdjust, TypeDesc.VOID, opcode, info);
}
// return style instructions
public void returnVoid() {
addInstruction(0, TypeDesc.VOID, Opcode.RETURN);
}
public void returnValue(TypeDesc type) {
int stackAdjust = -1;
byte op;
switch (type.getTypeCode()) {
case TypeDesc.INT_CODE:
case TypeDesc.BOOLEAN_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.SHORT_CODE:
case TypeDesc.CHAR_CODE:
op = Opcode.IRETURN;
break;
case TypeDesc.FLOAT_CODE:
op = Opcode.FRETURN;
break;
case TypeDesc.LONG_CODE:
stackAdjust = -2;
op = Opcode.LRETURN;
break;
case TypeDesc.DOUBLE_CODE:
stackAdjust = -2;
op = Opcode.DRETURN;
break;
case TypeDesc.VOID_CODE:
stackAdjust = 0;
op = Opcode.RETURN;
break;
default:
op = Opcode.ARETURN;
break;
}
addInstruction(stackAdjust, TypeDesc.VOID, op);
}
// numerical conversion style instructions
public void convert(TypeDesc fromType, TypeDesc toType) {
convert(fromType, toType, CONVERT_FP_NORMAL);
}
public void convert(TypeDesc fromType, TypeDesc toType, int fpConvertMode) {
if (fpConvertMode < 0 || fpConvertMode > CONVERT_FP_RAW_BITS) {
throw new IllegalArgumentException("Illegal floating point conversion mode");
}
if (toType == TypeDesc.OBJECT) {
if (fromType.isPrimitive()) {
toType = fromType.toObjectType();
} else {
return;
}
}
if (fromType == toType) {
return;
}
TypeDesc fromPrimitiveType = fromType.toPrimitiveType();
if (fromPrimitiveType == null) {
if (!toType.isPrimitive()) {
Class fromClass = fromType.toClass();
if (fromClass != null) {
Class toClass = toType.toClass();
if (toClass != null && toClass.isAssignableFrom(fromClass)) {
return;
}
}
}
throw invalidConversion(fromType, toType);
}
int fromTypeCode = fromPrimitiveType.getTypeCode();
if (toType.toClass() == Number.class) {
switch (fromTypeCode) {
case TypeDesc.INT_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.SHORT_CODE:
case TypeDesc.LONG_CODE:
case TypeDesc.FLOAT_CODE:
case TypeDesc.DOUBLE_CODE:
if (fromType.isPrimitive()) {
toType = fromType.toObjectType();
} else {
return;
}
}
}
TypeDesc toPrimitiveType = toType.toPrimitiveType();
if (toPrimitiveType == null) {
throw invalidConversion(fromType, toType);
}
int toTypeCode = toPrimitiveType.getTypeCode();
// Location is set at end, after doConversion.
Label end = createLabel();
doConversion: {
int stackAdjust = 0;
byte op;
switch (fromTypeCode) {
case TypeDesc.INT_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.SHORT_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.BOOLEAN_CODE:
switch (toTypeCode) {
case TypeDesc.BYTE_CODE:
op = (fromTypeCode == TypeDesc.BYTE_CODE) ?
Opcode.NOP : Opcode.I2B;
break;
case TypeDesc.SHORT_CODE:
op = (fromTypeCode == TypeDesc.SHORT_CODE) ?
Opcode.NOP : Opcode.I2S;
break;
case TypeDesc.CHAR_CODE:
op = (fromTypeCode == TypeDesc.CHAR_CODE) ?
Opcode.NOP : Opcode.I2C;
break;
case TypeDesc.FLOAT_CODE:
op = Opcode.I2F;
break;
case TypeDesc.LONG_CODE:
stackAdjust = 1;
op = Opcode.I2L;
break;
case TypeDesc.DOUBLE_CODE:
stackAdjust = 1;
op = Opcode.I2D;
break;
case TypeDesc.INT_CODE:
op = Opcode.NOP;
break;
case TypeDesc.BOOLEAN_CODE:
if (!fromType.isPrimitive()) {
if (!toType.isPrimitive()) {
nullConvert(end);
}
unbox(fromType, fromPrimitiveType);
}
toBoolean(!toType.isPrimitive());
break doConversion;
default:
throw invalidConversion(fromType, toType);
}
break;
case TypeDesc.LONG_CODE:
switch (toTypeCode) {
case TypeDesc.INT_CODE:
stackAdjust = -1;
op = Opcode.L2I;
break;
case TypeDesc.FLOAT_CODE:
stackAdjust = -1;
op = Opcode.L2F;
break;
case TypeDesc.DOUBLE_CODE:
op = Opcode.L2D;
break;
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
addInstruction(-1, TypeDesc.INT, Opcode.L2I);
convert(TypeDesc.INT, toPrimitiveType);
// fall through
case TypeDesc.LONG_CODE:
op = Opcode.NOP;
break;
case TypeDesc.BOOLEAN_CODE:
if (!fromType.isPrimitive()) {
if (!toType.isPrimitive()) {
nullConvert(end);
}
unbox(fromType, fromPrimitiveType);
}
loadConstant(0L);
math(Opcode.LCMP);
toBoolean(!toType.isPrimitive());
break doConversion;
default:
throw invalidConversion(fromType, toType);
}
break;
case TypeDesc.FLOAT_CODE:
switch (toTypeCode) {
case TypeDesc.INT_CODE:
op = Opcode.F2I;
break;
case TypeDesc.LONG_CODE:
stackAdjust = 1;
op = Opcode.F2L;
break;
case TypeDesc.DOUBLE_CODE:
stackAdjust = 1;
op = Opcode.F2D;
break;
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
addInstruction(0, TypeDesc.INT, Opcode.F2I);
convert(TypeDesc.INT, toPrimitiveType);
// fall through
case TypeDesc.FLOAT_CODE:
op = Opcode.NOP;
break;
case TypeDesc.BOOLEAN_CODE:
if (!fromType.isPrimitive()) {
if (!toType.isPrimitive()) {
nullConvert(end);
}
unbox(fromType, fromPrimitiveType);
}
loadConstant(0.0f);
math(Opcode.FCMPG);
toBoolean(!toType.isPrimitive());
break doConversion;
default:
throw invalidConversion(fromType, toType);
}
break;
case TypeDesc.DOUBLE_CODE:
switch (toTypeCode) {
case TypeDesc.INT_CODE:
stackAdjust = -1;
op = Opcode.D2I;
break;
case TypeDesc.FLOAT_CODE:
stackAdjust = -1;
op = Opcode.D2F;
break;
case TypeDesc.LONG_CODE:
op = Opcode.D2L;
break;
case TypeDesc.BYTE_CODE:
case TypeDesc.CHAR_CODE:
case TypeDesc.SHORT_CODE:
addInstruction(-1, TypeDesc.INT, Opcode.D2I);
convert(TypeDesc.INT, toPrimitiveType);
// fall through
case TypeDesc.DOUBLE_CODE:
op = Opcode.NOP;
break;
case TypeDesc.BOOLEAN_CODE:
if (!fromType.isPrimitive()) {
if (!toType.isPrimitive()) {
nullConvert(end);
}
unbox(fromType, fromPrimitiveType);
}
loadConstant(0.0d);
math(Opcode.DCMPG);
toBoolean(!toType.isPrimitive());
break doConversion;
default:
throw invalidConversion(fromType, toType);
}
break;
default:
throw invalidConversion(fromType, toType);
}
if (!fromType.isPrimitive()) {
if (!toType.isPrimitive()) {
nullConvert(end);
}
unbox(fromType, fromPrimitiveType);
}
if (toType.isPrimitive()) {
if (op != Opcode.NOP) {
convertPrimitive(stackAdjust, op, fpConvertMode);
}
} else {
if (op == Opcode.NOP) {
prebox(toPrimitiveType, toType);
} else if (!fromPrimitiveType.isDoubleWord() &&
toPrimitiveType.isDoubleWord()) {
// Slight optimization here. Perform prebox on single word value,
// depending on what conversion is being applied.
prebox(fromPrimitiveType, toType);
convertPrimitive(stackAdjust, op, fpConvertMode);
} else {
convertPrimitive(stackAdjust, op, fpConvertMode);
prebox(toPrimitiveType, toType);
}
box(toPrimitiveType, toType);
}
}
end.setLocation();
}
/**
* @param from must be an object type
* @param to must be a primitive type
*/
private void unbox(TypeDesc from, TypeDesc to) {
String methodName;
switch (to.getTypeCode()) {
case TypeDesc.BOOLEAN_CODE:
methodName = "booleanValue";
break;
case TypeDesc.CHAR_CODE:
methodName = "charValue";
break;
case TypeDesc.FLOAT_CODE:
methodName = "floatValue";
break;
case TypeDesc.DOUBLE_CODE:
methodName = "doubleValue";
break;
case TypeDesc.BYTE_CODE:
methodName = "byteValue";
break;
case TypeDesc.SHORT_CODE:
methodName = "shortValue";
break;
case TypeDesc.INT_CODE:
methodName = "intValue";
break;
case TypeDesc.LONG_CODE:
methodName = "longValue";
break;
default:
return;
}
invokeVirtual(from.getRootName(), methodName, to, null);
}
/**
* @param from must be a primitive type
* @param to must be an object type
*/
private void prebox(TypeDesc from, TypeDesc to) {
if (mTarget >= 0x00010005) {
// No need to prebox since static valueOf method can be called
// instead.
return;
}
// Wouldn't it be cool if I could walk backwards in the instruction
// list and insert the new-dup pair before the value to box was even
// put on the stack?
switch (from.getTypeCode()) {
default:
break;
case TypeDesc.BOOLEAN_CODE:
if (to.toPrimitiveType().getTypeCode() == TypeDesc.BOOLEAN_CODE) {
break;
}
// fall through
case TypeDesc.CHAR_CODE:
case TypeDesc.FLOAT_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.SHORT_CODE:
case TypeDesc.INT_CODE:
newObject(to);
dupX1();
swap();
break;
case TypeDesc.DOUBLE_CODE:
case TypeDesc.LONG_CODE:
newObject(to);
dupX2();
dupX2();
pop();
break;
}
}
/**
* @param from must be a primitive type
* @param to must be an object type
*/
private void box(TypeDesc from, TypeDesc to) {
if (mTarget >= 0x00010005) {
// Call the new valueOf method.
invokeStatic(to.getRootName(), "valueOf", to, new TypeDesc[] {from});
return;
}
switch (from.getTypeCode()) {
case TypeDesc.BOOLEAN_CODE:
toBoolean(true);
break;
case TypeDesc.CHAR_CODE:
case TypeDesc.FLOAT_CODE:
case TypeDesc.BYTE_CODE:
case TypeDesc.SHORT_CODE:
case TypeDesc.INT_CODE:
case TypeDesc.DOUBLE_CODE:
case TypeDesc.LONG_CODE:
invokeConstructor(to.getRootName(), new TypeDesc[]{from});
break;
}
}
// Converts an int on the stack to a boolean.
private void toBoolean(boolean box) {
if (box && mTarget >= 0x00010004) {
// Call the new valueOf method.
invokeStatic("java.lang.Boolean", "valueOf",
TypeDesc.BOOLEAN.toObjectType(),
new TypeDesc[] {TypeDesc.BOOLEAN});
return;
}
Label nonZero = createLabel();
Label done = createLabel();
ifZeroComparisonBranch(nonZero, "!=");
if (box) {
TypeDesc newType = TypeDesc.BOOLEAN.toObjectType();
loadStaticField(newType.getRootName(), "FALSE", newType);
branch(done);
nonZero.setLocation();
loadStaticField(newType.getRootName(), "TRUE", newType);
} else {
loadConstant(false);
branch(done);
nonZero.setLocation();
loadConstant(true);
}
done.setLocation();
}
private void convertPrimitive(int stackAdjust, byte op, int fpConvertMode) {
if (fpConvertMode != CONVERT_FP_NORMAL) {
switch (op) {
case Opcode.I2F:
invokeStatic("java.lang.Float", "intBitsToFloat", TypeDesc.FLOAT,
new TypeDesc[] {TypeDesc.INT});
return;
case Opcode.L2D:
invokeStatic("java.lang.Double", "longBitsToDouble", TypeDesc.DOUBLE,
new TypeDesc[] {TypeDesc.LONG});
return;
case Opcode.F2I:
if (fpConvertMode == CONVERT_FP_RAW_BITS) {
invokeStatic("java.lang.Float", "floatToRawIntBits", TypeDesc.INT,
new TypeDesc[] {TypeDesc.FLOAT});
} else {
invokeStatic("java.lang.Float", "floatToIntBits", TypeDesc.INT,
new TypeDesc[] {TypeDesc.FLOAT});
}
return;
case Opcode.D2L:
if (fpConvertMode == CONVERT_FP_RAW_BITS) {
invokeStatic("java.lang.Double", "doubleToRawLongBits", TypeDesc.LONG,
new TypeDesc[] {TypeDesc.DOUBLE});
} else {
invokeStatic("java.lang.Double", "doubleToLongBits", TypeDesc.LONG,
new TypeDesc[] {TypeDesc.DOUBLE});
}
return;
}
}
TypeDesc pushed;
switch (op) {
case Opcode.I2F:
pushed = TypeDesc.FLOAT;
break;
case Opcode.L2D:
pushed = TypeDesc.DOUBLE;
break;
case Opcode.F2I:
pushed = TypeDesc.INT;
break;
case Opcode.D2L:
pushed = TypeDesc.LONG;
break;
default:
pushed = TypeDesc.VOID;
break;
}
addInstruction(stackAdjust, pushed, op);
}
// Check if from object is null. If so, no need to convert, and don't throw
// a NullPointerException. Assumes that from type and to type are objects.
private void nullConvert(Label end) {
LocalVariable temp = createLocalVariable("temp", TypeDesc.OBJECT);
storeLocal(temp);
loadLocal(temp);
Label notNull = createLabel();
ifNullBranch(notNull, false);
loadNull();
branch(end);
notNull.setLocation();
loadLocal(temp);
}
private IllegalArgumentException invalidConversion
(TypeDesc from, TypeDesc to)
{
throw new IllegalArgumentException
("Invalid conversion: " + from.getFullName() + " to " +
to.getFullName());
}
// invocation style instructions
public void invokeVirtual(String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeVirtual(mClassFile.getClassName(), methodName, ret, params);
}
public void invokeVirtual(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
mInstructions.new InvokeInstruction
(Opcode.INVOKEVIRTUAL,
mCp.addConstantMethod(className, methodName, ret, params),
ret, params);
}
public void invokeVirtual(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeVirtual(getClassName(classDesc), methodName, ret, params);
}
public void invokeStatic(String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeStatic(mClassFile.getClassName(), methodName, ret, params);
}
public void invokeStatic(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
mInstructions.new InvokeInstruction
(Opcode.INVOKESTATIC,
mCp.addConstantMethod(className, methodName, ret, params),
ret, params);
}
public void invokeStatic(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeStatic(getClassName(classDesc), methodName, ret, params);
}
public void invokeInterface(String className,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
mInstructions.new InvokeInstruction
(Opcode.INVOKEINTERFACE,
mCp.addConstantInterfaceMethod(className, methodName, ret, params),
ret, params);
}
public void invokeInterface(TypeDesc classDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeInterface(getClassName(classDesc), methodName, ret, params);
}
public void invokePrivate(String methodName,
TypeDesc ret,
TypeDesc[] params) {
mInstructions.new InvokeInstruction
(Opcode.INVOKESPECIAL,
mCp.addConstantMethod(mClassFile.getClassName(), methodName, ret, params),
ret, params);
}
public void invokeSuper(String superClassName,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
mInstructions.new InvokeInstruction
(Opcode.INVOKESPECIAL,
mCp.addConstantMethod(superClassName, methodName, ret, params),
ret, params);
}
public void invokeSuper(TypeDesc superClassDesc,
String methodName,
TypeDesc ret,
TypeDesc[] params) {
invokeSuper(getClassName(superClassDesc), methodName, ret, params);
}
public void invokeConstructor(TypeDesc[] params) {
invokeConstructor(mClassFile.getClassName(), mClassFile.getType(), params);
}
public void invokeConstructor(String className, TypeDesc[] params) {
invokeConstructor(className, TypeDesc.forClass(className), params);
}
public void invokeConstructor(TypeDesc classDesc, TypeDesc[] params) {
invokeConstructor(getClassName(classDesc), classDesc, params);
}
private void invokeConstructor(String className, TypeDesc classDesc, TypeDesc[] params) {
mInstructions.new InvokeConstructorInstruction
(mCp.addConstantConstructor(className, params), classDesc, params);
}
public void invokeSuperConstructor(TypeDesc[] params) {
invokeConstructor(mClassFile.getSuperClassName(), params);
}
// creation style instructions
public void newObject(TypeDesc type) {
if (type.isArray()) {
newObject(type, 1);
} else {
mInstructions.new NewObjectInstruction(mCp.addConstantClass(type));
}
}
public void newObject(TypeDesc type, int dimensions) {
if (dimensions <= 0) {
// If type refers to an array, then this code is bogus.
mInstructions.new NewObjectInstruction(mCp.addConstantClass(type));
return;
}
TypeDesc componentType = type.getComponentType();
if (dimensions == 1) {
if (componentType.isPrimitive()) {
addInstruction(0, type, Opcode.NEWARRAY, (byte)componentType.getTypeCode());
return;
}
addInstruction(0, type, Opcode.ANEWARRAY, mCp.addConstantClass(componentType));
return;
}
int stackAdjust = -(dimensions - 1);
ConstantInfo info = mCp.addConstantClass(type);
byte[] bytes = new byte[4];
bytes[0] = Opcode.MULTIANEWARRAY;
//bytes[1] = (byte)0;
//bytes[2] = (byte)0;
bytes[3] = (byte)dimensions;
mInstructions.new ConstantOperandInstruction(stackAdjust, type, bytes, info);
}
// stack operation style instructions
public void dup() {
mInstructions.new StackOperationInstruction(Opcode.DUP);
}
public void dupX1() {
mInstructions.new StackOperationInstruction(Opcode.DUP_X1);
}
public void dupX2() {
mInstructions.new StackOperationInstruction(Opcode.DUP_X2);
}
public void dup2() {
mInstructions.new StackOperationInstruction(Opcode.DUP2);
}
public void dup2X1() {
mInstructions.new StackOperationInstruction(Opcode.DUP2_X1);
}
public void dup2X2() {
mInstructions.new StackOperationInstruction(Opcode.DUP2_X2);
}
public void pop() {
mInstructions.new StackOperationInstruction(Opcode.POP);
}
public void pop2() {
mInstructions.new StackOperationInstruction(Opcode.POP2);
}
public void swap() {
mInstructions.new StackOperationInstruction(Opcode.SWAP);
}
public void swap2() {
dup2X2();
pop2();
}
// flow control instructions
private void branch(int stackAdjust, Location location, byte opcode) {
if (!(location instanceof InstructionList.LabelInstruction)) {
throw new IllegalArgumentException("Branch location is not a label instruction");
}
mInstructions.new BranchInstruction(stackAdjust, opcode,
(InstructionList.LabelInstruction)location);
}
public void branch(Location location) {
branch(0, location, Opcode.GOTO);
}
public void ifNullBranch(Location location, boolean choice) {
branch(-1, location, choice ? Opcode.IFNULL : Opcode.IFNONNULL);
}
public void ifEqualBranch(Location location, boolean choice) {
branch(-2, location, choice ? Opcode.IF_ACMPEQ : Opcode.IF_ACMPNE);
}
public void ifZeroComparisonBranch(Location location, String choice)
throws IllegalArgumentException {
choice = choice.intern();
byte opcode;
if (choice == "==") {
opcode = Opcode.IFEQ;
} else if (choice == "!=") {
opcode = Opcode.IFNE;
} else if (choice == "<") {
opcode = Opcode.IFLT;
} else if (choice == ">=") {
opcode = Opcode.IFGE;
} else if (choice == ">") {
opcode = Opcode.IFGT;
} else if (choice == "<=") {
opcode = Opcode.IFLE;
} else {
throw new IllegalArgumentException
("Invalid comparision choice: " + choice);
}
branch(-1, location, opcode);
}
public void ifComparisonBranch(Location location, String choice)
throws IllegalArgumentException {
choice = choice.intern();
byte opcode;
if (choice == "==") {
opcode = Opcode.IF_ICMPEQ;
} else if (choice == "!=") {
opcode = Opcode.IF_ICMPNE;
} else if (choice == "<") {
opcode = Opcode.IF_ICMPLT;
} else if (choice == ">=") {
opcode = Opcode.IF_ICMPGE;
} else if (choice == ">") {
opcode = Opcode.IF_ICMPGT;
} else if (choice == "<=") {
opcode = Opcode.IF_ICMPLE;
} else {
throw new IllegalArgumentException
("Invalid comparision choice: " + choice);
}
branch(-2, location, opcode);
}
public void switchBranch(int[] cases, Location[] locations, Location defaultLocation) {
mInstructions.new SwitchInstruction(cases, locations, defaultLocation);
}
public void jsr(Location location) {
// Adjust the stack by one to make room for the return address.
branch(1, location, Opcode.JSR);
}
public void ret(LocalVariable local) {
if (local == null) {
throw new IllegalArgumentException("No local variable specified");
}
mInstructions.new RetInstruction(local);
}
// math instructions
public void math(byte opcode) {
int stackAdjust;
switch(opcode) {
case Opcode.INEG:
case Opcode.LNEG:
case Opcode.FNEG:
case Opcode.DNEG:
stackAdjust = 0;
break;
case Opcode.IADD:
case Opcode.ISUB:
case Opcode.IMUL:
case Opcode.IDIV:
case Opcode.IREM:
case Opcode.IAND:
case Opcode.IOR:
case Opcode.IXOR:
case Opcode.ISHL:
case Opcode.ISHR:
case Opcode.IUSHR:
case Opcode.FADD:
case Opcode.FSUB:
case Opcode.FMUL:
case Opcode.FDIV:
case Opcode.FREM:
case Opcode.FCMPG:
case Opcode.FCMPL:
case Opcode.LSHL:
case Opcode.LSHR:
case Opcode.LUSHR:
stackAdjust = -1;
break;
case Opcode.LADD:
case Opcode.LSUB:
case Opcode.LMUL:
case Opcode.LDIV:
case Opcode.LREM:
case Opcode.LAND:
case Opcode.LOR:
case Opcode.LXOR:
case Opcode.DADD:
case Opcode.DSUB:
case Opcode.DMUL:
case Opcode.DDIV:
case Opcode.DREM:
stackAdjust = -2;
break;
case Opcode.LCMP:
case Opcode.DCMPG:
case Opcode.DCMPL:
stackAdjust = -3;
break;
default:
throw new IllegalArgumentException
("Not a math opcode: " + Opcode.getMnemonic(opcode));
}
TypeDesc pushed;
switch(opcode) {
case Opcode.INEG:
case Opcode.IADD:
case Opcode.ISUB:
case Opcode.IMUL:
case Opcode.IDIV:
case Opcode.IREM:
case Opcode.IAND:
case Opcode.IOR:
case Opcode.IXOR:
case Opcode.ISHL:
case Opcode.ISHR:
case Opcode.IUSHR:
case Opcode.LCMP:
case Opcode.FCMPG:
case Opcode.FCMPL:
case Opcode.DCMPG:
case Opcode.DCMPL:
pushed = TypeDesc.INT;
break;
case Opcode.LNEG:
case Opcode.LADD:
case Opcode.LSUB:
case Opcode.LMUL:
case Opcode.LDIV:
case Opcode.LREM:
case Opcode.LAND:
case Opcode.LOR:
case Opcode.LXOR:
case Opcode.LSHL:
case Opcode.LSHR:
case Opcode.LUSHR:
pushed = TypeDesc.LONG;
break;
case Opcode.FNEG:
case Opcode.FADD:
case Opcode.FSUB:
case Opcode.FMUL:
case Opcode.FDIV:
case Opcode.FREM:
pushed = TypeDesc.FLOAT;
break;
case Opcode.DNEG:
case Opcode.DADD:
case Opcode.DSUB:
case Opcode.DMUL:
case Opcode.DDIV:
case Opcode.DREM:
pushed = TypeDesc.DOUBLE;
break;
default:
throw new IllegalArgumentException
("Not a math opcode: " + Opcode.getMnemonic(opcode));
}
addInstruction(stackAdjust, pushed, opcode);
}
// miscellaneous instructions
public void arrayLength() {
addInstruction(0, TypeDesc.INT, Opcode.ARRAYLENGTH);
}
public void throwObject() {
addInstruction(-1, TypeDesc.VOID, Opcode.ATHROW);
}
public void checkCast(TypeDesc type) {
ConstantInfo info = mCp.addConstantClass(type);
addInstruction(0, TypeDesc.VOID, Opcode.CHECKCAST, info);
}
public void instanceOf(TypeDesc type) {
ConstantInfo info = mCp.addConstantClass(type);
addInstruction(0, TypeDesc.INT, Opcode.INSTANCEOF, info);
}
public void integerIncrement(LocalVariable local, int amount) {
if (local == null) {
throw new IllegalArgumentException("No local variable specified");
}
if (-32768 <= amount && amount <= 32767) {
mInstructions.new ShortIncrementInstruction(local, (short)amount);
} else {
// Amount can't possibly fit in a 16-bit value, so use regular
// instructions instead.
loadLocal(local);
loadConstant(amount);
math(Opcode.IADD);
storeLocal(local);
}
}
public void monitorEnter() {
addInstruction(-1, TypeDesc.VOID, Opcode.MONITORENTER);
}
public void monitorExit() {
addInstruction(-1, TypeDesc.VOID, Opcode.MONITOREXIT);
}
public void nop() {
addInstruction(0, TypeDesc.VOID, Opcode.NOP);
}
public void breakpoint() {
addInstruction(0, TypeDesc.VOID, Opcode.BREAKPOINT);
}
}