use of org.jf.dexlib2 in project tinker by Tencent.
the class DexDiffDecoder method generateChangedClassesDexFile.
@SuppressWarnings("NewApi")
private void generateChangedClassesDexFile() throws IOException {
final String dexMode = config.mDexRaw ? "raw" : "jar";
List<File> oldDexList = new ArrayList<>();
List<File> newDexList = new ArrayList<>();
for (AbstractMap.SimpleEntry<File, File> oldAndNewDexFilePair : oldAndNewDexFilePairList) {
File oldDexFile = oldAndNewDexFilePair.getKey();
File newDexFile = oldAndNewDexFilePair.getValue();
if (oldDexFile != null) {
oldDexList.add(oldDexFile);
}
if (newDexFile != null) {
newDexList.add(newDexFile);
}
}
DexGroup oldDexGroup = DexGroup.wrap(oldDexList);
DexGroup newDexGroup = DexGroup.wrap(newDexList);
ChangedClassesDexClassInfoCollector collector = new ChangedClassesDexClassInfoCollector();
collector.setExcludedClassPatterns(config.mDexLoaderPattern);
collector.setLogger(dexPatcherLoggerBridge);
collector.setIncludeRefererToRefererAffectedClasses(true);
Set<DexClassInfo> classInfosInChangedClassesDex = collector.doCollect(oldDexGroup, newDexGroup);
Set<Dex> owners = new HashSet<>();
Map<Dex, Set<String>> ownerToDescOfChangedClassesMap = new HashMap<>();
for (DexClassInfo classInfo : classInfosInChangedClassesDex) {
owners.add(classInfo.owner);
Set<String> descOfChangedClasses = ownerToDescOfChangedClassesMap.get(classInfo.owner);
if (descOfChangedClasses == null) {
descOfChangedClasses = new HashSet<>();
ownerToDescOfChangedClassesMap.put(classInfo.owner, descOfChangedClasses);
}
descOfChangedClasses.add(classInfo.classDesc);
}
StringBuilder metaBuilder = new StringBuilder();
int changedDexId = 1;
for (Dex dex : owners) {
Set<String> descOfChangedClassesInCurrDex = ownerToDescOfChangedClassesMap.get(dex);
DexFile dexFile = new DexBackedDexFile(org.jf.dexlib2.Opcodes.forApi(20), dex.getBytes());
boolean isCurrentDexHasChangedClass = false;
for (org.jf.dexlib2.iface.ClassDef classDef : dexFile.getClasses()) {
if (descOfChangedClassesInCurrDex.contains(classDef.getType())) {
isCurrentDexHasChangedClass = true;
break;
}
}
if (!isCurrentDexHasChangedClass) {
continue;
}
DexBuilder dexBuilder = new DexBuilder(Opcodes.forApi(23));
for (org.jf.dexlib2.iface.ClassDef classDef : dexFile.getClasses()) {
if (!descOfChangedClassesInCurrDex.contains(classDef.getType())) {
continue;
}
Logger.d("Class %s will be added into changed classes dex ...", classDef.getType());
List<BuilderField> builderFields = new ArrayList<>();
for (Field field : classDef.getFields()) {
final BuilderField builderField = dexBuilder.internField(field.getDefiningClass(), field.getName(), field.getType(), field.getAccessFlags(), field.getInitialValue(), field.getAnnotations());
builderFields.add(builderField);
}
List<BuilderMethod> builderMethods = new ArrayList<>();
for (Method method : classDef.getMethods()) {
MethodImplementation methodImpl = method.getImplementation();
if (methodImpl != null) {
methodImpl = new BuilderMutableMethodImplementation(dexBuilder, methodImpl);
}
BuilderMethod builderMethod = dexBuilder.internMethod(method.getDefiningClass(), method.getName(), method.getParameters(), method.getReturnType(), method.getAccessFlags(), method.getAnnotations(), methodImpl);
builderMethods.add(builderMethod);
}
dexBuilder.internClassDef(classDef.getType(), classDef.getAccessFlags(), classDef.getSuperclass(), classDef.getInterfaces(), classDef.getSourceFile(), classDef.getAnnotations(), builderFields, builderMethods);
}
// Write constructed changed classes dex to file and record it in meta file.
String changedDexName = null;
if (changedDexId == 1) {
changedDexName = "classes.dex";
} else {
changedDexName = "classes" + changedDexId + ".dex";
}
final File dest = new File(config.mTempResultDir + "/" + changedDexName);
final FileDataStore fileDataStore = new FileDataStore(dest);
dexBuilder.writeTo(fileDataStore);
final String md5 = MD5.getMD5(dest);
appendMetaLine(metaBuilder, changedDexName, "", md5, md5, 0, 0, 0, dexMode);
++changedDexId;
}
final String meta = metaBuilder.toString();
Logger.d("\nDexDecoder:write changed classes dex meta file data:\n%s", meta);
metaWriter.writeLineToInfoFile(meta);
}
use of org.jf.dexlib2 in project tinker by Tencent.
the class DexDiffDecoder method collectClassesInDex.
private void collectClassesInDex(File dexFile) throws IOException {
Logger.d("Collect class descriptors in " + dexFile.getName());
final DexFile dex = DexFileFactory.loadDexFile(dexFile, Opcodes.forApi(29));
for (org.jf.dexlib2.iface.ClassDef classDef : dex.getClasses()) {
descOfClassesInApk.add(classDef.getType());
if (AccessFlags.SYNTHETIC.isSet(classDef.getAccessFlags())) {
descOfSyntheticClassesInApk.add(classDef.getType());
}
}
}
use of org.jf.dexlib2 in project tinker by Tencent.
the class DexDiffDecoder method checkIfLoaderClassesReferToNonLoaderClasses.
private void checkIfLoaderClassesReferToNonLoaderClasses() throws IOException, TinkerPatchException {
boolean hasInvalidCases = false;
for (File dexFile : oldDexFiles) {
Logger.d("Check if loader classes in " + dexFile.getName() + " refer to any classes that is not in loader class patterns.");
final DexFile dex = DexFileFactory.loadDexFile(dexFile, Opcodes.forApi(29));
for (org.jf.dexlib2.iface.ClassDef classDef : dex.getClasses()) {
final String currClassDesc = classDef.getType();
if (!Utils.isStringMatchesPatterns(currClassDesc, loaderClassPatterns)) {
continue;
}
for (Field field : classDef.getFields()) {
final String currFieldTypeDesc = field.getType();
if (!isReferenceFromLoaderClassValid(currFieldTypeDesc)) {
Logger.e("FATAL: field '%s' in loader class '%s' refers to class '%s' which " + "is not loader class, this may cause crash when patch is loaded.", field.getName(), currClassDesc, currFieldTypeDesc);
hasInvalidCases = true;
}
}
for (Method method : classDef.getMethods()) {
boolean isCurrentMethodInvalid = false;
final String currMethodRetTypeDesc = method.getReturnType();
if (!isReferenceFromLoaderClassValid(currMethodRetTypeDesc)) {
Logger.e("FATAL: method '%s:%s' in loader class '%s' refers to class '%s' which " + "is not loader class, this may cause crash when patch is loaded.", method.getName(), MethodUtil.getShorty(method.getParameterTypes(), currMethodRetTypeDesc), currClassDesc, currMethodRetTypeDesc);
isCurrentMethodInvalid = true;
} else {
for (CharSequence paramTypeDesc : method.getParameterTypes()) {
if (!isReferenceFromLoaderClassValid(paramTypeDesc.toString())) {
Logger.e("FATAL: method '%s:%s' in loader class '%s' refers to class '%s' which " + "is not loader class, this may cause crash when patch is loaded.", method.getName(), MethodUtil.getShorty(method.getParameterTypes(), currMethodRetTypeDesc), currClassDesc, paramTypeDesc);
isCurrentMethodInvalid = true;
break;
}
}
}
check_method_impl: {
final MethodImplementation methodImpl = method.getImplementation();
if (methodImpl == null) {
break check_method_impl;
}
final Iterable<? extends Instruction> insns = methodImpl.getInstructions();
if (!insns.iterator().hasNext()) {
break check_method_impl;
}
for (Instruction insn : insns) {
if (insn instanceof ReferenceInstruction) {
final ReferenceInstruction refInsn = (ReferenceInstruction) insn;
switch(refInsn.getReferenceType()) {
case ReferenceType.TYPE:
{
final TypeReference typeRefInsn = (TypeReference) refInsn.getReference();
final String refereeTypeDesc = typeRefInsn.getType();
if (isReferenceFromLoaderClassValid(refereeTypeDesc)) {
break;
}
Logger.e("FATAL: method '%s:%s' in loader class '%s' refers to class '%s' which " + "is not loader class, this may cause crash when patch is loaded.", method.getName(), MethodUtil.getShorty(method.getParameterTypes(), currMethodRetTypeDesc), currClassDesc, refereeTypeDesc);
isCurrentMethodInvalid = true;
break;
}
case ReferenceType.FIELD:
{
final FieldReference fieldRefInsn = (FieldReference) refInsn.getReference();
final String refereeFieldName = fieldRefInsn.getName();
final String refereeFieldDefTypeDesc = fieldRefInsn.getDefiningClass();
if (isReferenceFromLoaderClassValid(refereeFieldDefTypeDesc)) {
break;
}
Logger.e("FATAL: method '%s:%s' in loader class '%s' refers to field '%s' in class '%s' which " + "is not in loader class, this may cause crash when patch is loaded.", method.getName(), MethodUtil.getShorty(method.getParameterTypes(), currMethodRetTypeDesc), currClassDesc, refereeFieldName, refereeFieldDefTypeDesc);
isCurrentMethodInvalid = true;
break;
}
case ReferenceType.METHOD:
{
final MethodReference methodRefInsn = (MethodReference) refInsn.getReference();
final String refereeMethodName = methodRefInsn.getName();
final Collection<? extends CharSequence> refereeMethodParamTypes = methodRefInsn.getParameterTypes();
final String refereeMethodRetType = methodRefInsn.getReturnType();
final String refereeMethodDefClassDesc = methodRefInsn.getDefiningClass();
if (isReferenceFromLoaderClassValid(refereeMethodDefClassDesc)) {
break;
}
Logger.e("FATAL: method '%s:%s' in loader class '%s' refers to method '%s:%s' in class '%s' which " + "is not in loader class, this may cause crash when patch is loaded.", method.getName(), MethodUtil.getShorty(method.getParameterTypes(), currMethodRetTypeDesc), currClassDesc, refereeMethodName, MethodUtil.getShorty(refereeMethodParamTypes, refereeMethodRetType), refereeMethodDefClassDesc);
isCurrentMethodInvalid = true;
break;
}
default:
{
break;
}
}
}
}
}
if (isCurrentMethodInvalid) {
hasInvalidCases = true;
}
}
}
}
if (hasInvalidCases) {
throw new TinkerPatchException("There are fatal reasons that cause Tinker interrupt" + " patch generating procedure, see logs above.");
}
}
Aggregations