Open overlookmotel opened 1 week ago
The minifier is using TraverseCtx
as well.
Just to expand on what Boshen said: The problem he is pointing out is that the generic State
would mean compiler has to build 2 separate copies of Traverse
for the transformer and minifier (as they would have different State
s). All methods of Traverse
and also all the walk_*
functions take a TraverseCtx
.
Traverse
is not so big. walk.rs
is 5600 lines, but it actually boils down to not that much. A lot of it is verbose Rust code that is reduced to only a few assembly operations. e.g. &mut *((node as *mut u8).add(ancestor::OFFSET_PROGRAM_HASHBANG) as *mut Option<Hashbang>)
boils down to just ptr + 16
. So maybe having 2 copies of it is not so bad.
But another approach would be to try to replace the RefCell
s in transformer with GhostCell
s. I'll look into whether can make that work before resorting to generics.
RefCell
s with UnsafeCell
s delivered no significant speed-up. So switching to GhostCell
would be similarly un-impactful.I think it's worthwhile trying the generic State
approach, and seeing what impact it has on both runtime and compile time.
The problem
Currently we have
TransformCtx
which contains shared state between all transforms. It's stored in various transforms'self
as a reference&'ctx TransformCtx<'a>
.Because transforms only have an immutable reference to
TransformCtx
, all mutable state has to be wrapped in aRefCell
. e.g. the shared stackVarDeclarations
introduced in #6170.RefCell
is somewhat costly.Proposed solution
Instead, move
TransformCtx
to live withinTraverseCtx
.TraverseCtx
is a singleton and is passed to every visitor method in all transforms as a mutable&mut TraverseCtx<'a>
. So there only needs to be one instance ofTransformCtx
, and we can get rid of the'ctx
lifetime too.But we want to avoid leaking the transformer's internals into
Traverse
, asTraverse
is meant to be a tool which can be used for other purposes too (e.g. minifier).How?
I propose that we parameterize
TraverseCtx
so it can hold arbitrary state:Preconditions
We currently store
AstBuilder
in bothTransformCtx
andTraverseCtx
. We will need switch all uses of the copy inTransformCtx
over toTraverseCtx
i.e.self.ctx.ast.null_literal(SPAN)
->ctx.ast.null_literal(SPAN)
.We can then remove
AstBuilder
fromTransformCtx
.We will need to do this anyway once we have
NodeId
s on all AST nodes, and needAstBuilder
to be stateful to maintain a counter for next Node ID - which requires there to be only 1 copy ofAstBuilder
in the transformer.