Skip to main content

rsjsonnet_lang/program/
mod.rs

1//! Jsonnet program state and evaluation.
2//!
3//! # Example
4//!
5//! ```
6//! let source = b"local add_one(x) = x + 1; add_one(2)";
7//!
8//! // Implement callbacks
9//! struct Callbacks;
10//!
11//! impl<'p> rsjsonnet_lang::program::Callbacks<'p> for Callbacks {
12//!     fn import(
13//!         &mut self,
14//!         program: &mut rsjsonnet_lang::program::Program<'p>,
15//!         from: rsjsonnet_lang::span::SpanId,
16//!         path: &str,
17//!     ) -> Result<rsjsonnet_lang::program::Thunk<'p>, rsjsonnet_lang::program::ImportError>
18//!     {
19//!         unimplemented!();
20//!     }
21//!
22//!     fn import_str(
23//!         &mut self,
24//!         program: &mut rsjsonnet_lang::program::Program<'p>,
25//!         from: rsjsonnet_lang::span::SpanId,
26//!         path: &str,
27//!     ) -> Result<String, rsjsonnet_lang::program::ImportError> {
28//!         unimplemented!();
29//!     }
30//!
31//!     fn import_bin(
32//!         &mut self,
33//!         program: &mut rsjsonnet_lang::program::Program<'p>,
34//!         from: rsjsonnet_lang::span::SpanId,
35//!         path: &str,
36//!     ) -> Result<Vec<u8>, rsjsonnet_lang::program::ImportError> {
37//!         unimplemented!();
38//!     }
39//!
40//!     fn trace(
41//!         &mut self,
42//!         program: &mut rsjsonnet_lang::program::Program<'p>,
43//!         message: &str,
44//!         stack: &[rsjsonnet_lang::program::EvalStackTraceItem],
45//!     ) {
46//!         unimplemented!();
47//!     }
48//!
49//!     fn native_call(
50//!         &mut self,
51//!         program: &mut rsjsonnet_lang::program::Program<'p>,
52//!         name: rsjsonnet_lang::interner::InternedStr<'p>,
53//!         args: &[rsjsonnet_lang::program::Value<'p>],
54//!     ) -> Result<rsjsonnet_lang::program::Value<'p>, rsjsonnet_lang::program::NativeError>
55//!     {
56//!         unimplemented!();
57//!     }
58//! }
59//!
60//! // Create the program state.
61//! let arena = rsjsonnet_lang::arena::Arena::new();
62//! let mut program = rsjsonnet_lang::program::Program::new(&arena);
63//! let mut callbacks = Callbacks;
64//!
65//! // Import the source.
66//! let (span_ctx, _) = program
67//!     .span_manager_mut()
68//!     .insert_source_context(source.len());
69//! let thunk = program
70//!     .load_source(span_ctx, source, true, "<example>")
71//!     .unwrap();
72//!
73//! // Evaluate it.
74//! let value = program.eval_value(&thunk, &mut callbacks).unwrap();
75//! assert_eq!(value.as_number(), Some(3.0));
76//! ```
77
78use crate::arena::Arena;
79use crate::gc::{Gc, GcContext, GcTrace, GcView};
80use crate::interner::{InternedStr, StrInterner};
81use crate::lexer::Lexer;
82use crate::parser::Parser;
83use crate::span::{SourceId, SpanContextId, SpanId, SpanManager};
84use crate::{FHashMap, ast};
85
86mod analyze;
87mod data;
88mod error;
89mod eval;
90mod ir;
91mod stdlib;
92
93use data::{
94    ArrayData, BuiltInFunc, FuncData, FuncKind, FuncParams, ObjectData, ObjectLayer,
95    SimpleObjectBuilder, ThunkData, ThunkEnv, ThunkEnvData, ThunkState, ValueData,
96};
97pub use error::{AnalyzeError, EvalError, EvalErrorKind, EvalErrorValueType, LoadError};
98
99/// Error type that can be returned by [`Callbacks::import`],
100/// [`Callbacks::import_str`] and [`Callbacks::import_bin`].
101///
102/// It does not carry any additional information.
103#[derive(Clone, Debug)]
104pub struct ImportError;
105
106/// Error type that can be returned by [`Callbacks::native_call`].
107///
108/// It does not carry any additional information.
109#[derive(Clone, Debug)]
110pub struct NativeError;
111
112/// Trait to customize the behavior of operations during evaluation.
113///
114/// Some [`Program`] methods need to be provided with an implementor of this
115/// trait.
116pub trait Callbacks<'p> {
117    /// Called when an `import` expression is evaluated.
118    fn import(
119        &mut self,
120        program: &mut Program<'p>,
121        from: SpanId,
122        path: &str,
123    ) -> Result<Thunk<'p>, ImportError>;
124
125    /// Called when an `importstr` expression is evaluated.
126    fn import_str(
127        &mut self,
128        program: &mut Program<'p>,
129        from: SpanId,
130        path: &str,
131    ) -> Result<String, ImportError>;
132
133    /// Called when an `importbin` expression is evaluated.
134    fn import_bin(
135        &mut self,
136        program: &mut Program<'p>,
137        from: SpanId,
138        path: &str,
139    ) -> Result<Vec<u8>, ImportError>;
140
141    /// Called when a call to `std.trace` is evaluated.
142    fn trace(&mut self, program: &mut Program<'p>, message: &str, stack: &[EvalStackTraceItem]);
143
144    /// Called when a function returned by `std.native` is called.
145    ///
146    /// Native functions must be registered with
147    /// [`Program::register_native_func`].
148    fn native_call(
149        &mut self,
150        program: &mut Program<'p>,
151        name: InternedStr<'p>,
152        args: &[Value<'p>],
153    ) -> Result<Value<'p>, NativeError>;
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub enum EvalStackTraceItem {
158    Expr {
159        span: SpanId,
160    },
161    Call {
162        span: Option<SpanId>,
163        name: Option<String>,
164    },
165    Variable {
166        span: SpanId,
167        name: String,
168    },
169    ArrayItem {
170        span: Option<SpanId>,
171        index: usize,
172    },
173    ObjectField {
174        span: Option<SpanId>,
175        name: String,
176    },
177    CompareArrayItem {
178        index: usize,
179    },
180    CompareObjectField {
181        name: String,
182    },
183    ManifestArrayItem {
184        index: usize,
185    },
186    ManifestObjectField {
187        name: String,
188    },
189    Import {
190        span: SpanId,
191    },
192}
193
194/// Jsonnet program state and evaluator.
195///
196/// See the [module-level documentation](self) for more information.
197pub struct Program<'p> {
198    arena: &'p Arena,
199    str_interner: StrInterner<'p>,
200    span_mgr: SpanManager,
201    gc_ctx: GcContext<'p>,
202    objs_after_last_gc: usize,
203    max_stack: usize,
204    exprs: Exprs<'p>,
205    stdlib_src_id: SourceId,
206    stdlib_data: &'static [u8],
207    stdlib_base_obj: Option<GcView<ObjectData<'p>>>,
208    stdlib_extra: FHashMap<InternedStr<'p>, GcView<ThunkData<'p>>>,
209    empty_array: GcView<ArrayData<'p>>,
210    identity_func: GcView<FuncData<'p>>,
211    ext_vars: FHashMap<InternedStr<'p>, GcView<ThunkData<'p>>>,
212    native_funcs: FHashMap<InternedStr<'p>, GcView<FuncData<'p>>>,
213}
214
215struct Exprs<'p> {
216    null: &'p ir::Expr<'p>,
217    false_: &'p ir::Expr<'p>,
218    true_: &'p ir::Expr<'p>,
219    self_obj: &'p ir::Expr<'p>,
220    top_obj: &'p ir::Expr<'p>,
221}
222
223impl<'p> Program<'p> {
224    /// Creates a new [`Program`].
225    pub fn new(arena: &'p Arena) -> Self {
226        let str_interner = StrInterner::new();
227        let gc_ctx = GcContext::new();
228        let exprs = Exprs {
229            null: arena.alloc(ir::Expr::Null),
230            false_: arena.alloc(ir::Expr::Bool(false)),
231            true_: arena.alloc(ir::Expr::Bool(true)),
232            self_obj: arena.alloc(ir::Expr::SelfObj),
233            top_obj: arena.alloc(ir::Expr::TopObj),
234        };
235
236        let stdlib_data = stdlib::STDLIB_DATA;
237        let mut span_mgr = SpanManager::new();
238        let (stdlib_span_ctx, stdlib_src_id) = span_mgr.insert_source_context(stdlib_data.len());
239
240        let stdlib_extra = Self::build_stdlib_extra(arena, &str_interner, &gc_ctx, &exprs);
241
242        let empty_array: GcView<ArrayData<'p>> = gc_ctx.alloc_view(Box::new([]));
243        let identity_func = gc_ctx.alloc_view(FuncData::new_identity_func(
244            Some(str_interner.intern(arena, "id")),
245            arena.alloc([(str_interner.intern(arena, "x"), None)]),
246        ));
247
248        let mut this = Self {
249            arena,
250            str_interner,
251            span_mgr,
252            gc_ctx,
253            objs_after_last_gc: 0,
254            max_stack: 500,
255            exprs,
256            stdlib_src_id,
257            stdlib_data,
258            stdlib_base_obj: None,
259            stdlib_extra,
260            empty_array,
261            identity_func,
262            ext_vars: FHashMap::default(),
263            native_funcs: FHashMap::default(),
264        };
265        this.load_stdlib(stdlib_span_ctx);
266        this
267    }
268
269    #[inline]
270    pub fn str_interner(&self) -> &StrInterner<'p> {
271        &self.str_interner
272    }
273
274    #[inline]
275    pub fn intern_str(&self, s: &str) -> InternedStr<'p> {
276        self.str_interner.intern(self.arena, s)
277    }
278
279    #[inline]
280    pub fn span_manager(&self) -> &SpanManager {
281        &self.span_mgr
282    }
283
284    #[inline]
285    pub fn span_manager_mut(&mut self) -> &mut SpanManager {
286        &mut self.span_mgr
287    }
288
289    /// Runs garbage collection unconditionally.
290    pub fn gc(&mut self) {
291        self.gc_ctx.gc();
292        self.objs_after_last_gc = self.gc_ctx.num_objects();
293    }
294
295    /// Runs garbage collection under certain conditions.
296    pub fn maybe_gc(&mut self) {
297        let num_objects = self.gc_ctx.num_objects();
298        if num_objects > 1000 && (num_objects / 2) > self.objs_after_last_gc {
299            self.gc();
300        }
301    }
302
303    /// Sets the maximum call stack size.
304    ///
305    /// The default is 500.
306    pub fn set_max_stack(&mut self, max_stack: usize) {
307        self.max_stack = max_stack;
308    }
309
310    /// Returns the source of the part of the standard library that
311    /// is implemented in Jsonnet.
312    pub fn get_stdlib_source(&self) -> (SourceId, &[u8]) {
313        (self.stdlib_src_id, self.stdlib_data)
314    }
315
316    /// Adds an external variable.
317    ///
318    /// External variables can be accessed within a Jsonnet program
319    /// with the `std.extVar` function.
320    pub fn add_ext_var(&mut self, name: InternedStr<'p>, thunk: &Thunk<'p>) {
321        match self.ext_vars.entry(name) {
322            std::collections::hash_map::Entry::Occupied(entry) => {
323                panic!("external variable {:?} already set", entry.key());
324            }
325            std::collections::hash_map::Entry::Vacant(entry) => {
326                entry.insert(thunk.data.clone());
327            }
328        }
329    }
330
331    /// Registers a native function.
332    ///
333    /// Native functions can be accessed within a Jsonnet program
334    /// with the `std.native` function.
335    ///
336    /// The actual behavior of the native function should be implemented
337    /// in [`Callbacks::native_call`].
338    ///
339    /// # Panics
340    ///
341    /// Panics if a native function with the same name is already registered
342    /// or if a parameter name is repeated.
343    pub fn register_native_func(&mut self, name: InternedStr<'p>, params: &[InternedStr<'p>]) {
344        match self.native_funcs.entry(name) {
345            std::collections::hash_map::Entry::Occupied(entry) => {
346                panic!("native function {:?} already registered", entry.key());
347            }
348            std::collections::hash_map::Entry::Vacant(entry) => {
349                let params_order: Vec<_> = params.iter().map(|&name| (name, None)).collect();
350                entry.insert(self.gc_ctx.alloc_view(FuncData::new(
351                    self.arena.alloc_slice(&params_order),
352                    FuncKind::Native { name },
353                )));
354            }
355        }
356    }
357
358    #[must_use]
359    #[inline]
360    fn gc_alloc<T: GcTrace + 'p>(&self, data: T) -> Gc<T> {
361        self.gc_ctx.alloc(data)
362    }
363
364    #[must_use]
365    #[inline]
366    fn gc_alloc_view<T: GcTrace + 'p>(&self, data: T) -> GcView<T> {
367        self.gc_ctx.alloc_view(data)
368    }
369
370    fn insert_thunk_with_value(&mut self, value: ValueData<'p>) -> GcView<ThunkData<'p>> {
371        self.gc_alloc_view(ThunkData::new_done(value))
372    }
373
374    /// Creates a thunk with an already evaluated value.
375    pub fn value_to_thunk(&mut self, value: &Value<'p>) -> Thunk<'p> {
376        Thunk::new(self.insert_thunk_with_value(value.inner.clone()))
377    }
378
379    /// Creates an array value.
380    pub fn make_array(&mut self, items: &[Value<'p>]) -> Value<'p> {
381        let array = self.make_value_array(items.iter().map(|item| item.inner.clone()));
382        Value::from_value(ValueData::Array(array))
383    }
384
385    /// Creates an object value.
386    pub fn make_object(&mut self, obj_fields: &[(InternedStr<'p>, Value<'p>)]) -> Value<'p> {
387        let mut obj_builder = SimpleObjectBuilder::new();
388        for (name, value) in obj_fields.iter() {
389            obj_builder.insert_field(
390                *name,
391                ast::Visibility::Default,
392                self.gc_alloc(ThunkData::new_done(value.inner.clone())),
393            );
394        }
395
396        let obj = self.gc_alloc(obj_builder.build());
397        Value::from_value(ValueData::Object(obj))
398    }
399
400    /// Loads a Jsonnet source into a thunk.
401    ///
402    /// `with_stdlib` specifies whether the standard library will be available
403    /// as `std`. `this_file` will be the value of `std.thisFile`.
404    pub fn load_source(
405        &mut self,
406        span_ctx: SpanContextId,
407        input: &[u8],
408        with_stdlib: bool,
409        this_file: &str,
410    ) -> Result<Thunk<'p>, LoadError> {
411        let ast_arena = Arena::new();
412        let lexer = Lexer::new(
413            self.arena,
414            &ast_arena,
415            &self.str_interner,
416            &mut self.span_mgr,
417            span_ctx,
418            input,
419        );
420        let tokens = lexer.lex_to_eof(false)?;
421
422        let parser = Parser::new(
423            self.arena,
424            &ast_arena,
425            &self.str_interner,
426            &mut self.span_mgr,
427            tokens,
428        );
429        let root_expr = parser.parse_root_expr()?;
430
431        let env = if with_stdlib {
432            let stdlib_obj = self.make_custom_stdlib(this_file);
433            let stdlib_thunk =
434                self.gc_alloc_view(ThunkData::new_done(ValueData::Object(stdlib_obj)));
435
436            let mut env = FHashMap::default();
437            env.insert(self.intern_str("std"), Thunk::new(stdlib_thunk));
438
439            Some(env)
440        } else {
441            None
442        };
443        let thunk = self.analyze(&root_expr, env)?;
444        Ok(thunk)
445    }
446
447    fn analyze(
448        &mut self,
449        ast: &ast::Expr<'p, '_>,
450        env: Option<FHashMap<InternedStr<'p>, Thunk<'p>>>,
451    ) -> Result<Thunk<'p>, AnalyzeError> {
452        let analyze_env = env
453            .as_ref()
454            .map(|env| env.keys().cloned().collect())
455            .unwrap_or_default();
456        let ir_expr = analyze::Analyzer::new(self).analyze(ast, analyze_env)?;
457
458        let mut thunk_env_data = ThunkEnvData::new(None);
459        if let Some(env) = env {
460            for (name, value) in env.iter() {
461                thunk_env_data.set_var(*name, Gc::from(&value.data));
462            }
463        }
464        let thunk_env = self.gc_alloc(ThunkEnv::from(thunk_env_data));
465
466        let thunk = self.gc_alloc_view(ThunkData::new_pending_expr(ir_expr, thunk_env));
467
468        Ok(Thunk::new(thunk))
469    }
470
471    /// Evaluates a thunk into a value.
472    pub fn eval_value(
473        &mut self,
474        thunk: &Thunk<'p>,
475        callbacks: &mut dyn Callbacks<'p>,
476    ) -> Result<Value<'p>, EvalError> {
477        let output = eval::Evaluator::eval(
478            self,
479            Some(callbacks),
480            eval::EvalInput::Value(thunk.data.clone()),
481        )
482        .map_err(|e| *e)?;
483        let eval::EvalOutput::Value(value) = output else {
484            unreachable!();
485        };
486        Ok(Value::from_value(value))
487    }
488
489    fn eval_value_internal(&mut self, thunk: &Thunk<'p>) -> Result<ValueData<'p>, EvalError> {
490        let output = eval::Evaluator::eval(self, None, eval::EvalInput::Value(thunk.data.clone()))
491            .map_err(|e| *e)?;
492        let eval::EvalOutput::Value(value) = output else {
493            unreachable!();
494        };
495        Ok(value)
496    }
497
498    /// Evaluates a function call.
499    pub fn eval_call(
500        &mut self,
501        func: &Thunk<'p>,
502        pos_args: &[Thunk<'p>],
503        named_args: &[(InternedStr<'p>, Thunk<'p>)],
504        callbacks: &mut dyn Callbacks<'p>,
505    ) -> Result<Value<'p>, EvalError> {
506        let output = eval::Evaluator::eval(
507            self,
508            Some(callbacks),
509            eval::EvalInput::Call(
510                func.data.clone(),
511                eval::TopLevelArgs {
512                    positional: pos_args.iter().map(|thunk| thunk.data.clone()).collect(),
513                    named: named_args
514                        .iter()
515                        .map(|(name, thunk)| (*name, thunk.data.clone()))
516                        .collect(),
517                },
518            ),
519        )
520        .map_err(|e| *e)?;
521        let eval::EvalOutput::Value(value) = output else {
522            unreachable!();
523        };
524        Ok(Value::from_value(value))
525    }
526
527    /// Manifests a value as JSON.
528    pub fn manifest_json(
529        &mut self,
530        value: &Value<'p>,
531        multiline: bool,
532    ) -> Result<String, EvalError> {
533        let thunk = self.insert_thunk_with_value(value.inner.clone());
534        let output =
535            eval::Evaluator::eval(self, None, eval::EvalInput::ManifestJson(thunk, multiline))
536                .map_err(|e| *e)?;
537        let eval::EvalOutput::String(s) = output else {
538            unreachable!();
539        };
540        Ok(s)
541    }
542}
543
544/// A value that might not be evaluated yet.
545///
546/// Each instance of [`Thunk`] are tied to a [`Program`] instance. Instances of
547/// this type will only be valid as long as the [`Program`] they came from has
548/// not been dropped.
549#[derive(Clone)]
550pub struct Thunk<'p> {
551    data: GcView<ThunkData<'p>>,
552}
553
554impl<'p> Thunk<'p> {
555    #[inline]
556    fn new(data: GcView<ThunkData<'p>>) -> Self {
557        Self { data }
558    }
559}
560
561/// A fully evaluated value.
562///
563/// Each instance of [`Value`] are tied to a [`Program`] instance. Instances of
564/// this type will only be valid as long as the [`Program`] they came from has
565/// not been dropped.
566#[derive(Clone)]
567pub struct Value<'p> {
568    inner: ValueData<'p>,
569}
570
571impl<'p> Value<'p> {
572    #[inline]
573    fn from_value(inner: ValueData<'p>) -> Self {
574        Self { inner }
575    }
576
577    #[inline]
578    fn from_thunk(thunk: &ThunkData<'p>) -> Self {
579        Self {
580            inner: match *thunk.state() {
581                ThunkState::Done(ref value) => value.clone(),
582                _ => panic!("thunk not evaluated"),
583            },
584        }
585    }
586
587    /// Creates a null value.
588    ///
589    /// The returned [`Value`] will not be tied to any specific [`Program`]
590    /// instance.
591    #[inline]
592    pub fn null() -> Self {
593        Self::from_value(ValueData::Null)
594    }
595
596    /// Creates a boolean value.
597    ///
598    /// The returned [`Value`] will not be tied to any specific [`Program`]
599    /// instance.
600    #[inline]
601    pub fn bool(value: bool) -> Self {
602        Self::from_value(ValueData::Bool(value))
603    }
604
605    /// Creates a numberic value.
606    ///
607    /// The returned [`Value`] will not be tied to any specific [`Program`]
608    /// instance.
609    #[inline]
610    pub fn number(value: f64) -> Self {
611        Self::from_value(ValueData::Number(value))
612    }
613
614    /// Creates a string value.
615    ///
616    /// The returned [`Value`] will not be tied to any specific [`Program`]
617    /// instance.
618    #[inline]
619    pub fn string(s: &str) -> Self {
620        Self::from_value(ValueData::String(s.into()))
621    }
622
623    #[must_use]
624    pub fn kind(&self) -> ValueKind<'p> {
625        match self.inner {
626            ValueData::Null => ValueKind::Null,
627            ValueData::Bool(value) => ValueKind::Bool(value),
628            ValueData::Number(value) => ValueKind::Number(value),
629            ValueData::String(ref s) => ValueKind::String((**s).into()),
630            ValueData::Array(ref array) => ValueKind::Array(Self::extract_array(&array.view())),
631            ValueData::Object(ref object) => {
632                ValueKind::Object(Self::extract_object(&object.view()))
633            }
634            ValueData::Function(_) => ValueKind::Function,
635        }
636    }
637
638    #[must_use]
639    #[inline]
640    pub fn is_null(&self) -> bool {
641        matches!(self.inner, ValueData::Null)
642    }
643
644    #[must_use]
645    #[inline]
646    pub fn is_bool(&self) -> bool {
647        matches!(self.inner, ValueData::Bool(_))
648    }
649
650    #[must_use]
651    #[inline]
652    pub fn as_bool(&self) -> Option<bool> {
653        if let ValueData::Bool(value) = self.inner {
654            Some(value)
655        } else {
656            None
657        }
658    }
659
660    #[must_use]
661    #[inline]
662    pub fn is_number(&self) -> bool {
663        matches!(self.inner, ValueData::Number(_))
664    }
665
666    #[must_use]
667    #[inline]
668    pub fn as_number(&self) -> Option<f64> {
669        if let ValueData::Number(value) = self.inner {
670            Some(value)
671        } else {
672            None
673        }
674    }
675
676    #[must_use]
677    #[inline]
678    pub fn is_string(&self) -> bool {
679        matches!(self.inner, ValueData::String(_))
680    }
681
682    #[must_use]
683    #[inline]
684    pub fn to_string(&self) -> Option<String> {
685        if let ValueData::String(ref s) = self.inner {
686            Some((**s).into())
687        } else {
688            None
689        }
690    }
691
692    #[must_use]
693    #[inline]
694    pub fn is_array(&self) -> bool {
695        matches!(self.inner, ValueData::Array(_))
696    }
697
698    #[must_use]
699    pub fn to_array(&self) -> Option<Vec<Self>> {
700        if let ValueData::Array(ref array) = self.inner {
701            Some(Self::extract_array(&array.view()))
702        } else {
703            None
704        }
705    }
706
707    #[must_use]
708    #[inline]
709    pub fn is_object(&self) -> bool {
710        matches!(self.inner, ValueData::Object(_))
711    }
712
713    #[must_use]
714    pub fn to_object(&self) -> Option<Vec<(InternedStr<'p>, Self)>> {
715        if let ValueData::Object(ref object) = self.inner {
716            Some(Self::extract_object(&object.view()))
717        } else {
718            None
719        }
720    }
721
722    #[must_use]
723    #[inline]
724    pub fn is_function(&self) -> bool {
725        matches!(self.inner, ValueData::Function(_))
726    }
727
728    fn extract_array(array: &ArrayData<'p>) -> Vec<Self> {
729        array
730            .iter()
731            .map(|item| Self::from_thunk(&item.view()))
732            .collect()
733    }
734
735    fn extract_object(object: &ObjectData<'p>) -> Vec<(InternedStr<'p>, Self)> {
736        let mut fields = Vec::new();
737        for &(name, visibility) in object.get_fields_order().iter() {
738            if visibility != ast::Visibility::Hidden {
739                let (_, field) = object.find_field(0, name).unwrap();
740                let thunk = field.thunk.get().unwrap().clone();
741                fields.push((name, Self::from_thunk(&thunk.view())));
742            }
743        }
744        fields
745    }
746}
747
748#[derive(Clone)]
749pub enum ValueKind<'p> {
750    Null,
751    Bool(bool),
752    Number(f64),
753    String(String),
754    Array(Vec<Value<'p>>),
755    Object(Vec<(InternedStr<'p>, Value<'p>)>),
756    Function,
757}