/* * dex2jar - Tools to work with android .dex and java .class files * Copyright (c) 2009-2013 Panxiaobo * * 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. */ package com.googlecode.d2j.map; import com.googlecode.d2j.*; import com.googlecode.d2j.node.DexAnnotationNode; import com.googlecode.d2j.reader.Op; import com.googlecode.d2j.util.Mapper; import com.googlecode.d2j.util.Types; import com.googlecode.d2j.visitors.*; import org.objectweb.asm.signature.SignatureReader; import org.objectweb.asm.signature.SignatureWriter; public class DexMappingAdapter extends DexFileVisitor { protected Mapper mapper; public DexMappingAdapter(Mapper mapper, DexFileVisitor visitor) { super(visitor); this.mapper = mapper; } static String mapClassName(Mapper map, String oldDesc) { if (oldDesc == null) { return null; } int count = 0; while (count < oldDesc.length() && oldDesc.charAt(count) == '[') { count++; } if (count + 1 == oldDesc.length()) {// simple type return oldDesc; } if (count > 0) { String n = map.mapClassName(oldDesc.substring(count)); if (n == null) { return oldDesc; } return oldDesc.substring(0, count) + n; } else { String n = map.mapClassName(oldDesc); if (n == null) { return oldDesc; } return n; } } static String mapAnnotationElementName(Mapper map, String owner, String name) { String n = map.mapMethodName(owner, name, null, null); if (n == null) { return name; } return n; } static String[] mapClassNames(Mapper map, String[] oldDescs) { if (oldDescs != null && oldDescs.length > 0) { String copy[] = new String[oldDescs.length]; for (int i = 0; i < oldDescs.length; i++) { copy[i] = mapClassName(map, oldDescs[i]); } return copy; } else { return oldDescs; } } static String mapFieldName(Mapper map, String owner, String name, String type) { String n = map.mapFieldName(owner, name, type); if (n == null) { return name; } return n; } static Field mapField(Mapper map, Field f) { String n = map.mapFieldName(f.getOwner(), f.getName(), f.getType()); if (n == null) { n = f.getName(); } return new Field(mapClassName(map, f.getOwner()), n, mapClassName(map, f.getType())); } static Field mapFieldNameAndOwner(Mapper map, Field f) { String n = map.mapFieldName(f.getOwner(), f.getName(), f.getType()); if (n == null) { n = f.getName(); } return new Field(mapFieldOwner(map, f.getOwner(), f.getName(), f.getType()), n, mapClassName(map, f.getType())); } private static String mapFieldOwner(Mapper map, String owner, String name, String type) { String n = map.mapFieldOwner(owner, name, type); if (n == null) { return owner; } return n; } private static Method mapMethod(Mapper map, Method m) { String n = map.mapMethodName(m.getOwner(), m.getName(), m.getParameterTypes(), m.getReturnType()); if (n == null) { n = m.getName(); } return new Method(mapClassName(map, m.getOwner()), n, mapClassNames(map, m.getParameterTypes()), mapClassName( map, m.getReturnType())); } private static Method mapMethodNameAndOwner(Mapper map, Method m) { String n = map.mapMethodName(m.getOwner(), m.getName(), m.getParameterTypes(), m.getReturnType()); if (n == null) { n = m.getName(); } String owner = map.mapMethodOwner(m.getOwner(), m.getName(), m.getParameterTypes(), m.getReturnType()); if (owner == null) { owner = m.getOwner(); } return new Method(owner, n, mapClassNames(map, m.getParameterTypes()), mapClassName(map, m.getReturnType())); } static Object mapObject(Mapper map, Object value) { if (value instanceof DexType) { value = new DexType(mapClassName(map, ((DexType) value).desc)); } else if (value instanceof Field) { Field f = (Field) value; value = mapField(map, f); } else if (value instanceof Method) { Method m = (Method) value; value = mapMethod(map, m); } return value; } @Override public DexClassVisitor visit(int access_flags, final String className, String superClass, String[] interfaceNames) { DexClassVisitor dcv = super.visit(access_flags, mapClassName(mapper, className), mapClassName(mapper, superClass), mapClassNames(mapper, interfaceNames)); if (dcv != null) { return new MappingCV(mapper, className, dcv); } return dcv; } public static class MappingAV extends DexAnnotationVisitor { protected Mapper mapper; protected String oldAnnotationClassName; public MappingAV(String oldAnnotationClassName, Mapper mapper, DexAnnotationVisitor visitor) { super(visitor); this.mapper = mapper; this.oldAnnotationClassName = oldAnnotationClassName; } @Override public void visit(String name, Object value) { if (name != null && oldAnnotationClassName != null) { name = mapAnnotationElementName(mapper, oldAnnotationClassName, name); } super.visit(name, mapObject(mapper, value)); } @Override public DexAnnotationVisitor visitAnnotation(String name, String desc) { if (name != null && oldAnnotationClassName != null) { name = mapAnnotationElementName(mapper, oldAnnotationClassName, name); } DexAnnotationVisitor dav = super.visitAnnotation(name, mapClassName(mapper, desc)); if (dav != null) { return new MappingAV(desc, mapper, dav); } return dav; } @Override public DexAnnotationVisitor visitArray(String name) { if (name != null && oldAnnotationClassName != null) { name = mapAnnotationElementName(mapper, oldAnnotationClassName, name); } DexAnnotationVisitor dav = super.visitArray(name); if (dav != null) { return new MappingAV(null, mapper, dav); } return dav; } @Override public void visitEnum(String name, String desc, String value) { String fn = mapFieldName(mapper, desc, value, desc); if (name != null && oldAnnotationClassName != null) { name = mapAnnotationElementName(mapper, oldAnnotationClassName, name); } super.visitEnum(name, mapClassName(mapper, desc), fn); } } public static class MappingCode extends DexCodeVisitor { protected Mapper mapper; public MappingCode(Mapper mapper, DexCodeVisitor dcv) { super(dcv); this.mapper = mapper; } @Override public DexDebugVisitor visitDebug() { DexDebugVisitor v = super.visitDebug(); if (v != null) { return new DexDebugVisitor(v) { @Override public void visitStartLocal(int reg, DexLabel label, String name, String type, String signature) { if (signature != null) { signature = remapSignature(mapper, signature, true); } super.visitStartLocal(reg, label, name, mapClassName(mapper, type), signature); } }; } return v; } @Override public void visitConstStmt(Op op, int ra, Object value) { super.visitConstStmt(op, ra, mapObject(mapper, value)); } @Override public void visitFieldStmt(Op op, int a, int b, Field field) { super.visitFieldStmt(op, a, b, mapFieldNameAndOwner(mapper, field)); } @Override public void visitFilledNewArrayStmt(Op op, int[] args, String type) { super.visitFilledNewArrayStmt(op, args, mapClassName(mapper, type)); } @Override public void visitMethodStmt(Op op, int[] args, Method method) { switch (op) { case INVOKE_DIRECT: case INVOKE_DIRECT_RANGE: case INVOKE_STATIC: case INVOKE_STATIC_RANGE: case INVOKE_INTERFACE: case INVOKE_INTERFACE_RANGE: super.visitMethodStmt(op, args, mapMethodNameAndOwner(mapper, method)); break; default: super.visitMethodStmt(op, args, mapMethod(mapper, method)); break; } } @Override public void visitTryCatch(DexLabel start, DexLabel end, DexLabel[] handler, String[] type) { super.visitTryCatch(start, end, handler, mapClassNames(mapper, type)); } @Override public void visitTypeStmt(Op op, int a, int b, String type) { super.visitTypeStmt(op, a, b, mapClassName(mapper, type)); } } public static class MappingCV extends DexClassVisitor { protected String clzName; protected Mapper mapper; public MappingCV(Mapper mapper, String clzName, DexClassVisitor dcv) { super(dcv); this.mapper = mapper; this.clzName = clzName; } @Override public DexAnnotationVisitor visitAnnotation(String name, Visibility visibility) { final DexAnnotationVisitor dav = super.visitAnnotation(mapClassName(mapper, name), visibility); if (dav != null) { switch (name) { case DexConstants.ANNOTATION_SIGNATURE_TYPE: return new DexAnnotationNode(name, visibility) { @Override public void visitEnd() { super.visitEnd(); Item p = super.items.get(0); Object[] newVs = remapSignature(mapper, (Object[]) p.value, false); dav.visit(p.name, newVs); dav.visitEnd(); } }; case DexConstants.ANNOTATION_INNER_CLASS_TYPE: return new DexAnnotationVisitor(dav) { @Override public void visit(String name, Object value) { if (name.equals("name")) { String simpleName = (String) value; if (simpleName != null) { if (clzName.endsWith("$" + simpleName + ";")) { String nNameDesc = mapClassName(mapper, clzName); String containd = mapClassName(mapper, clzName.substring(0, clzName.length() - 2 - simpleName.length()) + ";"); String internalNameWitherOwner = nNameDesc.substring(1, nNameDesc.length() - 1); String internalNameOwner = containd.substring(1, containd.length() - 1); if (internalNameWitherOwner.startsWith(internalNameOwner + "$")) { value = internalNameWitherOwner.substring(1 + internalNameOwner.length()); } else { value = null; } } else { value = null; } } } super.visit(name, value); } }; case DexConstants.ANNOTATION_ENCLOSING_CLASS_TYPE: return new DexAnnotationVisitor(dav) { @Override public void visit(String name, Object value) { if (name.equals("value")) { super.visit(name, new DexType(mapClassName(mapper, ((DexType) value).desc))); } else { super.visit(name, value); } } }; case DexConstants.ANNOTATION_ENCLOSING_METHOD_TYPE: { return new DexAnnotationVisitor(dav) { @Override public void visit(String name, Object value) { if (name.equals("value")) { Method m = (Method) value; super.visit(name, mapMethod(mapper, m)); } else { super.visit(name, value); } } }; } default: return new MappingAV(name, mapper, dav); } } return dav; } @Override public DexFieldVisitor visitField(int accessFlags, Field field, Object value) { DexFieldVisitor dfv = super.visitField(accessFlags, mapField(mapper, field), mapObject(mapper, value)); if (dfv != null) { return new MappingFV(mapper, dfv); } return null; } @Override public DexMethodVisitor visitMethod(int accessFlags, Method method) { DexMethodVisitor dmv = super.visitMethod(accessFlags, mapMethod(mapper, method)); if (dmv != null) { return new MappingMV(mapper, dmv); } return dmv; } } public static class MappingFV extends DexFieldVisitor { protected Mapper mapper; public MappingFV(Mapper mapper, DexFieldVisitor dfv) { super(dfv); this.mapper = mapper; } @Override public DexAnnotationVisitor visitAnnotation(String name, Visibility visibility) { final DexAnnotationVisitor dav = super.visitAnnotation(mapClassName(mapper, name), visibility); if (dav != null) { if (DexConstants.ANNOTATION_SIGNATURE_TYPE.equals(name)) { return new DexAnnotationNode(name, visibility) { @Override public void visitEnd() { super.visitEnd(); Item p = super.items.get(0); Object[] newVs = remapSignature(mapper, (Object[]) p.value, true); dav.visit(p.name, newVs); dav.visitEnd(); } }; } return new MappingAV(name, mapper, dav); } return dav; } } public static class MappingMV extends DexMethodVisitor { protected Mapper mapper; public MappingMV(Mapper mapper, DexMethodVisitor dmv) { super(dmv); this.mapper = mapper; } @Override public DexAnnotationVisitor visitAnnotation(String name, Visibility visibility) { final DexAnnotationVisitor dav = super.visitAnnotation(mapClassName(mapper, name), visibility); if (dav != null) { if (DexConstants.ANNOTATION_SIGNATURE_TYPE.equals(name)) { return new DexAnnotationNode(name, visibility) { @Override public void visitEnd() { super.visitEnd(); Item p = super.items.get(0); Object[] newVs = remapSignature(mapper, (Object[]) p.value, false); dav.visit(p.name, newVs); dav.visitEnd(); } }; } return new MappingAV(name, mapper, dav); } return dav; } @Override public DexCodeVisitor visitCode() { DexCodeVisitor dcv = super.visitCode(); if (dcv != null) { return new MappingCode(mapper, dcv); } return dcv; } @Override public DexAnnotationAble visitParameterAnnotation(int index) { final DexAnnotationAble a = super.visitParameterAnnotation(index); if (a != null) { return new DexAnnotationAble() { @Override public DexAnnotationVisitor visitAnnotation(String name, Visibility visibility) { DexAnnotationVisitor dav = a.visitAnnotation(mapClassName(mapper, name), visibility); if (dav != null) { return new MappingAV(name, mapper, dav); } return dav; } }; } return a; } } static Object[] remapSignature(final Mapper mapper, Object vs[], boolean isType) { StringBuilder sb = new StringBuilder(); for (Object v0 : vs) { sb.append(v0); } return Types.buildDexStyleSignature(remapSignature(mapper, sb.toString(), isType)); } static String remapSignature(final Mapper mapper, String sig, boolean isType) { SignatureWriter w = new SignatureWriter() { String clzName; @Override public void visitClassType(String name) { super.visitClassType(mapClassName(mapper, name)); clzName = name; } @Override public void visitInnerClassType(String simpleName) { String value = simpleName; if (simpleName != null) { if (clzName.endsWith("$" + simpleName + ";")) { String nNameDesc = mapClassName(mapper, clzName); String containd = mapClassName(mapper, clzName.substring(0, clzName.length() - 2 - simpleName.length()) + ";"); String internalNameWitherOwner = nNameDesc.substring(1, nNameDesc.length() - 1); String internalNameOwner = containd.substring(1, containd.length() - 1); if (internalNameWitherOwner.startsWith(internalNameOwner + "$")) { value = internalNameWitherOwner.substring(1 + internalNameOwner.length()); } else { value = null; } } else { value = null; } } super.visitInnerClassType(value); } }; if (isType) { new SignatureReader(sig).acceptType(w); } else { new SignatureReader(sig).accept(w); } return w.toString(); } }