/** * Processes the fields of the given class. * * @param cf {@code non-null;} class being translated * @param out {@code non-null;} output class */ private static void processFields(DirectClassFile cf, ClassDefItem out) { CstType thisClass = cf.getThisClass(); FieldList fields = cf.getFields(); int sz = fields.size(); for (int i = 0; i < sz; i++) { Field one = fields.get(i); try { CstFieldRef field = new CstFieldRef(thisClass, one.getNat()); int accessFlags = one.getAccessFlags(); if (AccessFlags.isStatic(accessFlags)) { TypedConstant constVal = one.getConstantValue(); EncodedField fi = new EncodedField(field, accessFlags); if (constVal != null) { constVal = coerceConstant(constVal, field.getType()); } out.addStaticField(fi, constVal); } else { EncodedField fi = new EncodedField(field, accessFlags); out.addInstanceField(fi); } Annotations annotations = AttributeTranslator.getAnnotations(one.getAttributes()); if (annotations.size() != 0) { out.addFieldAnnotations(field, annotations); } } catch (RuntimeException ex) { String msg = "...while processing " + one.getName().toHuman() + " " + one.getDescriptor().toHuman(); throw ExceptionWithContext.withContext(ex, msg); } } }
/** 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); }
/** * 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; }
/** * 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); } } }