vue_compiler_core/
flags.rs

1//! This module defines a collection of flags used for Vue's runtime.
2//! Currently it includes preamble helper and vnode patch flags.
3//! Ideally we can make flags extensible by extracting them to trait.
4//! But currently it works well enough and adding traits makes compiler
5//! bloated with too many generic parameters.
6
7use bitflags::bitflags;
8#[cfg(feature = "serde")]
9use serde::Serialize;
10
11bitflags! {
12    #[derive(Default)]
13    #[cfg_attr(feature = "serde", derive(Serialize))]
14    /// Patch flags are optimization hints generated by the compiler.
15    /// when a block with dynamicChildren is encountered during diff, the algorithm
16    /// enters "optimized mode". In this mode, we know that the vdom is produced by
17    /// a render function generated by the compiler, so the algorithm only needs to
18    /// handle updates explicitly marked by these patch flags.
19    ///
20    /// Check the `patchElement` function in '../../runtime-core/src/renderer.ts' to see how the
21    /// flags are handled during diff.
22    pub struct PatchFlag: i32 {
23        /// Indicates an element with dynamic textContent (children fast path)
24        const TEXT = 1;
25        /// Indicates an element with dynamic class binding.
26        const CLASS = 1 << 1;
27
28        /// Indicates an element with dynamic style
29        /// The compiler pre-compiles static string styles into static objects
30        /// and detects and hoists inline static objects
31        /// e.g. style="color: red" and :style="{ color: 'red' }" both get hoisted as
32        ///   const style = { color: 'red' }
33        ///   render() { return e('div', { style }) }
34        const STYLE = 1 << 2;
35
36        /// Indicates an element that has non-class/style dynamic props.
37        /// Can also be on a component that has any dynamic props (includes
38        /// class/style). when this flag is present, the vnode also has a dynamicProps
39        /// array that contains the keys of the props that may change so the runtime
40        /// can diff them faster (without having to worry about removed props)
41        const PROPS = 1 << 3;
42
43        /// Indicates an element with props with dynamic keys. When keys change, a full
44        /// diff is always needed to remove the old key. This flag is mutually
45        /// exclusive with CLASS, STYLE and PROPS.
46        const FULL_PROPS = 1 << 4;
47
48        /// Indicates an element with event listeners (which need to be attached during hydration)
49        const HYDRATE_EVENTS = 1 << 5;
50
51        /// Indicates a fragment whose children order doesn't change.
52        const STABLE_FRAGMENT = 1 << 6;
53
54        /// Indicates a fragment with keyed or partially keyed children
55        const KEYED_FRAGMENT = 1 << 7;
56
57        /// Indicates a fragment with unkeyed children.
58        const UNKEYED_FRAGMENT = 1 << 8;
59
60        /// Indicates an element that only needs non-props patching, e.g. ref or
61        /// directives (onVnodeXXX hooks). since every patched vnode checks for refs
62        /// and onVnodeXXX hooks, it simply marks the vnode so that a parent block
63        /// will track it.
64        const NEED_PATCH = 1 << 9;
65
66        /// Indicates a component with dynamic slots (e.g. slot that references a v-for
67        /// iterated value, or dynamic slot names).
68        /// Components with this flag are always force updated.
69        const DYNAMIC_SLOTS = 1 << 10;
70
71        /// Indicates a fragment that was created only because the user has placed
72        /// comments at the root level of a template. This is a dev-only flag since
73        /// comments are stripped in production.
74        const DEV_ROOT_FRAGMENT = 1 << 11;
75
76        /// SPECIAL FLAGS -------------------------------------------------------------
77        /// Special flags are negative integers. They are never matched against using
78        /// bitwise operators (bitwise matching should only happen in branches where
79        /// patchFlag > 0), and are mutually exclusive. When checking for a special
80        /// flag, simply check patchFlag === FLAG.
81
82        /// Indicates a hoisted static vnode. This is a hint for hydration to skip
83        /// the entire sub tree since static content never needs to be updated.
84        const HOISTED = -1;
85        /// A special flag that indicates that the diffing algorithm should bail out
86        /// of optimized mode. For example, on block fragments created by renderSlot()
87        /// when encountering non-compiler generated slots (i.e. manually written
88        /// render functions, which should always be fully diffed)
89        /// OR manually cloneVNodes
90        const BAIL = -2;
91    }
92}
93
94/// Static level describes how much an IR node can be statically generated.
95/// Higher levels implies lower levels. e.g. a node that can be stringified
96/// can always be hoisted and skipped for patch.
97#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
98#[cfg_attr(feature = "serde", derive(Serialize))]
99pub enum StaticLevel {
100    NotStatic,
101    CanSkipPatch,
102    CanHoist,
103    CanStringify,
104}
105
106#[repr(u32)]
107#[derive(Clone, Copy, PartialEq, Eq)]
108#[cfg_attr(feature = "serde", derive(Serialize))]
109pub enum RuntimeHelper {
110    Fragment,
111    Teleport,
112    Suspense,
113    KeepAlive,
114    BaseTransition,
115    OpenBlock,
116    CreateBlock,
117    CreateElementBlock,
118    CreateVNode,
119    CreateElementVNode,
120    CreateComment,
121    CreateText,
122    CreateStatic,
123    ResolveComponent,
124    ResolveDynamicComponent,
125    ResolveDirective,
126    ResolveFilter,
127    WithDirectives,
128    RenderList,
129    RenderSlot,
130    CreateSlots,
131    ToDisplayString,
132    MergeProps,
133    NormalizeClass,
134    NormalizeStyle,
135    NormalizeProps,
136    GuardReactiveProps,
137    ToHandlers,
138    Camelize,
139    Capitalize,
140    ToHandlerKey,
141    SetBlockTracking,
142    PushScopeId,
143    PopScopeId,
144    WithScopeId,
145    WithCtx,
146    Unref,
147    IsRef,
148    WithMemo,
149    IsMemoSame,
150}
151
152use RuntimeHelper as RH;
153const HELPERS_IN_HOISTED: &[RH] = &[
154    RH::CreateComment,
155    RH::CreateElementVNode,
156    RH::CreateStatic,
157    RH::CreateText,
158    RH::CreateVNode,
159];
160#[derive(Clone, Default, PartialEq, Eq)]
161#[cfg_attr(feature = "serde", derive(Serialize))]
162pub struct HelperCollector(u64);
163impl HelperCollector {
164    pub fn new() -> Self {
165        Self(0)
166    }
167    pub fn is_empty(&self) -> bool {
168        self.0 == 0 || (cfg!(test) && self.0 == !0)
169    }
170    pub fn collect(&mut self, helper: RuntimeHelper) {
171        self.0 |= 1 << (helper as u64);
172    }
173    pub fn contains(&self, helper: RuntimeHelper) -> bool {
174        (self.0 & (1 << helper as u64)) != 0
175    }
176    pub fn hoist_helpers(&self) -> Self {
177        let mut n = Self(0);
178        for &rh in HELPERS_IN_HOISTED {
179            if self.contains(rh) {
180                n.collect(rh);
181            }
182        }
183        n
184    }
185    // ignore missing helpers in unit testing
186    #[cfg(test)]
187    pub fn ignore_missing(&mut self) {
188        self.0 = !0;
189    }
190}
191pub struct HelperIter(u64);
192impl Iterator for HelperIter {
193    type Item = RuntimeHelper;
194    fn next(&mut self) -> Option<Self::Item> {
195        if cfg!(test) && self.0 == !0 {
196            return None;
197        }
198        if self.0 == 0 {
199            return None;
200        }
201        let r = self.0.trailing_zeros();
202        self.0 ^= 1 << r;
203        unsafe { std::mem::transmute(r) }
204    }
205    fn size_hint(&self) -> (usize, Option<usize>) {
206        let bits = self.0.count_ones() as usize;
207        (bits, Some(bits))
208    }
209}
210impl ExactSizeIterator for HelperIter {}
211
212impl IntoIterator for HelperCollector {
213    type Item = RuntimeHelper;
214    type IntoIter = HelperIter;
215    fn into_iter(self) -> Self::IntoIter {
216        HelperIter(self.0)
217    }
218}
219
220/// PreambleHelper is a collection of JavaScript imports at the head of output
221/// e.g. v-for needs a list looping helper to make vdom
222/// preamble helper needs collect helper when traversing template ast
223/// and generates corresponding JavaScript imports in compilation output
224impl RuntimeHelper {
225    pub fn generate_imports(&self) -> String {
226        todo!()
227    }
228    pub fn helper_str(&self) -> &'static str {
229        use RuntimeHelper::*;
230        match *self {
231            Fragment => "Fragment",
232            Teleport => "Teleport",
233            Suspense => "Suspense",
234            KeepAlive => "KeepAlive",
235            BaseTransition => "BaseTransition",
236            OpenBlock => "openBlock",
237            CreateBlock => "createBlock",
238            CreateElementBlock => "createElementBlock",
239            CreateVNode => "createVNode",
240            CreateElementVNode => "createElementVNode",
241            CreateComment => "createCommentVNode",
242            CreateText => "createTextVNode",
243            CreateStatic => "createStaticVNode",
244            ResolveComponent => "resolveComponent",
245            ResolveDynamicComponent => "resolveDynamicComponent",
246            ResolveDirective => "resolveDirective",
247            ResolveFilter => "resolveFilter",
248            WithDirectives => "withDirectives",
249            RenderList => "renderList",
250            RenderSlot => "renderSlot",
251            CreateSlots => "createSlots",
252            ToDisplayString => "toDisplayString",
253            MergeProps => "mergeProps",
254            NormalizeClass => "normalizeClass",
255            NormalizeStyle => "normalizeStyle",
256            NormalizeProps => "normalizeProps",
257            GuardReactiveProps => "guardReactiveProps",
258            ToHandlers => "toHandlers",
259            Camelize => "camelize",
260            Capitalize => "capitalize",
261            ToHandlerKey => "toHandlerKey",
262            SetBlockTracking => "setBlockTracking",
263            PushScopeId => "pushScopeId",
264            PopScopeId => "popScopeId",
265            WithScopeId => "withScopeId",
266            WithCtx => "withCtx",
267            Unref => "unref",
268            IsRef => "isRef",
269            WithMemo => "withMemo",
270            IsMemoSame => "isMemoSame",
271        }
272    }
273}
274
275#[repr(u8)]
276#[derive(Clone, Copy, Debug)]
277#[cfg_attr(feature = "serde", derive(Serialize))]
278pub enum SlotFlag {
279    /// Stable slots that only reference slot props or context state. The slot
280    /// can fully capture its own dependencies so when passed down the parent won't
281    /// need to force the child to update.
282    Stable = 1,
283    /// Slots that reference scope variables (v-for or an outer slot prop), or
284    /// has conditional structure (v-if, v-for). The parent will need to force
285    /// the child to update because the slot does not fully capture its dependencies.
286    Dynamic = 2,
287    /// `<slot/>` being forwarded into a child component. Whether the parent needs
288    /// to update the child is dependent on what kind of slots the parent itself
289    /// received. This has to be refined at runtime, when the child's vnode
290    /// is being created (in `normalizeChildren`)
291    Forwarded = 3,
292}
293
294/*
295// we can extend helper by extracting trait like below.
296// but it does not pay off now.
297pub trait PreambleHelper {
298    fn generate_imports(&self) -> String;
299    fn helper_str(&self) -> &'static str;
300}
301*/