Multi-Round Transform
In Booster, Transformer is designed based on a single-round transform pipeline. However, in some special cases, complete information needs to be collected from bytecode before transform can proceed. Such requirements are not easy to implement through Transformer. To support collecting more information before transform, Booster provides the Collector APIopen in new window and Supervisor APIopen in new window, allowing developers to easily implement this requirement.
What is Collector?
Collector is a functional complement to the unidirectional pipeline of Transformer, used to collect information from the transform pipeline, and also determines whether the pipeline input needs to be updated.
What is Supervisor?
Supervisor is a special type of Collector that only observes the transform pipeline and collects information, but does not affect the update of pipeline input.
Provided Collector
- Collectors.ClassNameCollectoropen in new window
- Collectors.ServiceCollectoropen in new window
- NameCollectoropen in new window
- RegexCollectoropen in new window
Provided Supervisor
- ClassDescriptorSupervisoropen in new window
- ClassNameSupervisoropen in new window
- ServiceSupervisoropen in new window
Collecting SPI Services
@AutoService(ClassTransformer::class)
class MyTransformer : ClassTransformer {
private val services: MutableList<Pair<String, Collection<String>>> = mutableListOf()
override fun onPreTransform(context: TransformContext) {
context.registerCollector(ServiceSupervisor() {
services += it
})
}
override fun transform(context: TransformContext, klass: ClassNode) = klass.apply {
services.forEach { (api, implementation) ->
// TODO ...
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Force Update Transform Inputs
In the example below, NameCollector is used to indicate that we are interested in inputs in the transform pipeline that contain io/johnsonlee/framework/ServiceRegistry.class. If a matching input is found, it will be force-updated during each transform, regardless of whether it's an incremental build. The code is as follows:
@AutoService(ClassTransformer::class)
class ServiceRegistryTransformer : ClassTransformer {
private val services: MutableList<Pair<String, Collection<String>>> = mutableListOf()
override fun onPreTransform(context: TransformContext) {
context.registerCollector(ServiceSupervisor() {
services += it
})
context.registerCollector(NameCollector("io/johnsonlee/framework/ServiceRegistry.class"))
}
override fun transform(context: TransformContext, klass: ClassNode) = klass.apply {
when (klass.name) {
"io/johnsonlee/framework/ServiceRegistry" -> {
// TODO generate service registry
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Custom Collector/Supervisor
Developers can implement the Collectoropen in new window interface according to their needs:
class MyCollector : Collector<String> {
override fun accept(name: String): Boolean = true
override fun collect(name: String, data: () -> ByteArray): String {
val klass = data().asClassNode()
// TODO
return ...
}
}
2
3
4
5
6
7
8
9
10
11
Then, register it in the onPreTransform method:
override fun onPreTransform(context: TransformContext) {
context.registerCollector(MyCollector())
// TODO ...
}
2
3
4
