Skip to content

Commit

Permalink
Refactor transformer loading
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsonlee committed Jun 7, 2020
1 parent fee18cd commit c6499b0
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import com.android.build.gradle.internal.pipeline.TransformManager
import com.android.build.gradle.internal.pipeline.TransformManager.SCOPE_FULL_PROJECT
import com.didiglobal.booster.annotations.Priority
import com.didiglobal.booster.transform.AbstractKlassPool
import com.didiglobal.booster.transform.Transformer
import org.gradle.api.Project
import java.util.ServiceLoader

/**
* Represents the transform base
Expand All @@ -22,7 +20,7 @@ open class BoosterTransform(val project: Project) : Transform() {
/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
internal val transformers = ServiceLoader.load(Transformer::class.java, project.buildscript.classLoader).sortedBy {
internal val transformers = loadTransformers(project.buildscript.classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.didiglobal.booster.gradle

import com.didiglobal.booster.transform.Transformer
import java.net.URL
import java.nio.charset.StandardCharsets
import java.util.ServiceConfigurationError

private const val PREFIX = "META-INF/services/"

/**
* Load [Transformer]s with the specified [classLoader]
*/
@Throws(ServiceConfigurationError::class)
internal fun loadTransformers(classLoader: ClassLoader): List<Transformer> = classLoader.getResources(PREFIX + Transformer::class.java.name)?.asSequence()?.map(::parse)?.flatten()?.toSet()?.mapNotNull { provider ->
try {
val transformerClass = Class.forName(provider, false, classLoader)
if (!Transformer::class.java.isAssignableFrom(transformerClass)) {
throw ServiceConfigurationError("Provider $provider not a subtype")
}

try {
transformerClass.getConstructor(ClassLoader::class.java).newInstance(classLoader) as Transformer
} catch (e: NoSuchMethodException) {
transformerClass.newInstance() as Transformer
}
} catch (e: ClassNotFoundException) {
throw ServiceConfigurationError("Provider $provider not found")
}
} ?: emptyList()

@Throws(ServiceConfigurationError::class)
private fun parse(u: URL) = try {
u.openStream().bufferedReader(StandardCharsets.UTF_8).readLines().filter {
it.isNotEmpty() && it.isNotBlank() && !it.startsWith('#')
}.map(String::trim).filter(::isJavaClassName)
} catch (e: Throwable) {
emptyList<String>()
}

private fun isJavaClassName(text: String): Boolean {
if (!Character.isJavaIdentifierStart(text[0])) {
throw ServiceConfigurationError("Illegal provider-class name: $text")
}

for (i in 1 until text.length) {
val cp = text.codePointAt(i)
if (!Character.isJavaIdentifierPart(cp) && cp != '.'.toInt()) {
throw ServiceConfigurationError("Illegal provider-class name: $text")
}
}

return true
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,19 @@ class AsmTransformer : Transformer {

private val durations = mutableMapOf<ClassTransformer, Long>()

internal val transformers: Collection<ClassTransformer>

/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
constructor() : this(ServiceLoader.load(ClassTransformer::class.java, AsmTransformer::class.java.classLoader))

/**
* Initialize [AsmTransformer] with [transformers]
*/
constructor(vararg transformers: ClassTransformer) : this(transformers.toList())

/**
* Initialize [AsmTransformer] with [transformers]
*/
constructor(transformers: Iterable<ClassTransformer>) {
this.transformers = transformers.sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
private val classLoader: ClassLoader

internal val transformers: Iterable<ClassTransformer>

constructor() : this(Thread.currentThread().contextClassLoader)

constructor(classLoader: ClassLoader = Thread.currentThread().contextClassLoader) : this(ServiceLoader.load(ClassTransformer::class.java, classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}, classLoader)

constructor(transformers: Iterable<ClassTransformer>, classLoader: ClassLoader = Thread.currentThread().contextClassLoader) {
this.classLoader = classLoader
this.transformers = transformers
}

override fun onPreTransform(context: TransformContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class AsmTransformerTest {
fun checkTransformerOrder() {
val transformer = AsmTransformer()
val origin = transformer.transformers
val sorted = ArrayList(origin).sortedBy {
val sorted = origin.toList().sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
assertEquals(sorted, origin)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,25 +25,19 @@ class JavassistTransformer : Transformer {

private val durations = mutableMapOf<ClassTransformer, Long>()

internal val transformers: Collection<ClassTransformer>

/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
constructor() : this(ServiceLoader.load(ClassTransformer::class.java, JavassistTransformer::class.java.classLoader))

/**
* Initialize [JavassistTransformer] with [transformers]
*/
constructor(vararg transformers: ClassTransformer) : this(transformers.toList())

/**
* Initialize [JavassistTransformer] with [transformers]
*/
constructor(transformers: Iterable<ClassTransformer>) {
this.transformers = transformers.sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
}
private val classLoader: ClassLoader

private val transformers: Iterable<ClassTransformer>

constructor() : this(Thread.currentThread().contextClassLoader)

constructor(classLoader: ClassLoader = Thread.currentThread().contextClassLoader) : this(classLoader, ServiceLoader.load(ClassTransformer::class.java, classLoader).sortedBy {
it.javaClass.getAnnotation(Priority::class.java)?.value ?: 0
})

constructor(classLoader: ClassLoader = Thread.currentThread().contextClassLoader, transformers: Iterable<ClassTransformer>) {
this.classLoader = classLoader
this.transformers = transformers
}

override fun onPreTransform(context: TransformContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ class TransformHelperTest {
try {
val input = File(TransformHelperTest::class.java.classLoader.getResource("booster")!!.file)

TransformHelper(input).transform(AsmTransformer(object : ClassTransformer {
TransformHelper(input).transform(AsmTransformer(listOf(object : ClassTransformer {
override fun transform(context: TransformContext, klass: ClassNode): ClassNode {
println(klass.name)
return klass
}
}))
})))
} catch (e: RuntimeException) {
println(e.localizedMessage)
} catch (e: FileNotFoundException) {
Expand Down

0 comments on commit c6499b0

Please sign in to comment.