package com.taobao.atlas.dexmerge.dx.merge; /** * Created by lilong on 16/10/13. */ /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import com.taobao.atlas.dex.*; import java.io.IOException; import java.util.*; /** * Combine two dex files into one. */ public final class DexTransform { private final Dex dex; private final IndexMap indexMap; private final WriterSizes writerSizes; private final Dex dexOut; private final Dex.Section headerOut; /** All IDs and definitions sections */ private final Dex.Section idsDefsOut; private final Dex.Section mapListOut; private final Dex.Section typeListOut; private final Dex.Section classDataOut; private final Dex.Section codeOut; private final Dex.Section stringDataOut; private final Dex.Section debugInfoOut; private final Dex.Section encodedArrayOut; /** annotations directory on a type */ private final Dex.Section annotationsDirectoryOut; /** sets of annotations on a member, parameter or type */ private final Dex.Section annotationSetOut; /** parameter lists */ private final Dex.Section annotationSetRefListOut; /** individual annotations, each containing zero or more fields */ private final Dex.Section annotationOut; private final TableOfContents contentsOut; private final InstructionTransformer instructionTransformer; private TreeMap<String, DexItem> stringValueOffMaps = new TreeMap<String, DexItem>(); // 字符串的maps,key为value,value为index private Map<String, DexItem> methodCodeOffMaps = new HashMap<String, DexItem>(); // 方法名和字节码的off的对应关系 private List<Integer> debugInfoRemoveMethodss = new ArrayList<Integer>(); private Map<Integer,Integer> removeClassDefs = new HashMap<Integer, Integer>(); private final boolean onlyShrink; public DexTransform(Dex dex) throws IOException { this(dex, new WriterSizes(dex, true),false); } private DexTransform(Dex dex, WriterSizes writerSizes,boolean onlyShrink) throws IOException{ this.dex = dex; this.writerSizes = writerSizes; this.onlyShrink = onlyShrink; dexOut = new Dex(writerSizes.size()); indexMap = new IndexMap(dexOut, dex.getTableOfContents()); instructionTransformer = new InstructionTransformer(); headerOut = dexOut.appendSection(writerSizes.header, "header"); idsDefsOut = dexOut.appendSection(writerSizes.idsDefs, "ids defs"); contentsOut = dexOut.getTableOfContents(); contentsOut.dataOff = dexOut.getNextSectionStart(); contentsOut.mapList.off = dexOut.getNextSectionStart(); contentsOut.mapList.size = 1; mapListOut = dexOut.appendSection(writerSizes.mapList, "map list"); contentsOut.typeLists.off = dexOut.getNextSectionStart(); typeListOut = dexOut.appendSection(writerSizes.typeList, "type list"); contentsOut.annotationSetRefLists.off = dexOut.getNextSectionStart(); annotationSetRefListOut = dexOut.appendSection(writerSizes.annotationsSetRefList, "annotation set ref list"); contentsOut.annotationSets.off = dexOut.getNextSectionStart(); annotationSetOut = dexOut.appendSection(writerSizes.annotationsSet, "annotation sets"); contentsOut.classDatas.off = dexOut.getNextSectionStart(); classDataOut = dexOut.appendSection(writerSizes.classData, "class data"); contentsOut.codes.off = dexOut.getNextSectionStart(); codeOut = dexOut.appendSection(writerSizes.code, "code"); contentsOut.stringDatas.off = dexOut.getNextSectionStart(); stringDataOut = dexOut.appendSection(writerSizes.stringData, "string data"); contentsOut.debugInfos.off = dexOut.getNextSectionStart(); debugInfoOut = dexOut.appendSection(writerSizes.debugInfo, "debug info"); contentsOut.annotations.off = dexOut.getNextSectionStart(); annotationOut = dexOut.appendSection(writerSizes.annotation, "annotation"); contentsOut.encodedArrays.off = dexOut.getNextSectionStart(); encodedArrayOut = dexOut.appendSection(writerSizes.encodedArray, "encoded array"); contentsOut.annotationsDirectories.off = dexOut.getNextSectionStart(); annotationsDirectoryOut = dexOut.appendSection(writerSizes.annotationsDirectory, "annotations directory"); contentsOut.dataSize = dexOut.getNextSectionStart() - contentsOut.dataOff; if(!onlyShrink) { initIndexMaps(); } } /** * 初始化所有的Maps */ private void initIndexMaps() { intStringIndexMaps(dex); initCodeMaps(dex); } private void intStringIndexMaps(Dex dex) { TableOfContents.Section stringIds = dex.getTableOfContents().stringIds; Dex.Section stringDataSection = dex.open(stringIds.off); for (int i = 0; i < stringIds.size; i++) { int position = stringDataSection.getPosition(); int off = stringDataSection.readInt(); String value = null; if (off < 0) {// 如果为负数,则表示取下一个dex的值 value = String.valueOf(off); stringValueOffMaps.put(value, new DexItem(off, i, off)); } else { stringDataSection.getData().position(position); value = stringDataSection.readString(); stringValueOffMaps.put(value, new DexItem(off, i, off)); } } } private void initCodeMaps(Dex dex) { Iterable<ClassDef> classDefs = dex.classDefs(); for (ClassDef classDef : classDefs) { if(classDef.getClassDataOffset() > 0) { ClassData classData = dex.readClassData(classDef); ClassData.Method[] directMethods = classData.getDirectMethods(); ClassData.Method[] virtualMethods = classData.getVirtualMethods(); initMethodsMaps(dex, directMethods); initMethodsMaps(dex, virtualMethods); } } } private void initMethodsMaps(Dex dex, ClassData.Method[] methods) { for (ClassData.Method method : methods) { MethodId methodId = dex.methodIds().get(method.getMethodIndex()); String className = dex.typeNames().get(methodId.getDeclaringClassIndex()); String methodName = dex.strings().get(methodId.getNameIndex()) + dex.readTypeList(dex.protoIds().get(methodId.getProtoIndex()).getParametersOffset()); methodCodeOffMaps.put(className + "." + methodName, new DexItem(method.getCodeOffset(), method.getMethodIndex(), method.getCodeOffset())); } } private Set<String> getMethodMaps(Dex dex){ Set<String> methods = new HashSet<String>(); Iterable<ClassDef> classDefs = dex.classDefs(); for (ClassDef classDef : classDefs) { if(classDef.getClassDataOffset() > 0) { ClassData classData = dex.readClassData(classDef); ClassData.Method[] directMethods = classData.getDirectMethods(); ClassData.Method[] virtualMethods = classData.getVirtualMethods(); initMethodsMaps(dex, directMethods, methods); initMethodsMaps(dex, virtualMethods, methods); } } return methods; } private void initMethodsMaps(Dex dex, ClassData.Method[] methods, Set<String> methodSets) { for (ClassData.Method method : methods) { MethodId methodId = dex.methodIds().get(method.getMethodIndex()); String className = dex.typeNames().get(methodId.getDeclaringClassIndex()); String methodName = dex.strings().get(methodId.getNameIndex()) + dex.readTypeList(dex.protoIds().get(methodId.getProtoIndex()).getParametersOffset()); methodSets.add(className + "." + methodName); } } /** * 根据基线的dex,去除重复的string值 * * @param baseDex * @return */ public DexTransform uninoStringValues(Dex baseDex) { TableOfContents.Section stringIds = baseDex.getTableOfContents().stringIds; Dex.Section stringSection = baseDex.open(stringIds.off); for (int i = 0; i < stringIds.size; i++) { String value = stringSection.readString(); if (stringValueOffMaps.containsKey(value)) { DexItem dexItem = stringValueOffMaps.get(value); dexItem.newOffset = (i + 1) * -1; stringValueOffMaps.put(value, dexItem); } } return this; } public DexTransform markClassDef(Dex baseDex,List<String>classNames) { for (ClassDef classDef:dex.classDefs()) { int typeId = classDef.getTypeIndex(); String value = dex.typeNames().get(typeId); if (classNames.contains(value)) { for (ClassDef baseClassDef : baseDex.classDefs()) { if (baseDex.typeNames().get(baseClassDef.getTypeIndex()).equals(value)) { removeClassDefs.put(typeId, baseClassDef.getClassDataOffset() * -1); } } } } return this; } /** * 移除没有变动的方法的code和debug info信息 * * @param modifyMethods * @return */ public DexTransform removeMethodCodes(HashMap<String, ArrayList<String>> modifyMethods) { assert null != modifyMethods; List<String> fullModifyMethods = new ArrayList<String>(); for (Map.Entry<String, ArrayList<String>> entry : modifyMethods.entrySet()) { String className = entry.getKey(); for (String modifyMethodName : entry.getValue()) { String fullMehtodName = className + "." + modifyMethodName; fullModifyMethods.add(fullMehtodName); } } for (Map.Entry<String, DexItem> entry : methodCodeOffMaps.entrySet()) { String name = entry.getKey(); if (!fullModifyMethods.contains(name)) { DexItem dexItem = entry.getValue(); dexItem.newOffset = 1; methodCodeOffMaps.put(name, dexItem); } } return this; } public DexTransform removeMethodCodes(HashMap<String, ArrayList<String>> modifyMethods,Dex baseDex) { assert null != modifyMethods; List<String> fullModifyMethods = new ArrayList<String>(); for (Map.Entry<String, ArrayList<String>> entry : modifyMethods.entrySet()) { String className = entry.getKey(); for (String modifyMethodName : entry.getValue()) { String fullMehtodName = className + "." + modifyMethodName; fullModifyMethods.add(fullMehtodName); } } Set<String> baseMethods = getMethodMaps(baseDex); for (Map.Entry<String, DexItem> entry : methodCodeOffMaps.entrySet()) { String name = entry.getKey(); if (!fullModifyMethods.contains(name) && baseMethods.contains(name)) { DexItem dexItem = entry.getValue(); dexItem.newOffset = 1; methodCodeOffMaps.put(name, dexItem); } } return this; } /** * 要单独移除debugInfo的类 * * @param removeClasses * @return */ public DexTransform removeDebugInfos(List<String> removeClasses) { assert null != removeClasses; for (Map.Entry<String, DexItem> entry : methodCodeOffMaps.entrySet()) { String name = entry.getKey(); String className = name.substring(0, name.indexOf(".")); if (removeClasses.contains(className)) { debugInfoRemoveMethodss.add(entry.getValue().index); } } return this; } protected Dex transfromDex() throws IOException { transformStringIds(); transformTypeIds(); transformTypeLists(); transformProtoIds(); transformFieldIds(); transformMethodIds(); transformAnnotations(); transformAnnotationSets(dex, indexMap); transformAnnotationSetRefLists(dex, indexMap); transformAnnotationDirectories(dex, indexMap); transformStaticValues(dex, indexMap); // 处理ClassDef的值 transformClassDefs(); // write the header contentsOut.header.off = 0; contentsOut.header.size = 1; contentsOut.fileSize = dexOut.getLength(); contentsOut.computeSizesFromOffsets(); contentsOut.writeHeader(headerOut, dex.getTableOfContents().apiLevel); contentsOut.writeMap(mapListOut); // generate and write the hashes dexOut.writeHashes(); return dexOut; } /** * 处理StringIds的操作 */ private void transformStringIds() { if(onlyShrink){ contentsOut.stringIds.off = idsDefsOut.getPosition(); TableOfContents.Section stringIds = dex.getTableOfContents().stringIds; Dex.Section stringDataSection = dex.open(stringIds.off); for (int i = 0; i < stringIds.size; i++) { int position = stringDataSection.getPosition(); int off = stringDataSection.readInt(); if (off < 0) {// 如果为负数,则表示取下一个dex的值 idsDefsOut.writeInt(off); } else { stringDataSection.getData().position(position); contentsOut.stringDatas.size++; idsDefsOut.writeInt(stringDataOut.getPosition()); stringDataOut.writeStringData(stringDataSection.readString()); } indexMap.stringIds[i] = (short) i; } contentsOut.stringIds.size = stringIds.size; }else { contentsOut.stringIds.off = idsDefsOut.getPosition(); int index = 0; for (Map.Entry<String, DexItem> entry : stringValueOffMaps.entrySet()) { String value = entry.getKey(); DexItem item = entry.getValue(); if (item.newOffset > 0) { contentsOut.stringDatas.size++; idsDefsOut.writeInt(stringDataOut.getPosition()); stringDataOut.writeStringData(value); } else { idsDefsOut.writeInt(item.newOffset); } indexMap.stringIds[index] = (short) index; index++; } contentsOut.stringIds.size = stringValueOffMaps.size(); } } public Dex process() throws IOException { Dex result = transfromDex(); DexTransform dexShrink = new DexTransform(result, new WriterSizes(this),true); result = dexShrink.transfromDex(); return result; } /** * Reads an IDs section of two dex files and writes an IDs section of a * merged dex file. Populates maps from old to new indices in the merge. */ abstract class IdTransform<T extends Comparable<T>> { private final Dex.Section out; protected IdTransform(Dex.Section out){ this.out = out; } public void execute() { TableOfContents.Section dexSection = getSection(dex.getTableOfContents()); int size = dexSection.size; if (dexSection.exists()) { Dex.Section inSection = dex.open(dexSection.off); getSection(contentsOut).off = out.getPosition(); for (int i = 0; i < size; i++) { updateIndex(inSection.getPosition(), indexMap, i, i); T value = read(inSection, indexMap, i); write(value); } } else { getSection(contentsOut).off = 0; } getSection(contentsOut).size = dexSection.size; } abstract TableOfContents.Section getSection(TableOfContents tableOfContents); abstract T read(Dex.Section in, IndexMap indexMap, int index); abstract void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex); abstract void write(T value); } private void transformTypeIds() { new IdTransform<Integer>(idsDefsOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.typeIds; } @Override Integer read(Dex.Section in, IndexMap indexMap, int index) { int stringIndex = in.readInt(); return indexMap.adjustString(stringIndex); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.typeIds[oldIndex] = (short) newIndex; } @Override void write(Integer value) { idsDefsOut.writeInt(value); } }.execute(); } private void transformTypeLists() { new IdTransform<TypeList>(typeListOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.typeLists; } @Override TypeList read(Dex.Section in, IndexMap indexMap, int index) { return indexMap.adjustTypeList(in.readTypeList()); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.putTypeListOffset(offset, typeListOut.getPosition()); } @Override void write(TypeList value) { typeListOut.writeTypeList(value); } }.execute(); } private void transformProtoIds() { new IdTransform<ProtoId>(idsDefsOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.protoIds; } @Override ProtoId read(Dex.Section in, IndexMap indexMap, int index) { return indexMap.adjust(in.readProtoId()); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.protoIds[oldIndex] = (short) newIndex; } @Override void write(ProtoId value) { value.writeTo(idsDefsOut); } }.execute(); } private void transformFieldIds() { new IdTransform<FieldId>(idsDefsOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.fieldIds; } @Override FieldId read(Dex.Section in, IndexMap indexMap, int index) { return indexMap.adjust(in.readFieldId()); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.fieldIds[oldIndex] = (short) newIndex; } @Override void write(FieldId value) { value.writeTo(idsDefsOut); } }.execute(); } private void transformMethodIds() { new IdTransform<MethodId>(idsDefsOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.methodIds; } @Override MethodId read(Dex.Section in, IndexMap indexMap, int index) { return indexMap.adjust(in.readMethodId()); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.methodIds[oldIndex] = (short) newIndex; } @Override void write(MethodId methodId) { methodId.writeTo(idsDefsOut); } }.execute(); } private void transformAnnotations() { new IdTransform<Annotation>(annotationOut) { @Override TableOfContents.Section getSection(TableOfContents tableOfContents) { return tableOfContents.annotations; } @Override Annotation read(Dex.Section in, IndexMap indexMap, int index) { return indexMap.adjust(in.readAnnotation()); } @Override void updateIndex(int offset, IndexMap indexMap, int oldIndex, int newIndex) { indexMap.putAnnotationOffset(offset, annotationOut.getPosition()); } @Override void write(Annotation value) { value.writeTo(annotationOut); } }.execute(); } private void transformClassDefs() { TableOfContents.Section dexSection = dex.getTableOfContents().classDefs; if (dexSection.exists()) { contentsOut.classDefs.off = idsDefsOut.getPosition(); contentsOut.classDefs.size = dexSection.size; // 书写classData for (ClassDef oldClassDef : dex.classDefs()) { ClassDef classDef = indexMap.adjust(oldClassDef); transformClassDef(dex, classDef, indexMap); } } } private void transformAnnotationSets(Dex in, IndexMap indexMap) { TableOfContents.Section section = in.getTableOfContents().annotationSets; if (section.exists()) { Dex.Section setIn = in.open(section.off); for (int i = 0; i < section.size; i++) { transformAnnotationSet(indexMap, setIn); } } } private void transformAnnotationSetRefLists(Dex in, IndexMap indexMap) { TableOfContents.Section section = in.getTableOfContents().annotationSetRefLists; if (section.exists()) { Dex.Section setIn = in.open(section.off); for (int i = 0; i < section.size; i++) { transformAnnotationSetRefList(indexMap, setIn); } } } private void transformAnnotationDirectories(Dex in, IndexMap indexMap) { TableOfContents.Section section = in.getTableOfContents().annotationsDirectories; if (section.exists()) { Dex.Section directoryIn = in.open(section.off); for (int i = 0; i < section.size; i++) { transformAnnotationDirectory(directoryIn, indexMap); } } } private void transformStaticValues(Dex in, IndexMap indexMap) { TableOfContents.Section section = in.getTableOfContents().encodedArrays; if (section.exists()) { Dex.Section staticValuesIn = in.open(section.off); for (int i = 0; i < section.size; i++) { transformStaticValues(staticValuesIn, indexMap); } } } /** * Reads a class_def_item beginning at {@code in} and writes the index and * data. */ private void transformClassDef(Dex in, ClassDef classDef, IndexMap indexMap) { idsDefsOut.assertFourByteAligned(); idsDefsOut.writeInt(classDef.getTypeIndex()); idsDefsOut.writeInt(classDef.getAccessFlags()); idsDefsOut.writeInt(classDef.getSupertypeIndex()); idsDefsOut.writeInt(classDef.getInterfacesOffset()); int sourceFileIndex = indexMap.adjustString(classDef.getSourceFileIndex()); idsDefsOut.writeInt(sourceFileIndex); int annotationsOff = classDef.getAnnotationsOffset(); idsDefsOut.writeInt(indexMap.adjustAnnotationDirectory(annotationsOff)); int classDataOff = classDef.getClassDataOffset(); if (removeClassDefs.containsKey(classDef.getTypeIndex())&&!onlyShrink) { idsDefsOut.writeInt(removeClassDefs.get(classDef.getTypeIndex())); }else if (onlyShrink && classDataOff < 0){ idsDefsOut.writeInt(classDataOff); }else if (classDataOff == 0) { idsDefsOut.writeInt(0); } else { idsDefsOut.writeInt(classDataOut.getPosition()); ClassData classData = in.readClassData(classDef); transformClassData(in, classData, indexMap); } int staticValuesOff = classDef.getStaticValuesOffset(); idsDefsOut.writeInt(indexMap.adjustStaticValues(staticValuesOff)); } /** * Transform all annotations on a class. */ private void transformAnnotationDirectory(Dex.Section directoryIn, IndexMap indexMap) { contentsOut.annotationsDirectories.size++; annotationsDirectoryOut.assertFourByteAligned(); indexMap.putAnnotationDirectoryOffset(directoryIn.getPosition(), annotationsDirectoryOut.getPosition()); int classAnnotationsOffset = indexMap.adjustAnnotationSet(directoryIn.readInt()); annotationsDirectoryOut.writeInt(classAnnotationsOffset); int fieldsSize = directoryIn.readInt(); annotationsDirectoryOut.writeInt(fieldsSize); int methodsSize = directoryIn.readInt(); annotationsDirectoryOut.writeInt(methodsSize); int parameterListSize = directoryIn.readInt(); annotationsDirectoryOut.writeInt(parameterListSize); for (int i = 0; i < fieldsSize; i++) { // field index annotationsDirectoryOut.writeInt(indexMap.adjustField(directoryIn.readInt())); // annotations offset annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt())); } for (int i = 0; i < methodsSize; i++) { // method index annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); // annotation set offset annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSet(directoryIn.readInt())); } for (int i = 0; i < parameterListSize; i++) { // method index annotationsDirectoryOut.writeInt(indexMap.adjustMethod(directoryIn.readInt())); // annotations offset annotationsDirectoryOut.writeInt(indexMap.adjustAnnotationSetRefList(directoryIn.readInt())); } } /** * Transform all annotations on a single type, member or parameter. */ private void transformAnnotationSet(IndexMap indexMap, Dex.Section setIn) { contentsOut.annotationSets.size++; annotationSetOut.assertFourByteAligned(); indexMap.putAnnotationSetOffset(setIn.getPosition(), annotationSetOut.getPosition()); int size = setIn.readInt(); annotationSetOut.writeInt(size); for (int j = 0; j < size; j++) { annotationSetOut.writeInt(indexMap.adjustAnnotation(setIn.readInt())); } } /** * Transform all annotation set ref lists. */ private void transformAnnotationSetRefList(IndexMap indexMap, Dex.Section refListIn) { contentsOut.annotationSetRefLists.size++; annotationSetRefListOut.assertFourByteAligned(); indexMap.putAnnotationSetRefListOffset(refListIn.getPosition(), annotationSetRefListOut.getPosition()); int parameterCount = refListIn.readInt(); annotationSetRefListOut.writeInt(parameterCount); for (int p = 0; p < parameterCount; p++) { annotationSetRefListOut.writeInt(indexMap.adjustAnnotationSet(refListIn.readInt())); } } private void transformClassData(Dex in, ClassData classData, IndexMap indexMap) { contentsOut.classDatas.size++; ClassData.Field[] staticFields = classData.getStaticFields(); ClassData.Field[] instanceFields = classData.getInstanceFields(); ClassData.Method[] directMethods = classData.getDirectMethods(); ClassData.Method[] virtualMethods = classData.getVirtualMethods(); classDataOut.writeUleb128(staticFields.length); classDataOut.writeUleb128(instanceFields.length); classDataOut.writeUleb128(directMethods.length); classDataOut.writeUleb128(virtualMethods.length); transformFields(indexMap, staticFields); transformFields(indexMap, instanceFields); transformMethods(in, indexMap, directMethods); transformMethods(in, indexMap, virtualMethods); } private void transformFields(IndexMap indexMap, ClassData.Field[] fields) { int lastOutFieldIndex = 0; for (ClassData.Field field : fields) { int outFieldIndex = indexMap.adjustField(field.getFieldIndex()); classDataOut.writeUleb128(outFieldIndex - lastOutFieldIndex); lastOutFieldIndex = outFieldIndex; classDataOut.writeUleb128(field.getAccessFlags()); } } private void transformMethods(Dex in, IndexMap indexMap, ClassData.Method[] methods) { int lastOutMethodIndex = 0; for (ClassData.Method method : methods) { int outMethodIndex = indexMap.adjustMethod(method.getMethodIndex()); classDataOut.writeUleb128(outMethodIndex - lastOutMethodIndex); lastOutMethodIndex = outMethodIndex; classDataOut.writeUleb128(method.getAccessFlags()); if(onlyShrink) { if (method.getCodeOffset() <= 1) { classDataOut.writeUleb128(method.getCodeOffset()); } else { codeOut.alignToFourBytesWithZeroFill(); classDataOut.writeUleb128(codeOut.getPosition()); transformCode(in, in.readCode(method), indexMap,method.getMethodIndex()); } }else{ MethodId methodId = dex.methodIds().get(method.getMethodIndex()); String className = dex.typeNames().get(methodId.getDeclaringClassIndex()); String methodName = dex.strings().get(methodId.getNameIndex()) + dex.readTypeList(dex.protoIds().get(methodId.getProtoIndex()).getParametersOffset()); String fullMethodName = className + "." + methodName; DexItem dexItem = methodCodeOffMaps.get(fullMethodName); if (dexItem.newOffset <= 1) { classDataOut.writeUleb128(dexItem.newOffset); } else { codeOut.alignToFourBytesWithZeroFill(); classDataOut.writeUleb128(codeOut.getPosition()); transformCode(in, in.readCode(method), indexMap, method.getMethodIndex()); } } } } private void transformCode(Dex in, Code code, IndexMap indexMap, int methodIndex) { contentsOut.codes.size++; codeOut.assertFourByteAligned(); codeOut.writeUnsignedShort(code.getRegistersSize()); codeOut.writeUnsignedShort(code.getInsSize()); codeOut.writeUnsignedShort(code.getOutsSize()); Code.Try[] tries = code.getTries(); Code.CatchHandler[] catchHandlers = code.getCatchHandlers(); codeOut.writeUnsignedShort(tries.length); int debugInfoOffset = code.getDebugInfoOffset(); // 判断是否移除debugInfo if (debugInfoRemoveMethodss.contains(methodIndex) || debugInfoOffset <= 0) { codeOut.writeInt(0); } else { codeOut.writeInt(debugInfoOut.getPosition()); transformDebugInfoItem(in.open(debugInfoOffset), indexMap); } short[] instructions = code.getInstructions(); short[] newInstructions = instructionTransformer.transform(indexMap, instructions); codeOut.writeInt(newInstructions.length); codeOut.write(newInstructions); if (tries.length > 0) { if (newInstructions.length % 2 == 1) { codeOut.writeShort((short) 0); // padding } /* * We can't write the tries until we've written the catch handlers. * Unfortunately they're in the opposite order in the dex file so we * need to transform them out-of-order. */ Dex.Section triesSection = dexOut.open(codeOut.getPosition()); codeOut.skip(tries.length * SizeOf.TRY_ITEM); int[] offsets = transformCatchHandlers(indexMap, catchHandlers); transformTries(triesSection, tries, offsets); } } /** * Writes the catch handlers to {@code codeOut} and returns their indices. */ private int[] transformCatchHandlers(IndexMap indexMap, Code.CatchHandler[] catchHandlers) { int baseOffset = codeOut.getPosition(); codeOut.writeUleb128(catchHandlers.length); int[] offsets = new int[catchHandlers.length]; for (int i = 0; i < catchHandlers.length; i++) { offsets[i] = codeOut.getPosition() - baseOffset; transformEncodedCatchHandler(catchHandlers[i], indexMap); } return offsets; } private void transformTries(Dex.Section out, Code.Try[] tries, int[] catchHandlerOffsets) { for (Code.Try tryItem : tries) { out.writeInt(tryItem.getStartAddress()); out.writeUnsignedShort(tryItem.getInstructionCount()); out.writeUnsignedShort(catchHandlerOffsets[tryItem.getCatchHandlerIndex()]); } } private static final byte DBG_END_SEQUENCE = 0x00; private static final byte DBG_ADVANCE_PC = 0x01; private static final byte DBG_ADVANCE_LINE = 0x02; private static final byte DBG_START_LOCAL = 0x03; private static final byte DBG_START_LOCAL_EXTENDED = 0x04; private static final byte DBG_END_LOCAL = 0x05; private static final byte DBG_RESTART_LOCAL = 0x06; private static final byte DBG_SET_PROLOGUE_END = 0x07; private static final byte DBG_SET_EPILOGUE_BEGIN = 0x08; private static final byte DBG_SET_FILE = 0x09; private void transformDebugInfoItem(Dex.Section in, IndexMap indexMap) { contentsOut.debugInfos.size++; int lineStart = in.readUleb128(); debugInfoOut.writeUleb128(lineStart); int parametersSize = in.readUleb128(); debugInfoOut.writeUleb128(parametersSize); for (int p = 0; p < parametersSize; p++) { int parameterName = in.readUleb128p1(); debugInfoOut.writeUleb128p1(indexMap.adjustString(parameterName)); } int addrDiff; // uleb128 address delta. int lineDiff; // sleb128 line delta. int registerNum; // uleb128 register number. int nameIndex; // uleb128p1 string index. Needs indexMap adjustment. int typeIndex; // uleb128p1 type index. Needs indexMap adjustment. int sigIndex; // uleb128p1 string index. Needs indexMap adjustment. while (true) { int opcode = in.readByte(); debugInfoOut.writeByte(opcode); switch (opcode) { case DBG_END_SEQUENCE: return; case DBG_ADVANCE_PC: addrDiff = in.readUleb128(); debugInfoOut.writeUleb128(addrDiff); break; case DBG_ADVANCE_LINE: lineDiff = in.readSleb128(); debugInfoOut.writeSleb128(lineDiff); break; case DBG_START_LOCAL: case DBG_START_LOCAL_EXTENDED: registerNum = in.readUleb128(); debugInfoOut.writeUleb128(registerNum); nameIndex = in.readUleb128p1(); debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); typeIndex = in.readUleb128p1(); debugInfoOut.writeUleb128p1(indexMap.adjustType(typeIndex)); if (opcode == DBG_START_LOCAL_EXTENDED) { sigIndex = in.readUleb128p1(); debugInfoOut.writeUleb128p1(indexMap.adjustString(sigIndex)); } break; case DBG_END_LOCAL: case DBG_RESTART_LOCAL: registerNum = in.readUleb128(); debugInfoOut.writeUleb128(registerNum); break; case DBG_SET_FILE: nameIndex = in.readUleb128p1(); debugInfoOut.writeUleb128p1(indexMap.adjustString(nameIndex)); break; case DBG_SET_PROLOGUE_END: case DBG_SET_EPILOGUE_BEGIN: default: break; } } } private void transformEncodedCatchHandler(Code.CatchHandler catchHandler, IndexMap indexMap) { int catchAllAddress = catchHandler.getCatchAllAddress(); int[] typeIndexes = catchHandler.getTypeIndexes(); int[] addresses = catchHandler.getAddresses(); if (catchAllAddress != -1) { codeOut.writeSleb128(-typeIndexes.length); } else { codeOut.writeSleb128(typeIndexes.length); } for (int i = 0; i < typeIndexes.length; i++) { codeOut.writeUleb128(indexMap.adjustType(typeIndexes[i])); codeOut.writeUleb128(addresses[i]); } if (catchAllAddress != -1) { codeOut.writeUleb128(catchAllAddress); } } private void transformStaticValues(Dex.Section in, IndexMap indexMap) { contentsOut.encodedArrays.size++; indexMap.putStaticValuesOffset(in.getPosition(), encodedArrayOut.getPosition()); indexMap.adjustEncodedArray(in.readEncodedArray()).writeTo(encodedArrayOut); } /** * Byte counts for the sections written when creating a dex. Target sizes * are defined in one of two ways: * <ul> * <li>By pessimistically guessing how large the union of dex files will be. * We're pessimistic because we can't predict the amount of duplication * between dex files, nor can we predict the length of ULEB-encoded * offsets or indices. * <li>By exactly measuring an existing dex. * </ul> */ private static class WriterSizes { private int header = SizeOf.HEADER_ITEM; private int idsDefs; private int mapList; private int typeList; private int classData; private int code; private int stringData; private int debugInfo; private int encodedArray; private int annotationsDirectory; private int annotationsSet; private int annotationsSetRefList; private int annotation; /** * Compute sizes for merging several dexes. */ public WriterSizes(Dex dex, boolean exact){ plus(dex.getTableOfContents(), exact); fourByteAlign(); } public WriterSizes(DexTransform dexTransform){ header = dexTransform.headerOut.used(); idsDefs = dexTransform.idsDefsOut.used(); mapList = dexTransform.mapListOut.used(); typeList = dexTransform.typeListOut.used(); classData = dexTransform.classDataOut.used(); code = dexTransform.codeOut.used(); stringData = dexTransform.stringDataOut.used(); debugInfo = dexTransform.debugInfoOut.used(); encodedArray = dexTransform.encodedArrayOut.used(); annotationsDirectory = dexTransform.annotationsDirectoryOut.used(); annotationsSet = dexTransform.annotationSetOut.used(); annotationsSetRefList = dexTransform.annotationSetRefListOut.used(); annotation = dexTransform.annotationOut.used(); fourByteAlign(); } private void plus(TableOfContents contents, boolean exact) { idsDefs += contents.stringIds.size * SizeOf.STRING_ID_ITEM + contents.typeIds.size * SizeOf.TYPE_ID_ITEM + contents.protoIds.size * SizeOf.PROTO_ID_ITEM + contents.fieldIds.size * SizeOf.MEMBER_ID_ITEM + contents.methodIds.size * SizeOf.MEMBER_ID_ITEM + contents.classDefs.size * SizeOf.CLASS_DEF_ITEM; mapList = SizeOf.UINT + (contents.sections.length * SizeOf.MAP_ITEM); typeList += fourByteAlign(contents.typeLists.byteCount); // We count each dex's // typelists section as realigned on 4 bytes, because each typelist of each dex's // typelists section is aligned on 4 bytes. If we didn't, there is a case where each // size of both dex's typelists section is a multiple of 2 but not a multiple of 4, // and the sum of both sizes is a multiple of 4 but would not be sufficient to write // each typelist aligned on 4 bytes. stringData += contents.stringDatas.byteCount; annotationsDirectory += contents.annotationsDirectories.byteCount; annotationsSet += contents.annotationSets.byteCount; annotationsSetRefList += contents.annotationSetRefLists.byteCount; if (exact) { code += contents.codes.byteCount; classData += contents.classDatas.byteCount; encodedArray += contents.encodedArrays.byteCount; annotation += contents.annotations.byteCount; debugInfo += contents.debugInfos.byteCount; } else { // at most 1/4 of the bytes in a code section are uleb/sleb code += (int) Math.ceil(contents.codes.byteCount * 1.25); // at most 1/3 of the bytes in a class data section are uleb/sleb classData += (int) Math.ceil(contents.classDatas.byteCount * 1.34); // all of the bytes in an encoding arrays section may be uleb/sleb encodedArray += contents.encodedArrays.byteCount * 2; // all of the bytes in an annotations section may be uleb/sleb annotation += (int) Math.ceil(contents.annotations.byteCount * 2); // all of the bytes in a debug info section may be uleb/sleb debugInfo += contents.debugInfos.byteCount * 2; } } private void fourByteAlign() { header = fourByteAlign(header); idsDefs = fourByteAlign(idsDefs); mapList = fourByteAlign(mapList); typeList = fourByteAlign(typeList); classData = fourByteAlign(classData); code = fourByteAlign(code); stringData = fourByteAlign(stringData); debugInfo = fourByteAlign(debugInfo); encodedArray = fourByteAlign(encodedArray); annotationsDirectory = fourByteAlign(annotationsDirectory); annotationsSet = fourByteAlign(annotationsSet); annotationsSetRefList = fourByteAlign(annotationsSetRefList); annotation = fourByteAlign(annotation); } private static int fourByteAlign(int position) { return (position + 3) & ~3; } public int size() { return header + idsDefs + mapList + typeList + classData + code + stringData + debugInfo + encodedArray + annotationsDirectory + annotationsSet + annotationsSetRefList + annotation; } } class DexItem { int offset; int index; int newOffset; public DexItem(int offset, int index, int newOffset){ this.offset = offset; this.index = index; this.newOffset = newOffset; } } }