123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- import org.objectweb.asm.ClassReader
- import org.objectweb.asm.ClassWriter
- import org.objectweb.asm.Opcodes
- import org.objectweb.asm.tree.AnnotationNode
- import org.objectweb.asm.tree.ClassNode
- import org.objectweb.asm.tree.FieldNode
- import org.objectweb.asm.tree.InnerClassNode
- import org.objectweb.asm.tree.MethodNode
- import java.nio.file.Paths
- import java.util.jar.JarEntry
- import java.util.jar.JarFile
- import java.util.jar.JarOutputStream
- import java.util.zip.ZipEntry
- buildscript {
- repositories {
- maven { url "https://maven.aliyun.com/repository/public/" }
- }
- dependencies {
- classpath 'org.ow2.asm:asm-commons:9.2'
- }
- }
- Project frameworks = project(":Frameworks")
- gradle.projectsEvaluated {
- Task frameworksJarTask = null
- try {
- frameworksJarTask = frameworks.tasks.named("jar").get()
- } catch (Throwable ex) {
- ex.printStackTrace()
- }
- if (frameworksJarTask == null) {
- return
- }
- File frameworksJar = null
- frameworksJarTask.outputs.files.files.forEach {
- if (it.getName() == "Frameworks.jar") {
- frameworksJar = it
- }
- }
- if (frameworksJar == null) {
- return
- }
-
-
- File sdkJar = new File(Paths.get(android.sdkDirectory.getAbsolutePath(), "platforms",
- "android-${sdkInt}", "android.jar").toString())
- File resultJar = new File(frameworksJar.getParent(), "android-${sdkInt}.jar")
- File tempDir = new File(frameworks.getBuildDir(), "tmp")
- File inputFile = new File(tempDir, System.currentTimeMillis() + ".txt")
- Project currentProject = project
- Task currentPreBuild = currentProject.tasks.named("preBuild").get()
- currentPreBuild.doLast {
-
- if (!frameworksJarTask.getState().executed) {
- if (!tempDir.exists()) {
- tempDir.mkdirs()
- }
- if (!inputFile.exists()) {
- inputFile.createNewFile()
- }
-
- frameworksJarTask.inputs.file(inputFile)
- }
- }
- frameworksJarTask.doLast {
- inputFile.delete()
-
- if (currentPreBuild.getState().executed) {
- println("mergeJar -> $currentProject")
- try {
- if (resultJar.exists()) {
- resultJar.delete()
- }
- ClassMerge.mergeJar(resultJar, sdkJar, frameworksJar, "$sdkInt")
- } catch (Throwable ex) {
- ex.printStackTrace()
- }
- }
- }
-
- if (project.getPlugins().findPlugin("kotlin-android") != null) {
- project.dependencies {
- compileOnly files(resultJar)
- }
- }
- tasks.withType(JavaCompile).configureEach {
- String taskName = it.name
- if (taskName.contains("UnitTest") || taskName.contains("AndroidTest")) {
- return
- }
- it.doFirst {
-
-
-
- classpath = files(resultJar, classpath)
- options.bootstrapClasspath = files(resultJar, options.bootstrapClasspath)
- }
- }
- }
- dependencies {
- compileOnly frameworks
- }
- class ClassMerge {
-
- static void mergeJar(File resultJar, File androidJar, File frameworksJar, String sdkInt) throws Exception {
-
-
-
-
-
-
- if (resultJar.exists()) {
- return
- }
- JarOutputStream jos = new JarOutputStream(new FileOutputStream(resultJar))
- JarFile androidJarFile = new JarFile(androidJar)
- JarFile frameworksJarFile = new JarFile(frameworksJar)
- Enumeration<JarEntry> enumeration = frameworksJarFile.entries()
- while (enumeration.hasMoreElements()) {
- JarEntry jarEntry = enumeration.nextElement()
- String entryName = jarEntry.getName()
-
- if (entryName.startsWith("commons/") || entryName.startsWith("META-INF/")) {
- continue
- }
- ZipEntry zipEntry = new ZipEntry(entryName)
- jos.putNextEntry(zipEntry)
- ZipEntry androidJarZip = androidJarFile.getEntry(entryName)
- byte[] byteCode
- if (androidJarZip != null && entryName.endsWith(".class")) {
- byte[] baseClassByte = androidJarFile.getInputStream(jarEntry).readAllBytes()
- byte[] copyClassByte = frameworksJarFile.getInputStream(jarEntry).readAllBytes()
- byteCode = mergeAndFixClass(baseClassByte, copyClassByte, sdkInt)
- } else {
-
- byte[] baseClassByte = frameworksJarFile.getInputStream(jarEntry).readAllBytes()
-
-
- if (entryName.endsWith(".class")) {
- byteCode = mergeAndFixClass(baseClassByte, null, sdkInt)
- } else {
- byteCode = baseClassByte
- }
- }
- jos.write(byteCode)
- jos.closeEntry()
- }
- jos.close()
- androidJarFile.close()
- frameworksJarFile.close()
- }
- private static byte[] mergeAndFixClass(byte[] baseClassByte, byte[] copyClassByte, String sdkInt) throws Exception {
- int api = Opcodes.ASM9
- int flags = ClassWriter.COMPUTE_FRAMES
- ClassReader baseClassReader = new ClassReader(baseClassByte)
- ClassNode baseClassNode = new ClassNode(api)
- baseClassReader.accept(baseClassNode, 0)
-
- Set<String> publicClass = new HashSet<>()
- publicClass.add("android/annotation/NonNull")
- publicClass.add("android/annotation/Nullable")
- if (publicClass.contains(baseClassNode.name)) {
- baseClassNode.access = baseClassNode.access | Opcodes.ACC_PUBLIC
- }
- List<MethodNode> baseClassMethods = fixMethod(baseClassNode.methods, sdkInt)
- List<FieldNode> baseClassFields = fixField(baseClassNode.fields, sdkInt)
- if (copyClassByte != null) {
- ClassReader copyClassReader = new ClassReader(copyClassByte)
- ClassNode copyClassNode = new ClassNode(api)
- copyClassReader.accept(copyClassNode, 0)
- List<MethodNode> methods = fixMethod(copyClassNode.methods, sdkInt)
- Formatter formatter = new Formatter(Locale.getDefault())
- for (MethodNode node : methods) {
-
-
-
- if (methodExists(baseClassMethods, node)) {
- String access = formatter.format("0x%04x", node.access).toString()
- println("重复方法->[${copyClassNode.name}] $access ${node.name} ${node.desc}")
- } else {
- baseClassMethods.add(node)
- }
- }
-
- List<FieldNode> fields = fixField(copyClassNode.fields, sdkInt)
- for (FieldNode node : fields) {
- baseClassFields.add(node)
- }
-
- for (InnerClassNode node : copyClassNode.innerClasses) {
- if (!innerClassesExists(baseClassNode.innerClasses, node)) {
- baseClassNode.innerClasses.add(node)
- }
- }
- }
- ClassWriter baseClassWriter = new ClassWriter(flags)
- baseClassNode.accept(baseClassWriter)
- return baseClassWriter.toByteArray()
- }
- private static boolean innerClassesExists(List<InnerClassNode> classNodes, InnerClassNode innerClass) {
- for (InnerClassNode node : classNodes) {
- if (node.name == innerClass.name) {
- return true
- }
- }
- return false
- }
- private static boolean methodExists(List<MethodNode> methods, MethodNode method) {
- for (MethodNode node : methods) {
- if (node.name == method.name && node.desc == method.desc) {
- return true
- }
- }
- return false
- }
- private static List<FieldNode> fixField(List<FieldNode> fields, String sdkInt) {
- for (FieldNode node : fields) {
-
- fixNodeDesc(node, node.visibleAnnotations, sdkInt)
-
-
-
- node.visibleAnnotations = Collections.emptyList()
-
- node.invisibleAnnotations = Collections.emptyList()
- }
- return fields
- }
- private static List<MethodNode> fixMethod(List<MethodNode> methods, String sdkInt) {
- for (MethodNode node : methods) {
-
- fixNodeDesc(node, node.visibleAnnotations, sdkInt)
- int newAccess = node.access
-
- if ("<init>" == node.name) {
- int maskFlag = newAccess & 0x000F
-
- if (maskFlag == Opcodes.ACC_PRIVATE
- || maskFlag == Opcodes.ACC_PROTECTED
- || maskFlag == 0) {
- newAccess = (newAccess & 0xFFF0) | Opcodes.ACC_PUBLIC
- }
- }
-
- if ((newAccess & 0xF0000) == Opcodes.ACC_DEPRECATED) {
- newAccess = newAccess & 0x0FFFF
- }
- node.access = newAccess
-
-
-
-
- node.visibleAnnotations = Collections.emptyList()
- node.visibleParameterAnnotations = Collections.emptyList()
-
- node.invisibleAnnotations = Collections.emptyList()
- node.invisibleParameterAnnotations = Collections.emptyList()
- }
- return methods
- }
- private static void fixNodeDesc(Object node, List<AnnotationNode> annotations, String sdkInt) {
- if (node == null && annotations == null && annotations.size() == 0) {
- return
- }
- for (AnnotationNode annotation : annotations) {
- String desc = annotation.desc
- if (desc == "Lcommons/annotations/MethodDescriptor;"
- || desc == "Lcommons/annotations/FieldType;") {
- List<Object> values = annotation.values
-
-
-
- if (values != null && values.size() == 4 && values.get(1).toString() == sdkInt) {
- node.desc = values.get(3)
- }
- } else if (desc == "Lcommons/annotations/MethodDescriptor\$MethodDescriptors;"
- || desc == "Lcommons/annotations/FieldType\$FieldTypes;") {
-
-
-
- for (AnnotationNode object : annotation.values.get(1)) {
- List<Object> values = object.values
- if (values != null && values.size() == 4 && values.get(1).toString() == sdkInt) {
- node.desc = values.get(3)
- }
- }
- }
- }
- }
- }
|