pub(crate) mod arguments;
pub(crate) mod async_generator;
pub(crate) mod bigint;
pub(crate) mod class;
pub mod compile;
pub(crate) mod error;
pub mod ic;
mod interp;
pub(crate) mod iter;
pub(crate) mod jobs;
pub mod module;
pub mod native;
pub(crate) mod number;
pub(crate) mod object;
pub(crate) mod ops;
pub(crate) mod print;
pub(crate) mod property;
pub(crate) mod proxy;
pub(crate) mod suspend;
pub(crate) mod typed;
pub(crate) mod via;
pub(crate) mod waiter;
#[doc(hidden)]
pub mod radix;
pub mod regexp;
use std::fmt;
use crate::bytecode::{CodeStore, CodeUnitId, UnitRef};
use crate::heap::Heap;
use crate::runtime::Realm;
use crate::value::{BytecodeId, ObjectId, Value};
pub use self::error::ErrorKind;
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct Throw(pub Value);
impl fmt::Debug for Throw {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Throw({:?})", self.0)
}
}
pub mod frame_flags {
pub const ENTRY: u8 = 1 << 0;
pub const CONSTRUCT: u8 = 1 << 1;
pub const TAIL: u8 = 1 << 2;
pub const VIA_CALL: u8 = 1 << 3;
pub const VIA_APPLY: u8 = 1 << 4;
pub const VIA_CHAIN: u8 = 1 << 5;
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Frame {
pub unit: CodeUnitId,
pub function: ObjectId,
pub bytecode: BytecodeId,
pub base: u32,
pub return_pc: u32,
pub return_dst: u32,
pub argc: u32,
pub flags: u8,
}
const _: () = assert!(size_of::<Frame>() == 32);
pub(crate) const SPAN: usize = 512;
pub const NATIVE_STACK_BUDGET: usize = 1 << 20;
pub const DEFAULT_STACK_LIMIT: u32 = 1 << 20;
pub(crate) const NO_PC: u32 = u32::MAX;
#[derive(Clone, Copy, Debug)]
pub(crate) struct NativeSite {
pub depth: u32,
pub pc: u32,
pub callee: Value,
}
pub struct VmStack {
pub(crate) registers: Vec<Value>,
pub(crate) frames: Vec<Frame>,
pub(crate) top: u32,
pub(crate) stale_from: u32,
pub(crate) limit: u32,
pub(crate) nesting: u32,
pub(crate) native_base: usize,
pub(crate) native_budget: usize,
pub(crate) native_depth: u32,
pub(crate) natives: Vec<NativeSite>,
pub(crate) frameless_native: bool,
pub(crate) unpositioned: Option<(ObjectId, Vec<(u32, u32)>)>,
pub(crate) active: Vec<ObjectId>,
pub(crate) proxy_refused: bool,
pub(crate) via: Box<via::ViaCalls>,
pub(crate) poll: u32,
pub(crate) interrupted: bool,
}
pub const INTERRUPT_INTERVAL: u32 = 1_000;
impl Default for VmStack {
fn default() -> Self {
VmStack {
registers: Vec::new(),
frames: Vec::new(),
top: 0,
stale_from: u32::MAX,
limit: DEFAULT_STACK_LIMIT,
nesting: 0,
native_base: 0,
native_budget: NATIVE_STACK_BUDGET,
native_depth: u32::MAX,
natives: Vec::new(),
frameless_native: false,
via: Box::default(),
unpositioned: None,
active: Vec::new(),
proxy_refused: false,
poll: INTERRUPT_INTERVAL,
interrupted: false,
}
}
}
impl VmStack {
#[inline]
pub(crate) fn reserve(&mut self, end: usize) -> bool {
if end + SPAN <= self.registers.len() {
return true;
}
self.grow(end)
}
#[inline(never)]
fn grow(&mut self, end: usize) -> bool {
if end > self.limit as usize {
return false;
}
let target = (end + SPAN).max(self.registers.len() * 2).max(4 * SPAN);
let target = target.min(self.limit as usize + SPAN);
self.registers.resize(target, Value::UNDEFINED);
true
}
#[inline]
pub(crate) fn enter_native(&mut self) -> bool {
let marker = 0u8;
let here = std::ptr::addr_of!(marker) as usize;
if self.nesting == 0 {
self.native_base = here;
} else if self.native_base.abs_diff(here) > self.native_budget
|| self.nesting >= self.native_depth
{
return false;
}
self.nesting += 1;
true
}
pub(crate) fn native_remaining(&self) -> usize {
if self.nesting == 0 {
return self.native_budget;
}
let marker = 0u8;
let here = std::ptr::addr_of!(marker) as usize;
self.native_budget
.saturating_sub(self.native_base.abs_diff(here))
}
#[inline]
pub(crate) fn leave_native(&mut self) {
self.nesting -= 1;
}
pub fn depth(&self) -> usize {
self.frames.len()
}
pub fn registers_reserved(&self) -> usize {
self.registers.len()
}
}
#[derive(Debug, Default)]
pub enum Output {
#[default]
Stdout,
Capture(String),
}
pub struct Services {
pub output: Output,
pub(crate) hooks: Box<dyn crate::host::Host>,
random_state: u64,
pub(crate) symbol_registry: crate::heap::hash::FxHashMap<Vec<u16>, crate::SymbolId>,
pub can_block: bool,
pub(crate) compile: Option<compile::CompileFn>,
pub(crate) module_host: Option<module::ModuleHost>,
pub(crate) modules: crate::heap::hash::FxHashMap<String, ObjectId>,
pub(crate) script_runner: Option<crate::builtins::agent::ScriptRunner>,
pub(crate) agent_hub: Option<std::sync::Arc<crate::builtins::agent::AgentHub>>,
pub(crate) agent: Option<crate::builtins::agent::AgentSelf>,
pub(crate) track_rejections: bool,
pub(crate) interrupt: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl std::fmt::Debug for Services {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Services")
.field("output", &self.output)
.finish()
}
}
impl Default for Services {
fn default() -> Self {
let seed = crate::clock::time_seed();
Services {
output: Output::default(),
hooks: Box::new(crate::host::SystemHost),
random_state: seed.max(1),
symbol_registry: Default::default(),
can_block: true,
compile: None,
module_host: None,
modules: Default::default(),
script_runner: None,
agent_hub: None,
agent: None,
track_rejections: false,
interrupt: Default::default(),
}
}
}
impl Services {
pub(crate) fn random_state(&self) -> u64 {
self.random_state
}
pub(crate) fn set_random_state(&mut self, state: u64) {
self.random_state = state;
}
pub fn seed_random(&mut self, seed: u64) {
self.random_state = seed.max(1);
}
}
pub(crate) struct Cx<'a> {
pub heap: &'a mut Heap,
pub static_code: &'a CodeStore,
pub realm: &'a Realm,
pub host: &'a mut Services,
}
impl<'a> Cx<'a> {
#[inline(always)]
pub(crate) fn unit(&self, id: CodeUnitId) -> UnitRef<'a> {
if id.is_dynamic() {
return dynamic_unit(self.heap, id);
}
let code: &'a CodeStore = self.static_code;
UnitRef::Static(code.get(id))
}
}
#[cold]
#[inline(never)]
fn dynamic_unit(heap: &Heap, id: CodeUnitId) -> UnitRef<'static> {
UnitRef::Dynamic(heap.code.get(id))
}
impl Cx<'_> {
pub(crate) fn set_intrinsic(&mut self, intrinsic: crate::Intrinsic, object: ObjectId) {
self.heap.add_fixed_root(Value::object(object));
self.realm.record_intrinsic(intrinsic, object);
}
}
pub(crate) fn call(
cx: &mut Cx<'_>,
function: Value,
this: Value,
args: &[Value],
) -> Result<Value, Throw> {
let base = cx.heap.vm.top as usize;
let end = base + 1 + args.len();
if !cx.heap.vm.reserve(end) {
return Err(error::stack_overflow(cx));
}
let saved_top = cx.heap.vm.top;
let registers = &mut cx.heap.vm.registers;
registers[base] = this;
registers[base + 1..end].copy_from_slice(args);
cx.heap.vm.top = end as u32;
let result = interp::call_from_rust(cx, function, base, args.len());
cx.heap.vm.top = saved_top;
result
}
pub(crate) fn call_next(cx: &mut Cx<'_>, next: Value, iterator: Value) -> Result<Value, Throw> {
cx.heap.vm.frameless_native = true;
let result = call(cx, next, iterator, &[]);
cx.heap.vm.frameless_native = false;
result
}
pub(crate) fn construct(
cx: &mut Cx<'_>,
function: Value,
args: &[Value],
new_target: Value,
) -> Result<Value, Throw> {
let base = cx.heap.vm.top as usize;
let end = base + 1 + args.len();
if !cx.heap.vm.reserve(end) {
return Err(error::stack_overflow(cx));
}
let saved_top = cx.heap.vm.top;
let registers = &mut cx.heap.vm.registers;
registers[base] = Value::UNDEFINED;
registers[base + 1..end].copy_from_slice(args);
cx.heap.vm.top = end as u32;
let result = interp::construct_from_rust(cx, function, base, args.len(), new_target);
cx.heap.vm.top = saved_top;
result
}