The current callback-based approach isn't really amenable to multiple passes. In some transformers we put too much logic in preTransform() and postTransform().
One idea is for each transformer to just override a single method again, and then call methods that accept lambdas - like forEachField { ... }, forEachInsnList { ... }, etc.
Or perhaps provide methods that return a Sequence<T>. It isn't yet obvious what T would be (e.g. for a field we'd actually want the FieldNode, ClassNode and Library, so Sequence<FieldNode> isn't good enough). This would be useful for transformers that need to find a single method or group of fields (e.g. the opaque predicate transformer finding the control fields) as methods like filter() could be used.
What should these methods be defined on? The ClassPath object?
Another idea is separate Pass classes, with each Transformer being a list of passes. I'm not sure how we'd pass data between them. e.g. we could have Pass<I, O> with the first pass having I=Unit and the last pass having O=Unit, and the I/O types matching up between adjacent passes? Encoding that in the type system is probably difficult though.
The current callback-based approach isn't really amenable to multiple passes. In some transformers we put too much logic in `preTransform()` and `postTransform()`.
One idea is for each transformer to just override a single method again, and then call methods that accept lambdas - like `forEachField { ... }`, `forEachInsnList { ... }`, etc.
Or perhaps provide methods that return a `Sequence<T>`. It isn't yet obvious what T would be (e.g. for a field we'd actually want the `FieldNode`, `ClassNode` and `Library`, so `Sequence<FieldNode>` isn't good enough). This would be useful for transformers that need to find a single method or group of fields (e.g. the opaque predicate transformer finding the control fields) as methods like `filter()` could be used.
What should these methods be defined on? The `ClassPath` object?
Another idea is separate `Pass` classes, with each `Transformer` being a list of passes. I'm not sure how we'd pass data between them. e.g. we could have `Pass<I, O>` with the first pass having `I=Unit` and the last pass having `O=Unit`, and the `I`/`O` types matching up between adjacent passes? Encoding that in the type system is probably difficult though.
The current callback-based approach isn't really amenable to multiple passes. In some transformers we put too much logic in
preTransform()
andpostTransform()
.One idea is for each transformer to just override a single method again, and then call methods that accept lambdas - like
forEachField { ... }
,forEachInsnList { ... }
, etc.Or perhaps provide methods that return a
Sequence<T>
. It isn't yet obvious what T would be (e.g. for a field we'd actually want theFieldNode
,ClassNode
andLibrary
, soSequence<FieldNode>
isn't good enough). This would be useful for transformers that need to find a single method or group of fields (e.g. the opaque predicate transformer finding the control fields) as methods likefilter()
could be used.What should these methods be defined on? The
ClassPath
object?Another idea is separate
Pass
classes, with eachTransformer
being a list of passes. I'm not sure how we'd pass data between them. e.g. we could havePass<I, O>
with the first pass havingI=Unit
and the last pass havingO=Unit
, and theI
/O
types matching up between adjacent passes? Encoding that in the type system is probably difficult though.