vue_compiler_core/transformer/
pass.rs

1use super::{BaseInfo, BaseTransformer, BaseVNode, ConvertInfo, CoreTransformer, IRRoot, Js, C};
2use crate::Name;
3use rustc_hash::FxHashMap;
4
5pub trait CorePass<T: ConvertInfo> {
6    fn enter_root(&mut self, _: &mut IRRoot<T>) {}
7    fn exit_root(&mut self, _: &mut IRRoot<T>) {}
8    fn enter_text(&mut self, _: &mut C::TextIR<T>) {}
9    fn exit_text(&mut self, _: &mut C::TextIR<T>) {}
10    fn enter_if(&mut self, _: &mut C::IfNodeIR<T>) {}
11    fn exit_if(&mut self, _: &mut C::IfNodeIR<T>) {}
12    fn enter_for(&mut self, _: &mut C::ForNodeIR<T>) {}
13    fn exit_for(&mut self, _: &mut C::ForNodeIR<T>) {}
14    fn enter_vnode(&mut self, _: &mut C::VNodeIR<T>) {}
15    fn exit_vnode(&mut self, _: &mut C::VNodeIR<T>) {}
16    fn enter_slot_outlet(&mut self, _: &mut C::RenderSlotIR<T>) {}
17    fn exit_slot_outlet(&mut self, _: &mut C::RenderSlotIR<T>) {}
18    fn enter_v_slot(&mut self, _: &mut C::VSlotIR<T>) {}
19    fn exit_v_slot(&mut self, _: &mut C::VSlotIR<T>) {}
20    fn enter_slot_fn(&mut self, _: &mut C::Slot<T>) {}
21    fn exit_slot_fn(&mut self, _: &mut C::Slot<T>) {}
22    fn enter_js_expr(&mut self, _: &mut T::JsExpression) {}
23    fn exit_js_expr(&mut self, _: &mut T::JsExpression) {}
24    /// only v-for or slot fn
25    fn enter_fn_param(&mut self, _: &mut T::JsExpression) {}
26    /// only v-for or slot fn
27    fn exit_fn_param(&mut self, _: &mut T::JsExpression) {}
28    fn enter_comment(&mut self, _: &mut T::CommentType) {}
29    fn exit_comment(&mut self, _: &mut T::CommentType) {}
30}
31
32// TODO: refactor this to Future like chain
33pub struct MergedPass<'b, P> {
34    passes: &'b mut [P],
35}
36
37impl<'b, P> MergedPass<'b, P> {
38    pub fn new(passes: &'b mut [P]) -> Self {
39        Self { passes }
40    }
41    fn enter<F>(&mut self, mut f: F)
42    where
43        F: FnMut(&mut P),
44    {
45        for p in &mut self.passes.iter_mut() {
46            f(p)
47        }
48    }
49    fn exit<F>(&mut self, mut f: F)
50    where
51        F: FnMut(&mut P),
52    {
53        for p in self.passes.iter_mut().rev() {
54            f(p)
55        }
56    }
57}
58
59impl<'b, T> CorePass<T> for MergedPass<'b, &'b mut dyn CorePass<T>>
60where
61    T: ConvertInfo,
62{
63    fn enter_root(&mut self, r: &mut IRRoot<T>) {
64        self.enter(|p| p.enter_root(r))
65    }
66    fn exit_root(&mut self, r: &mut IRRoot<T>) {
67        self.exit(|p| p.exit_root(r))
68    }
69    fn enter_text(&mut self, t: &mut C::TextIR<T>) {
70        self.enter(|p| p.enter_text(t))
71    }
72    fn exit_text(&mut self, t: &mut C::TextIR<T>) {
73        self.exit(|p| p.exit_text(t))
74    }
75    fn enter_if(&mut self, i: &mut C::IfNodeIR<T>) {
76        self.enter(|p| p.enter_if(i))
77    }
78    fn exit_if(&mut self, i: &mut C::IfNodeIR<T>) {
79        self.exit(|p| p.exit_if(i))
80    }
81    fn enter_for(&mut self, f: &mut C::ForNodeIR<T>) {
82        self.enter(|p| p.enter_for(f))
83    }
84    fn exit_for(&mut self, f: &mut C::ForNodeIR<T>) {
85        self.exit(|p| p.exit_for(f))
86    }
87    fn enter_vnode(&mut self, v: &mut C::VNodeIR<T>) {
88        self.enter(|p| p.enter_vnode(v))
89    }
90    fn exit_vnode(&mut self, v: &mut C::VNodeIR<T>) {
91        self.exit(|p| p.exit_vnode(v))
92    }
93    fn enter_slot_outlet(&mut self, r: &mut C::RenderSlotIR<T>) {
94        self.enter(|p| p.enter_slot_outlet(r))
95    }
96    fn exit_slot_outlet(&mut self, r: &mut C::RenderSlotIR<T>) {
97        self.exit(|p| p.exit_slot_outlet(r))
98    }
99    fn enter_v_slot(&mut self, s: &mut C::VSlotIR<T>) {
100        self.enter(|p| p.enter_v_slot(s))
101    }
102    fn exit_v_slot(&mut self, s: &mut C::VSlotIR<T>) {
103        self.exit(|p| p.exit_v_slot(s))
104    }
105    fn enter_slot_fn(&mut self, s: &mut C::Slot<T>) {
106        self.enter(|p| p.enter_slot_fn(s))
107    }
108    fn exit_slot_fn(&mut self, s: &mut C::Slot<T>) {
109        self.exit(|p| p.exit_slot_fn(s))
110    }
111    fn enter_js_expr(&mut self, e: &mut T::JsExpression) {
112        self.enter(|p| p.enter_js_expr(e))
113    }
114    fn exit_js_expr(&mut self, e: &mut T::JsExpression) {
115        self.exit(|p| p.exit_js_expr(e))
116    }
117    fn enter_fn_param(&mut self, m: &mut T::JsExpression) {
118        self.enter(|p| p.enter_fn_param(m))
119    }
120    fn exit_fn_param(&mut self, m: &mut T::JsExpression) {
121        self.exit(|p| p.exit_fn_param(m))
122    }
123    fn enter_comment(&mut self, c: &mut T::CommentType) {
124        self.enter(|p| p.enter_comment(c))
125    }
126    fn exit_comment(&mut self, c: &mut T::CommentType) {
127        self.exit(|p| p.exit_comment(c))
128    }
129}
130
131pub trait CorePassExt<T: ConvertInfo, Shared> {
132    fn enter_js_expr(&mut self, _: &mut T::JsExpression, _: &mut Shared) {}
133    fn exit_js_expr(&mut self, _: &mut T::JsExpression, _: &mut Shared) {}
134    fn enter_fn_param(&mut self, _: &mut T::JsExpression, _: &mut Shared) {}
135    fn exit_fn_param(&mut self, _: &mut T::JsExpression, _: &mut Shared) {}
136
137    fn enter_vnode(&mut self, _: &mut C::VNodeIR<T>, _: &mut Shared) {}
138    fn exit_vnode(&mut self, _: &mut C::VNodeIR<T>, _: &mut Shared) {}
139}
140
141type Identifiers<'a> = FxHashMap<Name<'a>, usize>;
142#[derive(Default)]
143pub struct Scope<'a> {
144    pub identifiers: Identifiers<'a>,
145}
146
147/// Check if an IR contains expressions that reference current context scope ids
148/// e.g. identifiers referenced in the scope can skip prefixing
149// TODO: has_ref will repeatedly call on vnode regardless if new ids are introduced.
150// So it's a O(d^2) complexity where d is the depth of nested v-slot component.
151// we can optimize it by tracking how many IDs are introduced and skip unnecessary call
152// in practice it isn't a problem because stack overflow happens way faster :/
153impl<'a> Scope<'a> {
154    pub fn has_identifier(&self, id: Name<'a>) -> bool {
155        self.identifiers.contains_key(id)
156    }
157    pub fn add_identifier(&mut self, id: Name<'a>) {
158        *self.identifiers.entry(id).or_default() += 1;
159    }
160    pub fn remove_identifier(&mut self, id: Name<'a>) {
161        *self.identifiers.entry(id).or_default() -= 1;
162    }
163    pub fn has_ref_in_vnode(&self, node: &mut BaseVNode<'a>) -> bool {
164        if self.identifiers.is_empty() {
165            return false;
166        }
167        let mut ref_finder = RefFinder(&self.identifiers, false);
168        BaseTransformer::transform_vnode(node, &mut ref_finder);
169        ref_finder.1
170    }
171}
172struct RefFinder<'a, 'b>(&'b Identifiers<'a>, bool);
173// TODO: implement interruptible transformer for early return
174// TODO: current implmentaion has false alarms in code like below
175// <comp v-for="a in source">
176//  <p v-for="a in s">{{a}}</p> <- expect stable, got dynamic
177// </comp>
178// but it is fine since ref_usage is only for optimization
179impl<'a, 'b> CorePass<BaseInfo<'a>> for RefFinder<'a, 'b> {
180    fn enter_js_expr(&mut self, e: &mut Js<'a>) {
181        if let Js::Simple(e, _) = e {
182            if self.0.contains_key(e.raw) {
183                self.1 = true;
184            }
185        }
186    }
187}
188
189pub struct SharedInfoPasses<'b, Pass, Shared> {
190    pub passes: MergedPass<'b, Pass>,
191    pub shared_info: Shared,
192}
193// TODO: add transform used
194impl<'b, T, Shared> CorePass<T> for SharedInfoPasses<'b, &'b mut dyn CorePassExt<T, Shared>, Shared>
195where
196    T: ConvertInfo,
197{
198    fn enter_js_expr(&mut self, e: &mut T::JsExpression) {
199        let shared = &mut self.shared_info;
200        self.passes.enter(|p| p.enter_js_expr(e, shared));
201    }
202    fn exit_js_expr(&mut self, e: &mut T::JsExpression) {
203        let shared = &mut self.shared_info;
204        self.passes.exit(|p| p.exit_js_expr(e, shared));
205    }
206    fn enter_fn_param(&mut self, prm: &mut T::JsExpression) {
207        let shared = &mut self.shared_info;
208        self.passes.enter(|p| p.enter_fn_param(prm, shared));
209    }
210    fn exit_fn_param(&mut self, prm: &mut T::JsExpression) {
211        let shared = &mut self.shared_info;
212        self.passes.exit(|p| p.exit_fn_param(prm, shared));
213    }
214    fn enter_vnode(&mut self, v: &mut C::VNodeIR<T>) {
215        let shared = &mut self.shared_info;
216        self.passes.enter(|p| p.enter_vnode(v, shared))
217    }
218    fn exit_vnode(&mut self, v: &mut C::VNodeIR<T>) {
219        let shared = &mut self.shared_info;
220        self.passes.exit(|p| p.exit_vnode(v, shared))
221    }
222}