diff --git a/app/src/main/java/com/meituan/sample/MainActivity.java b/app/src/main/java/com/meituan/sample/MainActivity.java index 653fe775..ac1792a4 100644 --- a/app/src/main/java/com/meituan/sample/MainActivity.java +++ b/app/src/main/java/com/meituan/sample/MainActivity.java @@ -49,6 +49,8 @@ public class MainActivity extends AppCompatActivity { Hll hll = new Hll(false); + + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -104,27 +106,28 @@ public void onClick(View v) { e.printStackTrace(); } - System.out.println(" run(String x) "+run("robust ",123)); - System.out.println(" run(People x) "+run(new People(),123d)); - System.out.println(" run(float x) "+run(123f)); - System.out.println(" double run() "+run()); + System.out.println(" run(String x) " + run("robust ", 123)); + System.out.println(" run(People x) " + run(new People(), 123d)); + System.out.println(" run(float x) " + run(123f)); + System.out.println(" double run() " + run()); System.out.println("in MainActivity end "); } - - private String run(String x,int p){ - return x+"meituan"; + private String run(String x, int p) { + return x + "meituan"; } - private String run(People x,double d){ + + private String run(People x, double d) { x.setAddr("meituan"); return x.getAddr(); } - private int run(float x){ - return (int)x; + + private int run(float x) { + return (int) x; } - private double run(){ + private double run() { return 1d; } diff --git a/app/src/main/java/com/meituan/sample/robusttest/Super.java b/app/src/main/java/com/meituan/sample/robusttest/Super.java index 8b3416ab..14fb3613 100644 --- a/app/src/main/java/com/meituan/sample/robusttest/Super.java +++ b/app/src/main/java/com/meituan/sample/robusttest/Super.java @@ -28,7 +28,6 @@ public class Super extends Hll { } - public String[] methodWithArrayParameters(String[] flag) { return flag; } diff --git a/app/src/main/java/com/meituan/sample/robusttest/other/Hll.java b/app/src/main/java/com/meituan/sample/robusttest/other/Hll.java index a477ecf6..66933073 100644 --- a/app/src/main/java/com/meituan/sample/robusttest/other/Hll.java +++ b/app/src/main/java/com/meituan/sample/robusttest/other/Hll.java @@ -23,6 +23,7 @@ public Hll(boolean t) { public Hll() { } + private String privateMethod(int index, String name) { Log.d("robust", "in hll.getStrings() "); packageMethod(1,name); diff --git a/autopatchbase/src/main/java/com/meituan/robust/patch/RobustModify.java b/autopatchbase/src/main/java/com/meituan/robust/patch/RobustModify.java index ae06be22..e9a51ebb 100644 --- a/autopatchbase/src/main/java/com/meituan/robust/patch/RobustModify.java +++ b/autopatchbase/src/main/java/com/meituan/robust/patch/RobustModify.java @@ -2,6 +2,8 @@ /** * Created by mivanzhang on 16/12/9. + *

+ * A backup for annotation Modify, in some situation Modify will not work, such as Generic */ public final class RobustModify { diff --git a/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Add.java b/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Add.java index 03eddf46..856dd3fb 100644 --- a/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Add.java +++ b/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Add.java @@ -8,6 +8,8 @@ /** * Created by mivanzhang on 16/12/19. + * 用来标记新增的类和方法 + * annotaion used for add classes or methods,classes and methods will be packed into patch.jar/patch.apk */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR}) diff --git a/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Modify.java b/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Modify.java index 14fecd6a..5c35014f 100644 --- a/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Modify.java +++ b/autopatchbase/src/main/java/com/meituan/robust/patch/annotaion/Modify.java @@ -8,6 +8,7 @@ /** * Created by mivanzhang on 16/12/9. + * annotaion used for modify classes or methods,classes and methods will be packed into patch.jar/patch.apk */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE, ElementType.CONSTRUCTOR}) @Retention(RetentionPolicy.CLASS) diff --git a/autopatchbase/src/main/java/com/meituan/robust/utils/EnhancedRobustUtils.java b/autopatchbase/src/main/java/com/meituan/robust/utils/EnhancedRobustUtils.java index 22145346..3f96b00e 100644 --- a/autopatchbase/src/main/java/com/meituan/robust/utils/EnhancedRobustUtils.java +++ b/autopatchbase/src/main/java/com/meituan/robust/utils/EnhancedRobustUtils.java @@ -7,6 +7,8 @@ /** * Created by mivanzhang on 16/8/15. + *

+ * A reflect utility class, providing methods for reflecting methods and set/get fields */ public class EnhancedRobustUtils { public static boolean isThrowable = true; diff --git a/build.gradle b/build.gradle index 7b9de258..85388b4c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,8 +9,8 @@ buildscript { classpath 'com.android.tools.build:gradle:2.1.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath 'com.meituan.robust:gradle-plugin:0.4.5' - classpath 'com.meituan.robust:auto-patch-plugin:0.4.5' + classpath 'com.meituan.robust:gradle-plugin:0.4.7' + classpath 'com.meituan.robust:auto-patch-plugin:0.4.7' classpath 'me.tatarka:gradle-retrolambda:3.2.0' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.7.3' diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/InsertcodeStrategy.java b/gradle-plugin/src/main/groovy/robust/gradle/plugin/InsertcodeStrategy.java index 6739c38b..cee29cf7 100644 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/InsertcodeStrategy.java +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/InsertcodeStrategy.java @@ -18,13 +18,23 @@ */ public abstract class InsertcodeStrategy { - protected List hotfixPackageList = new ArrayList<>(); - protected List hotfixMethodList = new ArrayList<>(); - protected List exceptPackageList = new ArrayList<>(); - protected List exceptMethodList = new ArrayList<>(); - protected boolean isHotfixMethodLevel = false; - protected boolean isExceptMethodLevel = false; + //packnames need to be insert code 需要插桩的包名列表, + protected List hotfixPackageList = new ArrayList<>(); + //methods list need to insert code 需要插桩的方法列表 + protected List hotfixMethodList = new ArrayList<>(); + + //packnames don`t need to be insert code 不需要插桩的包名列表, + protected List exceptPackageList = new ArrayList<>(); + + //methods list do not need to insert code 不需要插桩的方法列表 + protected List exceptMethodList = new ArrayList<>(); + //a switch control whether need to filter method in hotfixMethodList, if false ,hotfixMethodList will be ignored + protected boolean isHotfixMethodLevel = false; + + //a switch control whether need to filter method in exceptMethodList, if false ,exceptMethodList will be ignored + protected boolean isExceptMethodLevel = false; protected AtomicInteger insertMethodCount = new AtomicInteger(0); + //record every method with unique method number public HashMap methodMap = new HashMap(); public InsertcodeStrategy(List hotfixPackageList, List hotfixMethodList, List exceptPackageList, List exceptMethodList, boolean isHotfixMethodLevel, boolean isExceptMethodLevel) { @@ -37,8 +47,16 @@ public InsertcodeStrategy(List hotfixPackageList, List hotfixMet insertMethodCount.set(0); } + /** + * @param box all classes which will be packed into apk,所有需要打入apk中的类 + * @param jarFile 所有的插桩处理过的class都会被输出的jarFile + * @throws CannotCompileException + * @throws IOException + * @throws NotFoundException + */ protected abstract void insertCode(List box, File jarFile) throws CannotCompileException, IOException, NotFoundException; - protected boolean isNeedInsertClass(String className) { + + protected boolean isNeedInsertClass(String className) { //这样子可以在需要埋点的剔除指定的类 for (String exceptName : exceptPackageList) { @@ -54,14 +72,14 @@ protected boolean isNeedInsertClass(String className) { return false; } - protected void zipFile(byte[] classBytesArray, ZipOutputStream zos, String entryName){ + protected void zipFile(byte[] classBytesArray, ZipOutputStream zos, String entryName) { try { ZipEntry entry = new ZipEntry(entryName); zos.putNextEntry(entry); zos.write(classBytesArray, 0, classBytesArray.length); zos.closeEntry(); zos.flush(); - }catch (Exception e){ + } catch (Exception e) { e.printStackTrace(); } } diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashAction.groovy b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashAction.groovy index a6ba12f3..9dc17133 100755 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashAction.groovy +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashAction.groovy @@ -8,6 +8,8 @@ import java.security.MessageDigest /** * Created by hedex on 17/2/14. + * + * calculate unique string for each apk,you can get the string in file located in build/outputs/robust/robust.apkhash */ class RobustApkHashAction implements Action { @Override diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashZipUtils.groovy b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashZipUtils.groovy index 9ad565c9..baeeba8e 100755 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashZipUtils.groovy +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustApkHashZipUtils.groovy @@ -3,6 +3,7 @@ package robust.gradle.plugin import com.meituan.robust.Constants import java.util.zip.* + /** * Created by hedex on 17/2/14. */ @@ -82,7 +83,7 @@ class RobustApkHashZipUtils { while (entries.hasMoreElements()) { // ZipEntry zipEntry = entries.nextElement();//保守 ZipEntry zipEntry = new ZipEntry(entries.nextElement().name); - if (null != zipEntry ) { + if (null != zipEntry) { addZipEntry(zipOutputStream, zipEntry, apZipFile.getInputStream(zipEntry)) } } @@ -116,7 +117,6 @@ class RobustApkHashZipUtils { return crc.getValue(); } - /** * add zip entry * @@ -125,7 +125,8 @@ class RobustApkHashZipUtils { * @param inputStream * @throws Exception */ - private static void addZipEntry(ZipOutputStream zipOutputStream, ZipEntry zipEntry, InputStream inputStream) throws Exception { + private + static void addZipEntry(ZipOutputStream zipOutputStream, ZipEntry zipEntry, InputStream inputStream) throws Exception { try { zipOutputStream.putNextEntry(zipEntry); byte[] buffer = new byte[1024]; diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustTransform.groovy b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustTransform.groovy index e768c175..fee26dfc 100644 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustTransform.groovy +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/RobustTransform.groovy @@ -40,7 +40,7 @@ class RobustTransform extends Transform implements Plugin { robust = new XmlSlurper().parse(new File("${project.projectDir}/${Constants.ROBUST_XML}")) logger = project.logger initConfig() - //turnOnDevelopModel 是true的话,则强制执行插入 + //isForceInsert 是true的话,则强制执行插入 if (!isForceInsert) { def taskNames = project.gradle.startParameter.taskNames def isDebugTask = false; diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/AsmInsertImpl.java b/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/AsmInsertImpl.java index 22c64a5a..9e375c75 100644 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/AsmInsertImpl.java +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/AsmInsertImpl.java @@ -34,6 +34,8 @@ /** * Created by zhangmeng on 2017/5/10. + *

+ * insert code using asm */ public class AsmInsertImpl extends InsertcodeStrategy { @@ -45,12 +47,15 @@ public AsmInsertImpl(List hotfixPackageList, List hotfixMethodLi @Override protected void insertCode(List box, File jarFile) throws IOException, CannotCompileException { - ZipOutputStream outStream=new JarOutputStream(new FileOutputStream(jarFile)); - for(CtClass ctClass:box) { + ZipOutputStream outStream = new JarOutputStream(new FileOutputStream(jarFile)); + //get every class in the box ,ready to insert code + for (CtClass ctClass : box) { + //change modifier to public ,so all the class in the apk will be public ,you will be able to access it in the patch ctClass.setModifiers(AccessFlag.setPublic(ctClass.getModifiers())); - if(isNeedInsertClass(ctClass.getName())&&!(ctClass.isInterface() || ctClass.getDeclaredMethods().length < 1)) { + if (isNeedInsertClass(ctClass.getName()) && !(ctClass.isInterface() || ctClass.getDeclaredMethods().length < 1)) { + //only insert code into specific classes zipFile(transformCode(ctClass.toBytecode(), ctClass.getName().replaceAll("\\.", "/")), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); - }else { + } else { zipFile(ctClass.toBytecode(), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); } @@ -58,65 +63,69 @@ protected void insertCode(List box, File jarFile) throws IOException, C outStream.close(); } - private class InsertMethodBodyAdapter extends ClassVisitor implements Opcodes { + private class InsertMethodBodyAdapter extends ClassVisitor implements Opcodes { public InsertMethodBodyAdapter() { super(Opcodes.ASM5); } + ClassWriter classWriter; private String className; //this maybe change in the future - private Map methodInstructionTypeMap; - public InsertMethodBodyAdapter(ClassWriter cw,String className, Map methodInstructionTypeMap) { - super(Opcodes.ASM5,cw); - this.classWriter =cw; - this.className=className; - this.methodInstructionTypeMap=methodInstructionTypeMap; - classWriter.visitField(Opcodes.ACC_PUBLIC|Opcodes.ACC_STATIC, Constants.INSERT_FIELD_NAME, Type.getDescriptor(ChangeQuickRedirect.class), null, null); + private Map methodInstructionTypeMap; + + public InsertMethodBodyAdapter(ClassWriter cw, String className, Map methodInstructionTypeMap) { + super(Opcodes.ASM5, cw); + this.classWriter = cw; + this.className = className; + this.methodInstructionTypeMap = methodInstructionTypeMap; + //insert the field + classWriter.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, Constants.INSERT_FIELD_NAME, Type.getDescriptor(ChangeQuickRedirect.class), null, null); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - if(isProtect(access)) { + if (isProtect(access)) { access = setPublic(access); } - // MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); - if (!isQualifiedMethod(access,name,desc,methodInstructionTypeMap)) { + if (!isQualifiedMethod(access, name, desc, methodInstructionTypeMap)) { return mv; } - StringBuilder parameters=new StringBuilder(); - Type[]types=Type.getArgumentTypes(desc); - for(Type type:types){ + StringBuilder parameters = new StringBuilder(); + Type[] types = Type.getArgumentTypes(desc); + for (Type type : types) { parameters.append(type.getClassName()).append(","); } - if(parameters.length() > 0 && parameters.charAt(parameters.length()-1)==','){ - parameters.deleteCharAt(parameters.length()-1); + //remove the last "," + if (parameters.length() > 0 && parameters.charAt(parameters.length() - 1) == ',') { + parameters.deleteCharAt(parameters.length() - 1); } + //record method number + methodMap.put(className.replace('/', '.') + "." + name + "(" + parameters.toString() + ")", insertMethodCount.incrementAndGet()); + return new MethodBodyInsertor(mv, className, desc, isStatic(access), String.valueOf(insertMethodCount.get()), name, access); + } - methodMap.put(className.replace('/','.')+"."+name+"("+parameters.toString()+")", insertMethodCount.incrementAndGet()); - return new MethodBodyInsertor(mv,className,desc,isStatic(access), String .valueOf(insertMethodCount.get()),name,access); + private boolean isProtect(int access) { + return (access & Opcodes.ACC_PROTECTED) != 0; } - private boolean isProtect(int access) { - return (access & Opcodes.ACC_PROTECTED) != 0; - } + private int setPublic(int access) { + return (access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC; + } - private int setPublic(int access){ - return (access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) | Opcodes.ACC_PUBLIC; - } - private boolean isQualifiedMethod(int access, String name, String desc,Map methodInstructionTypeMap) { + private boolean isQualifiedMethod(int access, String name, String desc, Map c) { //类初始化函数和构造函数过滤 - if(AsmUtils.CLASS_INITIALIZER.equals(name)||AsmUtils.CONSTRUCTOR.equals(name)){ + if (AsmUtils.CLASS_INITIALIZER.equals(name) || AsmUtils.CONSTRUCTOR.equals(name)) { return false; } //@warn 这部分代码请重点review一下,判断条件写错会要命 //这部分代码请重点review一下,判断条件写错会要命 // synthetic 方法暂时不aop 比如AsyncTask 会生成一些同名 synthetic方法,对synthetic 以及private的方法也插入的代码,主要是针对lambda表达式 - if(((access& Opcodes.ACC_SYNTHETIC) != 0)&&((access & Opcodes.ACC_PRIVATE)==0)){ + if (((access & Opcodes.ACC_SYNTHETIC) != 0) && ((access & Opcodes.ACC_PRIVATE) == 0)) { return false; } if ((access & Opcodes.ACC_ABSTRACT) != 0) { @@ -150,10 +159,9 @@ private boolean isQualifiedMethod(int access, String name, String desc,Map paramsTypeClass=new ArrayList(); + List paramsTypeClass = new ArrayList(); boolean isStatic; //目前methodid是int类型的,未来可能会修改为String类型的,这边进行了一次强转 String methodId; - public MethodBodyInsertor(MethodVisitor mv,String className, String desc, boolean isStatic,String methodId,String name,int access) { + public MethodBodyInsertor(MethodVisitor mv, String className, String desc, boolean isStatic, String methodId, String name, int access) { super(Opcodes.ASM5, mv, access, name, desc); - this.className=className; - this.returnType =Type.getReturnType(desc); + this.className = className; + this.returnType = Type.getReturnType(desc); Type[] argsType = Type.getArgumentTypes(desc); for (Type type : argsType) { paramsTypeClass.add(type); } - this.isStatic=isStatic; - this.methodId =methodId; + this.isStatic = isStatic; + this.methodId = methodId; } @Override public void visitCode() { - RobustAsmUtils.createInsertCode(this,className,paramsTypeClass, returnType,isStatic,Integer.valueOf(methodId)); + //insert code here + RobustAsmUtils.createInsertCode(this, className, paramsTypeClass, returnType, isStatic, Integer.valueOf(methodId)); } } - private boolean isStatic(int access){ + + private boolean isStatic(int access) { return (access & Opcodes.ACC_STATIC) != 0; } - - - } - public byte [] transformCode2(byte []b1, String className) throws IOException { - ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); - InsertMethodBodyAdapter insertMethodBodyAdapter=new InsertMethodBodyAdapter(cw,className,new HashMap()); - ClassReader cr = new ClassReader(b1); - cr.accept(insertMethodBodyAdapter,ClassReader.EXPAND_FRAMES); - return cw.toByteArray(); } - public byte [] transformCode(byte []b1, String className) throws IOException { + public byte[] transformCode(byte[] b1, String className) throws IOException { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); ClassReader cr = new ClassReader(b1); - ClassNode classNode = new ClassNode(); - Map methodInstructionTypeMap=new HashMap<>(); - cr.accept(classNode,0); + Map methodInstructionTypeMap = new HashMap<>(); + cr.accept(classNode, 0); final List methods = classNode.methods; - for(MethodNode m: methods){ + for (MethodNode m : methods) { InsnList inList = m.instructions; - boolean isMethodInvoke=false; - for(int i = 0; i< inList.size(); i++) { - if(inList.get(i).getType()==AbstractInsnNode.METHOD_INSN) { - isMethodInvoke=true; + boolean isMethodInvoke = false; + for (int i = 0; i < inList.size(); i++) { + if (inList.get(i).getType() == AbstractInsnNode.METHOD_INSN) { + isMethodInvoke = true; } } methodInstructionTypeMap.put(m.name + m.desc, isMethodInvoke); } -// printlnMap(methodInstructionTypeMap); - - InsertMethodBodyAdapter insertMethodBodyAdapter=new InsertMethodBodyAdapter(cw,className,methodInstructionTypeMap); - cr.accept(insertMethodBodyAdapter,ClassReader.EXPAND_FRAMES); + InsertMethodBodyAdapter insertMethodBodyAdapter = new InsertMethodBodyAdapter(cw, className, methodInstructionTypeMap); + cr.accept(insertMethodBodyAdapter, ClassReader.EXPAND_FRAMES); return cw.toByteArray(); } - - public static void main(String []args) throws IOException { - - AsmInsertImpl asmInsert=new AsmInsertImpl(null,null,null,null,false,false); -// byte[]bytes= org.apache.commons.io.FileUtils.readFileToByteArray(new File("/Users/zhangmeng/Downloads/asm-5.2/asm/com/meituan/robust/PatchProxy.class")); -// byte[]bytes= org.apache.commons.io.FileUtils.readFileToByteArray(new File("/Users/zhangmeng/Downloads/asm-5.2/com/meituan/robust/Patch.class")); - byte[]bytes= org.apache.commons.io.FileUtils.readFileToByteArray(new File("/Users/zhangmeng/Desktop/code/openSource/robust/app/build/intermediates/transforms/aspectJ/release/folders/1/1/main/com/meituan/sample/robusttest/People.class")); -// org.apache.commons.io.FileUtils.writeByteArrayToFile(new File("/Users/zhangmeng/Downloads/asm-5.2/asm/com/meituan/robust/PatchProxy2.class"),asmInsert.transformCode2(bytes,"com.meituan.robust.PatchProxy","1231")); -// org.apache.commons.io.FileUtils.writeByteArrayToFile(new File("/Users/zhangmeng/Downloads/asm-5.2/com/meituan/robust/Patch2.class"),asmInsert.transformCode2(bytes,"com.meituan.robust.Patch","1231")); - org.apache.commons.io.FileUtils.writeByteArrayToFile(new File("/Users/zhangmeng/Desktop/code/openSource/robust/app/build/intermediates/transforms/aspectJ/release/folders/1/1/main/com/meituan/sample/robusttest/People2.class"),asmInsert.transformCode2(bytes,"com.meituan.sample.robusttest.People")); - } - private void printlnMap(Map map){ - for (Map.Entry entry : map.entrySet()) { - System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); - - } - } - } diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/RobustAsmUtils.java b/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/RobustAsmUtils.java index b0d9ce5e..427ed253 100644 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/RobustAsmUtils.java +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/asm/RobustAsmUtils.java @@ -11,398 +11,404 @@ public final class RobustAsmUtils { - public final static String REDIRECTFIELD_NAME = "changeQuickRedirect"; - public final static String REDIRECTCLASSNAME = Type.getDescriptor(com.meituan.robust.ChangeQuickRedirect.class); - public final static String PROXYCLASSNAME = "com.meituan.robust.PatchProxy".replace(".", "/"); - - /** - * 插入代码 - * @param mv - * @param className - * @param args - * @param returnType - * @param isStatic - */ - public static void createInsertCode(GeneratorAdapter mv, String className, List args, Type returnType, boolean isStatic, int methodId){ - - /** - * 调用isSupport方法 - */ - prepareMethodParameters(mv,className,args,returnType,isStatic,methodId); - //开始调用 - mv.visitMethodInsn(Opcodes.INVOKESTATIC, - PROXYCLASSNAME, - "isSupport", - "([Ljava/lang/Object;Ljava/lang/Object;"+REDIRECTCLASSNAME+"ZI[Ljava/lang/Class;Ljava/lang/Class;)Z"); - Label l1 = new Label(); - mv.visitJumpInsn(Opcodes.IFEQ, l1); - prepareMethodParameters(mv,className,args,returnType,isStatic,methodId); - //开始调用 - mv.visitMethodInsn(Opcodes.INVOKESTATIC, - PROXYCLASSNAME, - "accessDispatch", - "([Ljava/lang/Object;Ljava/lang/Object;"+REDIRECTCLASSNAME+"ZI[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"); - - //判断是否有返回值,代码不同 - if("V".equals(returnType.getDescriptor())){ - mv.visitInsn(Opcodes.POP); - mv.visitInsn(Opcodes.RETURN); - }else{ - //强制转化类型 - if(!castPrimateToObj(mv, returnType.getDescriptor())){ - //这里需要注意,如果是数组类型的直接使用即可,如果非数组类型,就得去除前缀了,还有最终是没有结束符; - //比如:Ljava/lang/String; ==》 java/lang/String - String newTypeStr = null; - int len = returnType.getDescriptor().length(); - if(returnType.getDescriptor().startsWith("[")){ - newTypeStr = returnType.getDescriptor().substring(0, len); - }else{ - newTypeStr = returnType.getDescriptor().substring(1, len-1); - } - mv.visitTypeInsn(Opcodes.CHECKCAST, newTypeStr); - } - - //这里还需要做返回类型不同返回指令也不同 - mv.visitInsn(getReturnTypeCode(returnType.getDescriptor())); - } - - mv.visitLabel(l1); - } - - private static void prepareMethodParameters(GeneratorAdapter mv, String className, List args, Type returnType, boolean isStatic, int methodId) { - //第一个参数:new Object[]{...};,如果方法没有参数直接传入new Object[0] - if(args.size() == 0){ - mv.visitInsn(Opcodes.ICONST_0); - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - }else{ - createObjectArray(mv, args, isStatic); - } - - //第二个参数:this,如果方法是static的话就直接传入null - if(isStatic){ - mv.visitInsn(Opcodes.ACONST_NULL); - }else{ - mv.visitVarInsn(Opcodes.ALOAD, 0); - } - - //第三个参数:changeQuickRedirect - mv.visitFieldInsn(Opcodes.GETSTATIC, - className, - REDIRECTFIELD_NAME, - REDIRECTCLASSNAME); - - //第四个参数:false,标志是否为static - mv.visitInsn(isStatic ? Opcodes.ICONST_1 : Opcodes.ICONST_0); - //第五个参数: - mv.push(methodId); - //第六个参数:参数class数组 - createClassArray(mv,args); - //第七个参数:返回值类型class - createReturnClass(mv,returnType); - } - - private static void createReturnClass(GeneratorAdapter mv, Type returnType) { - redirectLocal(mv, returnType); - } - - private static void createClassArray(GeneratorAdapter mv, List args){ - // create an array of objects capable of containing all the parameters and optionally the "this" - - createLocals(mv, args); - // we need to maintain the stack index when loading parameters from, as for long and double - // values, it uses 2 stack elements, all others use only 1 stack element. - int stackIndex = 0; - for (int arrayIndex = 0; arrayIndex < args.size(); arrayIndex++) { - Type arg = args.get(arrayIndex); - // duplicate the array of objects reference, it will be used to store the value in. - mv.dup(); - // index in the array of objects to store the boxed parameter. - mv.push(arrayIndex); - // Pushes the appropriate local variable on the stack - redirectLocal(mv, arg); + public final static String REDIRECTFIELD_NAME = "changeQuickRedirect"; + public final static String REDIRECTCLASSNAME = Type.getDescriptor(com.meituan.robust.ChangeQuickRedirect.class); + public final static String PROXYCLASSNAME = "com.meituan.robust.PatchProxy".replace(".", "/"); + + /** + * 插入代码 + * + * @param mv + * @param className + * @param args + * @param returnType + * @param isStatic + */ + public static void createInsertCode(GeneratorAdapter mv, String className, List args, Type returnType, boolean isStatic, int methodId) { + + /** + * 调用isSupport方法 + */ + prepareMethodParameters(mv, className, args, returnType, isStatic, methodId); + //开始调用 + mv.visitMethodInsn(Opcodes.INVOKESTATIC, + PROXYCLASSNAME, + "isSupport", + "([Ljava/lang/Object;Ljava/lang/Object;" + REDIRECTCLASSNAME + "ZI[Ljava/lang/Class;Ljava/lang/Class;)Z"); + Label l1 = new Label(); + mv.visitJumpInsn(Opcodes.IFEQ, l1); + prepareMethodParameters(mv, className, args, returnType, isStatic, methodId); + //开始调用 + mv.visitMethodInsn(Opcodes.INVOKESTATIC, + PROXYCLASSNAME, + "accessDispatch", + "([Ljava/lang/Object;Ljava/lang/Object;" + REDIRECTCLASSNAME + "ZI[Ljava/lang/Class;Ljava/lang/Class;)Ljava/lang/Object;"); + + //判断是否有返回值,代码不同 + if ("V".equals(returnType.getDescriptor())) { + mv.visitInsn(Opcodes.POP); + mv.visitInsn(Opcodes.RETURN); + } else { + //强制转化类型 + if (!castPrimateToObj(mv, returnType.getDescriptor())) { + //这里需要注意,如果是数组类型的直接使用即可,如果非数组类型,就得去除前缀了,还有最终是没有结束符; + //比如:Ljava/lang/String; ==》 java/lang/String + String newTypeStr = null; + int len = returnType.getDescriptor().length(); + if (returnType.getDescriptor().startsWith("[")) { + newTypeStr = returnType.getDescriptor().substring(0, len); + } else { + newTypeStr = returnType.getDescriptor().substring(1, len - 1); + } + mv.visitTypeInsn(Opcodes.CHECKCAST, newTypeStr); + } + + //这里还需要做返回类型不同返回指令也不同 + mv.visitInsn(getReturnTypeCode(returnType.getDescriptor())); + } + + mv.visitLabel(l1); + } + + private static void prepareMethodParameters(GeneratorAdapter mv, String className, List args, Type returnType, boolean isStatic, int methodId) { + //第一个参数:new Object[]{...};,如果方法没有参数直接传入new Object[0] + if (args.size() == 0) { + mv.visitInsn(Opcodes.ICONST_0); + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + } else { + createObjectArray(mv, args, isStatic); + } + + //第二个参数:this,如果方法是static的话就直接传入null + if (isStatic) { + mv.visitInsn(Opcodes.ACONST_NULL); + } else { + mv.visitVarInsn(Opcodes.ALOAD, 0); + } + + //第三个参数:changeQuickRedirect + mv.visitFieldInsn(Opcodes.GETSTATIC, + className, + REDIRECTFIELD_NAME, + REDIRECTCLASSNAME); + + //第四个参数:false,标志是否为static + mv.visitInsn(isStatic ? Opcodes.ICONST_1 : Opcodes.ICONST_0); + //第五个参数: + mv.push(methodId); + //第六个参数:参数class数组 + createClassArray(mv, args); + //第七个参数:返回值类型class + createReturnClass(mv, returnType); + } + + private static void createReturnClass(GeneratorAdapter mv, Type returnType) { + redirectLocal(mv, returnType); + } + + private static void createClassArray(GeneratorAdapter mv, List args) { + // create an array of objects capable of containing all the parameters and optionally the "this" + + createLocals(mv, args); + // we need to maintain the stack index when loading parameters from, as for long and double + // values, it uses 2 stack elements, all others use only 1 stack element. + int stackIndex = 0; + for (int arrayIndex = 0; arrayIndex < args.size(); arrayIndex++) { + Type arg = args.get(arrayIndex); + // duplicate the array of objects reference, it will be used to store the value in. + mv.dup(); + // index in the array of objects to store the boxed parameter. + mv.push(arrayIndex); + // Pushes the appropriate local variable on the stack + redirectLocal(mv, arg); // mv.visitLdcInsn(Type.getType(arg.getDescriptor())); - // potentially box up intrinsic types. + // potentially box up intrinsic types. // mv.box(arg); - mv.arrayStore(Type.getType(Class.class)); - // stack index must progress according to the parameter type we just processed. + mv.arrayStore(Type.getType(Class.class)); + // stack index must progress according to the parameter type we just processed. // stackIndex += arg.getSize(); - } - } - - /** - * Creates and pushes to the stack the array to hold all the parameters to redirect, and - * optionally this. - */ - protected static void createLocals(GeneratorAdapter mv, List args) { - mv.push(args.size()); - mv.newArray(Type.getType(Class.class)); - } - /** - * Pushes in the stack the value that should be redirected for the given local. - */ - protected static void redirectLocal(GeneratorAdapter mv, Type arg) { - switch (arg.getDescriptor()){ - case "Z": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); - break; - case "B": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); - break; - case "C": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); - break; - case "S": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); - break; - case "I": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); - break; - case "F": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); - break; - case "D": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); - break; - case "J": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); - break; - case "V": - mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); - break; - default: - mv.visitLdcInsn(Type.getType(arg.getDescriptor())); - } - - } - - /** - * 创建局部参数代码 - * @param mv - * @param paramsTypeClass - * @param isStatic - */ - private static void createObjectArray(MethodVisitor mv, List paramsTypeClass, boolean isStatic){ - //Opcodes.ICONST_0 ~ Opcodes.ICONST_5 这个指令范围 - int argsCount = paramsTypeClass.size(); - //声明 Object[argsCount]; - if(argsCount >= 6){ - mv.visitIntInsn(Opcodes.BIPUSH, argsCount); - }else{ - mv.visitInsn(Opcodes.ICONST_0+argsCount); - } - mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); - - //如果是static方法,没有this隐含参数 - int loadIndex = (isStatic ? 0 : 1); - - //填充数组数据 - for(int i=0;i= 1){ - //这里需要判断当前参数的前面一个参数的类型是什么 - if("J".equals(paramsTypeClass.get(i-1).getDescriptor()) || "D".equals(paramsTypeClass.get(i-1).getDescriptor())){ - //如果前面一个参数是long,double类型,load指令索引就要增加1 - loadIndex ++; - } - } - if(!createPrimateTypeObj(mv, loadIndex, paramsTypeClass.get(i).getDescriptor())){ - mv.visitVarInsn(Opcodes.ALOAD, loadIndex); - mv.visitInsn(Opcodes.AASTORE); - } - loadIndex ++; - } - } - - private static void createBooleanObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Byte"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ILOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Byte", "", "(B)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createShortObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Short"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ILOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Short", "", "(S)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createCharObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Character"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ILOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Character", "", "(C)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createIntegerObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Integer"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.ILOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Integer", "", "(I)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createFloatObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Float"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.FLOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Float", "", "(F)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createDoubleObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Double"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.DLOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Double", "", "(D)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - private static void createLongObj(MethodVisitor mv, int argsPostion){ - mv.visitTypeInsn(Opcodes.NEW, "java/lang/Long"); - mv.visitInsn(Opcodes.DUP); - mv.visitVarInsn(Opcodes.LLOAD, argsPostion); - mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Long", "", "(J)V"); - mv.visitInsn(Opcodes.AASTORE); - } - - /** - * 创建基本类型对应的对象 - * @param mv - * @param argsPostion - * @param typeS - * @return - */ - private static boolean createPrimateTypeObj(MethodVisitor mv, int argsPostion, String typeS){ - if("Z".equals(typeS)){ - createBooleanObj(mv, argsPostion); - return true; - } - if("B".equals(typeS)){ - createBooleanObj(mv, argsPostion); - return true; - } - if("C".equals(typeS)){ - createCharObj(mv, argsPostion); - return true; - } - if("S".equals(typeS)){ - createShortObj(mv, argsPostion); - return true; - } - if("I".equals(typeS)){ - createIntegerObj(mv, argsPostion); - return true; - } - if("F".equals(typeS)){ - createFloatObj(mv, argsPostion); - return true; - } - if("D".equals(typeS)){ - createDoubleObj(mv, argsPostion); - return true; - } - if("J".equals(typeS)){ - createLongObj(mv, argsPostion); - return true; - } - return false; - } - - /** - * 基本类型需要做对象类型分装 - * @param mv - * @param typeS - * @return - */ - private static boolean castPrimateToObj(MethodVisitor mv, String typeS){ - if("Z".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); - return true; - } - if("B".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Byte");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); - return true; - } - if("C".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "intValue", "()C"); - return true; - } - if("S".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Short");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); - return true; - } - if("I".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); - return true; - } - if("F".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Float");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); - return true; - } - if("D".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); - return true; - } - if("J".equals(typeS)){ - mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long");//强制转化类型 - mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); - return true; - } - return false; - } - - /** - * 针对不同类型返回指令不一样 - * @param typeS - * @return - */ - private static int getReturnTypeCode(String typeS){ - if("Z".equals(typeS)){ - return Opcodes.IRETURN; - } - if("B".equals(typeS)){ - return Opcodes.IRETURN; - } - if("C".equals(typeS)){ - return Opcodes.IRETURN; - } - if("S".equals(typeS)){ - return Opcodes.IRETURN; - } - if("I".equals(typeS)){ - return Opcodes.IRETURN; - } - if("F".equals(typeS)){ - return Opcodes.FRETURN; - } - if("D".equals(typeS)){ - return Opcodes.DRETURN; - } - if("J".equals(typeS)){ - return Opcodes.LRETURN; - } - return Opcodes.ARETURN; - } + } + } + + /** + * Creates and pushes to the stack the array to hold all the parameters to redirect, and + * optionally this. + */ + protected static void createLocals(GeneratorAdapter mv, List args) { + mv.push(args.size()); + mv.newArray(Type.getType(Class.class)); + } + + /** + * Pushes in the stack the value that should be redirected for the given local. + */ + protected static void redirectLocal(GeneratorAdapter mv, Type arg) { + switch (arg.getDescriptor()) { + case "Z": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); + break; + case "B": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); + break; + case "C": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); + break; + case "S": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); + break; + case "I": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); + break; + case "F": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); + break; + case "D": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); + break; + case "J": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); + break; + case "V": + mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); + break; + default: + mv.visitLdcInsn(Type.getType(arg.getDescriptor())); + } + + } + + /** + * 创建局部参数代码 + * + * @param mv + * @param paramsTypeClass + * @param isStatic + */ + private static void createObjectArray(MethodVisitor mv, List paramsTypeClass, boolean isStatic) { + //Opcodes.ICONST_0 ~ Opcodes.ICONST_5 这个指令范围 + int argsCount = paramsTypeClass.size(); + //声明 Object[argsCount]; + if (argsCount >= 6) { + mv.visitIntInsn(Opcodes.BIPUSH, argsCount); + } else { + mv.visitInsn(Opcodes.ICONST_0 + argsCount); + } + mv.visitTypeInsn(Opcodes.ANEWARRAY, "java/lang/Object"); + + //如果是static方法,没有this隐含参数 + int loadIndex = (isStatic ? 0 : 1); + + //填充数组数据 + for (int i = 0; i < argsCount; i++) { + mv.visitInsn(Opcodes.DUP); + if (i <= 5) { + mv.visitInsn(Opcodes.ICONST_0 + i); + } else { + mv.visitIntInsn(Opcodes.BIPUSH, i); + } + + //这里又要做特殊处理,在实践过程中发现个问题:public void xxx(long a, boolean b, double c,int d) + //当一个参数的前面一个参数是long或者是double类型的话,后面参数在使用LOAD指令,加载数据索引值要+1 + //个人猜想是和long,double是8个字节的问题有关系。这里做了处理 + //比如这里的参数:[a=LLOAD 1] [b=ILOAD 3] [c=DLOAD 4] [d=ILOAD 6]; + if (i >= 1) { + //这里需要判断当前参数的前面一个参数的类型是什么 + if ("J".equals(paramsTypeClass.get(i - 1).getDescriptor()) || "D".equals(paramsTypeClass.get(i - 1).getDescriptor())) { + //如果前面一个参数是long,double类型,load指令索引就要增加1 + loadIndex++; + } + } + if (!createPrimateTypeObj(mv, loadIndex, paramsTypeClass.get(i).getDescriptor())) { + mv.visitVarInsn(Opcodes.ALOAD, loadIndex); + mv.visitInsn(Opcodes.AASTORE); + } + loadIndex++; + } + } + + private static void createBooleanObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Byte"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ILOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Byte", "", "(B)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createShortObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Short"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ILOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Short", "", "(S)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createCharObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Character"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ILOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Character", "", "(C)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createIntegerObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Integer"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.ILOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Integer", "", "(I)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createFloatObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Float"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.FLOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Float", "", "(F)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createDoubleObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Double"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.DLOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Double", "", "(D)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + private static void createLongObj(MethodVisitor mv, int argsPostion) { + mv.visitTypeInsn(Opcodes.NEW, "java/lang/Long"); + mv.visitInsn(Opcodes.DUP); + mv.visitVarInsn(Opcodes.LLOAD, argsPostion); + mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Long", "", "(J)V"); + mv.visitInsn(Opcodes.AASTORE); + } + + /** + * 创建基本类型对应的对象 + * + * @param mv + * @param argsPostion + * @param typeS + * @return + */ + private static boolean createPrimateTypeObj(MethodVisitor mv, int argsPostion, String typeS) { + if ("Z".equals(typeS)) { + createBooleanObj(mv, argsPostion); + return true; + } + if ("B".equals(typeS)) { + createBooleanObj(mv, argsPostion); + return true; + } + if ("C".equals(typeS)) { + createCharObj(mv, argsPostion); + return true; + } + if ("S".equals(typeS)) { + createShortObj(mv, argsPostion); + return true; + } + if ("I".equals(typeS)) { + createIntegerObj(mv, argsPostion); + return true; + } + if ("F".equals(typeS)) { + createFloatObj(mv, argsPostion); + return true; + } + if ("D".equals(typeS)) { + createDoubleObj(mv, argsPostion); + return true; + } + if ("J".equals(typeS)) { + createLongObj(mv, argsPostion); + return true; + } + return false; + } + + /** + * 基本类型需要做对象类型分装 + * + * @param mv + * @param typeS + * @return + */ + private static boolean castPrimateToObj(MethodVisitor mv, String typeS) { + if ("Z".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Boolean");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Boolean", "booleanValue", "()Z"); + return true; + } + if ("B".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Byte");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Byte", "byteValue", "()B"); + return true; + } + if ("C".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Character");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Character", "intValue", "()C"); + return true; + } + if ("S".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Short");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Short", "shortValue", "()S"); + return true; + } + if ("I".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Integer");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Integer", "intValue", "()I"); + return true; + } + if ("F".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Float");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Float", "floatValue", "()F"); + return true; + } + if ("D".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Double");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Double", "doubleValue", "()D"); + return true; + } + if ("J".equals(typeS)) { + mv.visitTypeInsn(Opcodes.CHECKCAST, "java/lang/Long");//强制转化类型 + mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Long", "longValue", "()J"); + return true; + } + return false; + } + + /** + * 针对不同类型返回指令不一样 + * + * @param typeS + * @return + */ + private static int getReturnTypeCode(String typeS) { + if ("Z".equals(typeS)) { + return Opcodes.IRETURN; + } + if ("B".equals(typeS)) { + return Opcodes.IRETURN; + } + if ("C".equals(typeS)) { + return Opcodes.IRETURN; + } + if ("S".equals(typeS)) { + return Opcodes.IRETURN; + } + if ("I".equals(typeS)) { + return Opcodes.IRETURN; + } + if ("F".equals(typeS)) { + return Opcodes.FRETURN; + } + if ("D".equals(typeS)) { + return Opcodes.DRETURN; + } + if ("J".equals(typeS)) { + return Opcodes.LRETURN; + } + return Opcodes.ARETURN; + } } \ No newline at end of file diff --git a/gradle-plugin/src/main/groovy/robust/gradle/plugin/javaassist/JavaAssistInsertImpl.java b/gradle-plugin/src/main/groovy/robust/gradle/plugin/javaassist/JavaAssistInsertImpl.java index 8e03769d..a077fd04 100644 --- a/gradle-plugin/src/main/groovy/robust/gradle/plugin/javaassist/JavaAssistInsertImpl.java +++ b/gradle-plugin/src/main/groovy/robust/gradle/plugin/javaassist/JavaAssistInsertImpl.java @@ -30,6 +30,7 @@ /** * Created by zhangmeng on 2017/5/10. + * this class do almost the same thing with AsmInsertImpl */ public class JavaAssistInsertImpl extends InsertcodeStrategy { @@ -40,57 +41,64 @@ public JavaAssistInsertImpl(List hotfixPackageList, List hotfixM @Override protected void insertCode(List box, File jarFile) throws CannotCompileException, IOException, NotFoundException { - ZipOutputStream outStream=new JarOutputStream(new FileOutputStream(jarFile)); + ZipOutputStream outStream = new JarOutputStream(new FileOutputStream(jarFile)); // new ForkJoinPool().submit { - for(CtClass ctClass:box) { - if (isNeedInsertClass(ctClass.getName())) { - ctClass.setModifiers(AccessFlag.setPublic(ctClass.getModifiers())); - if (ctClass.isInterface() || ctClass.getDeclaredMethods().length < 1) { - zipFile(ctClass.toBytecode(), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); + for (CtClass ctClass : box) { + if (isNeedInsertClass(ctClass.getName())) { + //change class modifier + ctClass.setModifiers(AccessFlag.setPublic(ctClass.getModifiers())); + if (ctClass.isInterface() || ctClass.getDeclaredMethods().length < 1) { + //skip the unsatisfied class + zipFile(ctClass.toBytecode(), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); + continue; + } + + boolean addIncrementalChange = false; + for (CtBehavior ctBehavior : ctClass.getDeclaredBehaviors()) { + if (!addIncrementalChange) { + //insert the field + addIncrementalChange = true; + ClassPool classPool = ctBehavior.getDeclaringClass().getClassPool(); + CtClass type = classPool.getOrNull(Constants.INTERFACE_NAME); + CtField ctField = new CtField(type, Constants.INSERT_FIELD_NAME, ctClass); + ctField.setModifiers(AccessFlag.PUBLIC | AccessFlag.STATIC); + ctClass.addField(ctField); + } + if (!isQualifiedMethod(ctBehavior)) { continue; } - - boolean addIncrementalChange = false; - for (CtBehavior ctBehavior : ctClass.getDeclaredBehaviors()) { - if (!addIncrementalChange) { - addIncrementalChange = true; - ClassPool classPool = ctBehavior.getDeclaringClass().getClassPool(); - CtClass type = classPool.getOrNull(Constants.INTERFACE_NAME); - CtField ctField = new CtField(type, Constants.INSERT_FIELD_NAME, ctClass); - ctField.setModifiers(AccessFlag.PUBLIC | AccessFlag.STATIC); - ctClass.addField(ctField); - } - if(!isQualifiedMethod(ctBehavior)){ - continue; - } - //here comes the method will be inserted code - methodMap.put(ctBehavior.getLongName(), insertMethodCount.incrementAndGet()); - try { - if (ctBehavior.getMethodInfo().isMethod()) { - CtMethod ctMethod=(CtMethod)ctBehavior; - boolean isStatic = (ctMethod.getModifiers() & AccessFlag.STATIC)!= 0; - CtClass returnType = ctMethod.getReturnType(); - String returnTypeString = returnType.getName(); - String body = "Object argThis = null;"; - if (!isStatic) { - body += "argThis = $0;"; - } - String parametersClassType=getParametersClassType(ctMethod); -// body += " if (com.meituan.robust.PatchProxy.isSupport(\$args, argThis, ${Constants.INSERT_FIELD_NAME}, $isStatic, " + methodMap.get(ctBehavior.longName) + ",${parametersClassType},${returnTypeString}.class)) {" - body += " if (com.meituan.robust.PatchProxy.isSupport($args, argThis, "+Constants.INSERT_FIELD_NAME+", "+isStatic+ - ", " + methodMap.get(ctBehavior.getLongName()) + ","+parametersClassType+","+returnTypeString+".class)) {"; - body += getReturnStatement(returnTypeString, isStatic, methodMap.get(ctBehavior.getLongName()),parametersClassType,returnTypeString+".class"); - body += " }"; - ctBehavior.insertBefore(body); + //here comes the method will be inserted code + methodMap.put(ctBehavior.getLongName(), insertMethodCount.incrementAndGet()); + try { + if (ctBehavior.getMethodInfo().isMethod()) { + CtMethod ctMethod = (CtMethod) ctBehavior; + boolean isStatic = (ctMethod.getModifiers() & AccessFlag.STATIC) != 0; + CtClass returnType = ctMethod.getReturnType(); + String returnTypeString = returnType.getName(); + //construct the code will be inserted in string format + String body = "Object argThis = null;"; + if (!isStatic) { + body += "argThis = $0;"; } - } catch (Throwable t ) { - t.printStackTrace(); - System.out.println("ctClass: " + ctClass.getName() + " error: " + t.getMessage()); + String parametersClassType = getParametersClassType(ctMethod); +// body += " if (com.meituan.robust.PatchProxy.isSupport(\$args, argThis, ${Constants.INSERT_FIELD_NAME}, $isStatic, " + methodMap.get(ctBehavior.longName) + ",${parametersClassType},${returnTypeString}.class)) {" + body += " if (com.meituan.robust.PatchProxy.isSupport($args, argThis, " + Constants.INSERT_FIELD_NAME + ", " + isStatic + + ", " + methodMap.get(ctBehavior.getLongName()) + "," + parametersClassType + "," + returnTypeString + ".class)) {"; + body += getReturnStatement(returnTypeString, isStatic, methodMap.get(ctBehavior.getLongName()), parametersClassType, returnTypeString + ".class"); + body += " }"; + //finish the insert-code body ,let`s insert it + ctBehavior.insertBefore(body); } + } catch (Throwable t) { + //here we ignore the error + t.printStackTrace(); + System.out.println("ctClass: " + ctClass.getName() + " error: " + t.getMessage()); } } - zipFile(ctClass.toBytecode(), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); } + //zip the inserted-classes into output file + zipFile(ctClass.toBytecode(), outStream, ctClass.getName().replaceAll("\\.", "/") + ".class"); + } // }.get() outStream.close(); } @@ -146,25 +154,28 @@ private boolean isQualifiedMethod(CtBehavior it) throws CannotCompileException { return !isHotfixMethodLevel; } - private String getParametersClassType(CtMethod method) throws NotFoundException { - if(method.getParameterTypes().length==0){ + private String getParametersClassType(CtMethod method) throws NotFoundException { + if (method.getParameterTypes().length == 0) { return " null "; } - StringBuilder parameterType=new StringBuilder(); + StringBuilder parameterType = new StringBuilder(); parameterType.append("new Class[]{"); - for(CtClass paramterClass:method.getParameterTypes()){ + for (CtClass paramterClass : method.getParameterTypes()) { parameterType.append(paramterClass.getName()).append(".class,"); } //remove last ',' - if(','==parameterType.charAt(parameterType.length()-1)) - parameterType.deleteCharAt(parameterType.length()-1); + if (',' == parameterType.charAt(parameterType.length() - 1)) + parameterType.deleteCharAt(parameterType.length() - 1); parameterType.append("}"); return parameterType.toString(); } + //判断代码中是否有方法调用 private boolean isCallMethod = false; + /** * 判断是否有方法调用 + * * @return 是否插桩 */ private boolean isMethodWithExpression(CtMethod ctMethod) throws CannotCompileException { @@ -189,14 +200,18 @@ private boolean isMethodWithExpression(CtMethod ctMethod) throws CannotCompileEx * @param a the new expression for creating an array. * @throws CannotCompileException */ - public void edit(NewArray a) throws CannotCompileException { isCallMethod = true; } + public void edit(NewArray a) throws CannotCompileException { + isCallMethod = true; + } /** * Edits a method call (overridable). * * The default implementation performs nothing. */ - public void edit(MethodCall m) throws CannotCompileException { isCallMethod = true; } + public void edit(MethodCall m) throws CannotCompileException { + isCallMethod = true; + } /** * Edits a constructor call (overridable). @@ -216,79 +231,87 @@ public void edit(ConstructorCall c) throws CannotCompileException { * Edits an instanceof expression (overridable). * The default implementation performs nothing. */ - public void edit(Instanceof i) throws CannotCompileException { isCallMethod = true; } + public void edit(Instanceof i) throws CannotCompileException { + isCallMethod = true; + } /** * Edits an expression for explicit type casting (overridable). * The default implementation performs nothing. */ - public void edit(Cast c) throws CannotCompileException { isCallMethod = true; } + public void edit(Cast c) throws CannotCompileException { + isCallMethod = true; + } /** * Edits a catch clause (overridable). * The default implementation performs nothing. */ - public void edit(Handler h) throws CannotCompileException { isCallMethod = true; } + public void edit(Handler h) throws CannotCompileException { + isCallMethod = true; + } }); return isCallMethod; } + /** * 根据传入类型判断调用PathProxy的方法 - * @param type 返回类型 - * @param isStatic 是否是静态方法 + * + * @param type 返回类型 + * @param isStatic 是否是静态方法 * @param methodNumber 方法数 * @return 返回return语句 */ - private String getReturnStatement(String type, boolean isStatic, int methodNumber,String parametersClassType,String returnTypeString) { + private String getReturnStatement(String type, boolean isStatic, int methodNumber, String parametersClassType, String returnTypeString) { switch (type) { case Constants.CONSTRUCTOR: - return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, "+isStatic+", "+methodNumber+","+parametersClassType+","+returnTypeString+"); "; + return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, " + isStatic + ", " + methodNumber + "," + parametersClassType + "," + returnTypeString + "); "; case Constants.LANG_VOID: - return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, "+isStatic+", "+methodNumber+","+parametersClassType+","+returnTypeString+"); return null;"; + return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, " + isStatic + ", " + methodNumber + "," + parametersClassType + "," + returnTypeString + "); return null;"; case Constants.VOID: - return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, "+isStatic+", "+methodNumber+","+parametersClassType+","+returnTypeString+"); return ;"; + return " com.meituan.robust.PatchProxy.accessDispatchVoid( $args, argThis, changeQuickRedirect, " + isStatic + ", " + methodNumber + "," + parametersClassType + "," + returnTypeString + "); return ;"; case Constants.LANG_BOOLEAN: - return " return ((java.lang.Boolean)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Boolean)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.BOOLEAN: - return " return ((java.lang.Boolean)com.meituan.robust.PatchProxy.accessDispatch($args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).booleanValue();"; + return " return ((java.lang.Boolean)com.meituan.robust.PatchProxy.accessDispatch($args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).booleanValue();"; case Constants.INT: - return " return ((java.lang.Integer)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).intValue();"; + return " return ((java.lang.Integer)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).intValue();"; case Constants.LANG_INT: - return " return ((java.lang.Integer)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+methodNumber+","+parametersClassType+","+returnTypeString+")); "; + return " return ((java.lang.Integer)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")); "; case Constants.LONG: - return " return ((java.lang.Long)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).longValue();"; + return " return ((java.lang.Long)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).longValue();"; case Constants.LANG_LONG: - return " return ((java.lang.Long)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Long)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.DOUBLE: - return " return ((java.lang.Double)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).doubleValue();"; + return " return ((java.lang.Double)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).doubleValue();"; case Constants.LANG_DOUBLE: - return " return ((java.lang.Double)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Double)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.FLOAT: - return " return ((java.lang.Float)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).floatValue();"; + return " return ((java.lang.Float)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).floatValue();"; case Constants.LANG_FLOAT: - return " return ((java.lang.Float)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Float)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.SHORT: - return " return ((java.lang.Short)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).shortValue();"; + return " return ((java.lang.Short)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).shortValue();"; case Constants.LANG_SHORT: - return " return ((java.lang.Short)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Short)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.BYTE: - return " return ((java.lang.Byte)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).byteValue();"; + return " return ((java.lang.Byte)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).byteValue();"; case Constants.LANG_BYTE: - return " return ((java.lang.Byte)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Byte)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; case Constants.CHAR: - return " return ((java.lang.Character)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+")).charValue();"; + return " return ((java.lang.Character)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ")).charValue();"; case Constants.LANG_CHARACTER: - return " return ((java.lang.Character)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+"));"; + return " return ((java.lang.Character)com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + "));"; default: - return " return ("+type+")com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, "+isStatic+","+ methodNumber+","+parametersClassType+","+returnTypeString+");"; + return " return (" + type + ")com.meituan.robust.PatchProxy.accessDispatch( $args, argThis, changeQuickRedirect, " + isStatic + "," + methodNumber + "," + parametersClassType + "," + returnTypeString + ");"; } } } diff --git "a/images/Robust\347\273\223\346\236\204\345\233\276.png" "b/images/Robust\347\273\223\346\236\204\345\233\276.png" new file mode 100644 index 00000000..35d8ab10 Binary files /dev/null and "b/images/Robust\347\273\223\346\236\204\345\233\276.png" differ diff --git "a/images/\350\241\245\344\270\201\344\273\243\347\240\201\347\273\223\346\236\204.png" "b/images/\350\241\245\344\270\201\344\273\243\347\240\201\347\273\223\346\236\204.png" new file mode 100644 index 00000000..e3aafcb9 Binary files /dev/null and "b/images/\350\241\245\344\270\201\344\273\243\347\240\201\347\273\223\346\236\204.png" differ diff --git a/patch/src/main/java/com/meituan/robust/PatchedClassInfo.java b/patch/src/main/java/com/meituan/robust/PatchedClassInfo.java index cf73c488..40e628c3 100644 --- a/patch/src/main/java/com/meituan/robust/PatchedClassInfo.java +++ b/patch/src/main/java/com/meituan/robust/PatchedClassInfo.java @@ -2,6 +2,7 @@ /** * Created by hedex on 16/6/3. + * a map record the class name before ProGuard and after ProGuard */ public class PatchedClassInfo { public String patchedClassName; diff --git a/patch/src/main/java/com/meituan/robust/PatchesInfo.java b/patch/src/main/java/com/meituan/robust/PatchesInfo.java index 80b9aa72..c2f9f950 100644 --- a/patch/src/main/java/com/meituan/robust/PatchesInfo.java +++ b/patch/src/main/java/com/meituan/robust/PatchesInfo.java @@ -4,6 +4,7 @@ /** * Created by c_kunwu on 16/5/12. + * an interface describe patch.jar info */ public interface PatchesInfo { List getPatchedClassesInfo();