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*/