Vsevolod Tolstopyatov | 74bcc92 | 2018-05-03 20:07:54 +0300 | [diff] [blame] | 1 | package kotlinx.coroutines.experimental.tools |
| 2 | |
| 3 | import org.objectweb.asm.* |
| 4 | import org.objectweb.asm.tree.* |
| 5 | import java.io.* |
| 6 | import java.util.jar.* |
| 7 | |
Vsevolod Tolstopyatov | 74bcc92 | 2018-05-03 20:07:54 +0300 | [diff] [blame] | 8 | fun JarFile.classEntries() = entries().asSequence().filter { |
| 9 | !it.isDirectory && it.name.endsWith(".class") && !it.name.startsWith("META-INF/") |
| 10 | } |
| 11 | |
| 12 | fun getBinaryAPI(jar: JarFile, visibilityMap: Map<String, ClassVisibility>): List<ClassBinarySignature> = |
| 13 | getBinaryAPI(jar.classEntries().map { entry -> jar.getInputStream(entry) }, visibilityMap) |
| 14 | |
| 15 | fun getBinaryAPI( |
| 16 | classStreams: Sequence<InputStream>, |
| 17 | visibilityMap: Map<String, ClassVisibility> |
| 18 | ): List<ClassBinarySignature> = |
| 19 | classStreams.map { |
| 20 | it.use { stream -> |
| 21 | val classNode = ClassNode() |
| 22 | ClassReader(stream).accept(classNode, ClassReader.SKIP_CODE) |
| 23 | classNode |
| 24 | } |
| 25 | }.map { |
| 26 | with(it) { |
| 27 | val classVisibility = visibilityMap[name] |
| 28 | val classAccess = AccessFlags(effectiveAccess and Opcodes.ACC_STATIC.inv()) |
| 29 | val supertypes = listOf(superName) - "java/lang/Object" + interfaces.sorted() |
| 30 | |
| 31 | val memberSignatures = ( |
| 32 | fields.map { |
| 33 | with(it) { |
| 34 | FieldBinarySignature( |
| 35 | name, |
| 36 | desc, |
| 37 | isPublishedApi(), |
| 38 | AccessFlags(access) |
| 39 | ) |
| 40 | } |
| 41 | } + |
| 42 | methods.map { |
| 43 | with(it) { |
| 44 | MethodBinarySignature( |
| 45 | name, |
| 46 | desc, |
| 47 | isPublishedApi(), |
| 48 | AccessFlags(access) |
| 49 | ) |
| 50 | } |
| 51 | } |
| 52 | ).filter { |
| 53 | it.isEffectivelyPublic(classAccess, classVisibility) |
| 54 | } |
| 55 | |
| 56 | ClassBinarySignature( |
| 57 | name, |
| 58 | superName, |
| 59 | outerClassName, |
| 60 | supertypes, |
| 61 | memberSignatures, |
| 62 | classAccess, |
| 63 | isEffectivelyPublic(classVisibility), |
| 64 | isFileOrMultipartFacade() || isDefaultImpls() |
| 65 | ) |
| 66 | } |
| 67 | }.asIterable().sortedBy { it.name } |
| 68 | |
| 69 | |
| 70 | fun List<ClassBinarySignature>.filterOutNonPublic(nonPublicPackages: List<String> = emptyList()): List<ClassBinarySignature> { |
| 71 | val nonPublicPaths = nonPublicPackages.map { it.replace('.', '/') + '/' } |
| 72 | val classByName = associateBy { it.name } |
| 73 | |
| 74 | fun ClassBinarySignature.isInNonPublicPackage() = |
| 75 | nonPublicPaths.any { name.startsWith(it) } |
| 76 | |
| 77 | fun ClassBinarySignature.isPublicAndAccessible(): Boolean = |
| 78 | isEffectivelyPublic && |
| 79 | (outerName == null || classByName[outerName]?.let { outerClass -> |
| 80 | !(this.access.isProtected && outerClass.access.isFinal) |
| 81 | && outerClass.isPublicAndAccessible() |
| 82 | } ?: true) |
| 83 | |
| 84 | fun supertypes(superName: String) = generateSequence({ classByName[superName] }, { classByName[it.superName] }) |
| 85 | |
| 86 | fun ClassBinarySignature.flattenNonPublicBases(): ClassBinarySignature { |
| 87 | |
| 88 | val nonPublicSupertypes = supertypes(superName).takeWhile { !it.isPublicAndAccessible() }.toList() |
| 89 | if (nonPublicSupertypes.isEmpty()) |
| 90 | return this |
| 91 | |
| 92 | val inheritedStaticSignatures = |
| 93 | nonPublicSupertypes.flatMap { it.memberSignatures.filter { it.access.isStatic } } |
| 94 | |
| 95 | // not covered the case when there is public superclass after chain of private superclasses |
| 96 | return this.copy( |
| 97 | memberSignatures = memberSignatures + inheritedStaticSignatures, |
| 98 | supertypes = supertypes - superName |
| 99 | ) |
| 100 | } |
| 101 | |
| 102 | return filter { !it.isInNonPublicPackage() && it.isPublicAndAccessible() } |
| 103 | .map { it.flattenNonPublicBases() } |
| 104 | .filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty() } |
| 105 | } |
| 106 | |
| 107 | fun List<ClassBinarySignature>.dump() = dump(to = System.out) |
| 108 | |
| 109 | fun <T : Appendable> List<ClassBinarySignature>.dump(to: T): T = to.apply { |
| 110 | this@dump.forEach { |
| 111 | append(it.signature).appendln(" {") |
| 112 | it.memberSignatures.sortedWith(MEMBER_SORT_ORDER).forEach { append("\t").appendln(it.signature) } |
| 113 | appendln("}\n") |
| 114 | } |
| 115 | } |
| 116 | |