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

Provided Supervisor

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 ...
        }
    }
}
1
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
            }
        }
    }

}
1
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 ...
    }

}
1
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 ...
}
1
2
3
4