Skip to main content

oxide_core/
runtime.rs

1//! The `Runtime`: the heap, the code store and the realm. Every operation is
2//! `fn(&mut Runtime, …)`; there is no shared interior mutability.
3
4use crate::PropertyKey;
5use crate::bytecode::{self, CodeStore, CodeUnit, CodeUnitId, ValidateError};
6use crate::heap::atom::KeyKind;
7use crate::heap::gc::{Handle, HeapLimits};
8use crate::heap::object::ObjectKind;
9use crate::heap::{Heap, HeapError};
10use crate::value::{ObjectId, ScopeId, StringId, SymbolId, Value};
11use crate::vm::regexp::RegExpStore;
12use crate::vm::{Cx, ErrorKind, Output, Services, Throw, VmStack};
13
14macro_rules! intrinsics {
15    ($($(#[$doc:meta])* $name:ident,)*) => {
16        /// Intrinsic objects held by the realm: the prototypes and the
17        /// constructors the natives and the loop name. Each is a fixed root.
18        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
19        #[repr(u8)]
20        pub enum Intrinsic {
21            $($(#[$doc])* $name,)*
22        }
23
24        impl Intrinsic {
25            pub const ALL: &'static [Intrinsic] = &[$(Intrinsic::$name,)*];
26            pub const COUNT: usize = Intrinsic::ALL.len();
27        }
28    };
29}
30
31intrinsics! {
32    ObjectPrototype,
33    FunctionPrototype,
34    ArrayPrototype,
35    StringPrototype,
36    RegExpPrototype,
37    NumberPrototype,
38    BooleanPrototype,
39    SymbolPrototype,
40    BigIntPrototype,
41    DatePrototype,
42    ErrorPrototype,
43    EvalErrorPrototype,
44    RangeErrorPrototype,
45    ReferenceErrorPrototype,
46    SyntaxErrorPrototype,
47    TypeErrorPrototype,
48    InternalErrorPrototype,
49    UriErrorPrototype,
50    AggregateErrorPrototype,
51    /// A preallocated `InternalError: out of memory`, thrown when the heap
52    /// cannot allocate the error itself.
53    OutOfMemoryError,
54    /// `%ThrowTypeError%`.
55    ThrowTypeError,
56    Object,
57    Function,
58    Array,
59    String,
60    Number,
61    Boolean,
62    Symbol,
63    BigInt,
64    RegExp,
65    Date,
66    Promise,
67    Proxy,
68    Eval,
69    IteratorPrototype,
70    AsyncIteratorPrototype,
71    ArrayIteratorPrototype,
72    StringIteratorPrototype,
73    MapIteratorPrototype,
74    SetIteratorPrototype,
75    RegExpStringIteratorPrototype,
76    IteratorHelperPrototype,
77    Iterator,
78    WrapForValidIteratorPrototype,
79    AsyncFromSyncIteratorPrototype,
80    /// `%GeneratorFunction.prototype%`.
81    GeneratorFunctionPrototype,
82    /// `%GeneratorFunction.prototype.prototype%`.
83    GeneratorPrototype,
84    AsyncFunctionPrototype,
85    AsyncGeneratorFunctionPrototype,
86    AsyncGeneratorPrototype,
87    GeneratorFunction,
88    AsyncFunction,
89    AsyncGeneratorFunction,
90    PromisePrototype,
91    MapPrototype,
92    SetPrototype,
93    WeakMapPrototype,
94    WeakSetPrototype,
95    WeakRefPrototype,
96    FinalizationRegistryPrototype,
97    ArrayBufferPrototype,
98    SharedArrayBufferPrototype,
99    DataViewPrototype,
100    TypedArray,
101    TypedArrayPrototype,
102    Int8ArrayPrototype,
103    Uint8ArrayPrototype,
104    Uint8ClampedArrayPrototype,
105    Int16ArrayPrototype,
106    Uint16ArrayPrototype,
107    Int32ArrayPrototype,
108    Uint32ArrayPrototype,
109    Float16ArrayPrototype,
110    Float32ArrayPrototype,
111    Float64ArrayPrototype,
112    BigInt64ArrayPrototype,
113    BigUint64ArrayPrototype,
114    Int8Array,
115    Uint8Array,
116    Uint8ClampedArray,
117    Int16Array,
118    Uint16Array,
119    Int32Array,
120    Uint32Array,
121    Float16Array,
122    Float32Array,
123    Float64Array,
124    BigInt64Array,
125    BigUint64Array,
126    ArrayBuffer,
127    /// `Array.prototype.values`, which is also `arguments[@@iterator]`.
128    ArrayValues,
129    /// The global lexical environment's dictionary.
130    GlobalLexical,
131}
132
133macro_rules! names {
134    ($($field:ident: $text:expr,)*) => {
135        /// Atoms the loop and the natives name: property names they read
136        /// themselves and the `typeof` results. They are interned while the
137        /// runtime is created, so they are pinned and stay valid.
138        #[derive(Clone, Copy, Debug)]
139        pub(crate) struct Names {
140            $(pub $field: StringId,)*
141        }
142
143        impl Names {
144            fn new(heap: &mut Heap) -> Result<Names, HeapError> {
145                Ok(Names {
146                    $($field: heap.intern($text)?,)*
147                })
148            }
149        }
150    };
151}
152
153names! {
154    length: "length",
155    callee: "callee",
156    caller: "caller",
157    arguments: "arguments",
158    last_index: "lastIndex",
159    index: "index",
160    indices: "indices",
161    input: "input",
162    groups: "groups",
163    prototype: "prototype",
164    constructor: "constructor",
165    message: "message",
166    name: "name",
167    cause: "cause",
168    stack: "stack",
169    file_name: "fileName",
170    line_number: "lineNumber",
171    column_number: "columnNumber",
172    errors: "errors",
173    value_of: "valueOf",
174    to_string: "toString",
175    to_json: "toJSON",
176    undefined: "undefined",
177    null: "null",
178    true_: "true",
179    false_: "false",
180    object: "object",
181    boolean: "boolean",
182    number: "number",
183    string: "string",
184    symbol: "symbol",
185    function: "function",
186    bigint: "bigint",
187    empty: "",
188    value: "value",
189    writable: "writable",
190    enumerable: "enumerable",
191    configurable: "configurable",
192    get: "get",
193    set: "set",
194    next: "next",
195    done: "done",
196    return_: "return",
197    throw: "throw",
198    then: "then",
199    proto: "__proto__",
200    raw: "raw",
201    join: "join",
202    default: "default",
203    flags: "flags",
204    source: "source",
205    exec: "exec",
206    size: "size",
207    iterator_tag: "Iterator",
208    resolve: "resolve",
209    reject: "reject",
210    promise: "promise",
211    status: "status",
212    reason: "reason",
213    fulfilled: "fulfilled",
214    rejected: "rejected",
215}
216
217impl Names {
218    #[inline]
219    pub fn key(id: StringId) -> PropertyKey {
220        PropertyKey::atom(id)
221    }
222}
223
224macro_rules! well_known {
225    ($($field:ident: $text:expr,)*) => {
226        /// The well-known symbols (`Symbol.iterator`, …), fixed roots.
227        #[derive(Clone, Copy, Debug)]
228        pub(crate) struct WellKnown {
229            $(pub $field: SymbolId,)*
230        }
231
232        impl WellKnown {
233            fn new(heap: &mut Heap) -> Result<WellKnown, HeapError> {
234                Ok(WellKnown {
235                    $($field: {
236                        let description = heap.intern($text)?;
237                        let symbol = heap.alloc_symbol(Some(description), false)?;
238                        heap.add_fixed_root(Value::symbol(symbol));
239                        symbol
240                    },)*
241                })
242            }
243
244            /// Every well-known symbol with its property name on `Symbol`.
245            pub fn all(&self) -> Vec<(&'static str, SymbolId)> {
246                vec![$((&$text["Symbol.".len()..], self.$field),)*]
247            }
248        }
249    };
250}
251
252well_known! {
253    async_iterator: "Symbol.asyncIterator",
254    has_instance: "Symbol.hasInstance",
255    is_concat_spreadable: "Symbol.isConcatSpreadable",
256    iterator: "Symbol.iterator",
257    match_: "Symbol.match",
258    match_all: "Symbol.matchAll",
259    replace: "Symbol.replace",
260    search: "Symbol.search",
261    species: "Symbol.species",
262    split: "Symbol.split",
263    to_primitive: "Symbol.toPrimitive",
264    to_string_tag: "Symbol.toStringTag",
265    unscopables: "Symbol.unscopables",
266}
267
268impl WellKnown {
269    #[inline]
270    pub fn key(id: SymbolId) -> PropertyKey {
271        PropertyKey::symbol(id)
272    }
273}
274
275/// The single realm of a runtime. Its objects are fixed roots of the heap.
276pub struct Realm {
277    global: ObjectId,
278    /// Cells so the builtins can record the intrinsics they create while the
279    /// realm is shared (installation runs with a `Cx`); each is a fixed root.
280    intrinsics: [std::cell::Cell<ObjectId>; Intrinsic::COUNT],
281    pub(crate) names: Names,
282    pub(crate) symbols: WellKnown,
283    /// The private key under which a home object keeps its class's brand.
284    pub(crate) brand: SymbolId,
285    /// `%RegExp.prototype%`'s flag accessors as installed, for the `flags`
286    /// getter's fast path.
287    pub(crate) regexp_flags: std::cell::Cell<Option<crate::builtins::RegExpFlagCache>>,
288    /// The shape of a fresh `%RegExp%` instance (only `lastIndex`) and the
289    /// slot of `lastIndex`.
290    pub(crate) regexp_instance_shape: std::cell::Cell<Option<(crate::value::ShapeId, u32)>>,
291}
292
293impl Realm {
294    #[inline]
295    pub fn global(&self) -> ObjectId {
296        self.global
297    }
298
299    #[inline]
300    pub fn intrinsic(&self, intrinsic: Intrinsic) -> ObjectId {
301        self.intrinsics[intrinsic as usize].get()
302    }
303
304    /// Records an intrinsic; the caller makes it a fixed root.
305    pub(crate) fn record_intrinsic(&self, intrinsic: Intrinsic, object: ObjectId) {
306        self.intrinsics[intrinsic as usize].set(object);
307    }
308}
309
310pub struct Runtime {
311    pub(crate) heap: Heap,
312    /// The units published before the program runs. Beside the heap, not in
313    /// it: the main loop borrows the running unit's code across heap calls.
314    pub(crate) code: CodeStore,
315    pub(crate) realm: Realm,
316    pub(crate) host: Services,
317}
318
319/// How a runtime is configured. `Default` is the behavior every runtime had
320/// before the options existed.
321#[derive(Clone, Copy, Debug, PartialEq)]
322pub struct RuntimeOptions {
323    /// The heap cap; `usize::MAX` bytes for none.
324    pub heap: HeapLimits,
325    /// The reserved-bytes budget's floor, in bytes.
326    pub min_heap: usize,
327    /// A collection that leaves `live` bytes allows `growth × live` more to
328    /// be allocated before the next: the budget is `(1 + growth) × live`.
329    pub growth: f64,
330    /// Registers the register file may hold before a call fails with
331    /// `InternalError: stack overflow`.
332    pub stack_limit: u32,
333    /// Bytes of host stack nested re-entries into the loop may use.
334    pub native_stack_budget: usize,
335    /// Nested re-entries into the loop allowed at most (unbounded by
336    /// default): for a host whose stack the byte measure cannot see, such as
337    /// a WebAssembly engine's.
338    pub native_depth_limit: u32,
339}
340
341impl Default for RuntimeOptions {
342    fn default() -> Self {
343        RuntimeOptions {
344            heap: HeapLimits::default(),
345            min_heap: crate::heap::gc::DEFAULT_MIN_HEAP,
346            growth: 2.0,
347            stack_limit: crate::vm::DEFAULT_STACK_LIMIT,
348            native_stack_budget: crate::vm::NATIVE_STACK_BUDGET,
349            native_depth_limit: u32::MAX,
350        }
351    }
352}
353
354impl From<HeapLimits> for RuntimeOptions {
355    fn from(heap: HeapLimits) -> Self {
356        RuntimeOptions {
357            heap,
358            ..RuntimeOptions::default()
359        }
360    }
361}
362
363/// [`Runtime::thrown_summary`]'s reading of a thrown object.
364#[derive(Clone, Debug, Default, PartialEq, Eq)]
365pub struct ThrownSummary {
366    pub name: Option<String>,
367    pub message: Option<String>,
368    pub stack: Option<String>,
369}
370
371impl Runtime {
372    pub fn new(options: impl Into<RuntimeOptions>) -> Result<Runtime, HeapError> {
373        let options = options.into();
374        let mut heap = Heap::new(options.heap);
375        let reserved = heap.bytes();
376        heap.gc
377            .configure(options.min_heap, options.growth, reserved);
378        heap.vm.limit = options.stack_limit;
379        heap.vm.native_budget = options.native_stack_budget;
380        heap.vm.native_depth = options.native_depth_limit;
381        let root = |heap: &mut Heap, object: ObjectId| {
382            heap.add_fixed_root(Value::object(object));
383            object
384        };
385        let object_prototype = heap.alloc_object(ObjectKind::Ordinary, None, 4)?;
386        let object_prototype = root(&mut heap, object_prototype);
387        // `%Function.prototype%` is itself a function that returns
388        // `undefined`.
389        let function_prototype = heap.alloc_native_function(
390            Some(object_prototype),
391            crate::builtins::Native::FunctionPrototype.index(),
392        )?;
393        let function_prototype = root(&mut heap, function_prototype);
394        // `%Array.prototype%` is itself an array (of length 0).
395        let array_prototype = heap.alloc_array(Some(object_prototype), 0)?;
396        let array_prototype = root(&mut heap, array_prototype);
397        let string_prototype =
398            heap.alloc_object(ObjectKind::Ordinary, Some(object_prototype), 4)?;
399        let string_prototype = root(&mut heap, string_prototype);
400        let regexp_prototype =
401            heap.alloc_object(ObjectKind::Ordinary, Some(object_prototype), 4)?;
402        let regexp_prototype = root(&mut heap, regexp_prototype);
403        // `%Number.prototype%` and `%Boolean.prototype%` are wrappers of 0
404        // and `false`; `%String.prototype%` stays ordinary (its exotic
405        // indices would be empty), so the string-receiver cache keeps it.
406        let number_prototype = heap.alloc_internal(
407            ObjectKind::NumberWrapper,
408            crate::heap::object::InternalTag::None,
409            Some(object_prototype),
410            4,
411            &[Value::int32(0)],
412            Default::default(),
413            0,
414        )?;
415        let number_prototype = root(&mut heap, number_prototype);
416        let boolean_prototype = heap.alloc_internal(
417            ObjectKind::BooleanWrapper,
418            crate::heap::object::InternalTag::None,
419            Some(object_prototype),
420            4,
421            &[Value::FALSE],
422            Default::default(),
423            0,
424        )?;
425        let boolean_prototype = root(&mut heap, boolean_prototype);
426        let global = heap.alloc_object(ObjectKind::GlobalObject, Some(object_prototype), 16)?;
427        let global = root(&mut heap, global);
428        // The global lexical environment: a dictionary like the global object,
429        // sharing its generation.
430        let lexical = heap.alloc_object(ObjectKind::GlobalObject, None, 16)?;
431        let lexical = root(&mut heap, lexical);
432        let names = Names::new(&mut heap)?;
433        let symbols = WellKnown::new(&mut heap)?;
434        let brand = heap.alloc_symbol(None, true)?;
435        heap.add_fixed_root(Value::symbol(brand));
436        let mut intrinsics = [object_prototype; Intrinsic::COUNT];
437        intrinsics[Intrinsic::FunctionPrototype as usize] = function_prototype;
438        intrinsics[Intrinsic::ArrayPrototype as usize] = array_prototype;
439        intrinsics[Intrinsic::StringPrototype as usize] = string_prototype;
440        intrinsics[Intrinsic::RegExpPrototype as usize] = regexp_prototype;
441        intrinsics[Intrinsic::NumberPrototype as usize] = number_prototype;
442        intrinsics[Intrinsic::BooleanPrototype as usize] = boolean_prototype;
443        intrinsics[Intrinsic::GlobalLexical as usize] = lexical;
444        let error_prototype = heap.alloc_object(ObjectKind::Ordinary, Some(object_prototype), 4)?;
445        intrinsics[Intrinsic::ErrorPrototype as usize] = root(&mut heap, error_prototype);
446        for kind in ErrorKind::ALL {
447            if kind != ErrorKind::Error {
448                let prototype =
449                    heap.alloc_object(ObjectKind::Ordinary, Some(error_prototype), 4)?;
450                intrinsics[kind.prototype() as usize] = root(&mut heap, prototype);
451            }
452        }
453        let realm = Realm {
454            global,
455            intrinsics: intrinsics.map(std::cell::Cell::new),
456            names,
457            symbols,
458            brand,
459            regexp_flags: std::cell::Cell::new(None),
460            regexp_instance_shape: std::cell::Cell::new(None),
461        };
462        let mut runtime = Runtime {
463            heap,
464            code: CodeStore::default(),
465            realm,
466            host: Services::default(),
467        };
468        let out_of_memory = crate::builtins::install(&mut runtime.cx())?;
469        runtime.set_intrinsic(Intrinsic::OutOfMemoryError, out_of_memory);
470        // The heap pinned every atom so far: the realm's names and the
471        // builtins' properties. From here atoms live as long as their holders.
472        runtime.heap.set_atom_mode(crate::heap::AtomMode::Weak);
473        Ok(runtime)
474    }
475
476    /// Runs `install` with the atoms it interns pinned, as the builtins'
477    /// are.
478    fn installing<T>(&mut self, install: impl FnOnce(&mut Cx<'_>) -> T) -> T {
479        self.native_cx().installing(install)
480    }
481
482    /// The loop's view of the runtime.
483    #[inline]
484    pub(crate) fn cx(&mut self) -> Cx<'_> {
485        Cx {
486            heap: &mut self.heap,
487            static_code: &self.code,
488            realm: &self.realm,
489            host: &mut self.host,
490        }
491    }
492
493    pub(crate) fn set_intrinsic(&mut self, intrinsic: Intrinsic, object: ObjectId) {
494        self.heap.add_fixed_root(Value::object(object));
495        self.realm.record_intrinsic(intrinsic, object);
496    }
497
498    pub fn regexps(&self) -> &RegExpStore {
499        &self.heap.regexps
500    }
501
502    pub fn realm(&self) -> &Realm {
503        &self.realm
504    }
505
506    pub fn host(&mut self) -> &mut Services {
507        &mut self.host
508    }
509
510    /// Whether `Atomics.wait` may block (Test262's `CanBlockIsFalse`
511    /// clears it).
512    pub fn set_can_block(&mut self, can_block: bool) {
513        self.host.can_block = can_block;
514    }
515
516    /// Installs the embedder's hooks and seeds `Math.random` from
517    /// [`Host::random_seed`](crate::host::Host::random_seed).
518    pub fn set_host(&mut self, mut hooks: Box<dyn crate::host::Host>) {
519        let seed = hooks.random_seed();
520        self.host.seed_random(seed);
521        self.host.hooks = hooks;
522    }
523
524    /// Keeps every rejected promise without a handler alive until a handler
525    /// is attached or [`Runtime::clear_unhandled_rejections`] (QuickJS's
526    /// `js_std_promise_rejection_tracker`). Off by default.
527    pub fn set_track_rejections(&mut self, track: bool) {
528        self.host.track_rejections = track;
529        if !track {
530            self.heap.rejections.clear();
531        }
532    }
533
534    /// The tracked unhandled rejected promises, in rejection order.
535    pub fn unhandled_rejections(&self) -> &[Value] {
536        &self.heap.rejections
537    }
538
539    pub fn clear_unhandled_rejections(&mut self) {
540        self.heap.rejections.clear();
541    }
542
543    /// Sends `print` output to a buffer; [`Runtime::take_output`] returns it.
544    pub fn capture_output(&mut self) {
545        self.host.output = Output::Capture(String::new());
546    }
547
548    /// The output captured since the last call.
549    pub fn take_output(&mut self) -> String {
550        match &mut self.host.output {
551            Output::Capture(text) => std::mem::take(text),
552            Output::Stdout => String::new(),
553        }
554    }
555
556    /// Defines the Test262 host object `$262` on the global object.
557    pub fn install_test262_host(&mut self) -> Result<(), HeapError> {
558        self.installing(crate::builtins::test262::install)
559    }
560
561    /// Defines qjs's `print` and `console.log` on the global object.
562    pub fn install_print(&mut self) -> Result<(), HeapError> {
563        self.installing(crate::builtins::install_print)
564    }
565
566    /// Defines qjs's `--std` globals, `load` and `read`, over the host's
567    /// [`read_file`](crate::host::Host::read_file).
568    pub fn install_std(&mut self) -> Result<(), HeapError> {
569        self.native_cx().install_std()
570    }
571
572    /// Defines `qjs`'s helpers as QuickJS's `js_std_add_helpers` does:
573    /// `console`, `scriptArgs` when given, `print`.
574    pub fn install_qjs_helpers(&mut self, script_args: Option<&[String]>) -> Result<(), HeapError> {
575        self.native_cx().install_qjs_helpers(script_args)
576    }
577
578    /// Defines qjs's `scriptArgs`.
579    pub fn install_script_args(&mut self, args: &[String]) -> Result<(), HeapError> {
580        self.installing(|cx| crate::builtins::install_script_args(cx, args))
581    }
582
583    /// Runs a script function (from [`Runtime::instantiate`]) with the
584    /// global object as `this`. The result is unrooted.
585    pub fn run_script(&mut self, script: ObjectId) -> Result<Value, Throw> {
586        self.native_cx().run_script(script)
587    }
588
589    /// Runs pending jobs until the queue is empty; the host calls it at its
590    /// boundary. A job that throws stops the drain.
591    pub fn run_jobs(&mut self) -> Result<(), Throw> {
592        crate::vm::jobs::run_jobs(&mut self.cx())
593    }
594
595    /// Runs the oldest pending job (then clears the kept objects); `false`
596    /// when none was pending. A job that throws reports the throw.
597    pub fn run_pending_job(&mut self) -> Result<bool, Throw> {
598        crate::vm::jobs::run_one_job(&mut self.cx())
599    }
600
601    /// `ClearKeptObjects`: the end of a synchronous execution sequence.
602    pub fn clear_kept_objects(&mut self) {
603        self.heap.kept.clear();
604    }
605
606    /// Whether jobs are pending.
607    pub fn has_pending_jobs(&self) -> bool {
608        !self.heap.jobs.is_empty()
609    }
610
611    /// Calls `function`; the result or the thrown value is unrooted. The
612    /// caller keeps `function` alive (a handle or a rooted object).
613    pub fn call(&mut self, function: Value, this: Value, args: &[Value]) -> Result<Value, Throw> {
614        self.native_cx().call(function, this, args)
615    }
616
617    /// `ToString(value)` as Rust text, for hosts printing a result or an
618    /// uncaught exception. Calls back into JavaScript for objects.
619    pub fn display(&mut self, value: Value) -> Result<String, Throw> {
620        crate::vm::ops::display(&mut self.cx(), value)
621    }
622
623    /// The register file and frame table.
624    pub fn vm_stack(&self) -> &VmStack {
625        &self.heap.vm
626    }
627
628    pub fn set_stack_limit(&mut self, registers: u32) {
629        self.heap.vm.limit = registers;
630    }
631
632    /// Sets the bytes of host stack nested re-entries into the loop may use
633    /// ([`RuntimeOptions::native_stack_budget`]): a host that knows how much
634    /// stack the thread has left sets it before an outermost entry.
635    pub fn set_native_stack_budget(&mut self, bytes: usize) {
636        self.heap.vm.native_budget = bytes;
637    }
638
639    #[inline]
640    pub fn heap(&self) -> &Heap {
641        &self.heap
642    }
643
644    #[inline]
645    pub fn heap_mut(&mut self) -> &mut Heap {
646        &mut self.heap
647    }
648
649    #[inline]
650    pub fn code(&self) -> &CodeStore {
651        &self.code
652    }
653
654    /// Every published unit: the code store's and the heap's.
655    #[inline]
656    pub fn units(&self) -> crate::bytecode::Units<'_> {
657        crate::bytecode::Units {
658            code: &self.code,
659            dynamic: &self.heap.code,
660        }
661    }
662
663    /// The heap and the code store borrowed together, so code can be read
664    /// while the heap is mutated.
665    #[inline]
666    pub fn heap_and_code(&mut self) -> (&mut Heap, &CodeStore) {
667        (&mut self.heap, &self.code)
668    }
669
670    /// Validates `unit` and appends it to the code store. Its children must
671    /// be published first.
672    pub fn publish(&mut self, unit: CodeUnit) -> Result<CodeUnitId, ValidateError> {
673        self.compiler().publish(unit)
674    }
675
676    /// A function object for the published unit `root`, with a bytecode
677    /// record for it and every unit below it. `constants` holds one table per
678    /// unit, in the pre-order of [`Units::tree`](crate::Units::tree); each must have the
679    /// unit's `constant_count` entries. The constants need no rooting.
680    pub fn instantiate(
681        &mut self,
682        root: CodeUnitId,
683        constants: &[&[Value]],
684    ) -> Result<ObjectId, HeapError> {
685        self.compiler().instantiate(root, constants)
686    }
687
688    /// The heap, the code store and the realm, for a compilation whose units
689    /// live as long as the runtime (in the code store, where the loop runs
690    /// them as a plain borrow): the program and its modules.
691    pub fn compiler(&mut self) -> crate::vm::compile::Compiler<'_> {
692        crate::vm::compile::Compiler::owned(&mut self.heap, &mut self.code, &self.realm)
693    }
694
695    /// [`Runtime::compiler`] for code the embedder compiles and lets go of (a
696    /// REPL's lines): its units go in the heap, as `eval`'s do, and are freed
697    /// once no function names them.
698    pub fn transient_compiler(&mut self) -> crate::vm::compile::Compiler<'_> {
699        crate::vm::compile::Compiler::new(&mut self.heap, &self.code, &self.realm)
700    }
701
702    /// Installs the embedder's compiler, for `eval`, the `Function`
703    /// constructors and `$262.evalScript`.
704    pub fn set_compiler(&mut self, compile: crate::vm::compile::CompileFn) {
705        self.host.compile = Some(compile);
706    }
707
708    /// The inline-cache report, when the counters exist (the `ic-stats` feature
709    /// or a debug build): hits and misses per cache kind, the site states, and
710    /// the `worst` sites with the most misses (unit name, instruction index,
711    /// key).
712    pub fn ic_report(&self, worst: usize) -> Option<String> {
713        use std::fmt::Write;
714
715        use crate::vm::ic::IcKind;
716        let ics = self.heap.ics();
717        let stats = ics.stats()?;
718        let mut out = String::new();
719        let _ = writeln!(
720            out,
721            "{:<10}{:>14}{:>14}{:>14}{:>14}{:>9}",
722            "ic", "mono hits", "poly hits", "fills", "unfilled", "hit %"
723        );
724        let kinds = [IcKind::Get, IcKind::Set, IcKind::Global, IcKind::Construct];
725        for kind in kinds {
726            let k = kind as usize;
727            let hits = stats.mono_hits[k] + stats.poly_hits[k];
728            let total = hits + stats.fills[k] + stats.unfilled[k];
729            let rate = if total == 0 {
730                0.0
731            } else {
732                100.0 * hits as f64 / total as f64
733            };
734            let _ = writeln!(
735                out,
736                "{:<10}{:>14}{:>14}{:>14}{:>14}{:>8.2}%",
737                kind.name(),
738                stats.mono_hits[k],
739                stats.poly_hits[k],
740                stats.fills[k],
741                stats.unfilled[k],
742                rate
743            );
744        }
745        let (mono, poly, mega) = ics.site_states();
746        let _ = writeln!(
747            out,
748            "sites: {} entries, {mono} monomorphic, {poly} polymorphic, {mega} megamorphic",
749            ics.len()
750        );
751        let _ = writeln!(
752            out,
753            "iteration: {} pristine arrays, {} through the protocol",
754            stats.iter_pristine, stats.iter_generic
755        );
756        let mut sites = Vec::new();
757        for (id, unit) in self.units().all() {
758            for (pc, insn) in unit.code.iter().enumerate() {
759                if let Some(site) = insn.site() {
760                    let entry = (unit.ic_base + site) as usize;
761                    let misses = stats.site_misses[entry];
762                    if misses > 0 {
763                        sites.push((misses, id, pc, *insn));
764                    }
765                }
766            }
767        }
768        sites.sort_by_key(|&(misses, id, pc, _)| (std::cmp::Reverse(misses), id, pc));
769        for &(misses, id, pc, insn) in sites.iter().take(worst) {
770            let unit = self.units().get(id);
771            let name = if unit.debug.name.is_empty() {
772                "<anonymous>"
773            } else {
774                &unit.debug.name
775            };
776            let key = match insn.op {
777                crate::bytecode::Opcode::New => String::new(),
778                _ => crate::vm::property::key_text(&self.heap, insn.key()),
779            };
780            let _ = writeln!(
781                out,
782                "  {misses:>10} misses  {name} @{pc} {} {key}",
783                insn.op.name()
784            );
785        }
786        Some(out)
787    }
788
789    /// The disassembly of `unit`, with atom keys spelled out.
790    pub fn disassemble(&self, unit: CodeUnitId) -> String {
791        let heap = &self.heap;
792        bytecode::disassemble(&self.units().get(unit), &mut |key| match key.kind() {
793            KeyKind::String(id) => format!(".{}", heap.string_text(id)),
794            _ => bytecode::disasm::key_placeholder(key),
795        })
796    }
797
798    pub fn global_object(&self) -> ObjectId {
799        self.realm.global
800    }
801
802    pub fn intrinsic(&self, intrinsic: Intrinsic) -> ObjectId {
803        self.realm.intrinsic(intrinsic)
804    }
805
806    /// A tagged template's site object: a frozen array of the cooked strings
807    /// with a frozen `raw` array of the raw ones (strings and `undefined`).
808    pub fn template_object(
809        &mut self,
810        cooked: &[Value],
811        raw: &[Value],
812    ) -> Result<ObjectId, HeapError> {
813        self.compiler().template_object(cooked, raw)
814    }
815
816    /// An ordinary object inheriting from `%Object.prototype%`.
817    pub fn new_object(&mut self) -> Result<ObjectId, HeapError> {
818        self.native_cx().new_object()
819    }
820
821    pub fn new_array(&mut self, capacity: u32) -> Result<ObjectId, HeapError> {
822        let prototype = self.intrinsic(Intrinsic::ArrayPrototype);
823        self.heap.alloc_array(Some(prototype), capacity)
824    }
825
826    pub fn new_function(&mut self, scope: Option<ScopeId>) -> Result<ObjectId, HeapError> {
827        let prototype = self.intrinsic(Intrinsic::FunctionPrototype);
828        self.heap.alloc_function(Some(prototype), scope)
829    }
830
831    pub fn new_scope(&mut self, parent: Option<ScopeId>, slots: u32) -> Result<ScopeId, HeapError> {
832        self.heap.alloc_scope(parent, slots)
833    }
834
835    /// What a test host reports of a thrown object without running
836    /// JavaScript: the error name (an own `name` string, else the
837    /// prototype's, else the prototype's `constructor`'s own `name` other
838    /// than `Object`), the own `message` string and the own `stack` string.
839    /// Accessors and non-string values count as absent; `None` for a
840    /// primitive.
841    pub fn thrown_summary(&mut self, value: Value) -> Option<ThrownSummary> {
842        let object = value.as_object()?;
843        let heap = &mut self.heap;
844        let (Ok(name), Ok(message), Ok(constructor)) = (
845            heap.intern("name"),
846            heap.intern("message"),
847            heap.intern("constructor"),
848        ) else {
849            return Some(ThrownSummary::default());
850        };
851        let heap = &self.heap;
852        let names = &self.realm.names;
853        let own = |object: ObjectId, key: StringId| {
854            crate::vm::property::own(heap, names, object, Names::key(key))
855        };
856        let own_string = |object: ObjectId, key: StringId| match own(object, key) {
857            Some(crate::vm::property::Found::Data(value)) => {
858                Some(value.as_string().map(|id| heap.string_text(id)))
859            }
860            Some(_) => Some(None),
861            None => None,
862        };
863        // `Some(Some(name))` names it, `Some(None)` stops the walk.
864        let named = |found: Option<Option<String>>| match found {
865            Some(Some(name)) if !name.is_empty() => Some(Some(name)),
866            Some(_) => Some(None),
867            None => None,
868        };
869        let error_name = named(own_string(object, name))
870            .or_else(|| {
871                let prototype = heap.prototype_of(object)?;
872                named(own_string(prototype, name)).or_else(|| {
873                    let Some(crate::vm::property::Found::Data(function)) =
874                        own(prototype, constructor)
875                    else {
876                        return Some(None);
877                    };
878                    let function = function.as_object()?;
879                    Some(
880                        own_string(function, name)
881                            .flatten()
882                            .filter(|name| !name.is_empty() && name != "Object"),
883                    )
884                })
885            })
886            .flatten();
887        Some(ThrownSummary {
888            name: error_name,
889            message: own_string(object, message).flatten(),
890            stack: own_string(object, self.realm.names.stack).flatten(),
891        })
892    }
893
894    /// What every part of the runtime holds, read without a collection. Its
895    /// reserved total equals [`Heap::bytes`].
896    pub fn memory_report(&self) -> crate::heap::report::MemoryReport {
897        let mut report = self.heap.report();
898        let code = &mut report.code;
899        code.static_units = self.code.len();
900        for (_, unit) in self.units().all() {
901            code.units += 1;
902            code.instructions += unit.code.len();
903            code.instruction_bytes += std::mem::size_of_val(&*unit.code);
904            code.position_bytes += std::mem::size_of_val(&*unit.debug.positions);
905            code.source_bytes += unit.source.as_ref().map_or(0, |source| source.len());
906        }
907        report
908    }
909
910    /// Holds `value` for the embedder until [`Runtime::release`].
911    pub fn hold(&mut self, value: Value) -> Handle {
912        self.native_cx().hold(value)
913    }
914
915    pub fn held(&self, handle: Handle) -> Value {
916        self.heap.handles_ref().get(handle)
917    }
918
919    pub fn release(&mut self, handle: Handle) {
920        self.native_cx().release(handle);
921    }
922}