/** {@inheritDoc} */ public int compareTo(Annotation other) { int result = type.compareTo(other.type); if (result != 0) { return result; } result = visibility.compareTo(other.visibility); if (result != 0) { return result; } Iterator<NameValuePair> thisIter = elements.values().iterator(); Iterator<NameValuePair> otherIter = other.elements.values().iterator(); while (thisIter.hasNext() && otherIter.hasNext()) { NameValuePair thisOne = thisIter.next(); NameValuePair otherOne = otherIter.next(); result = thisOne.compareTo(otherOne); if (result != 0) { return result; } } if (thisIter.hasNext()) { return 1; } else if (otherIter.hasNext()) { return -1; } return 0; }
/** {@inheritDoc} */ @Override public boolean equals(Object other) { if (!(other instanceof Annotation)) { return false; } Annotation otherAnnotation = (Annotation) other; if (!(type.equals(otherAnnotation.type) && (visibility == otherAnnotation.visibility))) { return false; } return elements.equals(otherAnnotation.elements); }
/** {@inheritDoc} */ public String toHuman() { StringBuilder sb = new StringBuilder(); sb.append(visibility.toHuman()); sb.append("-annotation "); sb.append(type.toHuman()); sb.append(" {"); boolean first = true; for (NameValuePair pair : elements.values()) { if (first) { first = false; } else { sb.append(", "); } sb.append(pair.getName().toHuman()); sb.append(": "); sb.append(pair.getValue().toHuman()); } sb.append("}"); return sb.toString(); }
/** * 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; }
/** {@inheritDoc} */ public int hashCode() { int hash = type.hashCode(); hash = (hash * 31) + elements.hashCode(); hash = (hash * 31) + visibility.hashCode(); return hash; }
/** * Processes the methods of the given class. * * @param cf {@code non-null;} class being translated * @param cfOptions {@code non-null;} options for class translation * @param dexOptions {@code non-null;} options for dex output * @param out {@code non-null;} output class */ private static void processMethods( DirectClassFile cf, CfOptions cfOptions, DexOptions dexOptions, ClassDefItem out) { CstType thisClass = cf.getThisClass(); MethodList methods = cf.getMethods(); int sz = methods.size(); for (int i = 0; i < sz; i++) { Method one = methods.get(i); try { CstMethodRef meth = new CstMethodRef(thisClass, one.getNat()); int accessFlags = one.getAccessFlags(); boolean isStatic = AccessFlags.isStatic(accessFlags); boolean isPrivate = AccessFlags.isPrivate(accessFlags); boolean isNative = AccessFlags.isNative(accessFlags); boolean isAbstract = AccessFlags.isAbstract(accessFlags); boolean isConstructor = meth.isInstanceInit() || meth.isClassInit(); DalvCode code; if (isNative || isAbstract) { // There's no code for native or abstract methods. code = null; } else { ConcreteMethod concrete = new ConcreteMethod( one, cf, (cfOptions.positionInfo != PositionList.NONE), cfOptions.localInfo); TranslationAdvice advice; advice = DexTranslationAdvice.THE_ONE; RopMethod rmeth = Ropper.convert(concrete, advice); RopMethod nonOptRmeth = null; int paramSize; paramSize = meth.getParameterWordCount(isStatic); String canonicalName = thisClass.getClassType().getDescriptor() + "." + one.getName().getString(); if (cfOptions.optimize && OptimizerOptions.shouldOptimize(canonicalName)) { if (DEBUG) { System.err.println("Optimizing " + canonicalName); } nonOptRmeth = rmeth; rmeth = Optimizer.optimize(rmeth, paramSize, isStatic, cfOptions.localInfo, advice); if (DEBUG) { OptimizerOptions.compareOptimizerStep( nonOptRmeth, paramSize, isStatic, cfOptions, advice, rmeth); } if (cfOptions.statistics) { CodeStatistics.updateRopStatistics(nonOptRmeth, rmeth); } } LocalVariableInfo locals = null; if (cfOptions.localInfo) { locals = LocalVariableExtractor.extract(rmeth); } code = RopTranslator.translate(rmeth, cfOptions.positionInfo, locals, paramSize, dexOptions); if (cfOptions.statistics && nonOptRmeth != null) { updateDexStatistics( cfOptions, dexOptions, rmeth, nonOptRmeth, locals, paramSize, concrete.getCode().size()); } } // Preserve the synchronized flag as its "declared" variant... if (AccessFlags.isSynchronized(accessFlags)) { accessFlags |= AccessFlags.ACC_DECLARED_SYNCHRONIZED; /* * ...but only native methods are actually allowed to be * synchronized. */ if (!isNative) { accessFlags &= ~AccessFlags.ACC_SYNCHRONIZED; } } if (isConstructor) { accessFlags |= AccessFlags.ACC_CONSTRUCTOR; } TypeList exceptions = AttributeTranslator.getExceptions(one); EncodedMethod mi = new EncodedMethod(meth, accessFlags, code, exceptions); if (meth.isInstanceInit() || meth.isClassInit() || isStatic || isPrivate) { out.addDirectMethod(mi); } else { out.addVirtualMethod(mi); } Annotations annotations = AttributeTranslator.getMethodAnnotations(one); if (annotations.size() != 0) { out.addMethodAnnotations(meth, annotations); } AnnotationsList list = AttributeTranslator.getParameterAnnotations(one); if (list.size() != 0) { out.addParameterAnnotations(meth, list); } } catch (RuntimeException ex) { String msg = "...while processing " + one.getName().toHuman() + " " + one.getDescriptor().toHuman(); throw ExceptionWithContext.withContext(ex, msg); } } }
/** 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); }