/** Parses a {@code LineNumberTable} attribute. */ private Attribute lineNumberTable( DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 2) { return throwSeverelyTruncated(); } ByteArray bytes = cf.getBytes(); int count = bytes.getUnsignedShort(offset); // line_number_table_length if (observer != null) { observer.parsed(bytes, offset, 2, "line_number_table_length: " + Hex.u2(count)); } offset += 2; length -= 2; if (length != (count * 4)) { throwBadLength((count * 4) + 2); } LineNumberList list = new LineNumberList(count); for (int i = 0; i < count; i++) { int startPc = bytes.getUnsignedShort(offset); int lineNumber = bytes.getUnsignedShort(offset + 2); list.set(i, startPc, lineNumber); if (observer != null) { observer.parsed(bytes, offset, 4, Hex.u2(startPc) + " " + lineNumber); } offset += 4; } list.setImmutable(); return new AttLineNumberTable(list); }
/** * Parse the table part of either a {@code LocalVariableTable} or a {@code * LocalVariableTypeTable}. * * @param bytes {@code non-null;} bytes to parse, which should <i>only</i> contain the table data * (no header) * @param pool {@code non-null;} constant pool to use * @param count {@code >= 0;} the number of entries * @param typeTable {@code true} iff this is for a type table * @return {@code non-null;} the constructed list */ private LocalVariableList parseLocalVariables( ByteArray bytes, ConstantPool pool, ParseObserver observer, int count, boolean typeTable) { if (bytes.size() != (count * 10)) { // "+ 2" is for the count. throwBadLength((count * 10) + 2); } ByteArray.MyDataInputStream in = bytes.makeDataInputStream(); LocalVariableList list = new LocalVariableList(count); try { for (int i = 0; i < count; i++) { int startPc = in.readUnsignedShort(); int length = in.readUnsignedShort(); int nameIdx = in.readUnsignedShort(); int typeIdx = in.readUnsignedShort(); int index = in.readUnsignedShort(); CstString name = (CstString) pool.get(nameIdx); CstString type = (CstString) pool.get(typeIdx); CstString descriptor = null; CstString signature = null; if (typeTable) { signature = type; } else { descriptor = type; } list.set(i, startPc, length, name, descriptor, signature, index); if (observer != null) { observer.parsed( bytes, i * 10, 10, Hex.u2(startPc) + ".." + Hex.u2(startPc + length) + " " + Hex.u2(index) + " " + name.toHuman() + " " + type.toHuman()); } } } catch (IOException ex) { throw new RuntimeException("shouldn't happen", ex); } list.setImmutable(); return list; }
/** Parses a {@code SourceFile} attribute. */ private Attribute sourceFile(DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length != 2) { throwBadLength(2); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int idx = bytes.getUnsignedShort(offset); CstString cst = (CstString) pool.get(idx); Attribute result = new AttSourceFile(cst); if (observer != null) { observer.parsed(bytes, offset, 2, "source: " + cst); } return result; }
/** Parses a {@code LocalVariableTypeTable} attribute. */ private Attribute localVariableTypeTable( DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 2) { return throwSeverelyTruncated(); } ByteArray bytes = cf.getBytes(); int count = bytes.getUnsignedShort(offset); if (observer != null) { observer.parsed(bytes, offset, 2, "local_variable_type_table_length: " + Hex.u2(count)); } LocalVariableList list = parseLocalVariables( bytes.slice(offset + 2, offset + length), cf.getConstantPool(), observer, count, true); return new AttLocalVariableTypeTable(list); }
/** Parses a {@code ConstantValue} attribute. */ private Attribute constantValue( DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length != 2) { return throwBadLength(2); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int idx = bytes.getUnsignedShort(offset); TypedConstant cst = (TypedConstant) pool.get(idx); Attribute result = new AttConstantValue(cst); if (observer != null) { observer.parsed(bytes, offset, 2, "value: " + cst); } return result; }
/** Parses an {@code Exceptions} attribute. */ private Attribute exceptions(DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 2) { return throwSeverelyTruncated(); } ByteArray bytes = cf.getBytes(); int count = bytes.getUnsignedShort(offset); // number_of_exceptions if (observer != null) { observer.parsed(bytes, offset, 2, "number_of_exceptions: " + Hex.u2(count)); } offset += 2; length -= 2; if (length != (count * 2)) { throwBadLength((count * 2) + 2); } TypeList list = cf.makeTypeList(offset, count); return new AttExceptions(list); }
/** Parses a {@code bootstrap} attribute. */ private Attribute bootstrap(DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 2) { throwBadLength(2); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int count = bytes.getUnsignedShort(offset); BootstrapMethodList list = parseBootstrapMethods( bytes.slice(offset + 2, offset + length), cf.getConstantPool(), observer, count); if (observer != null) { // observer.parsed(bytes, offset, 2, "source: " + cst); } AttBootstrapMethods attBootstrapMethods = new AttBootstrapMethods(list); // -6 for header (cf. 4.7.21) offset += attBootstrapMethods.byteLength() - 6; return attBootstrapMethods; }
/** Parses an {@code EnclosingMethod} attribute. */ private Attribute enclosingMethod( DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length != 4) { throwBadLength(4); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int idx = bytes.getUnsignedShort(offset); CstType type = (CstType) pool.get(idx); idx = bytes.getUnsignedShort(offset + 2); CstNat method = (CstNat) pool.get0Ok(idx); Attribute result = new AttEnclosingMethod(type, method); if (observer != null) { observer.parsed(bytes, offset, 2, "class: " + type); observer.parsed(bytes, offset + 2, 2, "method: " + DirectClassFile.stringOrNone(method)); } return result; }
/** Parses an {@code InnerClasses} attribute. */ private Attribute innerClasses( DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 2) { return throwSeverelyTruncated(); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int count = bytes.getUnsignedShort(offset); // number_of_classes if (observer != null) { observer.parsed(bytes, offset, 2, "number_of_classes: " + Hex.u2(count)); } offset += 2; length -= 2; if (length != (count * 8)) { throwBadLength((count * 8) + 2); } InnerClassList list = new InnerClassList(count); for (int i = 0; i < count; i++) { int innerClassIdx = bytes.getUnsignedShort(offset); int outerClassIdx = bytes.getUnsignedShort(offset + 2); int nameIdx = bytes.getUnsignedShort(offset + 4); int accessFlags = bytes.getUnsignedShort(offset + 6); CstType innerClass = (CstType) pool.get(innerClassIdx); CstType outerClass = (CstType) pool.get0Ok(outerClassIdx); CstString name = (CstString) pool.get0Ok(nameIdx); list.set(i, innerClass, outerClass, name, accessFlags); if (observer != null) { observer.parsed( bytes, offset, 2, "inner_class: " + DirectClassFile.stringOrNone(innerClass)); observer.parsed( bytes, offset + 2, 2, " outer_class: " + DirectClassFile.stringOrNone(outerClass)); observer.parsed(bytes, offset + 4, 2, " name: " + DirectClassFile.stringOrNone(name)); observer.parsed( bytes, offset + 6, 2, " access_flags: " + AccessFlags.innerClassString(accessFlags)); } offset += 8; } list.setImmutable(); return new AttInnerClasses(list); }
private BootstrapMethodList parseBootstrapMethods( ByteArray bytes, ConstantPool pool, ParseObserver observer, int count) { ByteArray.MyDataInputStream in = bytes.makeDataInputStream(); BootstrapMethodList list = new BootstrapMethodList(count); try { for (int i = 0; i < count; i++) { int bootstrap_method_ref = in.readUnsignedShort(); CstMethodHandle methodHandle = (CstMethodHandle) pool.get(bootstrap_method_ref); int num_bootstrap_arguments = in.readUnsignedShort(); BootstrapArgumentList bootstrapArgumentList = new BootstrapArgumentList(num_bootstrap_arguments); for (int j = 0; j < num_bootstrap_arguments; ++j) { int bootstrap_argument_idx = in.readUnsignedShort(); Constant constant = pool.get(bootstrap_argument_idx); bootstrapArgumentList.set(j, constant); } bootstrapArgumentList.setImmutable(); list.set(i, new BootstrapMethodList.BootstrapMethod(methodHandle, bootstrapArgumentList)); if (observer != null) { // observer.parsed(bytes, i * 10, 10, Hex.u2(startPc) + // ".." + Hex.u2(startPc + length) + " " + // Hex.u2(index) + " " + name.toHuman() + " " + // type.toHuman()); } } } catch (IOException ex) { throw new RuntimeException("shouldn't happen", ex); } list.setImmutable(); return list; }
/** * Does a registerizing dump. * * @param meth {@code non-null;} method data to dump */ private void ropDump(ConcreteMethod meth) { TranslationAdvice advice = DexTranslationAdvice.THE_ONE; BytecodeArray code = meth.getCode(); ByteArray bytes = code.getBytes(); RopMethod rmeth = Ropper.convert(meth, advice); StringBuffer sb = new StringBuffer(2000); if (optimize) { boolean isStatic = AccessFlags.isStatic(meth.getAccessFlags()); int paramWidth = computeParamWidth(meth, isStatic); rmeth = new Optimizer().optimize(rmeth, paramWidth, isStatic, true, advice); } BasicBlockList blocks = rmeth.getBlocks(); int[] order = blocks.getLabelsInOrder(); sb.append("first " + Hex.u2(rmeth.getFirstLabel()) + "\n"); for (int label : order) { BasicBlock bb = blocks.get(blocks.indexOfLabel(label)); sb.append("block "); sb.append(Hex.u2(label)); sb.append("\n"); IntList preds = rmeth.labelToPredecessors(label); int psz = preds.size(); for (int i = 0; i < psz; i++) { sb.append(" pred "); sb.append(Hex.u2(preds.get(i))); sb.append("\n"); } InsnList il = bb.getInsns(); int ilsz = il.size(); for (int i = 0; i < ilsz; i++) { Insn one = il.get(i); sb.append(" "); sb.append(il.get(i).toHuman()); sb.append("\n"); } IntList successors = bb.getSuccessors(); int ssz = successors.size(); if (ssz == 0) { sb.append(" returns\n"); } else { int primary = bb.getPrimarySuccessor(); for (int i = 0; i < ssz; i++) { int succ = successors.get(i); sb.append(" next "); sb.append(Hex.u2(succ)); if ((ssz != 1) && (succ == primary)) { sb.append(" *"); } sb.append("\n"); } } } suppressDump = false; setAt(bytes, 0); parsed(bytes, 0, bytes.size(), sb.toString()); suppressDump = true; }
/** * Does a regular basic block dump. * * @param meth {@code non-null;} method data to dump */ private void regularDump(ConcreteMethod meth) { BytecodeArray code = meth.getCode(); ByteArray bytes = code.getBytes(); ByteBlockList list = BasicBlocker.identifyBlocks(meth); int sz = list.size(); CodeObserver codeObserver = new CodeObserver(bytes, BlockDumper.this); // Reset the dump cursor to the start of the bytecode. setAt(bytes, 0); suppressDump = false; int byteAt = 0; for (int i = 0; i < sz; i++) { ByteBlock bb = list.get(i); int start = bb.getStart(); int end = bb.getEnd(); if (byteAt < start) { parsed(bytes, byteAt, start - byteAt, "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(start)); } parsed( bytes, start, 0, "block " + Hex.u2(bb.getLabel()) + ": " + Hex.u2(start) + ".." + Hex.u2(end)); changeIndent(1); int len; for (int j = start; j < end; j += len) { len = code.parseInstruction(j, codeObserver); codeObserver.setPreviousOffset(j); } IntList successors = bb.getSuccessors(); int ssz = successors.size(); if (ssz == 0) { parsed(bytes, end, 0, "returns"); } else { for (int j = 0; j < ssz; j++) { int succ = successors.get(j); parsed(bytes, end, 0, "next " + Hex.u2(succ)); } } ByteCatchList catches = bb.getCatches(); int csz = catches.size(); for (int j = 0; j < csz; j++) { ByteCatchList.Item one = catches.get(j); CstType exceptionClass = one.getExceptionClass(); parsed( bytes, end, 0, "catch " + ((exceptionClass == CstType.OBJECT) ? "<any>" : exceptionClass.toHuman()) + " -> " + Hex.u2(one.getHandlerPc())); } changeIndent(-1); byteAt = end; } int end = bytes.size(); if (byteAt < end) { parsed(bytes, byteAt, end - byteAt, "dead code " + Hex.u2(byteAt) + ".." + Hex.u2(end)); } suppressDump = true; }
/** Parses a {@code Code} attribute. */ private Attribute code(DirectClassFile cf, int offset, int length, ParseObserver observer) { if (length < 12) { return throwSeverelyTruncated(); } ByteArray bytes = cf.getBytes(); ConstantPool pool = cf.getConstantPool(); int maxStack = bytes.getUnsignedShort(offset); // u2 max_stack int maxLocals = bytes.getUnsignedShort(offset + 2); // u2 max_locals int codeLength = bytes.getInt(offset + 4); // u4 code_length int origOffset = offset; if (observer != null) { observer.parsed(bytes, offset, 2, "max_stack: " + Hex.u2(maxStack)); observer.parsed(bytes, offset + 2, 2, "max_locals: " + Hex.u2(maxLocals)); observer.parsed(bytes, offset + 4, 4, "code_length: " + Hex.u4(codeLength)); } offset += 8; length -= 8; if (length < (codeLength + 4)) { return throwTruncated(); } int codeOffset = offset; offset += codeLength; length -= codeLength; BytecodeArray code = new BytecodeArray(bytes.slice(codeOffset, codeOffset + codeLength), pool); if (observer != null) { code.forEach(new CodeObserver(code.getBytes(), observer)); } // u2 exception_table_length int exceptionTableLength = bytes.getUnsignedShort(offset); ByteCatchList catches = (exceptionTableLength == 0) ? ByteCatchList.EMPTY : new ByteCatchList(exceptionTableLength); if (observer != null) { observer.parsed(bytes, offset, 2, "exception_table_length: " + Hex.u2(exceptionTableLength)); } offset += 2; length -= 2; if (length < (exceptionTableLength * 8 + 2)) { return throwTruncated(); } for (int i = 0; i < exceptionTableLength; i++) { if (observer != null) { observer.changeIndent(1); } int startPc = bytes.getUnsignedShort(offset); int endPc = bytes.getUnsignedShort(offset + 2); int handlerPc = bytes.getUnsignedShort(offset + 4); int catchTypeIdx = bytes.getUnsignedShort(offset + 6); CstType catchType = (CstType) pool.get0Ok(catchTypeIdx); catches.set(i, startPc, endPc, handlerPc, catchType); if (observer != null) { observer.parsed( bytes, offset, 8, Hex.u2(startPc) + ".." + Hex.u2(endPc) + " -> " + Hex.u2(handlerPc) + " " + ((catchType == null) ? "<any>" : catchType.toHuman())); } offset += 8; length -= 8; if (observer != null) { observer.changeIndent(-1); } } catches.setImmutable(); AttributeListParser parser = new AttributeListParser(cf, CTX_CODE, offset, this); parser.setObserver(observer); StdAttributeList attributes = parser.getList(); attributes.setImmutable(); int attributeByteCount = parser.getEndOffset() - offset; if (attributeByteCount != length) { return throwBadLength(attributeByteCount + (offset - origOffset)); } return new AttCode(maxStack, maxLocals, code, catches, attributes); }