BytecodeArraypublic final class BytecodeArray extends Object Bytecode array, which is part of a standard Code attribute. |
Fields Summary |
---|
public static final Visitor | EMPTY_VISITORconvenient no-op implementation of {@link Visitor} | private final com.android.dx.util.ByteArray | bytesnon-null; underlying bytes | private final com.android.dx.rop.cst.ConstantPool | poolnon-null; constant pool to use when resolving constant pool indices |
Constructors Summary |
---|
public BytecodeArray(com.android.dx.util.ByteArray bytes, com.android.dx.rop.cst.ConstantPool pool)Constructs an instance.
if (bytes == null) {
throw new NullPointerException("bytes == null");
}
if (pool == null) {
throw new NullPointerException("pool == null");
}
this.bytes = bytes;
this.pool = pool;
|
Methods Summary |
---|
public int | byteLength()Gets the total length of this structure in bytes, when included in
a Code attribute. The returned value includes the
array size plus four bytes for code_length .
return 4 + bytes.size();
| public void | forEach(com.android.dx.cf.code.BytecodeArray$Visitor visitor)Parses each instruction in the array, in order.
int sz = bytes.size();
int at = 0;
while (at < sz) {
/*
* Don't record the previous offset here, so that we get to see the
* raw code that initializes the array
*/
at += parseInstruction(at, visitor);
}
| public com.android.dx.util.ByteArray | getBytes()Gets the underlying byte array.
return bytes;
| public int[] | getInstructionOffsets()Finds the offset to each instruction in the bytecode array. The
result is a bit set with the offset of each opcode-per-se flipped on.
int sz = bytes.size();
int[] result = Bits.makeBitSet(sz);
int at = 0;
while (at < sz) {
Bits.set(result, at, true);
int length = parseInstruction(at, null);
at += length;
}
return result;
| public int | parseInstruction(int offset, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Parses the instruction at the indicated offset. Indicate the
result by calling the visitor if supplied and by returning the
number of bytes consumed by the instruction.
In order to simplify further processing, the opcodes passed
to the visitor are canonicalized, altering the opcode to a more
universal one and making formerly implicit arguments
explicit. In particular:
- The opcodes to push literal constants of primitive types all become
ldc .
E.g., fconst_0 , sipush , and
lconst_0 qualify for this treatment.
aconst_null becomes ldc of a
"known null."
- Shorthand local variable accessors become the corresponding
longhand. E.g.
aload_2 becomes aload .
goto_w and jsr_w become goto
and jsr (respectively).
ldc_w becomes ldc .
tableswitch becomes lookupswitch .
- Arithmetic, array, and value-returning ops are collapsed
to the
int variant opcode, with the type
argument set to indicate the actual type. E.g.,
fadd becomes iadd , but
type is passed as Type.FLOAT in that
case. Similarly, areturn becomes
ireturn . (However, return remains
unchanged.
- Local variable access ops are collapsed to the
int
variant opcode, with the type argument set to indicate
the actual type. E.g., aload becomes iload ,
but type is passed as Type.OBJECT in
that case.
- Numeric conversion ops (
i2l , etc.) are left alone
to avoid too much confustion, but their type is
the pushed type. E.g., i2b gets type
Type.INT , and f2d gets type
Type.DOUBLE . Other unaltered opcodes also get
their pushed type. E.g., arraylength gets type
Type.INT .
if (visitor == null) {
visitor = EMPTY_VISITOR;
}
try {
int opcode = bytes.getUnsignedByte(offset);
int info = ByteOps.opInfo(opcode);
int fmt = info & ByteOps.FMT_MASK;
switch (opcode) {
case ByteOps.NOP: {
visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
return 1;
}
case ByteOps.ACONST_NULL: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstKnownNull.THE_ONE, 0);
return 1;
}
case ByteOps.ICONST_M1: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_M1, -1);
return 1;
}
case ByteOps.ICONST_0: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_0, 0);
return 1;
}
case ByteOps.ICONST_1: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_1, 1);
return 1;
}
case ByteOps.ICONST_2: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_2, 2);
return 1;
}
case ByteOps.ICONST_3: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_3, 3);
return 1;
}
case ByteOps.ICONST_4: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_4, 4);
return 1;
}
case ByteOps.ICONST_5: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstInteger.VALUE_5, 5);
return 1;
}
case ByteOps.LCONST_0: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstLong.VALUE_0, 0);
return 1;
}
case ByteOps.LCONST_1: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstLong.VALUE_1, 0);
return 1;
}
case ByteOps.FCONST_0: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstFloat.VALUE_0, 0);
return 1;
}
case ByteOps.FCONST_1: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstFloat.VALUE_1, 0);
return 1;
}
case ByteOps.FCONST_2: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstFloat.VALUE_2, 0);
return 1;
}
case ByteOps.DCONST_0: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstDouble.VALUE_0, 0);
return 1;
}
case ByteOps.DCONST_1: {
visitor.visitConstant(ByteOps.LDC, offset, 1,
CstDouble.VALUE_1, 0);
return 1;
}
case ByteOps.BIPUSH: {
int value = bytes.getByte(offset + 1);
visitor.visitConstant(ByteOps.LDC, offset, 2,
CstInteger.make(value), value);
return 2;
}
case ByteOps.SIPUSH: {
int value = bytes.getShort(offset + 1);
visitor.visitConstant(ByteOps.LDC, offset, 3,
CstInteger.make(value), value);
return 3;
}
case ByteOps.LDC: {
int idx = bytes.getUnsignedByte(offset + 1);
Constant cst = pool.get(idx);
int value = (cst instanceof CstInteger) ?
((CstInteger) cst).getValue() : 0;
visitor.visitConstant(ByteOps.LDC, offset, 2, cst, value);
return 2;
}
case ByteOps.LDC_W: {
int idx = bytes.getUnsignedShort(offset + 1);
Constant cst = pool.get(idx);
int value = (cst instanceof CstInteger) ?
((CstInteger) cst).getValue() : 0;
visitor.visitConstant(ByteOps.LDC, offset, 3, cst, value);
return 3;
}
case ByteOps.LDC2_W: {
int idx = bytes.getUnsignedShort(offset + 1);
Constant cst = pool.get(idx);
visitor.visitConstant(ByteOps.LDC2_W, offset, 3, cst, 0);
return 3;
}
case ByteOps.ILOAD: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
Type.INT, 0);
return 2;
}
case ByteOps.LLOAD: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
Type.LONG, 0);
return 2;
}
case ByteOps.FLOAD: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
Type.FLOAT, 0);
return 2;
}
case ByteOps.DLOAD: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
Type.DOUBLE, 0);
return 2;
}
case ByteOps.ALOAD: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ILOAD, offset, 2, idx,
Type.OBJECT, 0);
return 2;
}
case ByteOps.ILOAD_0:
case ByteOps.ILOAD_1:
case ByteOps.ILOAD_2:
case ByteOps.ILOAD_3: {
int idx = opcode - ByteOps.ILOAD_0;
visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
Type.INT, 0);
return 1;
}
case ByteOps.LLOAD_0:
case ByteOps.LLOAD_1:
case ByteOps.LLOAD_2:
case ByteOps.LLOAD_3: {
int idx = opcode - ByteOps.LLOAD_0;
visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
Type.LONG, 0);
return 1;
}
case ByteOps.FLOAD_0:
case ByteOps.FLOAD_1:
case ByteOps.FLOAD_2:
case ByteOps.FLOAD_3: {
int idx = opcode - ByteOps.FLOAD_0;
visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
Type.FLOAT, 0);
return 1;
}
case ByteOps.DLOAD_0:
case ByteOps.DLOAD_1:
case ByteOps.DLOAD_2:
case ByteOps.DLOAD_3: {
int idx = opcode - ByteOps.DLOAD_0;
visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
Type.DOUBLE, 0);
return 1;
}
case ByteOps.ALOAD_0:
case ByteOps.ALOAD_1:
case ByteOps.ALOAD_2:
case ByteOps.ALOAD_3: {
int idx = opcode - ByteOps.ALOAD_0;
visitor.visitLocal(ByteOps.ILOAD, offset, 1, idx,
Type.OBJECT, 0);
return 1;
}
case ByteOps.IALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.INT);
return 1;
}
case ByteOps.LALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.LONG);
return 1;
}
case ByteOps.FALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
Type.FLOAT);
return 1;
}
case ByteOps.DALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
Type.DOUBLE);
return 1;
}
case ByteOps.AALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
Type.OBJECT);
return 1;
}
case ByteOps.BALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.BYTE);
return 1;
}
case ByteOps.CALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1, Type.CHAR);
return 1;
}
case ByteOps.SALOAD: {
visitor.visitNoArgs(ByteOps.IALOAD, offset, 1,
Type.SHORT);
return 1;
}
case ByteOps.ISTORE: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
Type.INT, 0);
return 2;
}
case ByteOps.LSTORE: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
Type.LONG, 0);
return 2;
}
case ByteOps.FSTORE: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
Type.FLOAT, 0);
return 2;
}
case ByteOps.DSTORE: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
Type.DOUBLE, 0);
return 2;
}
case ByteOps.ASTORE: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(ByteOps.ISTORE, offset, 2, idx,
Type.OBJECT, 0);
return 2;
}
case ByteOps.ISTORE_0:
case ByteOps.ISTORE_1:
case ByteOps.ISTORE_2:
case ByteOps.ISTORE_3: {
int idx = opcode - ByteOps.ISTORE_0;
visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
Type.INT, 0);
return 1;
}
case ByteOps.LSTORE_0:
case ByteOps.LSTORE_1:
case ByteOps.LSTORE_2:
case ByteOps.LSTORE_3: {
int idx = opcode - ByteOps.LSTORE_0;
visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
Type.LONG, 0);
return 1;
}
case ByteOps.FSTORE_0:
case ByteOps.FSTORE_1:
case ByteOps.FSTORE_2:
case ByteOps.FSTORE_3: {
int idx = opcode - ByteOps.FSTORE_0;
visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
Type.FLOAT, 0);
return 1;
}
case ByteOps.DSTORE_0:
case ByteOps.DSTORE_1:
case ByteOps.DSTORE_2:
case ByteOps.DSTORE_3: {
int idx = opcode - ByteOps.DSTORE_0;
visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
Type.DOUBLE, 0);
return 1;
}
case ByteOps.ASTORE_0:
case ByteOps.ASTORE_1:
case ByteOps.ASTORE_2:
case ByteOps.ASTORE_3: {
int idx = opcode - ByteOps.ASTORE_0;
visitor.visitLocal(ByteOps.ISTORE, offset, 1, idx,
Type.OBJECT, 0);
return 1;
}
case ByteOps.IASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1, Type.INT);
return 1;
}
case ByteOps.LASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.LONG);
return 1;
}
case ByteOps.FASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.FLOAT);
return 1;
}
case ByteOps.DASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.DOUBLE);
return 1;
}
case ByteOps.AASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.OBJECT);
return 1;
}
case ByteOps.BASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.BYTE);
return 1;
}
case ByteOps.CASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.CHAR);
return 1;
}
case ByteOps.SASTORE: {
visitor.visitNoArgs(ByteOps.IASTORE, offset, 1,
Type.SHORT);
return 1;
}
case ByteOps.POP:
case ByteOps.POP2:
case ByteOps.DUP:
case ByteOps.DUP_X1:
case ByteOps.DUP_X2:
case ByteOps.DUP2:
case ByteOps.DUP2_X1:
case ByteOps.DUP2_X2:
case ByteOps.SWAP: {
visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
return 1;
}
case ByteOps.IADD:
case ByteOps.ISUB:
case ByteOps.IMUL:
case ByteOps.IDIV:
case ByteOps.IREM:
case ByteOps.INEG:
case ByteOps.ISHL:
case ByteOps.ISHR:
case ByteOps.IUSHR:
case ByteOps.IAND:
case ByteOps.IOR:
case ByteOps.IXOR: {
visitor.visitNoArgs(opcode, offset, 1, Type.INT);
return 1;
}
case ByteOps.LADD:
case ByteOps.LSUB:
case ByteOps.LMUL:
case ByteOps.LDIV:
case ByteOps.LREM:
case ByteOps.LNEG:
case ByteOps.LSHL:
case ByteOps.LSHR:
case ByteOps.LUSHR:
case ByteOps.LAND:
case ByteOps.LOR:
case ByteOps.LXOR: {
/*
* It's "opcode - 1" because, conveniently enough, all
* these long ops are one past the int variants.
*/
visitor.visitNoArgs(opcode - 1, offset, 1, Type.LONG);
return 1;
}
case ByteOps.FADD:
case ByteOps.FSUB:
case ByteOps.FMUL:
case ByteOps.FDIV:
case ByteOps.FREM:
case ByteOps.FNEG: {
/*
* It's "opcode - 2" because, conveniently enough, all
* these float ops are two past the int variants.
*/
visitor.visitNoArgs(opcode - 2, offset, 1, Type.FLOAT);
return 1;
}
case ByteOps.DADD:
case ByteOps.DSUB:
case ByteOps.DMUL:
case ByteOps.DDIV:
case ByteOps.DREM:
case ByteOps.DNEG: {
/*
* It's "opcode - 3" because, conveniently enough, all
* these double ops are three past the int variants.
*/
visitor.visitNoArgs(opcode - 3, offset, 1, Type.DOUBLE);
return 1;
}
case ByteOps.IINC: {
int idx = bytes.getUnsignedByte(offset + 1);
int value = bytes.getByte(offset + 2);
visitor.visitLocal(opcode, offset, 3, idx,
Type.INT, value);
return 3;
}
case ByteOps.I2L:
case ByteOps.F2L:
case ByteOps.D2L: {
visitor.visitNoArgs(opcode, offset, 1, Type.LONG);
return 1;
}
case ByteOps.I2F:
case ByteOps.L2F:
case ByteOps.D2F: {
visitor.visitNoArgs(opcode, offset, 1, Type.FLOAT);
return 1;
}
case ByteOps.I2D:
case ByteOps.L2D:
case ByteOps.F2D: {
visitor.visitNoArgs(opcode, offset, 1, Type.DOUBLE);
return 1;
}
case ByteOps.L2I:
case ByteOps.F2I:
case ByteOps.D2I:
case ByteOps.I2B:
case ByteOps.I2C:
case ByteOps.I2S:
case ByteOps.LCMP:
case ByteOps.FCMPL:
case ByteOps.FCMPG:
case ByteOps.DCMPL:
case ByteOps.DCMPG:
case ByteOps.ARRAYLENGTH: {
visitor.visitNoArgs(opcode, offset, 1, Type.INT);
return 1;
}
case ByteOps.IFEQ:
case ByteOps.IFNE:
case ByteOps.IFLT:
case ByteOps.IFGE:
case ByteOps.IFGT:
case ByteOps.IFLE:
case ByteOps.IF_ICMPEQ:
case ByteOps.IF_ICMPNE:
case ByteOps.IF_ICMPLT:
case ByteOps.IF_ICMPGE:
case ByteOps.IF_ICMPGT:
case ByteOps.IF_ICMPLE:
case ByteOps.IF_ACMPEQ:
case ByteOps.IF_ACMPNE:
case ByteOps.GOTO:
case ByteOps.JSR:
case ByteOps.IFNULL:
case ByteOps.IFNONNULL: {
int target = offset + bytes.getShort(offset + 1);
visitor.visitBranch(opcode, offset, 3, target);
return 3;
}
case ByteOps.RET: {
int idx = bytes.getUnsignedByte(offset + 1);
visitor.visitLocal(opcode, offset, 2, idx,
Type.RETURN_ADDRESS, 0);
return 2;
}
case ByteOps.TABLESWITCH: {
return parseTableswitch(offset, visitor);
}
case ByteOps.LOOKUPSWITCH: {
return parseLookupswitch(offset, visitor);
}
case ByteOps.IRETURN: {
visitor.visitNoArgs(ByteOps.IRETURN, offset, 1, Type.INT);
return 1;
}
case ByteOps.LRETURN: {
visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
Type.LONG);
return 1;
}
case ByteOps.FRETURN: {
visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
Type.FLOAT);
return 1;
}
case ByteOps.DRETURN: {
visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
Type.DOUBLE);
return 1;
}
case ByteOps.ARETURN: {
visitor.visitNoArgs(ByteOps.IRETURN, offset, 1,
Type.OBJECT);
return 1;
}
case ByteOps.RETURN:
case ByteOps.ATHROW:
case ByteOps.MONITORENTER:
case ByteOps.MONITOREXIT: {
visitor.visitNoArgs(opcode, offset, 1, Type.VOID);
return 1;
}
case ByteOps.GETSTATIC:
case ByteOps.PUTSTATIC:
case ByteOps.GETFIELD:
case ByteOps.PUTFIELD:
case ByteOps.INVOKEVIRTUAL:
case ByteOps.INVOKESPECIAL:
case ByteOps.INVOKESTATIC:
case ByteOps.NEW:
case ByteOps.ANEWARRAY:
case ByteOps.CHECKCAST:
case ByteOps.INSTANCEOF: {
int idx = bytes.getUnsignedShort(offset + 1);
Constant cst = pool.get(idx);
visitor.visitConstant(opcode, offset, 3, cst, 0);
return 3;
}
case ByteOps.INVOKEINTERFACE: {
int idx = bytes.getUnsignedShort(offset + 1);
int count = bytes.getUnsignedByte(offset + 3);
int expectZero = bytes.getUnsignedByte(offset + 4);
Constant cst = pool.get(idx);
visitor.visitConstant(opcode, offset, 5, cst,
count | (expectZero << 8));
return 5;
}
case ByteOps.NEWARRAY: {
return parseNewarray(offset, visitor);
}
case ByteOps.WIDE: {
return parseWide(offset, visitor);
}
case ByteOps.MULTIANEWARRAY: {
int idx = bytes.getUnsignedShort(offset + 1);
int dimensions = bytes.getUnsignedByte(offset + 3);
Constant cst = pool.get(idx);
visitor.visitConstant(opcode, offset, 4, cst, dimensions);
return 4;
}
case ByteOps.GOTO_W:
case ByteOps.JSR_W: {
int target = offset + bytes.getInt(offset + 1);
int newop =
(opcode == ByteOps.GOTO_W) ? ByteOps.GOTO :
ByteOps.JSR;
visitor.visitBranch(newop, offset, 5, target);
return 5;
}
default: {
visitor.visitInvalid(opcode, offset, 1);
return 1;
}
}
} catch (SimException ex) {
ex.addContext("...at bytecode offset " + Hex.u4(offset));
throw ex;
} catch (RuntimeException ex) {
SimException se = new SimException(ex);
se.addContext("...at bytecode offset " + Hex.u4(offset));
throw se;
}
| private int | parseLookupswitch(int offset, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Helper to deal with lookupswitch .
int at = (offset + 4) & ~3; // "at" skips the padding.
// Collect the padding.
int padding = 0;
for (int i = offset + 1; i < at; i++) {
padding = (padding << 8) | bytes.getUnsignedByte(i);
}
int defaultTarget = offset + bytes.getInt(at);
int npairs = bytes.getInt(at + 4);
at += 8;
SwitchList cases = new SwitchList(npairs);
for (int i = 0; i < npairs; i++) {
int match = bytes.getInt(at);
int target = offset + bytes.getInt(at + 4);
at += 8;
cases.add(match, target);
}
cases.setDefaultTarget(defaultTarget);
cases.removeSuperfluousDefaults();
cases.setImmutable();
int length = at - offset;
visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
padding);
return length;
| private int | parseNewarray(int offset, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Helper to deal with newarray .
int value = bytes.getUnsignedByte(offset + 1);
CstType type;
switch (value) {
case ByteOps.NEWARRAY_BOOLEAN: {
type = CstType.BOOLEAN_ARRAY;
break;
}
case ByteOps.NEWARRAY_CHAR: {
type = CstType.CHAR_ARRAY;
break;
}
case ByteOps.NEWARRAY_DOUBLE: {
type = CstType.DOUBLE_ARRAY;
break;
}
case ByteOps.NEWARRAY_FLOAT: {
type = CstType.FLOAT_ARRAY;
break;
}
case ByteOps.NEWARRAY_BYTE: {
type = CstType.BYTE_ARRAY;
break;
}
case ByteOps.NEWARRAY_SHORT: {
type = CstType.SHORT_ARRAY;
break;
}
case ByteOps.NEWARRAY_INT: {
type = CstType.INT_ARRAY;
break;
}
case ByteOps.NEWARRAY_LONG: {
type = CstType.LONG_ARRAY;
break;
}
default: {
throw new SimException("bad newarray code " +
Hex.u1(value));
}
}
// Revisit the previous bytecode to find out the length of the array
int previousOffset = visitor.getPreviousOffset();
ConstantParserVisitor constantVisitor = new ConstantParserVisitor();
int arrayLength = 0;
/*
* For visitors that don't record the previous offset, -1 will be
* seen here
*/
if (previousOffset >= 0) {
parseInstruction(previousOffset, constantVisitor);
if (constantVisitor.cst instanceof CstInteger &&
constantVisitor.length + previousOffset == offset) {
arrayLength = constantVisitor.value;
}
}
/*
* Try to match the array initialization idiom. For example, if the
* subsequent code is initializing an int array, we are expecting the
* following pattern repeatedly:
* dup
* push index
* push value
* *astore
*
* where the index value will be incrimented sequentially from 0 up.
*/
int nInit = 0;
int curOffset = offset+2;
int lastOffset = curOffset;
ArrayList<Constant> initVals = new ArrayList<Constant>();
if (arrayLength != 0) {
while (true) {
boolean punt = false;
// First check if the next bytecode is dup
int nextByte = bytes.getUnsignedByte(curOffset++);
if (nextByte != ByteOps.DUP)
break;
// Next check if the expected array index is pushed to the stack
parseInstruction(curOffset, constantVisitor);
if (constantVisitor.length == 0 ||
!(constantVisitor.cst instanceof CstInteger) ||
constantVisitor.value != nInit)
break;
// Next, fetch the init value and record it
curOffset += constantVisitor.length;
// Next find out what kind of constant is pushed onto the stack
parseInstruction(curOffset, constantVisitor);
if (constantVisitor.length == 0 ||
!(constantVisitor.cst instanceof CstLiteralBits))
break;
curOffset += constantVisitor.length;
initVals.add(constantVisitor.cst);
nextByte = bytes.getUnsignedByte(curOffset++);
// Now, check if the value is stored to the array properly
switch (value) {
case ByteOps.NEWARRAY_BYTE:
case ByteOps.NEWARRAY_BOOLEAN: {
if (nextByte != ByteOps.BASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_CHAR: {
if (nextByte != ByteOps.CASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_DOUBLE: {
if (nextByte != ByteOps.DASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_FLOAT: {
if (nextByte != ByteOps.FASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_SHORT: {
if (nextByte != ByteOps.SASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_INT: {
if (nextByte != ByteOps.IASTORE) {
punt = true;
}
break;
}
case ByteOps.NEWARRAY_LONG: {
if (nextByte != ByteOps.LASTORE) {
punt = true;
}
break;
}
default:
punt = true;
break;
}
if (punt) {
break;
}
lastOffset = curOffset;
nInit++;
}
}
/*
* For singleton arrays it is still more economical to
* generate the aput.
*/
if (nInit < 2 || nInit != arrayLength) {
visitor.visitNewarray(offset, 2, type, null);
return 2;
} else {
visitor.visitNewarray(offset, lastOffset - offset, type, initVals);
return lastOffset - offset;
}
| private int | parseTableswitch(int offset, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Helper to deal with tableswitch .
int at = (offset + 4) & ~3; // "at" skips the padding.
// Collect the padding.
int padding = 0;
for (int i = offset + 1; i < at; i++) {
padding = (padding << 8) | bytes.getUnsignedByte(i);
}
int defaultTarget = offset + bytes.getInt(at);
int low = bytes.getInt(at + 4);
int high = bytes.getInt(at + 8);
int count = high - low + 1;
at += 12;
if (low > high) {
throw new SimException("low / high inversion");
}
SwitchList cases = new SwitchList(count);
for (int i = 0; i < count; i++) {
int target = offset + bytes.getInt(at);
at += 4;
cases.add(low + i, target);
}
cases.setDefaultTarget(defaultTarget);
cases.removeSuperfluousDefaults();
cases.setImmutable();
int length = at - offset;
visitor.visitSwitch(ByteOps.LOOKUPSWITCH, offset, length, cases,
padding);
return length;
| private int | parseWide(int offset, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Helper to deal with wide .
int opcode = bytes.getUnsignedByte(offset + 1);
int idx = bytes.getUnsignedShort(offset + 2);
switch (opcode) {
case ByteOps.ILOAD: {
visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
Type.INT, 0);
return 4;
}
case ByteOps.LLOAD: {
visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
Type.LONG, 0);
return 4;
}
case ByteOps.FLOAD: {
visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
Type.FLOAT, 0);
return 4;
}
case ByteOps.DLOAD: {
visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
Type.DOUBLE, 0);
return 4;
}
case ByteOps.ALOAD: {
visitor.visitLocal(ByteOps.ILOAD, offset, 4, idx,
Type.OBJECT, 0);
return 4;
}
case ByteOps.ISTORE: {
visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
Type.INT, 0);
return 4;
}
case ByteOps.LSTORE: {
visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
Type.LONG, 0);
return 4;
}
case ByteOps.FSTORE: {
visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
Type.FLOAT, 0);
return 4;
}
case ByteOps.DSTORE: {
visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
Type.DOUBLE, 0);
return 4;
}
case ByteOps.ASTORE: {
visitor.visitLocal(ByteOps.ISTORE, offset, 4, idx,
Type.OBJECT, 0);
return 4;
}
case ByteOps.RET: {
visitor.visitLocal(opcode, offset, 4, idx,
Type.RETURN_ADDRESS, 0);
return 4;
}
case ByteOps.IINC: {
int value = bytes.getShort(offset + 4);
visitor.visitLocal(opcode, offset, 6, idx,
Type.INT, value);
return 6;
}
default: {
visitor.visitInvalid(ByteOps.WIDE, offset, 1);
return 1;
}
}
| public void | processWorkSet(int[] workSet, com.android.dx.cf.code.BytecodeArray$Visitor visitor)Processes the given "work set" by repeatedly finding the lowest bit
in the set, clearing it, and parsing and visiting the instruction at
the indicated offset (that is, the bit index), repeating until the
work set is empty. It is expected that the visitor will regularly
set new bits in the work set during the process.
if (visitor == null) {
throw new NullPointerException("visitor == null");
}
for (;;) {
int offset = Bits.findFirst(workSet, 0);
if (offset < 0) {
break;
}
Bits.clear(workSet, offset);
parseInstruction(offset, visitor);
visitor.setPreviousOffset(offset);
}
| public int | size()Gets the size of the bytecode array, per se.
return bytes.size();
|
|