Skip to main content

oxide_core/
embed.rs

1//! The embedder's entries: the object, property, conversion and
2//! value-construction operations the root crate's API exposes, each one engine
3//! entry over the internal operations the loop uses. They are implemented
4//! once, on [`NativeCx`], which a [`Runtime`] lends between entries
5//! ([`Runtime::native_cx`]) and the loop lends to a host function while it
6//! runs; `Runtime` keeps each as a delegation.
7//!
8//! Rooting: every `Value` passed in must be rooted by the caller (a handle
9//! from [`Runtime::hold`], a fixed root, or a value the caller just received
10//! and holds before its next entry). Results are unrooted; hold them before
11//! the next call that can allocate.
12
13use std::any::Any;
14use std::rc::Rc;
15
16use crate::bytecode::CodeStore;
17use crate::heap::atom::{KeyKind, PropertyKey};
18use crate::heap::object::{InternalData, InternalTag, flags};
19use crate::heap::string::StringRepr;
20use crate::runtime::{Realm, Runtime, WellKnown};
21use crate::value::{Kind, ObjectId, Value};
22use crate::vm::native::{HostBody, HostFn};
23use crate::vm::object::Descriptor;
24use crate::vm::{Cx, ErrorKind, Services, Throw, ops};
25use crate::{Heap, HeapError, ObjectKind};
26
27/// A property descriptor: absent fields are `None`. An accessor has `get`
28/// or `set` and no `value` or `writable`.
29#[derive(Clone, Copy, Debug, Default, PartialEq)]
30pub struct PropertyDescriptor {
31    pub value: Option<Value>,
32    pub get: Option<Value>,
33    pub set: Option<Value>,
34    pub writable: Option<bool>,
35    pub enumerable: Option<bool>,
36    pub configurable: Option<bool>,
37}
38
39impl PropertyDescriptor {
40    pub fn is_accessor(&self) -> bool {
41        self.get.is_some() || self.set.is_some()
42    }
43
44    fn to_internal(self) -> Descriptor {
45        Descriptor {
46            value: self.value,
47            get: self.get,
48            set: self.set,
49            writable: self.writable,
50            enumerable: self.enumerable,
51            configurable: self.configurable,
52        }
53    }
54
55    fn from_internal(descriptor: Descriptor) -> Self {
56        PropertyDescriptor {
57            value: descriptor.value,
58            get: descriptor.get,
59            set: descriptor.set,
60            writable: descriptor.writable,
61            enumerable: descriptor.enumerable,
62            configurable: descriptor.configurable,
63        }
64    }
65}
66
67/// What a property key names, read without running JavaScript.
68#[derive(Clone, Debug, PartialEq, Eq)]
69pub enum KeyName {
70    Index(u32),
71    String(Vec<u16>),
72    /// A symbol key: the symbol itself (a fixed or otherwise rooted value
73    /// whenever the key came from a rooted object).
74    Symbol(Value),
75}
76
77/// The embedder's view of a running engine: the heap, the realm, the
78/// services and the code store, owned (`Runtime::native_cx`) or borrowed
79/// (inside a host function).
80pub struct NativeCx<'a> {
81    pub(crate) heap: &'a mut Heap,
82    code: CodeView<'a>,
83    pub(crate) realm: &'a Realm,
84    pub(crate) host: &'a mut Services,
85}
86
87/// The code store a [`NativeCx`] holds: the runtime's own between entries,
88/// or the running loop's, shared, inside a host function.
89enum CodeView<'a> {
90    Owned(&'a mut CodeStore),
91    Shared(&'a CodeStore),
92}
93
94impl Runtime {
95    /// The embedder's view of the runtime between entries.
96    #[inline]
97    pub fn native_cx(&mut self) -> NativeCx<'_> {
98        NativeCx {
99            heap: &mut self.heap,
100            code: CodeView::Owned(&mut self.code),
101            realm: &self.realm,
102            host: &mut self.host,
103        }
104    }
105}
106
107/// [`NativeCx::new_host_function`] over the heap and the realm alone (a
108/// module loader's compile-time view makes them too).
109pub(crate) fn host_function(
110    heap: &mut Heap,
111    realm: &Realm,
112    name: &str,
113    length: u32,
114    constructor: bool,
115    data: &[Value],
116    body: Rc<HostFn>,
117) -> Result<ObjectId, HeapError> {
118    let record = heap.alloc_internal(
119        ObjectKind::Builtin,
120        InternalTag::NativeData,
121        None,
122        0,
123        data,
124        InternalData::HostFn(HostBody(body)),
125        0,
126    )?;
127    let mark = heap.roots().mark();
128    heap.roots().push(Value::object(record));
129    let result = (|| {
130        let native = if constructor {
131            crate::builtins::Native::HostConstructor
132        } else {
133            crate::builtins::Native::HostFunction
134        };
135        let prototype = realm.intrinsic(crate::Intrinsic::FunctionPrototype);
136        let function = heap.alloc_native_closure(Some(prototype), native.index(), Some(record))?;
137        heap.roots().push(Value::object(function));
138        if constructor {
139            heap.set_object_flag(function, flags::CONSTRUCTOR);
140        }
141        let name = heap.alloc_string(name)?;
142        crate::builtins::define_name_length(
143            heap,
144            &realm.names,
145            function,
146            Value::string(name),
147            length,
148        )?;
149        Ok(function)
150    })();
151    heap.roots().truncate(mark);
152    result
153}
154
155/// [`NativeCx::new_host_object`] over the heap alone.
156pub(crate) fn host_object(
157    heap: &mut Heap,
158    prototype: Option<ObjectId>,
159    data: Rc<dyn Any>,
160) -> Result<ObjectId, HeapError> {
161    heap.alloc_internal(
162        ObjectKind::Builtin,
163        InternalTag::HostData,
164        prototype,
165        4,
166        &[],
167        InternalData::HostData(data),
168        0,
169    )
170}
171
172impl<'a> NativeCx<'a> {
173    /// The same view for a shorter borrow.
174    pub fn reborrow(&mut self) -> NativeCx<'_> {
175        NativeCx {
176            heap: self.heap,
177            code: match &mut self.code {
178                CodeView::Owned(code) => CodeView::Owned(code),
179                CodeView::Shared(code) => CodeView::Shared(code),
180            },
181            realm: self.realm,
182            host: self.host,
183        }
184    }
185
186    /// The view of the loop's context, for a host function.
187    pub(crate) fn from_cx<'b>(cx: &'b mut Cx<'_>) -> NativeCx<'b> {
188        NativeCx {
189            heap: cx.heap,
190            code: CodeView::Shared(cx.static_code),
191            realm: cx.realm,
192            host: cx.host,
193        }
194    }
195
196    /// The loop's context for one operation.
197    #[inline]
198    pub(crate) fn cx(&mut self) -> Cx<'_> {
199        let static_code: &CodeStore = match &self.code {
200            CodeView::Owned(code) => code,
201            CodeView::Shared(code) => code,
202        };
203        Cx {
204            heap: self.heap,
205            static_code,
206            realm: self.realm,
207            host: self.host,
208        }
209    }
210
211    /// The heap, the code store and the realm for a compilation. Its units
212    /// go in the code store when the store is owned and `transient` is
213    /// false, else in the heap: inside a host function the running loop
214    /// borrows the store, so code compiled there is transient whatever
215    /// `transient` says.
216    pub fn compiler(&mut self, transient: bool) -> crate::vm::compile::Compiler<'_> {
217        match &mut self.code {
218            CodeView::Owned(code) => {
219                if transient {
220                    crate::vm::compile::Compiler::new(self.heap, code, self.realm)
221                } else {
222                    crate::vm::compile::Compiler::owned(self.heap, code, self.realm)
223                }
224            }
225            CodeView::Shared(code) => {
226                crate::vm::compile::Compiler::new(self.heap, code, self.realm)
227            }
228        }
229    }
230
231    /// Whether the code store is the runtime's own (between entries), not
232    /// the running loop's (inside a host function).
233    pub fn owns_code(&self) -> bool {
234        matches!(self.code, CodeView::Owned(_))
235    }
236
237    #[inline]
238    pub fn heap(&self) -> &Heap {
239        self.heap
240    }
241
242    #[inline]
243    pub fn heap_mut(&mut self) -> &mut Heap {
244        self.heap
245    }
246
247    #[inline]
248    pub fn realm(&self) -> &'a Realm {
249        self.realm
250    }
251
252    /// The flag another thread sets to interrupt the running script.
253    pub fn interrupt_flag(&self) -> std::sync::Arc<std::sync::atomic::AtomicBool> {
254        std::sync::Arc::clone(&self.host.interrupt)
255    }
256
257    /// Whether an interrupt is unwinding (the last throw was one).
258    pub fn is_interrupted(&self) -> bool {
259        self.heap.vm.interrupted
260    }
261
262    /// Ends an interrupt's unwinding, at the outermost entry: the runtime
263    /// runs scripts again.
264    pub fn clear_interrupted(&mut self) {
265        self.heap.vm.interrupted = false;
266        self.heap.vm.poll = crate::vm::INTERRUPT_INTERVAL;
267    }
268
269    /// Runs `install` with the atoms it interns pinned, as the builtins'
270    /// are.
271    pub(crate) fn installing<T>(&mut self, install: impl FnOnce(&mut Cx<'_>) -> T) -> T {
272        let mode = self.heap.set_atom_mode(crate::heap::AtomMode::Pin);
273        let result = install(&mut self.cx());
274        self.heap.set_atom_mode(mode);
275        result
276    }
277
278    /// [`Runtime::install_std`].
279    pub fn install_std(&mut self) -> Result<(), HeapError> {
280        self.installing(crate::builtins::install_std)
281    }
282
283    /// [`Runtime::install_qjs_helpers`].
284    pub fn install_qjs_helpers(&mut self, script_args: Option<&[String]>) -> Result<(), HeapError> {
285        self.installing(|cx| crate::builtins::install_qjs_helpers(cx, script_args))
286    }
287
288    /// The bytes of host stack nested re-entries into the loop may use.
289    pub fn set_native_stack_budget(&mut self, bytes: usize) {
290        self.heap.vm.native_budget = bytes;
291    }
292}
293
294impl NativeCx<'_> {
295    // ---- calls and handles ----
296
297    /// Calls `function`; the result or the thrown value is unrooted. The
298    /// caller keeps `function` alive (a handle or a rooted object).
299    pub fn call(&mut self, function: Value, this: Value, args: &[Value]) -> Result<Value, Throw> {
300        crate::vm::call(&mut self.cx(), function, this, args)
301    }
302
303    /// Runs a script function with the global object as `this`.
304    pub fn run_script(&mut self, script: ObjectId) -> Result<Value, Throw> {
305        let global = Value::object(self.realm.global());
306        self.call(Value::object(script), global, &[])
307    }
308
309    /// Holds `value` for the embedder until [`NativeCx::release`].
310    pub fn hold(&mut self, value: Value) -> crate::Handle {
311        self.heap.handles().create(value)
312    }
313
314    pub fn held(&self, handle: crate::Handle) -> Value {
315        self.heap.handles_ref().get(handle)
316    }
317
318    pub fn release(&mut self, handle: crate::Handle) {
319        self.heap.handles().release(handle);
320    }
321
322    pub fn global_object(&self) -> ObjectId {
323        self.realm.global()
324    }
325
326    pub fn intrinsic(&self, intrinsic: crate::Intrinsic) -> ObjectId {
327        self.realm.intrinsic(intrinsic)
328    }
329
330    /// An ordinary object inheriting from `%Object.prototype%`.
331    pub fn new_object(&mut self) -> Result<ObjectId, HeapError> {
332        let prototype = self.intrinsic(crate::Intrinsic::ObjectPrototype);
333        self.heap
334            .alloc_object(ObjectKind::Ordinary, Some(prototype), 4)
335    }
336
337    // ---- host functions and host objects ----
338
339    /// A host function named `name` with `length`, whose body is `body`
340    /// and whose captured values are `data` (read through
341    /// [`NativeCall::data`](crate::vm::native::NativeCall::data)). A
342    /// `constructor` may be called by `new`; it gets no `prototype`
343    /// property (the embedder defines one). `data` must be rooted by the
344    /// caller.
345    pub fn new_host_function(
346        &mut self,
347        name: &str,
348        length: u32,
349        constructor: bool,
350        data: &[Value],
351        body: Rc<HostFn>,
352    ) -> Result<ObjectId, HeapError> {
353        host_function(self.heap, self.realm, name, length, constructor, data, body)
354    }
355
356    /// An object inheriting from `prototype` (`None` for null) that holds
357    /// the embedder's `data`, read back by [`NativeCx::host_data`]. The datum
358    /// drops when the object is collected.
359    pub fn new_host_object(
360        &mut self,
361        prototype: Option<ObjectId>,
362        data: Rc<dyn Any>,
363    ) -> Result<ObjectId, HeapError> {
364        host_object(self.heap, prototype, data)
365    }
366
367    /// The datum of a host object, whatever its type; `None` for any other
368    /// object.
369    pub fn host_data(&self, object: ObjectId) -> Option<Rc<dyn Any>> {
370        if self.heap.internal_tag(object) != InternalTag::HostData {
371            return None;
372        }
373        match &self.heap.internal(object).data {
374            InternalData::HostData(data) => Some(Rc::clone(data)),
375            _ => None,
376        }
377    }
378
379    // ---- keys ----
380
381    /// The property key of the string `units` (an array index when it is
382    /// one's canonical form).
383    pub fn key_from_units(&mut self, units: &[u16]) -> Result<PropertyKey, HeapError> {
384        self.heap.property_key_units(units)
385    }
386
387    /// The key of a symbol value; `None` for any other value.
388    pub fn key_from_symbol(&self, symbol: Value) -> Option<PropertyKey> {
389        symbol.as_symbol().map(PropertyKey::symbol)
390    }
391
392    /// `ToPropertyKey(value)`.
393    pub fn to_property_key(&mut self, value: Value) -> Result<PropertyKey, Throw> {
394        ops::to_property_key(&mut self.cx(), value)
395    }
396
397    pub fn key_name(&self, key: PropertyKey) -> KeyName {
398        match key.kind() {
399            KeyKind::Index(index) => KeyName::Index(index),
400            KeyKind::String(id) => KeyName::String(self.heap.string(id).repr().units().collect()),
401            KeyKind::Symbol(id) => KeyName::Symbol(Value::symbol(id)),
402        }
403    }
404
405    // ---- objects ----
406
407    /// `object.[[Get]](key, receiver)`.
408    pub fn get(
409        &mut self,
410        object: ObjectId,
411        key: PropertyKey,
412        receiver: Value,
413    ) -> Result<Value, Throw> {
414        crate::vm::object::get(&mut self.cx(), object, key, receiver)
415    }
416
417    /// `object.[[Set]](key, value, receiver)`.
418    pub fn set(
419        &mut self,
420        object: ObjectId,
421        key: PropertyKey,
422        value: Value,
423        receiver: Value,
424    ) -> Result<bool, Throw> {
425        crate::vm::object::set(&mut self.cx(), object, key, value, receiver)
426    }
427
428    /// `object.[[GetOwnProperty]](key)`.
429    pub fn get_own_property(
430        &mut self,
431        object: ObjectId,
432        key: PropertyKey,
433    ) -> Result<Option<PropertyDescriptor>, Throw> {
434        Ok(
435            crate::vm::object::get_own_property(&mut self.cx(), object, key)?
436                .map(PropertyDescriptor::from_internal),
437        )
438    }
439
440    /// `object.[[DefineOwnProperty]](key, descriptor)`.
441    pub fn define_own_property(
442        &mut self,
443        object: ObjectId,
444        key: PropertyKey,
445        descriptor: &PropertyDescriptor,
446    ) -> Result<bool, Throw> {
447        crate::vm::object::define_own_property(
448            &mut self.cx(),
449            object,
450            key,
451            &descriptor.to_internal(),
452        )
453    }
454
455    /// `object.[[Delete]](key)`.
456    pub fn delete_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
457        crate::vm::object::delete(&mut self.cx(), object, key)
458    }
459
460    /// `HasProperty(object, key)`.
461    pub fn has_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
462        crate::vm::object::has_property(&mut self.cx(), object, key)
463    }
464
465    /// `HasOwnProperty(object, key)`.
466    pub fn has_own_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
467        crate::vm::object::has_own_property(&mut self.cx(), object, key)
468    }
469
470    /// `object.[[OwnPropertyKeys]]()`.
471    pub fn own_property_keys(&mut self, object: ObjectId) -> Result<Vec<PropertyKey>, Throw> {
472        crate::vm::object::own_property_keys(&mut self.cx(), object)
473    }
474
475    /// `object.[[GetPrototypeOf]]()`.
476    pub fn get_prototype_of(&mut self, object: ObjectId) -> Result<Option<ObjectId>, Throw> {
477        Ok(crate::vm::object::get_prototype_of(&mut self.cx(), object)?.as_object())
478    }
479
480    /// `object.[[SetPrototypeOf]](prototype)`.
481    pub fn set_prototype_of(
482        &mut self,
483        object: ObjectId,
484        prototype: Option<ObjectId>,
485    ) -> Result<bool, Throw> {
486        let prototype = prototype.map_or(Value::NULL, Value::object);
487        crate::vm::object::set_prototype_of(&mut self.cx(), object, prototype)
488    }
489
490    /// `object.[[PreventExtensions]]()`.
491    pub fn prevent_extensions(&mut self, object: ObjectId) -> Result<bool, Throw> {
492        crate::vm::object::prevent_extensions(&mut self.cx(), object)
493    }
494
495    /// `Construct(function, args, new_target)`; `new_target` is `function`
496    /// itself for a plain `new`.
497    pub fn construct(
498        &mut self,
499        function: Value,
500        args: &[Value],
501        new_target: Value,
502    ) -> Result<Value, Throw> {
503        crate::vm::construct(&mut self.cx(), function, args, new_target)
504    }
505
506    /// `IsCallable(value)`.
507    pub fn is_callable(&self, value: Value) -> bool {
508        ops::is_callable(self.heap, value)
509    }
510
511    /// `IsConstructor(value)`.
512    pub fn is_constructor(&self, value: Value) -> bool {
513        value
514            .as_object()
515            .is_some_and(|object| self.heap.header(object).has(flags::CONSTRUCTOR))
516    }
517
518    /// `IsArray(value)`: throws for a revoked proxy.
519    pub fn is_array(&mut self, value: Value) -> Result<bool, Throw> {
520        crate::vm::object::is_array(&mut self.cx(), value)
521    }
522
523    /// Whether `value` is an Error instance (has `[[ErrorData]]`).
524    pub fn is_error(&self, value: Value) -> bool {
525        value
526            .as_object()
527            .is_some_and(|object| self.heap.header(object).has(flags::ERROR))
528    }
529
530    /// Whether `value` is a promise.
531    pub fn is_promise(&mut self, value: Value) -> bool {
532        crate::vm::jobs::is_promise(&self.cx(), value)
533    }
534
535    pub fn object_kind(&self, object: ObjectId) -> ObjectKind {
536        self.heap.object_kind(object)
537    }
538
539    /// An ordinary object inheriting from `prototype` (`None` for null).
540    pub fn new_object_with_prototype(
541        &mut self,
542        prototype: Option<ObjectId>,
543    ) -> Result<ObjectId, HeapError> {
544        self.heap.alloc_object(ObjectKind::Ordinary, prototype, 4)
545    }
546
547    /// `CreateArrayFromList(values)`.
548    pub fn new_array_from(&mut self, values: &[Value]) -> Result<ObjectId, Throw> {
549        let mut cx = self.cx();
550        let length = u32::try_from(values.len()).unwrap_or(u32::MAX);
551        let prototype = cx.realm.intrinsic(crate::Intrinsic::ArrayPrototype);
552        let array = cx
553            .heap
554            .alloc_array(Some(prototype), length)
555            .map_err(|error| crate::vm::error::heap_error(&cx, error))?;
556        let mark = cx.heap.roots().mark();
557        cx.heap.roots().push(Value::object(array));
558        let mut result = Ok(array);
559        for (index, &value) in values.iter().enumerate() {
560            let key = crate::vm::object::index_key(&mut cx, index as u32);
561            if let Err(error) = key.and_then(|key| {
562                crate::vm::object::create_data_property_or_throw(&mut cx, array, key, value)
563            }) {
564                result = Err(error);
565                break;
566            }
567        }
568        cx.heap.roots().truncate(mark);
569        result
570    }
571
572    /// A new error object of `kind` with `message`, a `stack` from the
573    /// running frames.
574    pub fn new_error(&mut self, kind: ErrorKind, message: &str) -> Result<Value, HeapError> {
575        let message = self.heap.alloc_string(message)?;
576        crate::vm::error::new_error(&mut self.cx(), kind, Value::string(message))
577    }
578
579    /// A new error object located at `file:line:column` (a compile error):
580    /// `fileName`, `lineNumber` and `columnNumber`, and a `stack` that
581    /// starts there, the running frames after it.
582    pub fn new_located_error(
583        &mut self,
584        kind: ErrorKind,
585        message: &str,
586        file: &str,
587        line: u32,
588        column: u32,
589    ) -> Result<Value, HeapError> {
590        let message = self.heap.alloc_string(message)?;
591        let location = crate::vm::error::Location { file, line, column };
592        crate::vm::error::new_error_at(
593            &mut self.cx(),
594            kind,
595            Value::string(message),
596            Some(location),
597            0,
598        )
599    }
600
601    /// `NewPromiseCapability(%Promise%)`: the promise and its resolving
602    /// functions, unrooted.
603    pub fn new_promise_capability(&mut self) -> Result<(ObjectId, ObjectId, ObjectId), Throw> {
604        let mut cx = self.cx();
605        let prototype = cx.realm.intrinsic(crate::Intrinsic::PromisePrototype);
606        let promise = crate::vm::jobs::new_promise(&mut cx, prototype)?;
607        let mark = cx.heap.roots().mark();
608        cx.heap.roots().push(Value::object(promise));
609        let functions = crate::vm::jobs::resolving_functions(&mut cx, promise);
610        cx.heap.roots().truncate(mark);
611        let (resolve, reject) = functions?;
612        Ok((promise, resolve, reject))
613    }
614
615    /// A pending promise of `%Promise.prototype%`.
616    pub fn new_promise(&mut self) -> Result<ObjectId, Throw> {
617        let prototype = self.realm.intrinsic(crate::Intrinsic::PromisePrototype);
618        crate::vm::jobs::new_promise(&mut self.cx(), prototype)
619    }
620
621    // ---- buffers ----
622
623    /// A new `ArrayBuffer` of `len` zero bytes; a `RangeError` past
624    /// [`MAX_BUFFER_LENGTH`](crate::heap::buffer::MAX_BUFFER_LENGTH).
625    pub fn new_array_buffer(&mut self, len: usize) -> Result<ObjectId, Throw> {
626        let mut cx = self.cx();
627        let mark = cx.heap.roots().mark();
628        let buffer = crate::builtins::new_array_buffer(&mut cx, len);
629        cx.heap.roots().truncate(mark);
630        buffer
631    }
632
633    /// The buffer record of an `ArrayBuffer` or `SharedArrayBuffer`.
634    fn buffer(&self, object: ObjectId) -> Option<&crate::heap::buffer::Buffer> {
635        match self.heap.internal_tag(object) {
636            InternalTag::ArrayBuffer | InternalTag::SharedArrayBuffer => {
637                Some(crate::vm::typed::buffer(self.heap, object))
638            }
639            _ => None,
640        }
641    }
642
643    /// A buffer's length in bytes; `None` when `object` is not a buffer or
644    /// is detached.
645    pub fn array_buffer_len(&self, object: ObjectId) -> Option<usize> {
646        self.buffer(object)
647            .filter(|buffer| !buffer.detached)
648            .map(crate::heap::buffer::Buffer::len)
649    }
650
651    /// Checks that `offset..offset + len` is in an attached buffer: a
652    /// `TypeError` for anything else, a `RangeError` past its end.
653    fn buffer_range(&mut self, object: ObjectId, offset: usize, len: usize) -> Result<(), Throw> {
654        let Some(buffer) = self.buffer(object) else {
655            return Err(crate::vm::error::type_error(
656                &mut self.cx(),
657                "not an ArrayBuffer",
658            ));
659        };
660        if buffer.detached {
661            return Err(crate::vm::error::type_error(
662                &mut self.cx(),
663                "ArrayBuffer is detached",
664            ));
665        }
666        if offset.checked_add(len).is_none_or(|end| end > buffer.len()) {
667            return Err(crate::vm::error::range_error(
668                &mut self.cx(),
669                "out of bound",
670            ));
671        }
672        Ok(())
673    }
674
675    /// Copies `out.len()` bytes of the buffer from `offset` (a shared
676    /// buffer's byte by byte through its atomics).
677    pub fn array_buffer_read(
678        &mut self,
679        object: ObjectId,
680        offset: usize,
681        out: &mut [u8],
682    ) -> Result<(), Throw> {
683        self.buffer_range(object, offset, out.len())?;
684        crate::vm::typed::buffer(self.heap, object).read(offset, out);
685        Ok(())
686    }
687
688    /// Writes `data` into the buffer at `offset`.
689    pub fn array_buffer_write(
690        &mut self,
691        object: ObjectId,
692        offset: usize,
693        data: &[u8],
694    ) -> Result<(), Throw> {
695        self.buffer_range(object, offset, data.len())?;
696        crate::vm::typed::buffer_mut(self.heap, object).write(offset, data);
697        Ok(())
698    }
699
700    /// The bytes of an attached `ArrayBuffer`; `None` for anything else,
701    /// a `SharedArrayBuffer` included (its bytes are atomics).
702    pub fn array_buffer_bytes(&self, object: ObjectId) -> Option<&[u8]> {
703        self.buffer(object)
704            .filter(|buffer| !buffer.detached)
705            .and_then(crate::heap::buffer::Buffer::owned_slice)
706    }
707
708    /// A typed array's element kind, buffer, byte offset and current
709    /// length (`None` when it is out of bounds of its buffer or the buffer
710    /// is detached); `None` for anything but a typed array.
711    pub fn typed_array_view(
712        &self,
713        object: ObjectId,
714    ) -> Option<(
715        crate::heap::buffer::ElementKind,
716        ObjectId,
717        usize,
718        Option<usize>,
719    )> {
720        if self.heap.internal_tag(object) != InternalTag::TypedArray {
721            return None;
722        }
723        let (view, buffer) = crate::vm::typed::view(self.heap, object);
724        Some((
725            view.kind,
726            buffer,
727            view.byte_offset,
728            crate::vm::typed::length(self.heap, object),
729        ))
730    }
731
732    /// QuickJS's `JS_DetachArrayBuffer`: detaches an ArrayBuffer; anything
733    /// else is ignored.
734    pub fn detach_array_buffer(&mut self, value: Value) -> Result<(), Throw> {
735        let is_buffer = value.as_object().is_some_and(|object| {
736            self.heap.internal_tag(object) == crate::heap::object::InternalTag::ArrayBuffer
737        });
738        if is_buffer {
739            crate::builtins::detach_buffer(&mut self.cx(), value)?;
740        }
741        Ok(())
742    }
743
744    // ---- primitives ----
745
746    pub fn new_string(&mut self, text: &str) -> Result<Value, HeapError> {
747        self.heap.alloc_string(text).map(Value::string)
748    }
749
750    pub fn new_string_units(&mut self, units: &[u16]) -> Result<Value, HeapError> {
751        self.heap.alloc_string_units(units).map(Value::string)
752    }
753
754    /// A string of `repr`, which the caller made canonical (`OneByte`
755    /// whenever every unit fits in a byte).
756    pub fn new_string_repr(&mut self, repr: StringRepr) -> Result<Value, HeapError> {
757        self.heap.alloc_string_repr(repr).map(Value::string)
758    }
759
760    /// A string value's code units, flattened; `None` for any other value.
761    pub fn string_repr(&self, value: Value) -> Option<&StringRepr> {
762        value.as_string().map(|id| self.heap.string(id).repr())
763    }
764
765    /// A string value as Rust text (lone surrogates replaced).
766    pub fn string_lossy(&self, value: Value) -> Option<String> {
767        value.as_string().map(|id| self.heap.string_text(id))
768    }
769
770    /// The BigInt of the decimal text `decimal` (an optional `-`, then
771    /// digits); `None` when it does not parse.
772    pub fn new_bigint(&mut self, decimal: &str) -> Option<Result<Value, HeapError>> {
773        let big = crate::vm::bigint::Big::parse(decimal)?;
774        Some(
775            self.heap
776                .alloc_bigint(big.negative, &big.limbs)
777                .map(Value::bigint),
778        )
779    }
780
781    /// A BigInt value in decimal; `None` for any other value.
782    pub fn bigint_decimal(&self, value: Value) -> Option<String> {
783        value.as_bigint()?;
784        Some(ops::big(self.heap, value).to_string_radix10())
785    }
786
787    /// A new symbol with `description` (`Symbol(description)`).
788    pub fn new_symbol(&mut self, description: Option<&[u16]>) -> Result<Value, HeapError> {
789        let description = match description {
790            Some(units) => Some(self.heap.alloc_string_units(units)?),
791            None => None,
792        };
793        self.heap
794            .alloc_symbol(description, false)
795            .map(Value::symbol)
796    }
797
798    /// A symbol's `[[Description]]`; `None` when it is undefined or `value`
799    /// is not a symbol.
800    pub fn symbol_description(&self, value: Value) -> Option<Vec<u16>> {
801        let record = self.heap.symbol(value.as_symbol()?);
802        (record.description != crate::heap::object::NONE).then(|| {
803            self.heap
804                .string(crate::value::StringId(record.description))
805                .repr()
806                .units()
807                .collect()
808        })
809    }
810
811    /// The well-known symbol `Symbol.<name>` (`"iterator"`, …).
812    pub fn well_known_symbol(&self, name: &str) -> Option<Value> {
813        self.realm
814            .symbols
815            .all()
816            .into_iter()
817            .find(|&(candidate, _)| candidate == name)
818            .map(|(_, id)| Value::symbol(id))
819    }
820
821    pub fn well_known_symbol_key(&self, name: &str) -> Option<PropertyKey> {
822        self.well_known_symbol(name)
823            .and_then(|symbol| symbol.as_symbol())
824            .map(WellKnown::key)
825    }
826
827    // ---- conversions ----
828
829    /// `ToString(value)`.
830    pub fn to_string(&mut self, value: Value) -> Result<Value, Throw> {
831        ops::to_string(&mut self.cx(), value)
832    }
833
834    /// `ToNumber(value)`.
835    pub fn to_number(&mut self, value: Value) -> Result<f64, Throw> {
836        ops::to_number(&mut self.cx(), value)
837    }
838
839    /// `ToBoolean(value)`.
840    pub fn to_boolean(&self, value: Value) -> bool {
841        ops::to_boolean(self.heap, value)
842    }
843
844    /// `ToObject(value)`.
845    pub fn to_object(&mut self, value: Value) -> Result<ObjectId, Throw> {
846        crate::vm::object::to_object(&mut self.cx(), value)
847    }
848
849    /// `IsStrictlyEqual(x, y)`.
850    pub fn strict_equals(&self, x: Value, y: Value) -> bool {
851        ops::strict_eq(self.heap, x, y)
852    }
853
854    /// `IsLooselyEqual(x, y)`.
855    pub fn loose_equals(&mut self, x: Value, y: Value) -> Result<bool, Throw> {
856        ops::loose_eq(&mut self.cx(), x, y)
857    }
858
859    /// `SameValue(x, y)`.
860    pub fn same_value(&mut self, x: Value, y: Value) -> bool {
861        crate::vm::object::same_value(&self.cx(), x, y)
862    }
863
864    /// `SameValueZero(x, y)`.
865    pub fn same_value_zero(&self, x: Value, y: Value) -> bool {
866        ops::same_value_zero_heap(self.heap, x, y)
867    }
868
869    /// `typeof value`.
870    pub fn type_of(&mut self, value: Value) -> String {
871        let cx = self.cx();
872        let text = ops::type_of(&cx, value);
873        self.heap
874            .string_text(text.as_string().expect("a typeof atom"))
875    }
876
877    /// The kind of a value without running JavaScript.
878    pub fn kind_of(&self, value: Value) -> Kind {
879        value.kind()
880    }
881
882    // ---- collection ----
883
884    /// Runs a full collection. Only between entries: the embedder's handles
885    /// and the fixed roots are the roots.
886    pub fn run_gc(&mut self) {
887        self.heap.collect();
888    }
889}
890
891impl Runtime {
892    // ---- keys ----
893
894    /// [`NativeCx::key_from_units`].
895    pub fn key_from_units(&mut self, units: &[u16]) -> Result<PropertyKey, HeapError> {
896        self.native_cx().key_from_units(units)
897    }
898
899    /// The key of a symbol value; `None` for any other value.
900    pub fn key_from_symbol(&self, symbol: Value) -> Option<PropertyKey> {
901        symbol.as_symbol().map(PropertyKey::symbol)
902    }
903
904    /// [`NativeCx::to_property_key`].
905    pub fn to_property_key(&mut self, value: Value) -> Result<PropertyKey, Throw> {
906        self.native_cx().to_property_key(value)
907    }
908
909    pub fn key_name(&self, key: PropertyKey) -> KeyName {
910        match key.kind() {
911            KeyKind::Index(index) => KeyName::Index(index),
912            KeyKind::String(id) => KeyName::String(self.heap.string(id).repr().units().collect()),
913            KeyKind::Symbol(id) => KeyName::Symbol(Value::symbol(id)),
914        }
915    }
916
917    // ---- objects ----
918
919    /// [`NativeCx::get`].
920    pub fn get(
921        &mut self,
922        object: ObjectId,
923        key: PropertyKey,
924        receiver: Value,
925    ) -> Result<Value, Throw> {
926        self.native_cx().get(object, key, receiver)
927    }
928
929    /// [`NativeCx::set`].
930    pub fn set(
931        &mut self,
932        object: ObjectId,
933        key: PropertyKey,
934        value: Value,
935        receiver: Value,
936    ) -> Result<bool, Throw> {
937        self.native_cx().set(object, key, value, receiver)
938    }
939
940    /// [`NativeCx::get_own_property`].
941    pub fn get_own_property(
942        &mut self,
943        object: ObjectId,
944        key: PropertyKey,
945    ) -> Result<Option<PropertyDescriptor>, Throw> {
946        self.native_cx().get_own_property(object, key)
947    }
948
949    /// [`NativeCx::define_own_property`].
950    pub fn define_own_property(
951        &mut self,
952        object: ObjectId,
953        key: PropertyKey,
954        descriptor: &PropertyDescriptor,
955    ) -> Result<bool, Throw> {
956        self.native_cx()
957            .define_own_property(object, key, descriptor)
958    }
959
960    /// [`NativeCx::delete_property`].
961    pub fn delete_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
962        self.native_cx().delete_property(object, key)
963    }
964
965    /// [`NativeCx::has_property`].
966    pub fn has_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
967        self.native_cx().has_property(object, key)
968    }
969
970    /// [`NativeCx::has_own_property`].
971    pub fn has_own_property(&mut self, object: ObjectId, key: PropertyKey) -> Result<bool, Throw> {
972        self.native_cx().has_own_property(object, key)
973    }
974
975    /// [`NativeCx::own_property_keys`].
976    pub fn own_property_keys(&mut self, object: ObjectId) -> Result<Vec<PropertyKey>, Throw> {
977        self.native_cx().own_property_keys(object)
978    }
979
980    /// [`NativeCx::get_prototype_of`].
981    pub fn get_prototype_of(&mut self, object: ObjectId) -> Result<Option<ObjectId>, Throw> {
982        self.native_cx().get_prototype_of(object)
983    }
984
985    /// [`NativeCx::set_prototype_of`].
986    pub fn set_prototype_of(
987        &mut self,
988        object: ObjectId,
989        prototype: Option<ObjectId>,
990    ) -> Result<bool, Throw> {
991        self.native_cx().set_prototype_of(object, prototype)
992    }
993
994    /// [`NativeCx::prevent_extensions`].
995    pub fn prevent_extensions(&mut self, object: ObjectId) -> Result<bool, Throw> {
996        self.native_cx().prevent_extensions(object)
997    }
998
999    /// [`NativeCx::construct`].
1000    pub fn construct(
1001        &mut self,
1002        function: Value,
1003        args: &[Value],
1004        new_target: Value,
1005    ) -> Result<Value, Throw> {
1006        self.native_cx().construct(function, args, new_target)
1007    }
1008
1009    /// `IsCallable(value)`.
1010    pub fn is_callable(&self, value: Value) -> bool {
1011        ops::is_callable(&self.heap, value)
1012    }
1013
1014    /// `IsConstructor(value)`.
1015    pub fn is_constructor(&self, value: Value) -> bool {
1016        value
1017            .as_object()
1018            .is_some_and(|object| self.heap.header(object).has(flags::CONSTRUCTOR))
1019    }
1020
1021    /// [`NativeCx::is_array`].
1022    pub fn is_array(&mut self, value: Value) -> Result<bool, Throw> {
1023        self.native_cx().is_array(value)
1024    }
1025
1026    /// Whether `value` is an Error instance (has `[[ErrorData]]`).
1027    pub fn is_error(&self, value: Value) -> bool {
1028        value
1029            .as_object()
1030            .is_some_and(|object| self.heap.header(object).has(flags::ERROR))
1031    }
1032
1033    /// [`NativeCx::is_promise`].
1034    pub fn is_promise(&mut self, value: Value) -> bool {
1035        self.native_cx().is_promise(value)
1036    }
1037
1038    pub fn object_kind(&self, object: ObjectId) -> ObjectKind {
1039        self.heap.object_kind(object)
1040    }
1041
1042    /// [`NativeCx::new_object_with_prototype`].
1043    pub fn new_object_with_prototype(
1044        &mut self,
1045        prototype: Option<ObjectId>,
1046    ) -> Result<ObjectId, HeapError> {
1047        self.native_cx().new_object_with_prototype(prototype)
1048    }
1049
1050    /// [`NativeCx::new_array_from`].
1051    pub fn new_array_from(&mut self, values: &[Value]) -> Result<ObjectId, Throw> {
1052        self.native_cx().new_array_from(values)
1053    }
1054
1055    /// [`NativeCx::new_error`].
1056    pub fn new_error(&mut self, kind: ErrorKind, message: &str) -> Result<Value, HeapError> {
1057        self.native_cx().new_error(kind, message)
1058    }
1059
1060    /// [`NativeCx::new_located_error`].
1061    pub fn new_located_error(
1062        &mut self,
1063        kind: ErrorKind,
1064        message: &str,
1065        file: &str,
1066        line: u32,
1067        column: u32,
1068    ) -> Result<Value, HeapError> {
1069        self.native_cx()
1070            .new_located_error(kind, message, file, line, column)
1071    }
1072
1073    /// [`NativeCx::new_promise`].
1074    pub fn new_promise(&mut self) -> Result<ObjectId, Throw> {
1075        self.native_cx().new_promise()
1076    }
1077
1078    /// [`NativeCx::detach_array_buffer`].
1079    pub fn detach_array_buffer(&mut self, value: Value) -> Result<(), Throw> {
1080        self.native_cx().detach_array_buffer(value)
1081    }
1082
1083    // ---- primitives ----
1084
1085    /// [`NativeCx::new_string`].
1086    pub fn new_string(&mut self, text: &str) -> Result<Value, HeapError> {
1087        self.native_cx().new_string(text)
1088    }
1089
1090    /// [`NativeCx::new_string_units`].
1091    pub fn new_string_units(&mut self, units: &[u16]) -> Result<Value, HeapError> {
1092        self.native_cx().new_string_units(units)
1093    }
1094
1095    /// [`NativeCx::new_string_repr`].
1096    pub fn new_string_repr(&mut self, repr: StringRepr) -> Result<Value, HeapError> {
1097        self.native_cx().new_string_repr(repr)
1098    }
1099
1100    /// A string value's code units, flattened; `None` for any other value.
1101    pub fn string_repr(&self, value: Value) -> Option<&StringRepr> {
1102        value.as_string().map(|id| self.heap.string(id).repr())
1103    }
1104
1105    /// A string value as Rust text (lone surrogates replaced).
1106    pub fn string_lossy(&self, value: Value) -> Option<String> {
1107        value.as_string().map(|id| self.heap.string_text(id))
1108    }
1109
1110    /// [`NativeCx::new_bigint`].
1111    pub fn new_bigint(&mut self, decimal: &str) -> Option<Result<Value, HeapError>> {
1112        self.native_cx().new_bigint(decimal)
1113    }
1114
1115    /// A BigInt value in decimal; `None` for any other value.
1116    pub fn bigint_decimal(&self, value: Value) -> Option<String> {
1117        value.as_bigint()?;
1118        Some(ops::big(&self.heap, value).to_string_radix10())
1119    }
1120
1121    /// [`NativeCx::new_symbol`].
1122    pub fn new_symbol(&mut self, description: Option<&[u16]>) -> Result<Value, HeapError> {
1123        self.native_cx().new_symbol(description)
1124    }
1125
1126    /// A symbol's `[[Description]]`; `None` when it is undefined or `value`
1127    /// is not a symbol.
1128    pub fn symbol_description(&self, value: Value) -> Option<Vec<u16>> {
1129        let record = self.heap.symbol(value.as_symbol()?);
1130        (record.description != crate::heap::object::NONE).then(|| {
1131            self.heap
1132                .string(crate::value::StringId(record.description))
1133                .repr()
1134                .units()
1135                .collect()
1136        })
1137    }
1138
1139    /// The well-known symbol `Symbol.<name>` (`"iterator"`, …).
1140    pub fn well_known_symbol(&self, name: &str) -> Option<Value> {
1141        self.realm
1142            .symbols
1143            .all()
1144            .into_iter()
1145            .find(|&(candidate, _)| candidate == name)
1146            .map(|(_, id)| Value::symbol(id))
1147    }
1148
1149    pub fn well_known_symbol_key(&self, name: &str) -> Option<PropertyKey> {
1150        self.well_known_symbol(name)
1151            .and_then(|symbol| symbol.as_symbol())
1152            .map(WellKnown::key)
1153    }
1154
1155    // ---- conversions ----
1156
1157    /// [`NativeCx::to_string`].
1158    pub fn to_string(&mut self, value: Value) -> Result<Value, Throw> {
1159        self.native_cx().to_string(value)
1160    }
1161
1162    /// [`NativeCx::to_number`].
1163    pub fn to_number(&mut self, value: Value) -> Result<f64, Throw> {
1164        self.native_cx().to_number(value)
1165    }
1166
1167    /// `ToBoolean(value)`.
1168    pub fn to_boolean(&self, value: Value) -> bool {
1169        ops::to_boolean(&self.heap, value)
1170    }
1171
1172    /// [`NativeCx::to_object`].
1173    pub fn to_object(&mut self, value: Value) -> Result<ObjectId, Throw> {
1174        self.native_cx().to_object(value)
1175    }
1176
1177    /// `IsStrictlyEqual(x, y)`.
1178    pub fn strict_equals(&self, x: Value, y: Value) -> bool {
1179        ops::strict_eq(&self.heap, x, y)
1180    }
1181
1182    /// [`NativeCx::loose_equals`].
1183    pub fn loose_equals(&mut self, x: Value, y: Value) -> Result<bool, Throw> {
1184        self.native_cx().loose_equals(x, y)
1185    }
1186
1187    /// [`NativeCx::same_value`].
1188    pub fn same_value(&mut self, x: Value, y: Value) -> bool {
1189        self.native_cx().same_value(x, y)
1190    }
1191
1192    /// `SameValueZero(x, y)`.
1193    pub fn same_value_zero(&self, x: Value, y: Value) -> bool {
1194        ops::same_value_zero_heap(&self.heap, x, y)
1195    }
1196
1197    /// [`NativeCx::type_of`].
1198    pub fn type_of(&mut self, value: Value) -> String {
1199        self.native_cx().type_of(value)
1200    }
1201
1202    /// The kind of a value without running JavaScript.
1203    pub fn kind_of(&self, value: Value) -> Kind {
1204        value.kind()
1205    }
1206
1207    // ---- collection ----
1208
1209    /// [`NativeCx::run_gc`].
1210    pub fn run_gc(&mut self) {
1211        self.native_cx().run_gc()
1212    }
1213}