package js.tinyvm;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantDouble;
import org.apache.bcel.classfile.ConstantFieldref;
import org.apache.bcel.classfile.ConstantFloat;
import org.apache.bcel.classfile.ConstantInteger;
import org.apache.bcel.classfile.ConstantLong;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantString;
import org.apache.bcel.classfile.JavaClass;
public class CodeUtilities implements OpCodeConstants, OpCodeInfo
{
String iFullName;
JavaClass iCF;
Binary iBinary;
public CodeUtilities (String aMethodName, JavaClass aCF, Binary aBinary)
{
iFullName = fullMethod(aCF, aMethodName);
iCF = aCF;
iBinary = aBinary;
}
/**
* Return the ClassRecord for a class signature.
* @param className the signature to lookup.
* @return the associated class record, or null if it is a primitive array.
* @throws js.tinyvm.TinyVMException
*/
ClassRecord getClassRecord(String className) throws TinyVMException
{
ClassRecord ret;
ret = iBinary.getClassRecord(className);
if (ret == null)
{
throw new TinyVMException("Bug CU-3: Didn't find class " + className
+ " from class " + iCF.getClassName() + " method " + this.iFullName);
}
return ret;
}
ClassRecord getClassRecord(int aPoolIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aPoolIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantClass))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Class entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantClass pClassEntry = (ConstantClass) pEntry;
return getClassRecord(pClassEntry.getBytes(iCF.getConstantPool()));
}
public void exitOnBadOpCode (int aOpCode) throws TinyVMException
{
throw new TinyVMException("Unsupported " + OPCODE_NAME[aOpCode] + " in "
+ iFullName + ".\n"
+ "The following features/conditions are currently unsupported:\n"
+ "- Too many locals ( > 255).\n"
+ "- Too many constants ( > 1024).\n"
+ "- Too many static fields ( > 1024).\n"
+ "- Method code too long ( > 64 Kb!).\n" + "");
}
public static String fullMethod (JavaClass aCF, String aMethodName)
{
return aCF.getClassName() + ":" + aMethodName;
}
/**
* Return the index of a given constant record
* @param pRecord
* @return the index of the record
* @throws js.tinyvm.TinyVMException
*/
int getConstantIndex(ConstantRecord pRecord) throws TinyVMException
{
int pIdx = iBinary.getConstantIndex(pRecord);
if (pIdx == -1)
{
throw new TinyVMException("Bug CU-2: Didn't find constant " + pRecord.toString()
+ " of class " + iCF.getClassName());
}
return pIdx;
}
/**
* Process a constant index.
* Given a reference to the constant pool, return the corresponding index
* into the leJOS constant table.
* @param aPoolIndex the constant pool index
* @return The constant table index
* @throws js.tinyvm.TinyVMException
*/
public int processConstantIndex (int aPoolIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aPoolIndex);
if (!(pEntry instanceof ConstantInteger)
&& !(pEntry instanceof ConstantFloat)
&& !(pEntry instanceof ConstantString)
&& !(pEntry instanceof ConstantDouble)
&& !(pEntry instanceof ConstantLong)
&& !(pEntry instanceof ConstantClass))
{
throw new TinyVMException("Classfile error: LDC-type instruction "
+ "does not refer to a suitable constant. Class " + iCF.getClassName() + " method " + iFullName);
}
ConstantRecord pRecord = new ConstantRecord(iCF.getConstantPool(), pEntry, iBinary);
return getConstantIndex(pRecord);
}
/**
* Process a class index.
* Given a constant pool index for a class object, return the corresponding
* index into the leJOS class table.
* @param aPoolIndex the constant pool index
* @return the class table index
* @throws js.tinyvm.TinyVMException
*/
public int processClassIndex (int aPoolIndex) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(aPoolIndex);
int pIdx = iBinary.getClassIndex(pClassRecord);
if (pIdx == -1)
{
throw new TinyVMException("Bug CU-3: Didn't find class " + pClassRecord.iName
+ " from class " + iCF.getClassName() + " method " + this.iFullName);
}
return pIdx;
}
/**
* Process and array index.
* Given an index into the constant pool for an array class, return the
* corresponding index into the leJOS class table.
* @param aPoolIndex the constant index for the array
* @return the class table index.
* @throws js.tinyvm.TinyVMException
*/
public int processArray(int aPoolIndex) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(aPoolIndex);
ClassRecord pArray = iBinary.getClassRecordForArray(pClassRecord);
if (pArray == null)
{
throw new TinyVMException("Classfile error: Failed to locate array class for " +
pClassRecord.iName + " in class " + iCF.getClassName() + " method " + this.iFullName);
}
int pIdx = iBinary.getClassIndex(pArray);
if (pIdx == -1)
{
throw new TinyVMException("Bug CU-3: Didn't find class " + pClassRecord.iName
+ " from class " + iCF.getClassName() + " method " + this.iFullName);
}
return pIdx;
}
public int processMultiArray (int aPoolIndex) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(aPoolIndex);
int pIdx = iBinary.getClassIndex(pClassRecord);
if (pIdx == -1)
{
throw new TinyVMException("Bug CU-3: Didn't find class " + pClassRecord.iName
+ " from class " + iCF.getClassName() + " method " + this.iFullName);
}
return pIdx;
}
StaticFieldRecord getStaticFieldRecord(int aFieldIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aFieldIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantFieldref))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Fieldref entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantFieldref pFieldEntry = (ConstantFieldref) pEntry;
String className = pFieldEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Attempt to use a field from a primitive array " +
className + " from class " + iCF.getClassName() + " method " + iFullName);
}
ConstantNameAndType cnat = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pFieldEntry.getNameAndTypeIndex());
String pName = cnat.getName(iCF.getConstantPool());
// First find the actual defining class
StaticFieldRecord pFieldRecord = pClassRecord.getStaticFieldRecord(pName);
if (pFieldRecord == null)
{
throw new TinyVMException("Failed to locate static field " + pName +
" refrenced via class " + className + " from class " + iCF.getClassName());
}
return pFieldRecord;
}
/**
* Mark the class as being used.
* @param aPoolIndex
* @throws js.tinyvm.TinyVMException
*/
public void markClass (int aPoolIndex) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(aPoolIndex);
iBinary.markClassUsed(pClassRecord, true);
}
/**
* Mark an array as being used.
* @param aPoolIndex The constant pool index for the array.
* @throws js.tinyvm.TinyVMException
*/
public void markArray(int aPoolIndex) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(aPoolIndex);
ClassRecord pArray = iBinary.getClassRecordForArray(pClassRecord);
if (pArray == null)
{
throw new TinyVMException("Classfile error: Failed to locate array class for " +
pClassRecord.iName + " in class " + iCF.getClassName() + " method " + this.iFullName);
}
iBinary.markClassUsed(pArray, true);
}
/**
* Mark a primitive array as being used.
* @param type The primitive type of the array.
* @throws js.tinyvm.TinyVMException
*/
public void markPrimitiveArray(byte type) throws TinyVMException
{
ClassRecord pClassRecord = getClassRecord(TinyVMType.tinyVMType(type).cname());
ClassRecord pArray = iBinary.getClassRecordForArray(pClassRecord);
if (pArray == null)
{
throw new TinyVMException("Classfile error: Failed to locate array class for " +
pClassRecord.iName + " in class " + iCF.getClassName() + " method " + this.iFullName);
}
iBinary.markClassUsed(pArray, true);
}
/**
* Mark the static field as being used.
* @param aFieldIndex
* @throws js.tinyvm.TinyVMException
*/
void markStaticField (int aFieldIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aFieldIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantFieldref))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Fieldref entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantFieldref pFieldEntry = (ConstantFieldref) pEntry;
String className = pFieldEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null) return;
ConstantNameAndType cnat = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pFieldEntry.getNameAndTypeIndex());
String pName = cnat.getName(iCF.getConstantPool());
iBinary.markClassUsed(pClassRecord, false);
StaticFieldRecord pFieldRecord = pClassRecord.getStaticFieldRecord(pName);
if (pFieldRecord == null)
{
throw new TinyVMException("Failed to mark/locate static field " + pName +
" refrenced via class " + className + " from class " + iCF.getClassName());
}
iBinary.markClassUsed(pFieldRecord.getClassRecord(), false);
pFieldRecord.markUsed();
}
/**
* Obtain the class that contains a specified static field.
* @param aFieldIndex The index in the constant pool of the static field
* @return The class record of the defining class
* @throws js.tinyvm.TinyVMException
*/
ClassRecord getStaticFieldClass (int aFieldIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aFieldIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantFieldref))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Fieldref entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantFieldref pFieldEntry = (ConstantFieldref) pEntry;
String className = pFieldEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Classfile error: Failed to find class "
+ className);
}
return pClassRecord;
}
/**
* Return the name of a static field
* @param aFieldIndex The constant pool index for the field.
* @throws js.tinyvm.TinyVMException
*/
String getStaticFieldName (int aFieldIndex) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aFieldIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantFieldref))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Fieldref entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantFieldref pFieldEntry = (ConstantFieldref) pEntry;
String className = pFieldEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Classfile error: Failed to find class "
+ className);
}
ConstantNameAndType cnat = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pFieldEntry.getNameAndTypeIndex());
String pName = cnat.getName(iCF.getConstantPool());
return pName;
}
/**
* Check the field reference to see if it is for the special TYPE entry in a
* primitive class.
* @param aFieldIndex The constant pool index for the field
* @throws js.tinyvm.TinyVMException
*/
boolean isWrapperTYPEField (int aFieldIndex) throws TinyVMException
{
ClassRecord pClass = getStaticFieldClass(aFieldIndex);
if (!pClass.isWrapper()) return false;
String pName = getStaticFieldName(aFieldIndex);
if (pName.equals("TYPE")) return true;
return false;
}
/**
* Mark a constant as being used.
* @param aPoolIndex The constant pool index.
* @throws js.tinyvm.TinyVMException
*/
void markConstant(int aPoolIndex) throws TinyVMException
{
int constIdx = processConstantIndex(aPoolIndex);
ConstantRecord pRec = iBinary.getConstantRecord(constIdx);
pRec.markUsed();
}
/**
* @return The word that should be written as parameter of an invocation
* opcode.
* @throws TinyVMException
*/
int processMethod (int aMethodIndex, boolean aSpecial, boolean aInterface)
throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aMethodIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantCP))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_MethodRef or CONSTANT_InterfaceMethodRef " + "got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantCP pMethodEntry = (ConstantCP) pEntry;
String className = pMethodEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ConstantNameAndType pNT = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pMethodEntry.getNameAndTypeIndex());
Signature pSig = new Signature(pNT.getName(iCF.getConstantPool()), pNT
.getSignature(iCF.getConstantPool()));
if (className.startsWith("["))
{
// For arrays we use the methods contained in Object
className = "java/lang/Object";
}
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Bug CU-7: Didn't find class " + className
+ " from class " + iCF.getClassName() + " method " + iFullName);
}
MethodRecord pMethod;
if (aInterface)
pMethod = pClassRecord.getInterfaceMethodRecord(pSig);
else
pMethod = pClassRecord.getVirtualMethodRecord(pSig);
if (pMethod == null)
{
throw new TinyVMException("Method " + pSig + " not found in "
+ className +" interface " + aInterface);
}
ClassRecord pTopClass = pMethod.getClassRecord();
if (aSpecial)
{
int pClassIndex = iBinary.getClassIndex(pTopClass);
assert pClassIndex != -1 && pClassIndex < TinyVMConstants.MAX_CLASSES: "Check: class index in range";
int pMethodIndex = pTopClass.getMethodIndex(pMethod);
assert pMethodIndex != -1
&& pMethodIndex < TinyVMConstants.MAX_METHODS: "Check: method index in range";
// _logger.log(Level.INFO, "processMethod: special: " + pClassIndex
// + ", " + pMethodIndex);
return (pClassIndex << 8) | (pMethodIndex & 0xFF);
}
else
{
int pNumParams = pMethod.getNumParameterWords() - 1;
assert pNumParams < TinyVMConstants.MAX_PARAMETER_WORDS: "Check: number of parameters not to high";
int pSignature = pMethod.getSignatureId();
assert pSignature < TinyVMConstants.MAX_SIGNATURES: "Check: signature in range";
return (pNumParams << TinyVMConstants.M_ARGS_SHIFT) | pSignature;
}
}
/**
* @return The word that should be written as parameter of an invocation
* opcode.
* @throws TinyVMException
*/
MethodRecord findMethod (int aMethodIndex, boolean aSpecial, boolean aInterface)
throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aMethodIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantCP))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_MethodRef or CONSTANT_InterfaceMethodRef " + "got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantCP pMethodEntry = (ConstantCP) pEntry;
String className = pMethodEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ConstantNameAndType pNT = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pMethodEntry.getNameAndTypeIndex());
Signature pSig = new Signature(pNT.getName(iCF.getConstantPool()), pNT
.getSignature(iCF.getConstantPool()));
if (className.startsWith("["))
{
// For arrays we use the methods contained in Object
className = "java/lang/Object";
}
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Bug CU-7: Didn't find class " + className
+ " from class " + iCF.getClassName());
}
MethodRecord pMethod;
if (aInterface)
pMethod = pClassRecord.getInterfaceMethodRecord(pSig);
else
pMethod = pClassRecord.getVirtualMethodRecord(pSig);
//if (pMethod == null)
// _logger.log(Level.INFO, "Failed to find " + pSig + " class " + className);
return pMethod;
}
/**
* Process a constant load operation.
* Given a reference to the constant pool, return the corresponding
* instruction and index required to load it.
* @param aPoolIndex the constant pool index
* @return The constant load instruction.
* @throws js.tinyvm.TinyVMException
*/
public int genConstantLoad (int aPoolIndex) throws TinyVMException
{
int idx = processConstantIndex(aPoolIndex);
if (idx > TinyVMConstants.MAX_CONSTANTS) exitOnBadOpCode(OP_LDC);
ConstantRecord pRecord = iBinary.getConstantRecord(idx);
// Decide if we can use the optimized version of constant load to access
// this value.
int instruction;
if (pRecord.constantValue().getAlignment() == 4 && idx < 256)
{
instruction = OP_LDC;
iBinary.constOpLoads++;
}
else
{
// need to use normal form.
instruction = OP_LDC_1 + idx/256;
idx = idx % 256;
iBinary.constNormLoads++;
if (pRecord.constantValue().getType() == TinyVMType.T_OBJECT)
iBinary.constString++;
}
return (instruction << 8) | idx;
}
/**
* Generate and instruction to access a static field.
* @param aPoolIndex The field to access
* @param optInst The optimixed version of the instruction
* @param normInst The normal version of the instruction.
* @return
* @throws TinyVMException
*/
public int genStaticAccess(int aPoolIndex, int optInst, int normInst) throws TinyVMException
{
StaticFieldRecord fieldRec = getStaticFieldRecord(aPoolIndex);
ClassRecord classRec = fieldRec.getClassRecord();
int classIndex = iBinary.getClassIndex(classRec);
assert classIndex >= 0 && classIndex <= 0xFF: "Check: class index in range";
String name = fieldRec.getName();
int fieldIndex = classRec.getStaticFieldIndex(name);
StaticValue valueRec = classRec.getStaticValue(name);
int offset = classRec.getStaticFieldOffset(name);
int instruction;
// Can we generate optimized version of the instruction?
if (valueRec.getAlignment() == 4 && offset/4 < 256)
{
instruction = optInst;
offset = offset/4;
iBinary.staticOpLoads++;
}
else
{
// Need to use the normal form, check the index is ok
if (fieldIndex > TinyVMConstants.MAX_STATICS) exitOnBadOpCode(optInst);
offset = fieldIndex % 256;
instruction = normInst + (fieldIndex/256);
iBinary.staticNormLoads++;
}
return (instruction << 16) | (classIndex << 8) | offset;
}
/**
* Generate an instruction to access an instance field.
* We use an optimized version of the opcode if the field is aligned correctly.
* @param aFieldIndex The field we need to access.
* @param optInst The optimized version of the instruction.
* @param normInst The normal version of the instruction.
* @return The field access instruction
* @throws TinyVMException
*/
int genFieldAccess (int aFieldIndex, int optInst, int normInst) throws TinyVMException
{
Constant pEntry = iCF.getConstantPool().getConstant(aFieldIndex); // TODO catch all (runtime) exceptions
if (!(pEntry instanceof ConstantFieldref))
{
throw new TinyVMException("Classfile error: Instruction requiring "
+ "CONSTANT_Fieldref entry got "
+ (pEntry == null? "null" : pEntry.getClass().getName()));
}
ConstantFieldref pFieldEntry = (ConstantFieldref) pEntry;
String className = pFieldEntry.getClass(iCF.getConstantPool()).replace(
'.', '/');
ClassRecord pClassRecord = getClassRecord(className);
if (pClassRecord == null)
{
throw new TinyVMException("Attempt to use a field from a primitive array " +
className + " from class " + iCF.getClassName() + " method " + iFullName);
}
ConstantNameAndType cnat = (ConstantNameAndType) iCF.getConstantPool()
.getConstant(pFieldEntry.getNameAndTypeIndex());
String pName = cnat.getName(iCF.getConstantPool());
int pOffset = pClassRecord.getInstanceFieldOffset(pName);
if (pOffset == -1)
{
throw new TinyVMException("Error: Didn't find field " + className
+ ":" + pName + " from class " + iCF.getClassName());
}
assert pOffset <= TinyVMConstants.MAX_FIELD_OFFSET: "Check: field offset in range";
TinyVMType fieldType = TinyVMType.tinyVMTypeFromSignature(cnat
.getSignature(iCF.getConstantPool()));
// Decide which form of the instruction to use.
int instruction;
if ((fieldType.type() == TinyVMType.T_INT_TYPE || fieldType.type() == TinyVMType.T_FLOAT_TYPE ||
fieldType.type() == TinyVMType.T_REFERENCE_TYPE) && (pOffset & 0x3) == 0)
{
instruction = optInst;
iBinary.fieldOpOp++;
}
else
{
instruction = normInst;
iBinary.fieldNormOp++;
}
return (instruction << 16) | (fieldType.type() << TinyVMConstants.F_SIZE_SHIFT) | pOffset;
}
static int getAndCopyFourBytesInt( byte[] aCode, int ix, byte[] pOutCode, int ox)
{
int a = 0;
a |= (aCode[ ix + 0] & 0xFF) << 24;
a |= (aCode[ ix + 1] & 0xFF) << 16;
a |= (aCode[ ix + 2] & 0xFF) << 8;
a |= (aCode[ ix + 3] & 0xFF) << 0;
for( int i = 0; i < 4; i ++)
pOutCode[ ox + i] = aCode[ ix + i];
return a;
}
static int getFourBytesInt( byte[] aCode, int ix )
{
int a = 0;
a |= (aCode[ ix + 0] & 0xFF) << 24;
a |= (aCode[ ix + 1] & 0xFF) << 16;
a |= (aCode[ ix + 2] & 0xFF) << 8;
a |= (aCode[ ix + 3] & 0xFF) << 0;
return a;
}
public byte[] processCode (byte[] aCode) throws TinyVMException
{
byte[] pOutCode = new byte[aCode.length];
int i = 0;
while (i < aCode.length)
{
pOutCode[i] = aCode[i];
int pOpCode = pOutCode[i] & 0xFF;
i++;
//System.out.println("Opcode " + OPCODE_NAME[pOpCode] + " addr " + i);
switch (pOpCode)
{
case OP_LDC:
{
int inst = genConstantLoad(aCode[i] & 0xFF);
pOutCode[i-1] = (byte) (inst >> 8);
pOutCode[i++] = (byte) (inst & 0xFF);
}
break;
case OP_LDC_W:
{
// Convert long version into short version plus noop
int inst = genConstantLoad((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
pOutCode[i-1] = (byte) (inst >> 8);
pOutCode[i++] = (byte) (inst & 0xFF);
pOutCode[i++] = (byte) OP_NOP;
iBinary.constWideLoads++;
}
break;
case OP_LDC2_W:
int pIdx1 = processConstantIndex((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF));
pOutCode[i++] = (byte) (pIdx1 >> 8);
pOutCode[i++] = (byte) (pIdx1 & 0xFF);
break;
case OP_ANEWARRAY:
int pIdx5 = processArray((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF));
// Write the two byte signature
pOutCode[i++] = (byte) (pIdx5 >> 8);
pOutCode[i++] = (byte) (pIdx5 & 0xFF);
break;
case OP_MULTIANEWARRAY:
int pIdx2 = processMultiArray((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF));
// Write the two byte signature
pOutCode[i++] = (byte) (pIdx2 >> 8);
pOutCode[i++] = (byte) (pIdx2 & 0xFF);
// Include the number of actual dimensions required
pOutCode[i] = aCode[i];
i++;
break;
case OP_CHECKCAST:
case OP_INSTANCEOF:
int pIdx3 = processClassIndex((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF));
pOutCode[i++] = (byte) (pIdx3 >> 8);
pOutCode[i++] = (byte) (pIdx3 & 0xFF);
break;
case OP_NEW:
int pIdx4 = processClassIndex((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF));
assert pIdx4 < TinyVMConstants.MAX_CLASSES: "Check: class index in range";
pOutCode[i++] = (byte) (pIdx4 >> 8);
pOutCode[i++] = (byte) (pIdx4 & 0xFF);
break;
case OP_GETSTATIC:
{
int idx = (aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF);
if (isWrapperTYPEField(idx))
{
// If this is one of the rather odd static fields in the
// wrapper classes, then replace reads of it with a load
// of the appropriate class constant.
idx = getConstantIndex(getStaticFieldClass(idx).getPrimitiveClass().getClassConstant());
pOutCode[i-1] = (byte)OP_LDC_1;
pOutCode[i++] = (byte) (idx & 0xFF);
pOutCode[i++] = (byte) OP_NOP;
break;
}
// fall through
int inst = genStaticAccess((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF), OP_GETSTATIC, OP_GETSTATIC_1);
pOutCode[i-1] = (byte)(inst >> 16);
pOutCode[i++] = (byte)(inst >> 8);
pOutCode[i++] = (byte)inst;
}
break;
case OP_PUTSTATIC:
{
int inst = genStaticAccess((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF), OP_PUTSTATIC, OP_PUTSTATIC_1);
pOutCode[i-1] = (byte)(inst >> 16);
pOutCode[i++] = (byte)(inst >> 8);
pOutCode[i++] = (byte)inst;
}
break;
case OP_GETFIELD:
{
int inst = genFieldAccess((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF), OP_GETFIELD, OP_GETFIELD_1);
pOutCode[i-1] = (byte) (inst >> 16);
pOutCode[i++] = (byte)(inst >> 8);
pOutCode[i++] = (byte) inst;
}
break;
case OP_PUTFIELD:
{
int inst = genFieldAccess((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF), OP_PUTFIELD, OP_PUTFIELD_1);
pOutCode[i-1] = (byte) (inst >> 16);
pOutCode[i++] = (byte)(inst >> 8);
pOutCode[i++] = (byte) inst;
}
break;
case OP_INVOKEINTERFACE:
// Opcode is changed:
pOutCode[i - 1] = (byte) OP_INVOKEVIRTUAL;
int pWord3 = processMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), false, true);
pOutCode[i++] = (byte) (pWord3 >> 8);
pOutCode[i++] = (byte) (pWord3 & 0xFF);
pOutCode[i++] = (byte) OP_NOP; // before: count
pOutCode[i++] = (byte) OP_NOP; // before: 0
break;
case OP_INVOKESPECIAL:
case OP_INVOKESTATIC:
// Opcode is changed:
int pWord4 = processMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), true, false);
pOutCode[i++] = (byte) (pWord4 >> 8);
pOutCode[i++] = (byte) (pWord4 & 0xFF);
break;
case OP_INVOKEVIRTUAL:
// Opcode is changed:
int pWord5 = processMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), false, false);
pOutCode[i++] = (byte) (pWord5 >> 8);
pOutCode[i++] = (byte) (pWord5 & 0xFF);
break;
case OP_LOOKUPSWITCH:
{
int oi = i;
while( (i % 4) != 0)
i ++;
int dft = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
int npairs = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
//System.out.println( "lookupswitch: dft: " + dft + ", npairs: " + npairs + ", padding: " + (i - oi));
for( int k = 0; k < npairs; k ++)
{
int idx = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
int off = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
//System.out.println( "lookupswitch: idx: " + idx + ", off: " + off);
}
while( oi < i)
pOutCode[oi++] = 0;
}
break;
case OP_TABLESWITCH:
{
int oi = i;
while( (i % 4) != 0)
i ++;
int dft = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
int low = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
int hig = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
//System.out.println( "tableswitch: dft: " + dft + ", low: " + low + ", hig: " + hig + ", padding: " + (i - oi));
for( int k = low; k <= hig; k ++)
{
int idx = getAndCopyFourBytesInt( aCode, i, pOutCode, oi); i += 4; oi += 4;
//System.out.println( "tableswitch: idx: " + idx);
}
while( oi < i)
pOutCode[oi++] = 0;
}
break;
case OP_WIDE:
if( (aCode[i] & 0xFF) == OP_IINC && aCode[i+1] == 0)
{
for( int k = 0; k < 5; k ++, i ++)
pOutCode[i] = aCode[i];
break;
}
// Fall through
case OP_GOTO_W:
case OP_JSR_W:
exitOnBadOpCode(pOpCode);
break;
case OP_BREAKPOINT:
{
throw new TinyVMException("Invalid opcode detected: " + pOpCode
+ " " + OPCODE_NAME[pOpCode]);
}
default:
int pArgs = OPCODE_ARGS[pOpCode];
if (pArgs == -1)
{
throw new TinyVMException("Bug CU-1: Got " + pOpCode + " in "
+ iFullName + ".");
}
for (int ctr = 0; ctr < pArgs; ctr++)
pOutCode[i + ctr] = aCode[i + ctr];
i += pArgs;
break;
}
}
return pOutCode;
}
public void processCalls (byte[] aCode, JavaClass aClassFile, Binary aBinary) throws TinyVMException
{
int i = 0;
while (i < aCode.length)
{
int pOpCode = aCode[i] & 0xFF;
i++;
//System.out.println("Opcode " + OPCODE_NAME[pOpCode] + " addr " + i);
switch (pOpCode)
{
case OP_LDC:
{
markConstant(aCode[i] & 0xFF);
i++;
}
break;
case OP_LDC_W:
case OP_LDC2_W:
{
markConstant((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 2;
}
break;
case OP_ANEWARRAY:
markArray((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 2;
break;
case OP_NEWARRAY:
markPrimitiveArray(aCode[i]);
i += 1;
break;
case OP_MULTIANEWARRAY:
markClass((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 3;
break;
case OP_NEW:
case OP_CHECKCAST:
case OP_INSTANCEOF:
markClass((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 2;
break;
case OP_GETSTATIC:
{
int idx = (aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF);
if (isWrapperTYPEField(idx))
{
getStaticFieldClass(idx).getPrimitiveClass().getClassConstant().markUsed();
}
else
markStaticField((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 2;
break;
}
case OP_PUTSTATIC:
markStaticField((aCode[i] & 0xFF) << 8 | (aCode[i + 1] & 0xFF));
i += 2;
break;
case OP_INVOKEINTERFACE:
// Opcode is changed:
// _logger.log(Level.INFO, "Interface");
MethodRecord pMeth0 = findMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), false, true);
if (pMeth0 != null) pMeth0.getClassRecord().markMethod(pMeth0, true);
i += 4;
break;
case OP_INVOKEVIRTUAL:
// Opcode is changed:
MethodRecord pMeth1 = findMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), false, false);
if (pMeth1 != null) pMeth1.getClassRecord().markMethod(pMeth1, true);
i += 2;
break;
case OP_INVOKESPECIAL:
case OP_INVOKESTATIC:
// Opcode is changed:
MethodRecord pMeth2 = findMethod((aCode[i] & 0xFF) << 8
| (aCode[i + 1] & 0xFF), true, false);
if (pMeth2 != null) pMeth2.getClassRecord().markMethod(pMeth2, true);
i += 2;
break;
case OP_LOOKUPSWITCH:
{
while( (i % 4) != 0)
i ++;
int dft = getFourBytesInt( aCode, i); i += 4;
int npairs = getFourBytesInt( aCode, i); i += 4;
//System.out.println( "lookupswitch: dft: " + dft + ", npairs: " + npairs + ", padding: " + (i - oi));
i += 8*npairs;
}
break;
case OP_TABLESWITCH:
{
while( (i % 4) != 0)
i ++;
int dft = getFourBytesInt( aCode, i); i += 4;
int low = getFourBytesInt( aCode, i); i += 4;
int hig = getFourBytesInt( aCode, i); i += 4;
//System.out.println( "tableswitch: dft: " + dft + ", low: " + low + ", hig: " + hig + ", padding: " + (i - oi));
for( int k = low; k <= hig; k ++)
{
i += 4;
//System.out.println( "tableswitch: idx: " + idx);
}
}
break;
case OP_WIDE:
if( (aCode[i] & 0xFF) == OP_IINC && aCode[i+1] == 0)
{
i += 5;
break;
}
// Fall Through
case OP_GOTO_W:
case OP_JSR_W:
//case OP_FREM:
//case OP_DREM:
exitOnBadOpCode(pOpCode);
break;
default:
int pArgs = OPCODE_ARGS[pOpCode];
if (pArgs == -1)
{
throw new TinyVMException("Bug CU-1: Got " + pOpCode + " in "
+ iFullName + ".");
}
i += pArgs;
}
}
}
// private static final Logger _logger = Logger.getLogger("TinyVM");
}