Skip to main content

turbopack_ecmascript/analyzer/jsvalue/
mod.rs

1use std::{
2    fmt::{self},
3    hash::Hash,
4    mem::take,
5    sync::Arc,
6};
7
8use anyhow::Result;
9use bumpalo::boxed::Box as BumpBox;
10use num_bigint::BigInt;
11use smallvec::SmallVec;
12use swc_core::ecma::{ast::Id, atoms::Atom};
13use turbo_rcstr::{RcStr, rcstr};
14use turbopack_core::compile_time_info::{
15    CompileTimeDefineValue, DefinableNameSegmentRef, DefinableNameSegmentRefs, FreeVarReference,
16};
17
18use crate::analyzer::{
19    Bump, BumpVec, WellKnownFunctionKind, WellKnownObjectKind,
20    graph::{EvalContext, VarGraph},
21};
22
23mod constants;
24mod display;
25mod explain;
26mod normalize;
27mod predicates;
28mod similar;
29mod traverse;
30
31use constants::JsValueMetaKind;
32pub use constants::*;
33
34/// Sum of [`JsValue::total_nodes`] across a slice of values.
35fn total_nodes(vec: &[JsValue<'_>]) -> u32 {
36    vec.iter().map(|v| v.total_nodes()).sum::<u32>()
37}
38
39/// Join `items` for display, switching to a multi-line layout when the content
40/// is long. Shared by the `Display` impl and the `explain` formatting.
41fn pretty_join(
42    items: &[String],
43    indent_depth: usize,
44    single_line_separator: &str,
45    multi_line_separator_end: &str,
46    multi_line_separator_start: &str,
47) -> String {
48    let multi_line = items
49        .iter()
50        .any(|item| item.len() > 50 || item.contains('\n'))
51        || items
52            .iter()
53            .map(|item| item.len() + single_line_separator.len())
54            .sum::<usize>()
55            > 100;
56    if !multi_line {
57        items.join(single_line_separator)
58    } else if multi_line_separator_start.is_empty() {
59        format!(
60            "\n{}{}\n{}",
61            "    ".repeat(indent_depth + 1),
62            items.join(&format!(
63                "{multi_line_separator_end}\n{}",
64                "    ".repeat(indent_depth + 1)
65            )),
66            "    ".repeat(indent_depth)
67        )
68    } else {
69        format!(
70            "\n{}{multi_line_separator_start}{}\n{}",
71            " ".repeat(indent_depth * 4 + 4 - multi_line_separator_start.len()),
72            items.join(&format!(
73                "{multi_line_separator_end}\n{}{multi_line_separator_start}",
74                " ".repeat(indent_depth * 4 + 4 - multi_line_separator_start.len())
75            )),
76            "    ".repeat(indent_depth)
77        )
78    }
79}
80
81/// TODO: Use `Arc`
82///
83/// There are 4 kinds of values: Leaves, Nested, Operations, and Placeholders
84/// (see `JsValueMetaKind` for details). Values are processed in two phases:
85/// - Analyze phase: We convert AST into `JsValue`s. We don't have contextual information so we need
86///   to insert placeholders to represent that.
87/// - Link phase: We try to reduce a value to a constant value. The link phase has 5 substeps that
88///   are executed on each node in the graph depth-first. When a value is modified, we need to visit
89///   the new children again.
90/// - Replace variables with their values. This replaces [JsValue::Variable]. No variable should be
91///   remaining after that.
92/// - Replace placeholders with contextual information. This usually replaces [JsValue::FreeVar] and
93///   [JsValue::Module]. Some [JsValue::Call] on well- known functions might also be replaced. No
94///   free vars or modules should be remaining after that.
95/// - Replace operations on well-known objects and functions. This handles [JsValue::Call] and
96///   [JsValue::Member] on well-known objects and functions.
97/// - Replace all built-in functions with their values when they are compile-time constant.
98/// - For optimization, any nested operations are replaced with [JsValue::Unknown]. So only one
99///   layer of operation remains. Any remaining operation or placeholder can be treated as unknown.
100#[derive(Debug, Hash, PartialEq)]
101pub enum JsValue<'a> {
102    // LEAF VALUES
103    // ----------------------------
104    /// A constant primitive value.
105    Constant(ConstantValue),
106    /// A constant URL object.
107    Url(ConstantString, JsValueUrlKind),
108    /// Some kind of well-known object
109    /// (must not be an array, otherwise Array.concat needs to be changed)
110    WellKnownObject(WellKnownObjectKind),
111    /// Some kind of well-known function
112    WellKnownFunction(WellKnownFunctionKind<'a>),
113    /// Not-analyzable value. Might contain the original value for additional
114    /// info. Has a reason string for explanation.
115    Unknown {
116        original_value: Option<Arc<JsValue<'a>>>,
117        reason: RcStr,
118        has_side_effects: bool,
119    },
120
121    // NESTED VALUES
122    // ----------------------------
123    /// An array of nested values
124    Array {
125        total_nodes: u32,
126        items: BumpVec<'a, JsValue<'a>>,
127        mutable: bool,
128    },
129    /// An object of nested values
130    Object {
131        total_nodes: u32,
132        parts: BumpVec<'a, ObjectPart<'a>>,
133        mutable: bool,
134    },
135    /// A list of alternative values
136    Alternatives {
137        total_nodes: u32,
138        values: BumpVec<'a, JsValue<'a>>,
139        logical_property: Option<LogicalProperty>,
140    },
141    /// A function reference. The return value might contain [JsValue::Argument]
142    /// placeholders that need to be replaced when calling this function.
143    /// `(total_node_count, func_ident, return_value)`
144    Function(u32, u32, BumpBox<'a, JsValue<'a>>),
145
146    // OPERATIONS
147    // ----------------------------
148    /// A string concatenation of values.
149    /// `foo.${unknownVar}.js` => 'foo' + Unknown + '.js'
150    Concat(u32, BumpVec<'a, JsValue<'a>>),
151    /// An addition of values.
152    /// This can be converted to [JsValue::Concat] if the type of the variable
153    /// is string.
154    Add(u32, BumpVec<'a, JsValue<'a>>),
155    /// Logical negation `!expr`
156    Not(u32, BumpBox<'a, JsValue<'a>>),
157    /// Logical operator chain e. g. `expr && expr`
158    Logical(u32, LogicalOperator, BumpVec<'a, JsValue<'a>>),
159    /// Binary expression e. g. `expr == expr`
160    Binary(
161        u32,
162        BumpBox<'a, JsValue<'a>>,
163        BinaryOperator,
164        BumpBox<'a, JsValue<'a>>,
165    ),
166    /// A constructor call. `(total_node_count, list)` — see [`CallList`].
167    New(u32, CallList<'a>),
168    /// A function call without a `this` context. `(total_node_count, list)` — see [`CallList`].
169    Call(u32, CallList<'a>),
170    /// A super call to the parent constructor.
171    /// `(total_node_count, args)`
172    SuperCall(u32, BumpBox<'a, [JsValue<'a>]>),
173    /// A function call with a `this` context. `(total_node_count, list)` — see [`MemberCallList`].
174    MemberCall(u32, MemberCallList<'a>),
175    /// A member access `obj[prop]`
176    /// `(total_node_count, obj, prop)`
177    Member(u32, BumpBox<'a, JsValue<'a>>, BumpBox<'a, JsValue<'a>>),
178    /// A tenary operator `test ? cons : alt`
179    /// `(total_node_count, test, cons, alt)`
180    Tenary(
181        u32,
182        BumpBox<'a, JsValue<'a>>,
183        BumpBox<'a, JsValue<'a>>,
184        BumpBox<'a, JsValue<'a>>,
185    ),
186    /// A promise resolving to some value
187    /// `(total_node_count, value)`
188    Promise(u32, BumpBox<'a, JsValue<'a>>),
189    /// An await call (potentially) unwrapping a promise.
190    /// `(total_node_count, value)`
191    Awaited(u32, BumpBox<'a, JsValue<'a>>),
192
193    /// A for-of loop
194    ///
195    /// `(total_node_count, iterable)`
196    Iterated(u32, BumpBox<'a, JsValue<'a>>),
197
198    /// A `typeof` expression.
199    ///
200    /// `(total_node_count, operand)`
201    TypeOf(u32, BumpBox<'a, JsValue<'a>>),
202
203    /// A `in` expression `left in right`
204    /// `(total_node_count, left, right)`
205    In(u32, BumpBox<'a, JsValue<'a>>, BumpBox<'a, JsValue<'a>>),
206
207    // PLACEHOLDERS
208    // ----------------------------
209    /// A reference to a variable.
210    Variable(Id),
211    /// A reference to an function argument.
212    /// (func_ident, arg_index)
213    Argument(u32, usize),
214    // TODO no predefined kinds, only Atom
215    /// A reference to a free variable.
216    FreeVar(Atom),
217    /// This is a reference to a imported module.
218    Module(ModuleValue),
219}
220
221/// Storage for [`JsValue::MemberCall`]: `[args..., prop, obj]`.
222///
223/// The reversed layout (obj/prop at the tail) is what makes the `replace_builtin`
224/// fallthrough path cheap: `pop` obj, `pop` prop, and the remaining `Vec` **is** the args
225/// `Vec` with no reallocation.
226///
227/// The custom `Debug` impl re-emits the pre-refactor derived shape
228/// (`MemberCall(total, obj, prop, [args])`) by writing obj/prop/args as siblings inside the
229/// parent's `debug_tuple`. This keeps fixture snapshots identical to the 4-tuple-payload
230/// version without forcing a hand-written `Debug` on every `JsValue` arm.
231#[derive(Hash, PartialEq)]
232pub struct MemberCallList<'a>(BumpVec<'a, JsValue<'a>>);
233
234impl fmt::Debug for MemberCallList<'_> {
235    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236        // Layout: [args..., prop, obj]
237        let n = self.0.len();
238        let obj = &self.0[n - 1];
239        let prop = &self.0[n - 2];
240        let args = &self.0[..n - 2];
241        if f.alternate() {
242            // The parent `debug_tuple` writes the field's leading indent for us (via
243            // PadAdapter) and appends `,\n` after we return. Emitting
244            // `<obj>,\n<prop>,\n<args>` with no trailing comma makes us appear as three
245            // sibling fields in the parent's pretty-print output.
246            writeln!(f, "{obj:#?},")?;
247            writeln!(f, "{prop:#?},")?;
248            write!(f, "{args:#?}")
249        } else {
250            write!(f, "{obj:?}, {prop:?}, {args:?}")
251        }
252    }
253}
254
255impl<'a> MemberCallList<'a> {
256    fn from_parts(
257        arena: &'a Bump,
258        obj: JsValue<'a>,
259        prop: JsValue<'a>,
260        args: BumpVec<'a, JsValue<'a>>,
261    ) -> Self {
262        let mut list = args;
263        list.push(arena, prop);
264        list.push(arena, obj);
265        Self(list)
266    }
267
268    fn from_iter<I>(arena: &'a Bump, obj: JsValue<'a>, prop: JsValue<'a>, args: I) -> Self
269    where
270        I: IntoIterator<Item = JsValue<'a>>,
271        I::IntoIter: ExactSizeIterator,
272    {
273        let args = args.into_iter();
274        let mut list = BumpVec::with_capacity_in(arena, args.len() + 2);
275        list.extend(arena, args);
276        list.push(arena, prop);
277        list.push(arena, obj);
278        Self(list)
279    }
280
281    fn clone_in(&self, arena: &'a Bump) -> Self {
282        Self(BumpVec::from_iter_in(
283            arena,
284            self.0.iter().map(|v| v.clone_in(arena)),
285        ))
286    }
287
288    /// The receiver object. Lives at the tail of the underlying `Vec`.
289    pub fn obj(&self) -> &JsValue<'a> {
290        &self.0[self.0.len() - 1]
291    }
292
293    pub fn obj_mut(&mut self) -> &mut JsValue<'a> {
294        let n = self.0.len();
295        &mut self.0[n - 1]
296    }
297
298    /// The accessed property. Lives one slot before `obj`.
299    pub fn prop(&self) -> &JsValue<'a> {
300        &self.0[self.0.len() - 2]
301    }
302
303    pub fn prop_mut(&mut self) -> &mut JsValue<'a> {
304        let n = self.0.len();
305        &mut self.0[n - 2]
306    }
307
308    /// The call arguments — everything before `prop` and `obj`.
309    pub fn args(&self) -> &[JsValue<'a>] {
310        let n = self.0.len();
311        &self.0[..n - 2]
312    }
313
314    pub fn args_mut(&mut self) -> &mut [JsValue<'a>] {
315        let n = self.0.len();
316        &mut self.0[..n - 2]
317    }
318
319    /// Borrow `args`, `prop`, and `obj` simultaneously as mutable references. The single
320    /// `Vec` storage means callers can't get these via separate accessor calls.
321    pub fn as_parts_mut(&mut self) -> (&mut [JsValue<'a>], &mut JsValue<'a>, &mut JsValue<'a>) {
322        let n = self.0.len();
323        let (args, tail) = self.0.split_at_mut(n - 2);
324        let (prop_slot, obj_slot) = tail.split_at_mut(1);
325        (args, &mut prop_slot[0], &mut obj_slot[0])
326    }
327
328    /// Take everything out. The returned `args` `Vec` reuses the original allocation — no
329    /// copy. That's the point of storing obj/prop at the tail.
330    pub fn into_parts(mut self) -> (JsValue<'a>, JsValue<'a>, BumpVec<'a, JsValue<'a>>) {
331        let obj = self.0.pop().unwrap();
332        let prop = self.0.pop().unwrap();
333        (obj, prop, self.0)
334    }
335
336    fn total_nodes(&self) -> u32 {
337        total_nodes(&self.0)
338    }
339
340    fn for_each_children(&self, visitor: &mut impl FnMut(&JsValue<'a>)) {
341        self.0.iter().for_each(visitor)
342    }
343    fn for_each_children_mut(
344        &mut self,
345        visitor: &mut impl FnMut(&mut JsValue<'a>) -> bool,
346    ) -> bool {
347        let mut modified = false;
348        for child in self.0.iter_mut() {
349            if visitor(child) {
350                modified = true;
351            }
352        }
353
354        modified
355    }
356
357    fn all_similar(l: &Self, r: &Self, depth: usize) -> bool {
358        JsValue::all_similar(&l.0, &r.0, depth)
359    }
360}
361
362/// Storage for [`JsValue::Call`] and [`JsValue::New`]: `[args..., callee]`.
363///
364/// Same trick as [`MemberCallList`]: keeping the callee at the tail lets
365/// `replace_builtin`-style fallthrough paths `pop` it off cheaply and reuse the remaining
366/// `Vec` as the owned args with no reallocation.
367///
368/// The custom `Debug` impl re-emits the pre-refactor `(callee, [args])` shape so fixture
369/// snapshots remain identical to the 3-tuple-payload version.
370#[derive(Hash, PartialEq)]
371pub struct CallList<'a>(BumpVec<'a, JsValue<'a>>);
372
373impl fmt::Debug for CallList<'_> {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        // Layout: [args..., callee]
376        let n = self.0.len();
377        let callee = &self.0[n - 1];
378        let args = &self.0[..n - 1];
379        if f.alternate() {
380            // Same trick as MemberCallList: emit two sibling fields inside the parent
381            // `debug_tuple`'s pretty-print output.
382            writeln!(f, "{callee:#?},")?;
383            write!(f, "{args:#?}")
384        } else {
385            write!(f, "{callee:?}, {args:?}")
386        }
387    }
388}
389
390impl<'a> CallList<'a> {
391    fn from_parts(arena: &'a Bump, callee: JsValue<'a>, args: BumpVec<'a, JsValue<'a>>) -> Self {
392        let mut list = args;
393        list.push(arena, callee);
394        Self(list)
395    }
396
397    fn from_iter<I>(arena: &'a Bump, callee: JsValue<'a>, args: I) -> Self
398    where
399        I: IntoIterator<Item = JsValue<'a>>,
400        I::IntoIter: ExactSizeIterator,
401    {
402        let args = args.into_iter();
403        let mut list = BumpVec::with_capacity_in(arena, args.len() + 1);
404        list.extend(arena, args);
405        list.push(arena, callee);
406        Self(list)
407    }
408
409    fn clone_in(&self, arena: &'a Bump) -> Self {
410        Self(BumpVec::from_iter_in(
411            arena,
412            self.0.iter().map(|v| v.clone_in(arena)),
413        ))
414    }
415
416    /// The callee. Lives at the tail of the underlying `Vec`.
417    pub fn callee(&self) -> &JsValue<'a> {
418        self.0.last().expect("CallList must always have a callee")
419    }
420
421    pub fn callee_mut(&mut self) -> &mut JsValue<'a> {
422        self.0
423            .last_mut()
424            .expect("CallList must always have a callee")
425    }
426
427    /// The call arguments — everything before the callee.
428    pub fn args(&self) -> &[JsValue<'a>] {
429        let n = self.0.len();
430        &self.0[..n - 1]
431    }
432
433    pub fn args_mut(&mut self) -> &mut [JsValue<'a>] {
434        let n = self.0.len();
435        &mut self.0[..n - 1]
436    }
437
438    /// Borrow `args` and `callee` simultaneously as mutable references. The single `Vec`
439    /// storage means callers can't get these via separate accessor calls.
440    pub fn as_parts_mut(&mut self) -> (&mut [JsValue<'a>], &mut JsValue<'a>) {
441        let n = self.0.len();
442        let (args, callee_slot) = self.0.split_at_mut(n - 1);
443        (args, &mut callee_slot[0])
444    }
445
446    /// Take everything out. The returned `args` `Vec` reuses the original allocation — no
447    /// copy. That's the point of storing the callee at the tail.
448    pub fn into_parts(mut self) -> (JsValue<'a>, BumpVec<'a, JsValue<'a>>) {
449        let callee = self.0.pop().unwrap();
450        (callee, self.0)
451    }
452
453    fn total_nodes(&self) -> u32 {
454        total_nodes(&self.0)
455    }
456
457    fn for_each_children(&self, visitor: &mut impl FnMut(&JsValue<'a>)) {
458        self.0.iter().for_each(visitor)
459    }
460    fn for_each_children_mut(
461        &mut self,
462        visitor: &mut impl FnMut(&mut JsValue<'a>) -> bool,
463    ) -> bool {
464        let mut modified = false;
465        for child in self.0.iter_mut() {
466            if visitor(child) {
467                modified = true;
468            }
469        }
470
471        modified
472    }
473
474    fn all_similar(l: &Self, r: &Self, depth: usize) -> bool {
475        JsValue::all_similar(&l.0, &r.0, depth)
476    }
477}
478
479impl<'a> From<&'_ str> for JsValue<'a> {
480    fn from(v: &str) -> Self {
481        ConstantValue::Str(ConstantString::Atom(v.into())).into()
482    }
483}
484
485impl<'a> From<Atom> for JsValue<'a> {
486    fn from(v: Atom) -> Self {
487        ConstantValue::Str(ConstantString::Atom(v)).into()
488    }
489}
490
491impl<'a> From<BigInt> for JsValue<'a> {
492    fn from(v: BigInt) -> Self {
493        Self::from(Box::new(v))
494    }
495}
496
497impl<'a> From<Box<BigInt>> for JsValue<'a> {
498    fn from(v: Box<BigInt>) -> Self {
499        ConstantValue::BigInt(v).into()
500    }
501}
502
503impl<'a> From<f64> for JsValue<'a> {
504    fn from(v: f64) -> Self {
505        ConstantValue::Num(ConstantNumber(v)).into()
506    }
507}
508
509impl<'a> From<RcStr> for JsValue<'a> {
510    fn from(v: RcStr) -> Self {
511        ConstantValue::Str(v.into()).into()
512    }
513}
514
515impl<'a> From<String> for JsValue<'a> {
516    fn from(v: String) -> Self {
517        RcStr::from(v).into()
518    }
519}
520
521impl<'a> From<swc_core::ecma::ast::Str> for JsValue<'a> {
522    fn from(v: swc_core::ecma::ast::Str) -> Self {
523        ConstantValue::Str(ConstantString::Atom(v.value.to_atom_lossy().into_owned())).into()
524    }
525}
526
527impl<'a> From<ConstantValue> for JsValue<'a> {
528    fn from(v: ConstantValue) -> Self {
529        JsValue::Constant(v)
530    }
531}
532
533impl<'a> JsValue<'a> {
534    /// Build a [`JsValue`] from a [`CompileTimeDefineValue`], allocating any nested structure in
535    /// `arena`. (Cannot be a `TryFrom` impl because the conversion needs the arena.)
536    pub fn from_compile_time_define_value_in(
537        arena: &'a Bump,
538        value: &CompileTimeDefineValue,
539    ) -> Result<Self> {
540        Ok(JsValue::Constant(match value {
541            CompileTimeDefineValue::Undefined => ConstantValue::Undefined,
542            CompileTimeDefineValue::Null => ConstantValue::Null,
543            CompileTimeDefineValue::Bool(b) => (*b).into(),
544            CompileTimeDefineValue::Number(n) => ConstantValue::Num(ConstantNumber(
545                n.as_f64()
546                    .expect("unreachable: serde-json has arbitrary_precision disabled"),
547            )),
548            CompileTimeDefineValue::BigInt(n) => ConstantValue::BigInt(n.clone()),
549            CompileTimeDefineValue::String(s) => s.as_str().into(),
550            CompileTimeDefineValue::Regex(pattern, flags) => {
551                ConstantValue::Regex(Box::new((pattern.as_str().into(), flags.as_str().into())))
552            }
553            CompileTimeDefineValue::Array(a) => {
554                let mut items = BumpVec::with_capacity_in(arena, a.len());
555                for i in a {
556                    items.push(arena, JsValue::from_compile_time_define_value_in(arena, i)?);
557                }
558                let mut js_value = JsValue::Array {
559                    total_nodes: a.len() as u32,
560                    items,
561                    mutable: false,
562                };
563                js_value.update_total_nodes();
564                return Ok(js_value);
565            }
566            CompileTimeDefineValue::Object(m) => {
567                let mut parts = BumpVec::with_capacity_in(arena, m.len());
568                for (k, v) in m {
569                    parts.push(
570                        arena,
571                        ObjectPart::KeyValue(
572                            k.clone().into(),
573                            JsValue::from_compile_time_define_value_in(arena, v)?,
574                        ),
575                    );
576                }
577                let mut js_value = JsValue::Object {
578                    total_nodes: m.len() as u32,
579                    parts,
580                    mutable: false,
581                };
582                js_value.update_total_nodes();
583                return Ok(js_value);
584            }
585            CompileTimeDefineValue::Evaluate(s) => {
586                return EvalContext::eval_single_expr_lit(arena, s);
587            }
588        }))
589    }
590}
591
592impl TryFrom<&ConstantValue> for CompileTimeDefineValue {
593    type Error = anyhow::Error;
594
595    fn try_from(value: &ConstantValue) -> Result<Self> {
596        Ok(match value {
597            ConstantValue::Undefined => CompileTimeDefineValue::Undefined,
598            ConstantValue::Null => CompileTimeDefineValue::Null,
599            ConstantValue::True => CompileTimeDefineValue::Bool(true),
600            ConstantValue::False => CompileTimeDefineValue::Bool(false),
601            ConstantValue::Num(n) => CompileTimeDefineValue::Number(
602                serde_json::Number::from_f64(n.0)
603                    .ok_or_else(|| anyhow::anyhow!("NaN and Infinity cannot be represented"))?,
604            ),
605            ConstantValue::Str(s) => CompileTimeDefineValue::String(s.as_rcstr()),
606            ConstantValue::BigInt(n) => CompileTimeDefineValue::BigInt(n.clone()),
607            ConstantValue::Regex(regex) => CompileTimeDefineValue::Regex(
608                RcStr::from(regex.0.as_str()),
609                RcStr::from(regex.1.as_str()),
610            ),
611        })
612    }
613}
614
615impl<'a> JsValue<'a> {
616    /// Build a [`JsValue`] from a [`FreeVarReference`], allocating any nested structure in `arena`.
617    /// (Cannot be a `TryFrom` impl because the conversion needs the arena.)
618    pub fn from_free_var_reference_in(arena: &'a Bump, value: &FreeVarReference) -> Result<Self> {
619        match value {
620            FreeVarReference::Value(v) => JsValue::from_compile_time_define_value_in(arena, v),
621            FreeVarReference::Ident(_) => Ok(JsValue::unknown_empty(
622                false,
623                rcstr!("compile time injected ident"),
624            )),
625            FreeVarReference::Member(_, _) => Ok(JsValue::unknown_empty(
626                false,
627                rcstr!("compile time injected member"),
628            )),
629            FreeVarReference::EcmaScriptModule { .. } => Ok(JsValue::unknown_empty(
630                false,
631                rcstr!("compile time injected free var module"),
632            )),
633            FreeVarReference::ReportUsage { inner, .. } => {
634                if let Some(inner) = &inner {
635                    JsValue::from_free_var_reference_in(arena, inner.as_ref())
636                } else {
637                    Ok(JsValue::unknown_empty(
638                        false,
639                        rcstr!("compile time injected free var error"),
640                    ))
641                }
642            }
643            FreeVarReference::InputRelative(kind) => {
644                use turbopack_core::compile_time_info::InputRelativeConstant;
645                Ok(JsValue::unknown_empty(
646                    false,
647                    match kind {
648                        InputRelativeConstant::DirName => {
649                            rcstr!("compile time injected free var referencing the directory name")
650                        }
651                        InputRelativeConstant::FileName => {
652                            rcstr!("compile time injected free var referencing the file name")
653                        }
654                    },
655                ))
656            }
657        }
658    }
659}
660
661impl Default for JsValue<'_> {
662    fn default() -> Self {
663        JsValue::unknown_empty(false, rcstr!(""))
664    }
665}
666
667// Private meta methods
668impl JsValue<'_> {
669    fn meta_type(&self) -> JsValueMetaKind {
670        match self {
671            JsValue::Constant(..)
672            | JsValue::Url(..)
673            | JsValue::WellKnownObject(..)
674            | JsValue::WellKnownFunction(..)
675            | JsValue::Unknown { .. } => JsValueMetaKind::Leaf,
676            JsValue::Array { .. }
677            | JsValue::Object { .. }
678            | JsValue::Alternatives { .. }
679            | JsValue::Function(..)
680            | JsValue::Promise(..)
681            | JsValue::Member(..) => JsValueMetaKind::Nested,
682            JsValue::Concat(..)
683            | JsValue::Add(..)
684            | JsValue::Not(..)
685            | JsValue::Logical(..)
686            | JsValue::Binary(..)
687            | JsValue::New(..)
688            | JsValue::Call(..)
689            | JsValue::SuperCall(..)
690            | JsValue::Tenary(..)
691            | JsValue::MemberCall(..)
692            | JsValue::Iterated(..)
693            | JsValue::Awaited(..)
694            | JsValue::TypeOf(..)
695            | JsValue::In(..) => JsValueMetaKind::Operation,
696            JsValue::Variable(..)
697            | JsValue::Argument(..)
698            | JsValue::FreeVar(..)
699            | JsValue::Module(..) => JsValueMetaKind::Placeholder,
700        }
701    }
702}
703
704// Constructors
705impl<'a> JsValue<'a> {
706    pub fn alternatives(list: BumpVec<'a, JsValue<'a>>) -> Self {
707        Self::Alternatives {
708            total_nodes: 1 + total_nodes(&list),
709            values: list,
710            logical_property: None,
711        }
712    }
713
714    pub fn alternatives_with_additional_property(
715        list: BumpVec<'a, JsValue<'a>>,
716        logical_property: LogicalProperty,
717    ) -> Self {
718        Self::Alternatives {
719            total_nodes: 1 + total_nodes(&list),
720            values: list,
721            logical_property: Some(logical_property),
722        }
723    }
724
725    pub fn concat(list: BumpVec<'a, JsValue<'a>>) -> Self {
726        Self::Concat(1 + total_nodes(&list), list)
727    }
728
729    pub fn add(list: BumpVec<'a, JsValue<'a>>) -> Self {
730        Self::Add(1 + total_nodes(&list), list)
731    }
732
733    pub fn logical_and(list: BumpVec<'a, JsValue<'a>>) -> Self {
734        Self::Logical(1 + total_nodes(&list), LogicalOperator::And, list)
735    }
736
737    pub fn logical_or(list: BumpVec<'a, JsValue<'a>>) -> Self {
738        Self::Logical(1 + total_nodes(&list), LogicalOperator::Or, list)
739    }
740
741    pub fn nullish_coalescing(list: BumpVec<'a, JsValue<'a>>) -> Self {
742        Self::Logical(
743            1 + total_nodes(&list),
744            LogicalOperator::NullishCoalescing,
745            list,
746        )
747    }
748
749    pub fn tenary(arena: &'a Bump, test: JsValue<'a>, cons: JsValue<'a>, alt: JsValue<'a>) -> Self {
750        Self::Tenary(
751            1 + test.total_nodes() + cons.total_nodes() + alt.total_nodes(),
752            BumpBox::new_in(test, arena),
753            BumpBox::new_in(cons, arena),
754            BumpBox::new_in(alt, arena),
755        )
756    }
757
758    pub fn iterated(arena: &'a Bump, iterable: JsValue<'a>) -> Self {
759        Self::Iterated(1 + iterable.total_nodes(), BumpBox::new_in(iterable, arena))
760    }
761
762    pub fn equal(arena: &'a Bump, a: JsValue<'a>, b: JsValue<'a>) -> Self {
763        Self::Binary(
764            1 + a.total_nodes() + b.total_nodes(),
765            BumpBox::new_in(a, arena),
766            BinaryOperator::Equal,
767            BumpBox::new_in(b, arena),
768        )
769    }
770
771    pub fn not_equal(arena: &'a Bump, a: JsValue<'a>, b: JsValue<'a>) -> Self {
772        Self::Binary(
773            1 + a.total_nodes() + b.total_nodes(),
774            BumpBox::new_in(a, arena),
775            BinaryOperator::NotEqual,
776            BumpBox::new_in(b, arena),
777        )
778    }
779
780    pub fn strict_equal(arena: &'a Bump, a: JsValue<'a>, b: JsValue<'a>) -> Self {
781        Self::Binary(
782            1 + a.total_nodes() + b.total_nodes(),
783            BumpBox::new_in(a, arena),
784            BinaryOperator::StrictEqual,
785            BumpBox::new_in(b, arena),
786        )
787    }
788
789    pub fn strict_not_equal(arena: &'a Bump, a: JsValue<'a>, b: JsValue<'a>) -> Self {
790        Self::Binary(
791            1 + a.total_nodes() + b.total_nodes(),
792            BumpBox::new_in(a, arena),
793            BinaryOperator::StrictNotEqual,
794            BumpBox::new_in(b, arena),
795        )
796    }
797
798    pub fn r#in(arena: &'a Bump, a: JsValue<'a>, b: JsValue<'a>) -> Self {
799        Self::In(
800            1 + a.total_nodes() + b.total_nodes(),
801            BumpBox::new_in(a, arena),
802            BumpBox::new_in(b, arena),
803        )
804    }
805
806    pub fn logical_not(arena: &'a Bump, inner: JsValue<'a>) -> Self {
807        Self::Not(1 + inner.total_nodes(), BumpBox::new_in(inner, arena))
808    }
809
810    pub fn type_of(arena: &'a Bump, operand: JsValue<'a>) -> Self {
811        Self::TypeOf(1 + operand.total_nodes(), BumpBox::new_in(operand, arena))
812    }
813
814    pub fn array(items: BumpVec<'a, JsValue<'a>>) -> Self {
815        Self::Array {
816            total_nodes: 1 + total_nodes(&items),
817            items,
818            mutable: true,
819        }
820    }
821
822    pub fn frozen_array(items: BumpVec<'a, JsValue<'a>>) -> Self {
823        Self::Array {
824            total_nodes: 1 + total_nodes(&items),
825            items,
826            mutable: false,
827        }
828    }
829
830    pub fn function(
831        arena: &'a Bump,
832        func_ident: u32,
833        is_async: bool,
834        is_generator: bool,
835        return_value: JsValue<'a>,
836    ) -> Self {
837        // Check generator first to handle async generators
838        let return_value = if is_generator {
839            JsValue::WellKnownObject(WellKnownObjectKind::Generator)
840        } else if is_async {
841            JsValue::promise(arena, return_value)
842        } else {
843            return_value
844        };
845        Self::Function(
846            1 + return_value.total_nodes(),
847            func_ident,
848            BumpBox::new_in(return_value, arena),
849        )
850    }
851
852    pub fn object(list: BumpVec<'a, ObjectPart<'a>>) -> Self {
853        Self::Object {
854            total_nodes: 1 + list
855                .iter()
856                .map(|v| match v {
857                    ObjectPart::KeyValue(k, v) => k.total_nodes() + v.total_nodes(),
858                    ObjectPart::Spread(s) => s.total_nodes(),
859                })
860                .sum::<u32>(),
861            parts: list,
862            mutable: true,
863        }
864    }
865
866    pub fn frozen_object(list: BumpVec<'a, ObjectPart<'a>>) -> Self {
867        Self::Object {
868            total_nodes: 1 + list
869                .iter()
870                .map(|v| match v {
871                    ObjectPart::KeyValue(k, v) => k.total_nodes() + v.total_nodes(),
872                    ObjectPart::Spread(s) => s.total_nodes(),
873                })
874                .sum::<u32>(),
875            parts: list,
876            mutable: false,
877        }
878    }
879
880    /// Build a `JsValue::New` from a callee and an owned args `Vec`.
881    ///
882    /// Pushes `f` onto `args` to form the `[args..., callee]` layout. If `args.capacity()`
883    /// equals `args.len()`, this triggers a Vec realloc — only use this overload when the
884    /// caller already has a `Vec` that is likely to have spare capacity for the trailing
885    /// slot (e.g. an `args` Vec returned from [`CallList::into_parts`] or
886    /// [`MemberCallList::into_parts`]). For from-scratch construction use
887    /// [`JsValue::new_from_iter`], which pre-sizes the underlying allocation exactly.
888    pub fn new_from_parts(arena: &'a Bump, f: JsValue<'a>, args: BumpVec<'a, JsValue<'a>>) -> Self {
889        let total = 1 + f.total_nodes() + total_nodes(&args);
890        Self::New(total, CallList::from_parts(arena, f, args))
891    }
892
893    /// Build a `JsValue::New` from a callee and an args iterator with a known length.
894    ///
895    /// Allocates the underlying `Vec` with exact capacity (`args.len() + 1`), so no realloc
896    /// occurs.
897    pub fn new_from_iter<I>(arena: &'a Bump, f: JsValue<'a>, args: I) -> Self
898    where
899        I: IntoIterator<Item = JsValue<'a>>,
900        I::IntoIter: ExactSizeIterator,
901    {
902        let list = CallList::from_iter(arena, f, args);
903        let total = 1 + total_nodes(&list.0);
904        Self::New(total, list)
905    }
906
907    /// Build a `JsValue::Call` from a callee and an owned args `Vec`.
908    ///
909    /// See [`JsValue::new_from_parts`] for the realloc caveat — only use this when the
910    /// caller already has a `Vec` that is likely to be correctly sized (typically one
911    /// obtained from [`CallList::into_parts`] / [`MemberCallList::into_parts`]). For
912    /// from-scratch construction use [`JsValue::call_from_iter`].
913    pub fn call_from_parts(
914        arena: &'a Bump,
915        f: JsValue<'a>,
916        args: BumpVec<'a, JsValue<'a>>,
917    ) -> Self {
918        let total = 1 + f.total_nodes() + total_nodes(&args);
919        Self::Call(total, CallList::from_parts(arena, f, args))
920    }
921
922    /// Build a `JsValue::Call` from a callee and an args iterator with a known length.
923    ///
924    /// Allocates the underlying `Vec` with exact capacity (`args.len() + 1`), so no realloc
925    /// occurs.
926    pub fn call_from_iter<I>(arena: &'a Bump, f: JsValue<'a>, args: I) -> Self
927    where
928        I: IntoIterator<Item = JsValue<'a>>,
929        I::IntoIter: ExactSizeIterator,
930    {
931        let list = CallList::from_iter(arena, f, args);
932        let total = 1 + total_nodes(&list.0);
933        Self::Call(total, list)
934    }
935
936    pub fn super_call(args: BumpBox<'a, [JsValue<'a>]>) -> Self {
937        Self::SuperCall(1 + total_nodes(&args), args)
938    }
939
940    /// Build a `JsValue::MemberCall` from `obj`, `prop`, and an owned args `Vec`.
941    ///
942    /// See [`JsValue::new_from_parts`] for the realloc caveat — only use this when the
943    /// caller already has a `Vec` that is likely to be correctly sized (typically one
944    /// obtained from [`MemberCallList::into_parts`]). For from-scratch construction use
945    /// [`JsValue::member_call_from_iter`].
946    pub fn member_call_from_parts(
947        arena: &'a Bump,
948        o: JsValue<'a>,
949        p: JsValue<'a>,
950        args: BumpVec<'a, JsValue<'a>>,
951    ) -> Self {
952        let total = 1 + o.total_nodes() + p.total_nodes() + total_nodes(&args);
953        Self::MemberCall(total, MemberCallList::from_parts(arena, o, p, args))
954    }
955
956    /// Build a `JsValue::MemberCall` from `obj`, `prop`, and an args iterator with a known
957    /// length.
958    ///
959    /// Allocates the underlying `Vec` with exact capacity (`args.len() + 2`), so no realloc
960    /// occurs.
961    pub fn member_call_from_iter<I>(
962        arena: &'a Bump,
963        o: JsValue<'a>,
964        p: JsValue<'a>,
965        args: I,
966    ) -> Self
967    where
968        I: IntoIterator<Item = JsValue<'a>>,
969        I::IntoIter: ExactSizeIterator,
970    {
971        let list = MemberCallList::from_iter(arena, o, p, args);
972        let total = 1 + total_nodes(&list.0);
973        Self::MemberCall(total, list)
974    }
975
976    pub fn member(arena: &'a Bump, o: JsValue<'a>, p: JsValue<'a>) -> Self {
977        Self::Member(
978            1 + o.total_nodes() + p.total_nodes(),
979            BumpBox::new_in(o, arena),
980            BumpBox::new_in(p, arena),
981        )
982    }
983
984    pub fn promise(arena: &'a Bump, operand: JsValue<'a>) -> Self {
985        // In ecmascript Promise<Promise<T>> is equivalent to Promise<T>
986        if let JsValue::Promise(_, _) = operand {
987            return operand;
988        }
989        Self::Promise(1 + operand.total_nodes(), BumpBox::new_in(operand, arena))
990    }
991
992    pub fn awaited(arena: &'a Bump, operand: JsValue<'a>) -> Self {
993        Self::Awaited(1 + operand.total_nodes(), BumpBox::new_in(operand, arena))
994    }
995
996    pub fn unknown(value: impl Into<Arc<JsValue<'a>>>, side_effects: bool, reason: RcStr) -> Self {
997        Self::Unknown {
998            original_value: Some(value.into()),
999            reason,
1000            has_side_effects: side_effects,
1001        }
1002    }
1003
1004    pub fn unknown_empty(side_effects: bool, reason: RcStr) -> Self {
1005        Self::Unknown {
1006            original_value: None,
1007            reason,
1008            has_side_effects: side_effects,
1009        }
1010    }
1011
1012    pub fn unknown_if(
1013        is_unknown: bool,
1014        value: JsValue<'a>,
1015        side_effects: bool,
1016        reason: RcStr,
1017    ) -> Self {
1018        if is_unknown {
1019            Self::Unknown {
1020                original_value: Some(value.into()),
1021                reason,
1022                has_side_effects: side_effects,
1023            }
1024        } else {
1025            value
1026        }
1027    }
1028}
1029
1030// Methods regarding node count
1031impl JsValue<'_> {
1032    pub fn has_children(&self) -> bool {
1033        self.total_nodes() > 1
1034    }
1035
1036    pub fn total_nodes(&self) -> u32 {
1037        match self {
1038            JsValue::Constant(_)
1039            | JsValue::Url(_, _)
1040            | JsValue::FreeVar(_)
1041            | JsValue::Variable(_)
1042            | JsValue::Module(..)
1043            | JsValue::WellKnownObject(_)
1044            | JsValue::WellKnownFunction(_)
1045            | JsValue::Unknown { .. }
1046            | JsValue::Argument(..) => 1,
1047
1048            JsValue::Array { total_nodes: c, .. }
1049            | JsValue::Object { total_nodes: c, .. }
1050            | JsValue::Alternatives { total_nodes: c, .. }
1051            | JsValue::Concat(c, _)
1052            | JsValue::Add(c, _)
1053            | JsValue::Not(c, _)
1054            | JsValue::Logical(c, _, _)
1055            | JsValue::Binary(c, _, _, _)
1056            | JsValue::Tenary(c, _, _, _)
1057            | JsValue::New(c, _)
1058            | JsValue::Call(c, _)
1059            | JsValue::SuperCall(c, _)
1060            | JsValue::MemberCall(c, _)
1061            | JsValue::Member(c, _, _)
1062            | JsValue::Function(c, _, _)
1063            | JsValue::Iterated(c, ..)
1064            | JsValue::Promise(c, ..)
1065            | JsValue::Awaited(c, ..)
1066            | JsValue::TypeOf(c, ..)
1067            | JsValue::In(c, ..) => *c,
1068        }
1069    }
1070
1071    pub(crate) fn update_total_nodes(&mut self) {
1072        match self {
1073            JsValue::Constant(_)
1074            | JsValue::Url(_, _)
1075            | JsValue::FreeVar(_)
1076            | JsValue::Variable(_)
1077            | JsValue::Module(..)
1078            | JsValue::WellKnownObject(_)
1079            | JsValue::WellKnownFunction(_)
1080            | JsValue::Unknown { .. }
1081            | JsValue::Argument(..) => {}
1082
1083            JsValue::Array {
1084                total_nodes: c,
1085                items: list,
1086                ..
1087            }
1088            | JsValue::Alternatives {
1089                total_nodes: c,
1090                values: list,
1091                ..
1092            }
1093            | JsValue::Concat(c, list)
1094            | JsValue::Add(c, list)
1095            | JsValue::Logical(c, _, list) => {
1096                *c = 1 + total_nodes(list);
1097            }
1098
1099            JsValue::Binary(c, a, _, b) => {
1100                *c = 1 + a.total_nodes() + b.total_nodes();
1101            }
1102            JsValue::Tenary(c, test, cons, alt) => {
1103                *c = 1 + test.total_nodes() + cons.total_nodes() + alt.total_nodes();
1104            }
1105            JsValue::Not(c, r) => {
1106                *c = 1 + r.total_nodes();
1107            }
1108            JsValue::Promise(c, r) => {
1109                *c = 1 + r.total_nodes();
1110            }
1111            JsValue::Awaited(c, r) => {
1112                *c = 1 + r.total_nodes();
1113            }
1114
1115            JsValue::Object {
1116                total_nodes: c,
1117                parts,
1118                mutable: _,
1119            } => {
1120                *c = 1 + parts
1121                    .iter()
1122                    .map(|v| match v {
1123                        ObjectPart::KeyValue(k, v) => k.total_nodes() + v.total_nodes(),
1124                        ObjectPart::Spread(s) => s.total_nodes(),
1125                    })
1126                    .sum::<u32>();
1127            }
1128            JsValue::New(c, call) => {
1129                *c = 1 + call.total_nodes();
1130            }
1131            JsValue::Call(c, call) => {
1132                *c = 1 + call.total_nodes();
1133            }
1134            JsValue::SuperCall(c, args) => {
1135                *c = 1 + total_nodes(args);
1136            }
1137            JsValue::MemberCall(c, call) => {
1138                *c = 1 + call.total_nodes();
1139            }
1140            JsValue::Member(c, o, p) => {
1141                *c = 1 + o.total_nodes() + p.total_nodes();
1142            }
1143            JsValue::Function(c, _, r) => {
1144                *c = 1 + r.total_nodes();
1145            }
1146
1147            JsValue::Iterated(c, iterable) => {
1148                *c = 1 + iterable.total_nodes();
1149            }
1150
1151            JsValue::TypeOf(c, operand) => {
1152                *c = 1 + operand.total_nodes();
1153            }
1154            JsValue::In(c, l, r) => {
1155                *c = 1 + l.total_nodes() + r.total_nodes();
1156            }
1157        }
1158    }
1159
1160    #[cfg(debug_assertions)]
1161    pub fn debug_assert_total_nodes_up_to_date(&mut self) {
1162        let old = self.total_nodes();
1163        self.update_total_nodes();
1164        assert_eq!(
1165            old,
1166            self.total_nodes(),
1167            "total nodes not up to date {self:?}"
1168        );
1169    }
1170
1171    #[cfg(not(debug_assertions))]
1172    pub fn debug_assert_total_nodes_up_to_date(&mut self) {}
1173}
1174
1175// Unknown management
1176impl<'a> JsValue<'a> {
1177    /// Convert the value into unknown with a specific reason.
1178    pub fn make_unknown(&mut self, side_effects: bool, reason: RcStr) {
1179        *self = JsValue::unknown(take(self), side_effects || self.has_side_effects(), reason);
1180    }
1181
1182    /// Convert the owned value into unknown with a specific reason.
1183    pub fn into_unknown(mut self, side_effects: bool, reason: RcStr) -> Self {
1184        self.make_unknown(side_effects, reason);
1185        self
1186    }
1187
1188    /// Convert the value into unknown with a specific reason, but don't retain
1189    /// the original value.
1190    pub fn make_unknown_without_content(&mut self, side_effects: bool, reason: RcStr) {
1191        *self = JsValue::unknown_empty(side_effects || self.has_side_effects(), reason);
1192    }
1193
1194    /// Make all nested operations unknown when the value is an operation.
1195    pub fn make_nested_operations_unknown(&mut self) -> bool {
1196        fn inner(this: &mut JsValue) -> bool {
1197            if matches!(this.meta_type(), JsValueMetaKind::Operation) {
1198                this.make_unknown(false, rcstr!("nested operation"));
1199                true
1200            } else {
1201                this.for_each_children_mut(&mut inner)
1202            }
1203        }
1204        if matches!(self.meta_type(), JsValueMetaKind::Operation) {
1205            self.for_each_children_mut(&mut inner)
1206        } else {
1207            false
1208        }
1209    }
1210
1211    pub fn add_unknown_mutations(&mut self, arena: &'a Bump, side_effects: bool) {
1212        self.add_alt(
1213            arena,
1214            JsValue::unknown_empty(side_effects, rcstr!("unknown mutation")),
1215        );
1216    }
1217}
1218
1219// Definable name management
1220impl JsValue<'_> {
1221    /// When the value has a user-definable name, return it in segments. Otherwise
1222    /// returns None.
1223    /// It also returns a boolean whether the variable was potentially reassigned.
1224    /// - any free var has itself as user-definable name: ["foo"]
1225    /// - any member access adds the identifier as segment after the object: ["foo", "prop"]
1226    /// - some well-known objects/functions have a user-definable names: ["import"]
1227    /// - member calls without arguments also have a user-definable name: ["foo", Call("func")]
1228    /// - typeof expressions add `typeof` after the argument's segments: ["foo", "typeof"]
1229    pub fn get_definable_name(
1230        &self,
1231        var_graph: Option<&VarGraph<'_>>,
1232    ) -> Option<(DefinableNameSegmentRefs<'_>, bool)> {
1233        let mut current = self;
1234        let mut segments = SmallVec::new();
1235        let mut potentially_reassigned = false;
1236        loop {
1237            match current {
1238                JsValue::FreeVar(name) => {
1239                    if var_graph.is_some_and(|var_graph| {
1240                        var_graph
1241                            .free_var_ids
1242                            .get(name)
1243                            .is_some_and(|id| var_graph.values.contains_key(id))
1244                    }) {
1245                        // `foo` was potentially reassigned
1246                        potentially_reassigned = true;
1247                    }
1248                    segments.push(DefinableNameSegmentRef::Name(name));
1249                    break;
1250                }
1251                JsValue::Member(_, obj, prop) => {
1252                    segments.push(DefinableNameSegmentRef::Name(prop.as_str()?));
1253                    current = obj;
1254                }
1255                JsValue::WellKnownObject(obj) => {
1256                    segments.extend(
1257                        obj.as_define_name()?
1258                            .iter()
1259                            .rev()
1260                            .copied()
1261                            .map(DefinableNameSegmentRef::Name),
1262                    );
1263                    break;
1264                }
1265                JsValue::WellKnownFunction(func) => {
1266                    segments.extend(
1267                        func.as_define_name()?
1268                            .iter()
1269                            .rev()
1270                            .copied()
1271                            .map(DefinableNameSegmentRef::Name),
1272                    );
1273                    break;
1274                }
1275                JsValue::MemberCall(_, call) if call.args().is_empty() => {
1276                    segments.push(DefinableNameSegmentRef::Call(call.prop().as_str()?));
1277                    current = call.obj();
1278                }
1279                JsValue::TypeOf(_, arg) => {
1280                    segments.push(DefinableNameSegmentRef::TypeOf);
1281                    current = arg;
1282                }
1283                _ => return None,
1284            }
1285        }
1286        segments.reverse();
1287        Some((DefinableNameSegmentRefs(segments), potentially_reassigned))
1288    }
1289}
1290
1291// Arena-aware cloning (replaces the derived `Clone`, which is unavailable because the arena-backed
1292// `Box`/`Vec` children can't clone without the allocator).
1293impl<'a> JsValue<'a> {
1294    /// Deep-clone this value into `arena`, returning a fresh tree owned by that arena.
1295    pub fn clone_in(&self, arena: &'a Bump) -> JsValue<'a> {
1296        match self {
1297            JsValue::Constant(v) => JsValue::Constant(v.clone()),
1298            JsValue::Url(s, k) => JsValue::Url(s.clone(), *k),
1299            JsValue::WellKnownObject(k) => JsValue::WellKnownObject(k.clone()),
1300            JsValue::WellKnownFunction(k) => JsValue::WellKnownFunction(k.clone()),
1301            JsValue::Unknown {
1302                original_value,
1303                reason,
1304                has_side_effects,
1305            } => JsValue::Unknown {
1306                original_value: original_value.clone(),
1307                reason: reason.clone(),
1308                has_side_effects: *has_side_effects,
1309            },
1310            JsValue::Array {
1311                total_nodes,
1312                items,
1313                mutable,
1314            } => JsValue::Array {
1315                total_nodes: *total_nodes,
1316                items: BumpVec::from_iter_in(arena, items.iter().map(|v| v.clone_in(arena))),
1317                mutable: *mutable,
1318            },
1319            JsValue::Object {
1320                total_nodes,
1321                parts,
1322                mutable,
1323            } => JsValue::Object {
1324                total_nodes: *total_nodes,
1325                parts: BumpVec::from_iter_in(arena, parts.iter().map(|p| p.clone_in(arena))),
1326                mutable: *mutable,
1327            },
1328            JsValue::Alternatives {
1329                total_nodes,
1330                values,
1331                logical_property,
1332            } => JsValue::Alternatives {
1333                total_nodes: *total_nodes,
1334                values: BumpVec::from_iter_in(arena, values.iter().map(|v| v.clone_in(arena))),
1335                logical_property: *logical_property,
1336            },
1337            JsValue::Function(c, id, r) => {
1338                JsValue::Function(*c, *id, BumpBox::new_in(r.clone_in(arena), arena))
1339            }
1340            JsValue::Concat(c, list) => JsValue::Concat(
1341                *c,
1342                BumpVec::from_iter_in(arena, list.iter().map(|v| v.clone_in(arena))),
1343            ),
1344            JsValue::Add(c, list) => JsValue::Add(
1345                *c,
1346                BumpVec::from_iter_in(arena, list.iter().map(|v| v.clone_in(arena))),
1347            ),
1348            JsValue::Not(c, v) => JsValue::Not(*c, BumpBox::new_in(v.clone_in(arena), arena)),
1349            JsValue::Logical(c, op, list) => JsValue::Logical(
1350                *c,
1351                *op,
1352                BumpVec::from_iter_in(arena, list.iter().map(|v| v.clone_in(arena))),
1353            ),
1354            JsValue::Binary(c, a, op, b) => JsValue::Binary(
1355                *c,
1356                BumpBox::new_in(a.clone_in(arena), arena),
1357                *op,
1358                BumpBox::new_in(b.clone_in(arena), arena),
1359            ),
1360            JsValue::New(c, call) => JsValue::New(*c, call.clone_in(arena)),
1361            JsValue::Call(c, call) => JsValue::Call(*c, call.clone_in(arena)),
1362            JsValue::SuperCall(c, args) => JsValue::SuperCall(
1363                *c,
1364                bumpalo::collections::Vec::from_iter_in(
1365                    args.iter().map(|v| v.clone_in(arena)),
1366                    arena,
1367                )
1368                .into_boxed_slice(),
1369            ),
1370            JsValue::MemberCall(c, call) => JsValue::MemberCall(*c, call.clone_in(arena)),
1371            JsValue::Member(c, o, p) => JsValue::Member(
1372                *c,
1373                BumpBox::new_in(o.clone_in(arena), arena),
1374                BumpBox::new_in(p.clone_in(arena), arena),
1375            ),
1376            JsValue::Tenary(c, test, cons, alt) => JsValue::Tenary(
1377                *c,
1378                BumpBox::new_in(test.clone_in(arena), arena),
1379                BumpBox::new_in(cons.clone_in(arena), arena),
1380                BumpBox::new_in(alt.clone_in(arena), arena),
1381            ),
1382            JsValue::Promise(c, v) => {
1383                JsValue::Promise(*c, BumpBox::new_in(v.clone_in(arena), arena))
1384            }
1385            JsValue::Awaited(c, v) => {
1386                JsValue::Awaited(*c, BumpBox::new_in(v.clone_in(arena), arena))
1387            }
1388            JsValue::Iterated(c, v) => {
1389                JsValue::Iterated(*c, BumpBox::new_in(v.clone_in(arena), arena))
1390            }
1391            JsValue::TypeOf(c, v) => JsValue::TypeOf(*c, BumpBox::new_in(v.clone_in(arena), arena)),
1392            JsValue::In(c, l, r) => JsValue::In(
1393                *c,
1394                BumpBox::new_in(l.clone_in(arena), arena),
1395                BumpBox::new_in(r.clone_in(arena), arena),
1396            ),
1397            JsValue::Variable(id) => JsValue::Variable(id.clone()),
1398            JsValue::Argument(i, idx) => JsValue::Argument(*i, *idx),
1399            JsValue::FreeVar(a) => JsValue::FreeVar(a.clone()),
1400            JsValue::Module(m) => JsValue::Module(m.clone()),
1401        }
1402    }
1403}
1404
1405#[cfg(test)]
1406mod tests {
1407    use super::*;
1408
1409    #[test]
1410    #[cfg(target_pointer_width = "64")]
1411    fn jsvalue_size() {
1412        assert_eq!(32, size_of::<JsValue>());
1413    }
1414}