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