diff --git a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs index b189bcbba..ed3e54551 100644 --- a/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs +++ b/nova_vm/src/ecmascript/abstract_operations/operations_on_objects.rs @@ -643,19 +643,19 @@ pub(crate) trait EnumerablePropertiesKind { pub(crate) mod enumerable_properties_kind { use super::{EnumPropKind, EnumerablePropertiesKind}; - pub(crate) struct Key; - pub(crate) struct Value; - pub(crate) struct KeyValue; + pub(crate) struct EnumerateKeys; + pub(crate) struct EnumerateValues; + pub(crate) struct EnumerateKeysAndValues; - impl EnumerablePropertiesKind for Key { + impl EnumerablePropertiesKind for EnumerateKeys { const KIND: EnumPropKind = EnumPropKind::Key; } - impl EnumerablePropertiesKind for Value { + impl EnumerablePropertiesKind for EnumerateValues { const KIND: EnumPropKind = EnumPropKind::Value; } - impl EnumerablePropertiesKind for KeyValue { + impl EnumerablePropertiesKind for EnumerateKeysAndValues { const KIND: EnumPropKind = EnumPropKind::KeyValue; } } diff --git a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs index f87c72f85..8406e4b26 100644 --- a/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs +++ b/nova_vm/src/ecmascript/builtins/control_abstraction_objects/generator_objects.rs @@ -15,7 +15,10 @@ use crate::{ InternalMethods, InternalSlots, IntoObject, IntoValue, Object, OrdinaryObject, Value, }, }, - engine::{local_value::ObjectScopeRoot, Executable, ExecutionResult, SuspendedVm, Vm}, + engine::{ + rootable::{HeapRootData, HeapRootRef, Rootable, Scoped}, + Executable, ExecutionResult, SuspendedVm, Vm, + }, heap::{ indexes::{BaseIndex, GeneratorIndex}, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -72,7 +75,7 @@ impl Generator { // execution context. agent.execution_context_stack.push(execution_context); - let saved = ObjectScopeRoot::root(self, agent); + let saved = Scoped::new(agent, self); // 9. Resume the suspended evaluation of genContext using NormalCompletion(value) as the // result of the operation that suspended it. Let result be the value returned by the @@ -335,6 +338,34 @@ impl IndexMut for Vec> { } } +impl Rootable for Generator { + type RootRepr = HeapRootRef; + + #[inline] + fn to_root_repr(value: Self) -> Result { + Err(HeapRootData::Generator(value)) + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + Err(*value) + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + heap_ref + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + if let HeapRootData::Generator(value) = heap_data { + Some(value) + } else { + None + } + } +} + impl HeapMarkAndSweep for Generator { fn mark_values(&self, queues: &mut WorkQueues) { queues.generators.push(*self); diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs index cf76746e8..205cc2e9b 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/object_objects/object_constructor.rs @@ -402,8 +402,9 @@ impl ObjectConstructor { // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o)?; // 2. Let entryList be ? EnumerableOwnProperties(obj, KEY+VALUE). - let entry_list = - enumerable_own_properties::(agent, obj)?; + let entry_list = enumerable_own_properties::< + enumerable_properties_kind::EnumerateKeysAndValues, + >(agent, obj)?; // 3. Return CreateArrayFromList(entryList). Ok(create_array_from_list(agent, &entry_list).into_value()) } @@ -710,7 +711,8 @@ impl ObjectConstructor { // 1. Let obj be ? ToObject(O). let obj = to_object(agent, o)?; // 2. Let keyList be ? EnumerableOwnProperties(obj, KEY). - let key_list = enumerable_own_properties::(agent, obj)?; + let key_list = + enumerable_own_properties::(agent, obj)?; // 3. Return CreateArrayFromList(keyList). Ok(create_array_from_list(agent, &key_list).into_value()) } @@ -802,7 +804,7 @@ impl ObjectConstructor { let obj = to_object(agent, o)?; // 2. Let valueList be ? EnumerableOwnProperties(obj, VALUE). let value_list = - enumerable_own_properties::(agent, obj)?; + enumerable_own_properties::(agent, obj)?; // 3. Return CreateArrayFromList(valueList). Ok(create_array_from_list(agent, &value_list).into_value()) } diff --git a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs index 2b7ab8401..83953d6e1 100644 --- a/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/numbers_and_dates/number_objects/number_constructor.rs @@ -114,7 +114,7 @@ impl NumberConstructor { agent[o].data = match n { Numeric::Number(d) => PrimitiveObjectData::Number(d), Numeric::Integer(d) => PrimitiveObjectData::Integer(d), - Numeric::Float(d) => PrimitiveObjectData::Float(d), + Numeric::SmallF64(d) => PrimitiveObjectData::Float(d), _ => unreachable!(), }; // 6. Return O. diff --git a/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs b/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs index ad28468fd..17a763354 100644 --- a/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs +++ b/nova_vm/src/ecmascript/builtins/structured_data/json_object.rs @@ -223,7 +223,8 @@ pub(crate) fn internalize_json_property( } else { // c. Else, // i. Let keys be ? EnumerableOwnProperties(val, key). - let keys = enumerable_own_properties::(agent, val)?; + let keys = + enumerable_own_properties::(agent, val)?; // ii. For each String P of keys, do for p in keys { diff --git a/nova_vm/src/ecmascript/execution/agent.rs b/nova_vm/src/ecmascript/execution/agent.rs index 251a7c922..7ee5d6317 100644 --- a/nova_vm/src/ecmascript/execution/agent.rs +++ b/nova_vm/src/ecmascript/execution/agent.rs @@ -18,7 +18,7 @@ use crate::{ builtins::{control_abstraction_objects::promise_objects::promise_abstract_operations::promise_jobs::{PromiseReactionJob, PromiseResolveThenableJob}, error::ErrorHeapData, promise::Promise}, scripts_and_modules::ScriptOrModule, types::{Function, IntoValue, Object, Reference, String, Symbol, Value}, - }, engine::Vm, heap::{heap_gc::heap_gc, CreateHeapData, PrimitiveHeapIndexable}, Heap + }, engine::{rootable::HeapRootData, Vm}, heap::{heap_gc::heap_gc, CreateHeapData, PrimitiveHeapIndexable}, Heap }; use std::{any::Any, cell::RefCell, ptr::NonNull}; @@ -235,7 +235,7 @@ impl GcAgent { let result = self.agent.run_in_realm(realm, func); assert!(self.agent.execution_context_stack.is_empty()); assert!(self.agent.vm_stack.is_empty()); - self.agent.stack_values.borrow_mut().clear(); + self.agent.stack_refs.borrow_mut().clear(); result } @@ -257,11 +257,11 @@ pub struct Agent { pub(crate) global_symbol_registry: AHashMap<&'static str, Symbol>, pub(crate) host_hooks: &'static dyn HostHooks, pub(crate) execution_context_stack: Vec, - /// Temporary storage for on-stack values. + /// Temporary storage for on-stack heap roots. /// /// TODO: With Realm-specific heaps we'll need a side-table to define which /// Realm a particular stack value points to. - pub(crate) stack_values: RefCell>, + pub(crate) stack_refs: RefCell>, /// Temporary storage for on-stack VMs. pub(crate) vm_stack: Vec>, } @@ -275,7 +275,7 @@ impl Agent { global_symbol_registry: AHashMap::default(), host_hooks, execution_context_stack: Vec::new(), - stack_values: RefCell::new(Vec::with_capacity(64)), + stack_refs: RefCell::new(Vec::with_capacity(64)), vm_stack: Vec::with_capacity(16), } } diff --git a/nova_vm/src/ecmascript/execution/realm.rs b/nova_vm/src/ecmascript/execution/realm.rs index d34958d00..12694cabb 100644 --- a/nova_vm/src/ecmascript/execution/realm.rs +++ b/nova_vm/src/ecmascript/execution/realm.rs @@ -1220,7 +1220,7 @@ mod test { assert_eq!(agent.heap.environments.global.len(), 1); assert_eq!(agent.heap.environments.object.len(), 1); assert!(agent.heap.errors.is_empty()); - assert!(agent.heap.globals.is_empty()); + assert!(agent.heap.globals.borrow().is_empty()); assert!(agent.heap.modules.is_empty()); let missing_number = agent .heap diff --git a/nova_vm/src/ecmascript/types.rs b/nova_vm/src/ecmascript/types.rs index d1d37c878..4ab2486bc 100644 --- a/nova_vm/src/ecmascript/types.rs +++ b/nova_vm/src/ecmascript/types.rs @@ -7,9 +7,9 @@ mod spec; pub(crate) use language::*; pub use language::{ - bigint, BigInt, Function, Global, HeapNumber, HeapString, InternalMethods, InternalSlots, - IntoFunction, IntoNumeric, IntoObject, IntoPrimitive, IntoValue, Number, Numeric, Object, - OrdinaryObject, Primitive, PropertyKey, String, Symbol, Value, + bigint, BigInt, Function, HeapNumber, HeapString, InternalMethods, InternalSlots, IntoFunction, + IntoNumeric, IntoObject, IntoPrimitive, IntoValue, Number, Numeric, Object, OrdinaryObject, + Primitive, PropertyKey, String, Symbol, Value, }; pub use spec::PropertyDescriptor; pub(crate) use spec::*; diff --git a/nova_vm/src/ecmascript/types/language.rs b/nova_vm/src/ecmascript/types/language.rs index 1b728c979..868815885 100644 --- a/nova_vm/src/ecmascript/types/language.rs +++ b/nova_vm/src/ecmascript/types/language.rs @@ -4,7 +4,6 @@ pub mod bigint; mod function; -mod global_value; mod into_numeric; mod into_primitive; mod into_value; @@ -25,7 +24,6 @@ pub(crate) use function::{ ECMAScriptFunctionHeapData, FunctionInternalProperties, }; pub use function::{Function, IntoFunction}; -pub use global_value::Global; pub use into_numeric::IntoNumeric; pub use into_primitive::IntoPrimitive; pub use into_value::IntoValue; @@ -39,15 +37,31 @@ pub use primitive::Primitive; pub use string::{HeapString, String, StringHeapData, BUILTIN_STRINGS_LIST, BUILTIN_STRING_MEMORY}; pub use symbol::{Symbol, SymbolHeapData}; pub use value::Value; -#[cfg(feature = "array-buffer")] +#[cfg(feature = "date")] +pub(crate) use value::DATE_DISCRIMINANT; +#[cfg(feature = "shared-array-buffer")] +pub(crate) use value::SHARED_ARRAY_BUFFER_DISCRIMINANT; pub(crate) use value::{ - BIGINT_64_ARRAY_DISCRIMINANT, BIGUINT_64_ARRAY_DISCRIMINANT, FLOAT_32_ARRAY_DISCRIMINANT, - FLOAT_64_ARRAY_DISCRIMINANT, INT_16_ARRAY_DISCRIMINANT, INT_32_ARRAY_DISCRIMINANT, - INT_8_ARRAY_DISCRIMINANT, UINT_16_ARRAY_DISCRIMINANT, UINT_32_ARRAY_DISCRIMINANT, - UINT_8_ARRAY_DISCRIMINANT, UINT_8_CLAMPED_ARRAY_DISCRIMINANT, + ARGUMENTS_DISCRIMINANT, ARRAY_DISCRIMINANT, ARRAY_ITERATOR_DISCRIMINANT, + ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_ITERATOR_DISCRIMINANT, BIGINT_DISCRIMINANT, + BOOLEAN_DISCRIMINANT, BOUND_FUNCTION_DISCRIMINANT, BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, + BUILTIN_FUNCTION_DISCRIMINANT, BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, + BUILTIN_PROMISE_COLLECTOR_FUNCTION_DISCRIMINANT, + BUILTIN_PROMISE_RESOLVING_FUNCTION_DISCRIMINANT, BUILTIN_PROXY_REVOKER_FUNCTION, + ECMASCRIPT_FUNCTION_DISCRIMINANT, EMBEDDER_OBJECT_DISCRIMINANT, ERROR_DISCRIMINANT, + FINALIZATION_REGISTRY_DISCRIMINANT, FLOAT_DISCRIMINANT, GENERATOR_DISCRIMINANT, + INTEGER_DISCRIMINANT, ITERATOR_DISCRIMINANT, MAP_DISCRIMINANT, MAP_ITERATOR_DISCRIMINANT, + MODULE_DISCRIMINANT, NUMBER_DISCRIMINANT, OBJECT_DISCRIMINANT, PROMISE_DISCRIMINANT, + PROXY_DISCRIMINANT, REGEXP_DISCRIMINANT, SET_DISCRIMINANT, SET_ITERATOR_DISCRIMINANT, + SMALL_BIGINT_DISCRIMINANT, SMALL_STRING_DISCRIMINANT, STRING_DISCRIMINANT, SYMBOL_DISCRIMINANT, }; +#[cfg(feature = "array-buffer")] pub(crate) use value::{ - BIGINT_DISCRIMINANT, BOOLEAN_DISCRIMINANT, FLOAT_DISCRIMINANT, INTEGER_DISCRIMINANT, - NULL_DISCRIMINANT, NUMBER_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT, SMALL_STRING_DISCRIMINANT, - STRING_DISCRIMINANT, SYMBOL_DISCRIMINANT, UNDEFINED_DISCRIMINANT, + ARRAY_BUFFER_DISCRIMINANT, BIGINT_64_ARRAY_DISCRIMINANT, BIGUINT_64_ARRAY_DISCRIMINANT, + DATA_VIEW_DISCRIMINANT, FLOAT_32_ARRAY_DISCRIMINANT, FLOAT_64_ARRAY_DISCRIMINANT, + INT_16_ARRAY_DISCRIMINANT, INT_32_ARRAY_DISCRIMINANT, INT_8_ARRAY_DISCRIMINANT, + UINT_16_ARRAY_DISCRIMINANT, UINT_32_ARRAY_DISCRIMINANT, UINT_8_ARRAY_DISCRIMINANT, + UINT_8_CLAMPED_ARRAY_DISCRIMINANT, }; +#[cfg(feature = "weak-refs")] +pub(crate) use value::{WEAK_MAP_DISCRIMINANT, WEAK_REF_DISCRIMINANT, WEAK_SET_DISCRIMINANT}; diff --git a/nova_vm/src/ecmascript/types/language/bigint.rs b/nova_vm/src/ecmascript/types/language/bigint.rs index 46508b5c2..fde6647f0 100644 --- a/nova_vm/src/ecmascript/types/language/bigint.rs +++ b/nova_vm/src/ecmascript/types/language/bigint.rs @@ -11,6 +11,7 @@ use super::{ }; use crate::{ ecmascript::execution::{agent::ExceptionType, Agent, JsResult}, + engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{ indexes::BigIntIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, PrimitiveHeap, WorkQueues, @@ -189,6 +190,13 @@ pub enum BigInt { SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, } +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum BigIntRootRepr { + SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, +} + impl BigInt { pub const fn zero() -> Self { Self::SmallBigInt(SmallBigInt::zero()) @@ -667,3 +675,36 @@ impl HeapMarkAndSweep for HeapBigInt { compactions.bigints.shift_index(&mut self.0); } } + +impl Rootable for BigInt { + type RootRepr = BigIntRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + match value { + Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int)), + Self::SmallBigInt(small_big_int) => Ok(Self::RootRepr::SmallBigInt(small_big_int)), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::SmallBigInt(small_big_int) => Ok(Self::SmallBigInt(small_big_int)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::BigInt(heap_big_int) => Some(Self::BigInt(heap_big_int)), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/function.rs b/nova_vm/src/ecmascript/types/language/function.rs index 7d4d3f813..273f3d4c1 100644 --- a/nova_vm/src/ecmascript/types/language/function.rs +++ b/nova_vm/src/ecmascript/types/language/function.rs @@ -21,8 +21,7 @@ use crate::{ }, execution::{Agent, JsResult, ProtoIntrinsics}, types::PropertyDescriptor, - }, - heap::{indexes::BuiltinFunctionIndex, CompactionLists, HeapMarkAndSweep, WorkQueues}, + }, engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{indexes::BuiltinFunctionIndex, CompactionLists, HeapMarkAndSweep, WorkQueues} }; pub(crate) use data::*; @@ -521,3 +520,70 @@ impl Function { self.internal_call(agent, this_argument, ArgumentsList(args)) } } + +impl Rootable for Function { + type RootRepr = HeapRootRef; + + fn to_root_repr(value: Self) -> Result { + match value { + Self::BoundFunction(bound_function) => Err(HeapRootData::BoundFunction(bound_function)), + Self::BuiltinFunction(builtin_function) => { + Err(HeapRootData::BuiltinFunction(builtin_function)) + } + Self::ECMAScriptFunction(ecmascript_function) => { + Err(HeapRootData::ECMAScriptFunction(ecmascript_function)) + } + Self::BuiltinGeneratorFunction => Err(HeapRootData::BuiltinGeneratorFunction), + Self::BuiltinConstructorFunction(builtin_constructor_function) => Err( + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function), + ), + Self::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => Err( + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function), + ), + Self::BuiltinPromiseCollectorFunction => { + Err(HeapRootData::BuiltinPromiseCollectorFunction) + } + Self::BuiltinProxyRevokerFunction => Err(HeapRootData::BuiltinProxyRevokerFunction), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + Err(*value) + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + heap_ref + } + + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::BoundFunction(bound_function) => { + Some(Self::BoundFunction(bound_function)) + } + HeapRootData::BuiltinFunction(builtin_function) => { + Some(Self::BuiltinFunction(builtin_function)) + } + HeapRootData::ECMAScriptFunction(ecmascript_function) => { + Some(Self::ECMAScriptFunction(ecmascript_function)) + } + HeapRootData::BuiltinGeneratorFunction => Some(Self::BuiltinGeneratorFunction), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function) => Some( + Self::BuiltinConstructorFunction(builtin_constructor_function), + ), + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + Some(Self::BuiltinPromiseResolvingFunction( + builtin_promise_resolving_function, + )) + } + HeapRootData::BuiltinPromiseCollectorFunction => { + Some(Self::BuiltinPromiseCollectorFunction) + } + HeapRootData::BuiltinProxyRevokerFunction => Some(Self::BuiltinProxyRevokerFunction), + // Note: We use a catch-all here as we expect function variant + // additions to be rare. + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/global_value.rs b/nova_vm/src/ecmascript/types/language/global_value.rs deleted file mode 100644 index e082d01d4..000000000 --- a/nova_vm/src/ecmascript/types/language/global_value.rs +++ /dev/null @@ -1,79 +0,0 @@ -use std::marker::PhantomData; - -use crate::ecmascript::execution::Agent; - -use super::{IntoValue, Value}; - -/// Stores a Value on the Agent heap as a rooted Value. -/// -/// A rooted Value cannot be garbage collected. It is safe to thus get the -/// Value out of a Global at any time. The Global can be thought of as a -/// unique pointer to a heap allocation in system programming languages. As -/// long as the pointer lives, the memory on the heap will not be released. -#[derive(Debug, PartialEq)] -pub struct Global>(u32, PhantomData); - -impl> Global { - /// Register a value as global. - #[must_use] - pub fn new(agent: &mut Agent, value: T) -> Self { - let reused_index = agent - .heap - .globals - .iter_mut() - .enumerate() - .find_map(|(index, entry)| { - if entry.is_none() { - *entry = Some(value.into_value()); - let index = u32::try_from(index).expect("Globals overflowed"); - Some(index) - } else { - None - } - }); - if let Some(reused_index) = reused_index { - Global(reused_index, Default::default()) - } else { - let next_index = agent.heap.globals.len(); - let next_index = u32::try_from(next_index).expect("Globals overflowed"); - agent.heap.globals.push(Some(value.into_value())); - Global(next_index, Default::default()) - } - } - - /// Unregister this global value. - pub fn take(self, agent: &mut Agent) -> T { - // Leave a `None` in the index and return the value - let value = agent - .heap - .globals - .get_mut(self.0 as usize) - .unwrap() - .take() - .unwrap(); - let Ok(value) = T::try_from(value) else { - panic!("Invalid Global returned different type than expected"); - }; - value - } - - pub fn get(&self, agent: &mut Agent) -> T { - let value = *agent - .heap - .globals - .get_mut(self.0 as usize) - .unwrap() - .as_ref() - .unwrap(); - let Ok(value) = T::try_from(value) else { - panic!("Invalid Global returned different type than expected"); - }; - value - } - - #[must_use] - pub fn clone(&self, agent: &mut Agent) -> Self { - let value = self.get(agent); - Self::new(agent, value) - } -} diff --git a/nova_vm/src/ecmascript/types/language/number.rs b/nova_vm/src/ecmascript/types/language/number.rs index d412ce544..2e477514f 100644 --- a/nova_vm/src/ecmascript/types/language/number.rs +++ b/nova_vm/src/ecmascript/types/language/number.rs @@ -15,7 +15,10 @@ use crate::{ abstract_operations::type_conversion::{to_int32, to_uint32}, execution::{Agent, JsResult}, }, - engine::small_f64::SmallF64, + engine::{ + rootable::{HeapRootData, HeapRootRef, Rootable}, + small_f64::SmallF64, + }, heap::{ indexes::NumberIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, PrimitiveHeap, WorkQueues, @@ -53,6 +56,14 @@ pub enum Number { SmallF64(SmallF64) = FLOAT_DISCRIMINANT, } +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum NumberRootRepr { + Integer(SmallInteger) = INTEGER_DISCRIMINANT, + SmallF64(SmallF64) = FLOAT_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, +} + impl IntoValue for HeapNumber { fn into_value(self) -> Value { Value::Number(self) @@ -108,7 +119,7 @@ impl IntoNumeric for Number { match self { Number::Number(idx) => Numeric::Number(idx), Number::Integer(data) => Numeric::Integer(data), - Number::SmallF64(data) => Numeric::Float(data), + Number::SmallF64(data) => Numeric::SmallF64(data), } } } @@ -216,7 +227,7 @@ impl TryFrom for Number { match value { Numeric::Number(data) => Ok(Number::Number(data)), Numeric::Integer(data) => Ok(Number::Integer(data)), - Numeric::Float(data) => Ok(Number::SmallF64(data)), + Numeric::SmallF64(data) => Ok(Number::SmallF64(data)), _ => Err(()), } } @@ -1351,3 +1362,38 @@ impl HeapMarkAndSweep for HeapNumber { compactions.numbers.shift_index(&mut self.0); } } + +impl Rootable for Number { + type RootRepr = NumberRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + match value { + Self::Number(heap_number) => Err(HeapRootData::Number(heap_number)), + Self::Integer(integer) => Ok(Self::RootRepr::Integer(integer)), + Self::SmallF64(small_f64) => Ok(Self::RootRepr::SmallF64(small_f64)), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::Integer(small_integer) => Ok(Self::Integer(small_integer)), + Self::RootRepr::SmallF64(small_f64) => Ok(Self::SmallF64(small_f64)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::Number(heap_number) => Some(Self::Number(heap_number)), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/numeric.rs b/nova_vm/src/ecmascript/types/language/numeric.rs index bf0fac32b..27c5a9ef1 100644 --- a/nova_vm/src/ecmascript/types/language/numeric.rs +++ b/nova_vm/src/ecmascript/types/language/numeric.rs @@ -7,7 +7,10 @@ use crate::{ abstract_operations::type_conversion::to_number, execution::{Agent, JsResult}, }, - engine::small_f64::SmallF64, + engine::{ + rootable::{HeapRootData, HeapRootRef, Rootable}, + small_f64::SmallF64, + }, SmallInteger, }; @@ -26,18 +29,27 @@ use super::{ pub enum Numeric { Number(HeapNumber) = NUMBER_DISCRIMINANT, Integer(SmallInteger) = INTEGER_DISCRIMINANT, - Float(SmallF64) = FLOAT_DISCRIMINANT, + SmallF64(SmallF64) = FLOAT_DISCRIMINANT, BigInt(HeapBigInt) = BIGINT_DISCRIMINANT, SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, } +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum NumericRootRepr { + Integer(SmallInteger) = INTEGER_DISCRIMINANT, + SmallF64(SmallF64) = FLOAT_DISCRIMINANT, + SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, +} + impl Numeric { pub fn is_bigint(self) -> bool { matches!(self, Self::BigInt(_) | Self::SmallBigInt(_)) } pub fn is_number(self) -> bool { - matches!(self, Self::Number(_) | Self::Float(_) | Self::Integer(_)) + matches!(self, Self::Number(_) | Self::SmallF64(_) | Self::Integer(_)) } pub fn is_pos_zero(self, agent: &mut Agent) -> bool { @@ -75,7 +87,7 @@ impl Numeric { Ok(match self { Self::Number(n) => agent[n], Self::Integer(i) => i.into_i64() as f64, - Self::Float(f) => f.into_f64(), + Self::SmallF64(f) => f.into_f64(), // NOTE: Converting to a number should give us a nice error message. _ => to_number(agent, self)?.into_f64(agent), }) @@ -87,7 +99,7 @@ impl IntoValue for Numeric { match self { Numeric::Number(data) => Value::Number(data), Numeric::Integer(data) => Value::Integer(data), - Numeric::Float(data) => Value::SmallF64(data), + Numeric::SmallF64(data) => Value::SmallF64(data), Numeric::BigInt(data) => Value::BigInt(data), Numeric::SmallBigInt(data) => Value::SmallBigInt(data), } @@ -99,7 +111,7 @@ impl IntoPrimitive for Numeric { match self { Numeric::Number(data) => Primitive::Number(data), Numeric::Integer(data) => Primitive::Integer(data), - Numeric::Float(data) => Primitive::SmallF64(data), + Numeric::SmallF64(data) => Primitive::SmallF64(data), Numeric::BigInt(data) => Primitive::BigInt(data), Numeric::SmallBigInt(data) => Primitive::SmallBigInt(data), } @@ -125,7 +137,7 @@ impl TryFrom for Numeric { match value { Value::Number(data) => Ok(Numeric::Number(data)), Value::Integer(data) => Ok(Numeric::Integer(data)), - Value::SmallF64(data) => Ok(Numeric::Float(data)), + Value::SmallF64(data) => Ok(Numeric::SmallF64(data)), Value::BigInt(data) => Ok(Numeric::BigInt(data)), Value::SmallBigInt(data) => Ok(Numeric::SmallBigInt(data)), _ => Err(()), @@ -140,10 +152,49 @@ impl TryFrom for Numeric { match value { Primitive::Number(data) => Ok(Numeric::Number(data)), Primitive::Integer(data) => Ok(Numeric::Integer(data)), - Primitive::SmallF64(data) => Ok(Numeric::Float(data)), + Primitive::SmallF64(data) => Ok(Numeric::SmallF64(data)), Primitive::BigInt(data) => Ok(Numeric::BigInt(data)), Primitive::SmallBigInt(data) => Ok(Numeric::SmallBigInt(data)), _ => Err(()), } } } + +impl Rootable for Numeric { + type RootRepr = NumericRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + match value { + Self::Number(heap_number) => Err(HeapRootData::Number(heap_number)), + Self::Integer(integer) => Ok(Self::RootRepr::Integer(integer)), + Self::SmallF64(small_f64) => Ok(Self::RootRepr::SmallF64(small_f64)), + Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int)), + Self::SmallBigInt(small_big_int) => Ok(Self::RootRepr::SmallBigInt(small_big_int)), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::Integer(small_integer) => Ok(Self::Integer(small_integer)), + Self::RootRepr::SmallF64(small_f64) => Ok(Self::SmallF64(small_f64)), + Self::RootRepr::SmallBigInt(small_big_int) => Ok(Self::SmallBigInt(small_big_int)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::Number(heap_number) => Some(Self::Number(heap_number)), + HeapRootData::BigInt(heap_big_int) => Some(Self::BigInt(heap_big_int)), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/object.rs b/nova_vm/src/ecmascript/types/language/object.rs index 20658608b..1785f88f1 100644 --- a/nova_vm/src/ecmascript/types/language/object.rs +++ b/nova_vm/src/ecmascript/types/language/object.rs @@ -83,6 +83,7 @@ use crate::{ execution::{Agent, JsResult}, types::PropertyDescriptor, }, + engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{ indexes::{ArrayIndex, ObjectIndex}, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, @@ -2279,3 +2280,204 @@ impl CreateHeapData for Heap { OrdinaryObject(ObjectIndex::last(&self.objects)) } } + +impl Rootable for Object { + type RootRepr = HeapRootRef; + + fn to_root_repr(value: Self) -> Result { + match value { + Self::Object(ordinary_object) => Err(HeapRootData::Object(ordinary_object)), + Self::BoundFunction(bound_function) => Err(HeapRootData::BoundFunction(bound_function)), + Self::BuiltinFunction(builtin_function) => { + Err(HeapRootData::BuiltinFunction(builtin_function)) + } + Self::ECMAScriptFunction(ecmascript_function) => { + Err(HeapRootData::ECMAScriptFunction(ecmascript_function)) + } + Self::BuiltinGeneratorFunction => Err(HeapRootData::BuiltinGeneratorFunction), + Self::BuiltinConstructorFunction(builtin_constructor_function) => Err( + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function), + ), + Self::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => Err( + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function), + ), + Self::BuiltinPromiseCollectorFunction => { + Err(HeapRootData::BuiltinPromiseCollectorFunction) + } + Self::BuiltinProxyRevokerFunction => Err(HeapRootData::BuiltinProxyRevokerFunction), + Self::PrimitiveObject(primitive_object) => { + Err(HeapRootData::PrimitiveObject(primitive_object)) + } + Self::Arguments(ordinary_object) => Err(HeapRootData::Arguments(ordinary_object)), + Self::Array(array) => Err(HeapRootData::Array(array)), + #[cfg(feature = "array-buffer")] + Self::ArrayBuffer(array_buffer) => Err(HeapRootData::ArrayBuffer(array_buffer)), + #[cfg(feature = "array-buffer")] + Self::DataView(data_view) => Err(HeapRootData::DataView(data_view)), + #[cfg(feature = "date")] + Self::Date(date) => Err(HeapRootData::Date(date)), + Self::Error(error) => Err(HeapRootData::Error(error)), + Self::FinalizationRegistry(finalization_registry) => { + Err(HeapRootData::FinalizationRegistry(finalization_registry)) + } + Self::Map(map) => Err(HeapRootData::Map(map)), + Self::Promise(promise) => Err(HeapRootData::Promise(promise)), + Self::Proxy(proxy) => Err(HeapRootData::Proxy(proxy)), + Self::RegExp(reg_exp) => Err(HeapRootData::RegExp(reg_exp)), + Self::Set(set) => Err(HeapRootData::Set(set)), + #[cfg(feature = "shared-array-buffer")] + Self::SharedArrayBuffer(shared_array_buffer) => { + Err(HeapRootData::SharedArrayBuffer(shared_array_buffer)) + } + #[cfg(feature = "weak-refs")] + Self::WeakMap(weak_map) => Err(HeapRootData::WeakMap(weak_map)), + #[cfg(feature = "weak-refs")] + Self::WeakRef(weak_ref) => Err(HeapRootData::WeakRef(weak_ref)), + #[cfg(feature = "weak-refs")] + Self::WeakSet(weak_set) => Err(HeapRootData::WeakSet(weak_set)), + #[cfg(feature = "array-buffer")] + Self::Int8Array(base_index) => Err(HeapRootData::Int8Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint8Array(base_index) => Err(HeapRootData::Uint8Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint8ClampedArray(base_index) => Err(HeapRootData::Uint8ClampedArray(base_index)), + #[cfg(feature = "array-buffer")] + Self::Int16Array(base_index) => Err(HeapRootData::Int16Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint16Array(base_index) => Err(HeapRootData::Uint16Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Int32Array(base_index) => Err(HeapRootData::Int32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint32Array(base_index) => Err(HeapRootData::Uint32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::BigInt64Array(base_index) => Err(HeapRootData::BigInt64Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::BigUint64Array(base_index) => Err(HeapRootData::BigUint64Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Float32Array(base_index) => Err(HeapRootData::Float32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Float64Array(base_index) => Err(HeapRootData::Float64Array(base_index)), + Self::AsyncFromSyncIterator => Err(HeapRootData::AsyncFromSyncIterator), + Self::AsyncIterator => Err(HeapRootData::AsyncIterator), + Self::Iterator => Err(HeapRootData::Iterator), + Self::ArrayIterator(array_iterator) => Err(HeapRootData::ArrayIterator(array_iterator)), + Self::SetIterator(set_iterator) => Err(HeapRootData::SetIterator(set_iterator)), + Self::MapIterator(map_iterator) => Err(HeapRootData::MapIterator(map_iterator)), + Self::Generator(generator) => Err(HeapRootData::Generator(generator)), + Self::Module(module) => Err(HeapRootData::Module(module)), + Self::EmbedderObject(embedder_object) => { + Err(HeapRootData::EmbedderObject(embedder_object)) + } + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + Err(*value) + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + heap_ref + } + + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::String(_) => None, + HeapRootData::Symbol(_) => None, + HeapRootData::Number(_) => None, + HeapRootData::BigInt(_) => None, + HeapRootData::Object(ordinary_object) => Some(Self::Object(ordinary_object)), + HeapRootData::BoundFunction(bound_function) => { + Some(Self::BoundFunction(bound_function)) + } + HeapRootData::BuiltinFunction(builtin_function) => { + Some(Self::BuiltinFunction(builtin_function)) + } + HeapRootData::ECMAScriptFunction(ecmascript_function) => { + Some(Self::ECMAScriptFunction(ecmascript_function)) + } + HeapRootData::BuiltinGeneratorFunction => Some(Self::BuiltinGeneratorFunction), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function) => Some( + Self::BuiltinConstructorFunction(builtin_constructor_function), + ), + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + Some(Self::BuiltinPromiseResolvingFunction( + builtin_promise_resolving_function, + )) + } + HeapRootData::BuiltinPromiseCollectorFunction => { + Some(Self::BuiltinPromiseCollectorFunction) + } + HeapRootData::BuiltinProxyRevokerFunction => Some(Self::BuiltinProxyRevokerFunction), + HeapRootData::PrimitiveObject(primitive_object) => { + Some(Self::PrimitiveObject(primitive_object)) + } + HeapRootData::Arguments(ordinary_object) => Some(Self::Arguments(ordinary_object)), + HeapRootData::Array(array) => Some(Self::Array(array)), + #[cfg(feature = "array-buffer")] + HeapRootData::ArrayBuffer(array_buffer) => Some(Self::ArrayBuffer(array_buffer)), + #[cfg(feature = "array-buffer")] + HeapRootData::DataView(data_view) => Some(Self::DataView(data_view)), + #[cfg(feature = "date")] + HeapRootData::Date(date) => Some(Self::Date(date)), + HeapRootData::Error(error) => Some(Self::Error(error)), + HeapRootData::FinalizationRegistry(finalization_registry) => { + Some(Self::FinalizationRegistry(finalization_registry)) + } + HeapRootData::Map(map) => Some(Self::Map(map)), + HeapRootData::Promise(promise) => Some(Self::Promise(promise)), + HeapRootData::Proxy(proxy) => Some(Self::Proxy(proxy)), + HeapRootData::RegExp(reg_exp) => Some(Self::RegExp(reg_exp)), + HeapRootData::Set(set) => Some(Self::Set(set)), + #[cfg(feature = "shared-array-buffer")] + HeapRootData::SharedArrayBuffer(shared_array_buffer) => { + Some(Self::SharedArrayBuffer(shared_array_buffer)) + } + #[cfg(feature = "weak-refs")] + HeapRootData::WeakMap(weak_map) => Some(Self::WeakMap(weak_map)), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakRef(weak_ref) => Some(Self::WeakRef(weak_ref)), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakSet(weak_set) => Some(Self::WeakSet(weak_set)), + #[cfg(feature = "array-buffer")] + HeapRootData::Int8Array(base_index) => Some(Self::Int8Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8Array(base_index) => Some(Self::Uint8Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8ClampedArray(base_index) => { + Some(Self::Uint8ClampedArray(base_index)) + } + #[cfg(feature = "array-buffer")] + HeapRootData::Int16Array(base_index) => Some(Self::Int16Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint16Array(base_index) => Some(Self::Uint16Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Int32Array(base_index) => Some(Self::Int32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint32Array(base_index) => Some(Self::Uint32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::BigInt64Array(base_index) => Some(Self::BigInt64Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::BigUint64Array(base_index) => Some(Self::BigUint64Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Float32Array(base_index) => Some(Self::Float32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Float64Array(base_index) => Some(Self::Float64Array(base_index)), + HeapRootData::AsyncFromSyncIterator => Some(Self::AsyncFromSyncIterator), + HeapRootData::AsyncIterator => Some(Self::AsyncIterator), + HeapRootData::Iterator => Some(Self::Iterator), + HeapRootData::ArrayIterator(array_iterator) => { + Some(Self::ArrayIterator(array_iterator)) + } + HeapRootData::SetIterator(set_iterator) => Some(Self::SetIterator(set_iterator)), + HeapRootData::MapIterator(map_iterator) => Some(Self::MapIterator(map_iterator)), + HeapRootData::Generator(generator) => Some(Self::Generator(generator)), + HeapRootData::Module(module) => Some(Self::Module(module)), + HeapRootData::EmbedderObject(embedder_object) => { + Some(Self::EmbedderObject(embedder_object)) + } // Note: Do not use _ => Err(()) to make sure any added + // HeapRootData Value variants cause compile errors if not handled. + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/primitive.rs b/nova_vm/src/ecmascript/types/language/primitive.rs index 5b5134212..75e154c2c 100644 --- a/nova_vm/src/ecmascript/types/language/primitive.rs +++ b/nova_vm/src/ecmascript/types/language/primitive.rs @@ -4,7 +4,13 @@ use small_string::SmallString; -use crate::{engine::small_f64::SmallF64, SmallInteger}; +use crate::{ + engine::{ + rootable::{HeapRootData, HeapRootRef, Rootable}, + small_f64::SmallF64, + }, + SmallInteger, +}; use super::{ bigint::{HeapBigInt, SmallBigInt}, @@ -66,6 +72,19 @@ pub enum Primitive { SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, } +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum PrimitiveRootRepr { + Undefined = UNDEFINED_DISCRIMINANT, + Null = NULL_DISCRIMINANT, + Boolean(bool) = BOOLEAN_DISCRIMINANT, + SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, + Integer(SmallInteger) = INTEGER_DISCRIMINANT, + SmallF64(SmallF64) = FLOAT_DISCRIMINANT, + SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, +} + /// A primitive value that is stored on the heap. /// /// Note: Symbol is not considered a primitive in this sense, as while its data @@ -187,3 +206,54 @@ impl TryFrom for Primitive { } } } + +impl Rootable for Primitive { + type RootRepr = PrimitiveRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + match value { + Self::Undefined => Ok(Self::RootRepr::Undefined), + Self::Null => Ok(Self::RootRepr::Null), + Self::Boolean(bool) => Ok(Self::RootRepr::Boolean(bool)), + Self::String(heap_string) => Err(HeapRootData::String(heap_string)), + Self::SmallString(small_string) => Ok(Self::RootRepr::SmallString(small_string)), + Self::Symbol(symbol) => Err(HeapRootData::Symbol(symbol)), + Self::Number(heap_number) => Err(HeapRootData::Number(heap_number)), + Self::Integer(integer) => Ok(Self::RootRepr::Integer(integer)), + Self::SmallF64(small_f64) => Ok(Self::RootRepr::SmallF64(small_f64)), + Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int)), + Self::SmallBigInt(small_big_int) => Ok(Self::RootRepr::SmallBigInt(small_big_int)), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::Undefined => Ok(Self::Undefined), + Self::RootRepr::Null => Ok(Self::Null), + Self::RootRepr::Boolean(bool) => Ok(Self::Boolean(bool)), + Self::RootRepr::SmallString(small_string) => Ok(Self::SmallString(small_string)), + Self::RootRepr::Integer(small_integer) => Ok(Self::Integer(small_integer)), + Self::RootRepr::SmallF64(small_f64) => Ok(Self::SmallF64(small_f64)), + Self::RootRepr::SmallBigInt(small_big_int) => Ok(Self::SmallBigInt(small_big_int)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::String(heap_string) => Some(Self::String(heap_string)), + HeapRootData::Symbol(symbol) => Some(Self::Symbol(symbol)), + HeapRootData::Number(heap_number) => Some(Self::Number(heap_number)), + HeapRootData::BigInt(heap_big_int) => Some(Self::BigInt(heap_big_int)), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/string.rs b/nova_vm/src/ecmascript/types/language/string.rs index 4d9d7ce25..12ba710db 100644 --- a/nova_vm/src/ecmascript/types/language/string.rs +++ b/nova_vm/src/ecmascript/types/language/string.rs @@ -7,9 +7,13 @@ mod data; use std::ops::{Index, IndexMut}; -use super::{IntoPrimitive, IntoValue, Primitive, PropertyKey, Value}; +use super::{ + IntoPrimitive, IntoValue, Primitive, PropertyKey, Value, SMALL_STRING_DISCRIMINANT, + STRING_DISCRIMINANT, +}; use crate::{ ecmascript::{execution::Agent, types::PropertyDescriptor}, + engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{ indexes::StringIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, PrimitiveHeap, WorkQueues, @@ -87,8 +91,15 @@ impl IndexMut for Vec> { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum String { - String(HeapString), - SmallString(SmallString), + String(HeapString) = STRING_DISCRIMINANT, + SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum StringRootRepr { + SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, } impl IntoValue for HeapString { @@ -486,3 +497,36 @@ impl HeapMarkAndSweep for HeapString { compactions.strings.shift_index(&mut self.0); } } + +impl Rootable for String { + type RootRepr = StringRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + match value { + Self::String(heap_string) => Err(HeapRootData::String(heap_string)), + Self::SmallString(small_string) => Ok(Self::RootRepr::SmallString(small_string)), + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::SmallString(small_string) => Ok(Self::SmallString(small_string)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::String(heap_string) => Some(Self::String(heap_string)), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/symbol.rs b/nova_vm/src/ecmascript/types/language/symbol.rs index b876c5e1c..bc19cb5e0 100644 --- a/nova_vm/src/ecmascript/types/language/symbol.rs +++ b/nova_vm/src/ecmascript/types/language/symbol.rs @@ -10,8 +10,10 @@ pub use data::SymbolHeapData; use crate::{ ecmascript::{execution::Agent, types::String}, + engine::rootable::{HeapRootData, HeapRootRef, Rootable}, heap::{ - indexes::SymbolIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, WorkQueues, + indexes::SymbolIndex, CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, + WellKnownSymbolIndexes, WorkQueues, LAST_WELL_KNOWN_SYMBOL_INDEX, }, }; @@ -21,6 +23,18 @@ use super::{IntoPrimitive, IntoValue, Primitive, Value, BUILTIN_STRING_MEMORY}; #[repr(transparent)] pub struct Symbol(pub(crate) SymbolIndex); +/// Inner root repr type to hide WellKnownSymbolIndexes. +#[derive(Debug, Clone, Copy)] +enum SymbolRootReprInner { + // Note: Handle a special case of avoiding rooting well-known symbols. + WellKnown(WellKnownSymbolIndexes), + HeapRef(HeapRootRef), +} + +#[repr(transparent)] +#[derive(Debug, Clone, Copy)] +pub struct SymbolRootRepr(SymbolRootReprInner); + impl Symbol { pub(crate) const fn _def() -> Self { Self(SymbolIndex::from_u32_index(0)) @@ -143,3 +157,42 @@ impl CreateHeapData for Heap { Symbol(SymbolIndex::last(&self.symbols)) } } + +impl Rootable for Symbol { + type RootRepr = SymbolRootRepr; + + #[inline] + fn to_root_repr(value: Self) -> Result { + if value.0.into_u32_index() <= LAST_WELL_KNOWN_SYMBOL_INDEX { + Ok(SymbolRootRepr(SymbolRootReprInner::WellKnown( + // SAFETY: Value is within the maximum number of well-known symbol indexes. + unsafe { + std::mem::transmute::(value.0.into_u32_index()) + }, + ))) + } else { + Err(HeapRootData::Symbol(value)) + } + } + + #[inline] + fn from_root_repr(value: &Self::RootRepr) -> Result { + match value.0 { + SymbolRootReprInner::WellKnown(well_known) => Ok(Self(well_known.into())), + SymbolRootReprInner::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + SymbolRootRepr(SymbolRootReprInner::HeapRef(heap_ref)) + } + + #[inline] + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::Symbol(heap_symbol) => Some(heap_symbol), + _ => None, + } + } +} diff --git a/nova_vm/src/ecmascript/types/language/value.rs b/nova_vm/src/ecmascript/types/language/value.rs index 40eb174ab..b10b128ed 100644 --- a/nova_vm/src/ecmascript/types/language/value.rs +++ b/nova_vm/src/ecmascript/types/language/value.rs @@ -52,7 +52,10 @@ use crate::{ execution::{Agent, JsResult}, types::BUILTIN_STRING_MEMORY, }, - engine::small_f64::SmallF64, + engine::{ + rootable::{HeapRootData, HeapRootRef, Rootable}, + small_f64::SmallF64, + }, heap::{CompactionLists, HeapMarkAndSweep, WorkQueues}, SmallInteger, SmallString, }; @@ -1072,6 +1075,239 @@ impl IntoValue for Value { } } +impl Rootable for Value { + type RootRepr = ValueRootRepr; + + fn to_root_repr(value: Self) -> Result { + match value { + Self::Undefined => Ok(Self::RootRepr::Undefined), + Self::Null => Ok(Self::RootRepr::Null), + Self::Boolean(bool) => Ok(Self::RootRepr::Boolean(bool)), + Self::String(heap_string) => Err(HeapRootData::String(heap_string)), + Self::SmallString(small_string) => Ok(Self::RootRepr::SmallString(small_string)), + Self::Symbol(symbol) => Err(HeapRootData::Symbol(symbol)), + Self::Number(heap_number) => Err(HeapRootData::Number(heap_number)), + Self::Integer(small_integer) => Ok(Self::RootRepr::Integer(small_integer)), + Self::SmallF64(small_f64) => Ok(Self::RootRepr::SmallF64(small_f64)), + Self::BigInt(heap_big_int) => Err(HeapRootData::BigInt(heap_big_int)), + Self::SmallBigInt(small_big_int) => Ok(Self::RootRepr::SmallBigInt(small_big_int)), + Self::Object(ordinary_object) => Err(HeapRootData::Object(ordinary_object)), + Self::BoundFunction(bound_function) => Err(HeapRootData::BoundFunction(bound_function)), + Self::BuiltinFunction(builtin_function) => { + Err(HeapRootData::BuiltinFunction(builtin_function)) + } + Self::ECMAScriptFunction(ecmascript_function) => { + Err(HeapRootData::ECMAScriptFunction(ecmascript_function)) + } + Self::BuiltinGeneratorFunction => Err(HeapRootData::BuiltinGeneratorFunction), + Self::BuiltinConstructorFunction(builtin_constructor_function) => Err( + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function), + ), + Self::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => Err( + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function), + ), + Self::BuiltinPromiseCollectorFunction => { + Err(HeapRootData::BuiltinPromiseCollectorFunction) + } + Self::BuiltinProxyRevokerFunction => Err(HeapRootData::BuiltinProxyRevokerFunction), + Self::PrimitiveObject(primitive_object) => { + Err(HeapRootData::PrimitiveObject(primitive_object)) + } + Self::Arguments(ordinary_object) => Err(HeapRootData::Arguments(ordinary_object)), + Self::Array(array) => Err(HeapRootData::Array(array)), + #[cfg(feature = "array-buffer")] + Self::ArrayBuffer(array_buffer) => Err(HeapRootData::ArrayBuffer(array_buffer)), + #[cfg(feature = "array-buffer")] + Self::DataView(data_view) => Err(HeapRootData::DataView(data_view)), + #[cfg(feature = "date")] + Self::Date(date) => Err(HeapRootData::Date(date)), + Self::Error(error) => Err(HeapRootData::Error(error)), + Self::FinalizationRegistry(finalization_registry) => { + Err(HeapRootData::FinalizationRegistry(finalization_registry)) + } + Self::Map(map) => Err(HeapRootData::Map(map)), + Self::Promise(promise) => Err(HeapRootData::Promise(promise)), + Self::Proxy(proxy) => Err(HeapRootData::Proxy(proxy)), + Self::RegExp(reg_exp) => Err(HeapRootData::RegExp(reg_exp)), + Self::Set(set) => Err(HeapRootData::Set(set)), + #[cfg(feature = "shared-array-buffer")] + Self::SharedArrayBuffer(shared_array_buffer) => { + Err(HeapRootData::SharedArrayBuffer(shared_array_buffer)) + } + #[cfg(feature = "weak-refs")] + Self::WeakMap(weak_map) => Err(HeapRootData::WeakMap(weak_map)), + #[cfg(feature = "weak-refs")] + Self::WeakRef(weak_ref) => Err(HeapRootData::WeakRef(weak_ref)), + #[cfg(feature = "weak-refs")] + Self::WeakSet(weak_set) => Err(HeapRootData::WeakSet(weak_set)), + #[cfg(feature = "array-buffer")] + Self::Int8Array(base_index) => Err(HeapRootData::Int8Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint8Array(base_index) => Err(HeapRootData::Uint8Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint8ClampedArray(base_index) => Err(HeapRootData::Uint8ClampedArray(base_index)), + #[cfg(feature = "array-buffer")] + Self::Int16Array(base_index) => Err(HeapRootData::Int16Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint16Array(base_index) => Err(HeapRootData::Uint16Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Int32Array(base_index) => Err(HeapRootData::Int32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Uint32Array(base_index) => Err(HeapRootData::Uint32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::BigInt64Array(base_index) => Err(HeapRootData::BigInt64Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::BigUint64Array(base_index) => Err(HeapRootData::BigUint64Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Float32Array(base_index) => Err(HeapRootData::Float32Array(base_index)), + #[cfg(feature = "array-buffer")] + Self::Float64Array(base_index) => Err(HeapRootData::Float64Array(base_index)), + Self::AsyncFromSyncIterator => Err(HeapRootData::AsyncFromSyncIterator), + Self::AsyncIterator => Err(HeapRootData::AsyncIterator), + Self::Iterator => Err(HeapRootData::Iterator), + Self::ArrayIterator(array_iterator) => Err(HeapRootData::ArrayIterator(array_iterator)), + Self::SetIterator(set_iterator) => Err(HeapRootData::SetIterator(set_iterator)), + Self::MapIterator(map_iterator) => Err(HeapRootData::MapIterator(map_iterator)), + Self::Generator(generator) => Err(HeapRootData::Generator(generator)), + Self::Module(module) => Err(HeapRootData::Module(module)), + Self::EmbedderObject(embedder_object) => { + Err(HeapRootData::EmbedderObject(embedder_object)) + } + } + } + + fn from_root_repr(value: &Self::RootRepr) -> Result { + match *value { + Self::RootRepr::Undefined => Ok(Self::Undefined), + Self::RootRepr::Null => Ok(Self::Null), + Self::RootRepr::Boolean(bool) => Ok(Self::Boolean(bool)), + Self::RootRepr::SmallString(small_string) => Ok(Self::SmallString(small_string)), + Self::RootRepr::Integer(small_integer) => Ok(Self::Integer(small_integer)), + Self::RootRepr::SmallF64(small_f64) => Ok(Self::SmallF64(small_f64)), + Self::RootRepr::SmallBigInt(small_big_int) => Ok(Self::SmallBigInt(small_big_int)), + Self::RootRepr::HeapRef(heap_root_ref) => Err(heap_root_ref), + } + } + + #[inline] + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr { + Self::RootRepr::HeapRef(heap_ref) + } + + fn from_heap_data(heap_data: HeapRootData) -> Option { + match heap_data { + HeapRootData::String(heap_string) => Some(Self::String(heap_string)), + HeapRootData::Symbol(symbol) => Some(Self::Symbol(symbol)), + HeapRootData::Number(heap_number) => Some(Self::Number(heap_number)), + HeapRootData::BigInt(heap_big_int) => Some(Self::BigInt(heap_big_int)), + HeapRootData::Object(ordinary_object) => Some(Self::Object(ordinary_object)), + HeapRootData::BoundFunction(bound_function) => { + Some(Self::BoundFunction(bound_function)) + } + HeapRootData::BuiltinFunction(builtin_function) => { + Some(Self::BuiltinFunction(builtin_function)) + } + HeapRootData::ECMAScriptFunction(ecmascript_function) => { + Some(Self::ECMAScriptFunction(ecmascript_function)) + } + HeapRootData::BuiltinGeneratorFunction => Some(Self::BuiltinGeneratorFunction), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function) => Some( + Self::BuiltinConstructorFunction(builtin_constructor_function), + ), + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + Some(Self::BuiltinPromiseResolvingFunction( + builtin_promise_resolving_function, + )) + } + HeapRootData::BuiltinPromiseCollectorFunction => { + Some(Self::BuiltinPromiseCollectorFunction) + } + HeapRootData::BuiltinProxyRevokerFunction => Some(Self::BuiltinProxyRevokerFunction), + HeapRootData::PrimitiveObject(primitive_object) => { + Some(Self::PrimitiveObject(primitive_object)) + } + HeapRootData::Arguments(ordinary_object) => Some(Self::Arguments(ordinary_object)), + HeapRootData::Array(array) => Some(Self::Array(array)), + #[cfg(feature = "array-buffer")] + HeapRootData::ArrayBuffer(array_buffer) => Some(Self::ArrayBuffer(array_buffer)), + #[cfg(feature = "array-buffer")] + HeapRootData::DataView(data_view) => Some(Self::DataView(data_view)), + #[cfg(feature = "date")] + HeapRootData::Date(date) => Some(Self::Date(date)), + HeapRootData::Error(error) => Some(Self::Error(error)), + HeapRootData::FinalizationRegistry(finalization_registry) => { + Some(Self::FinalizationRegistry(finalization_registry)) + } + HeapRootData::Map(map) => Some(Self::Map(map)), + HeapRootData::Promise(promise) => Some(Self::Promise(promise)), + HeapRootData::Proxy(proxy) => Some(Self::Proxy(proxy)), + HeapRootData::RegExp(reg_exp) => Some(Self::RegExp(reg_exp)), + HeapRootData::Set(set) => Some(Self::Set(set)), + #[cfg(feature = "shared-array-buffer")] + HeapRootData::SharedArrayBuffer(shared_array_buffer) => { + Some(Self::SharedArrayBuffer(shared_array_buffer)) + } + #[cfg(feature = "weak-refs")] + HeapRootData::WeakMap(weak_map) => Some(Self::WeakMap(weak_map)), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakRef(weak_ref) => Some(Self::WeakRef(weak_ref)), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakSet(weak_set) => Some(Self::WeakSet(weak_set)), + #[cfg(feature = "array-buffer")] + HeapRootData::Int8Array(base_index) => Some(Self::Int8Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8Array(base_index) => Some(Self::Uint8Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8ClampedArray(base_index) => { + Some(Self::Uint8ClampedArray(base_index)) + } + #[cfg(feature = "array-buffer")] + HeapRootData::Int16Array(base_index) => Some(Self::Int16Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint16Array(base_index) => Some(Self::Uint16Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Int32Array(base_index) => Some(Self::Int32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint32Array(base_index) => Some(Self::Uint32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::BigInt64Array(base_index) => Some(Self::BigInt64Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::BigUint64Array(base_index) => Some(Self::BigUint64Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Float32Array(base_index) => Some(Self::Float32Array(base_index)), + #[cfg(feature = "array-buffer")] + HeapRootData::Float64Array(base_index) => Some(Self::Float64Array(base_index)), + HeapRootData::AsyncFromSyncIterator => Some(Self::AsyncFromSyncIterator), + HeapRootData::AsyncIterator => Some(Self::AsyncIterator), + HeapRootData::Iterator => Some(Self::Iterator), + HeapRootData::ArrayIterator(array_iterator) => { + Some(Self::ArrayIterator(array_iterator)) + } + HeapRootData::SetIterator(set_iterator) => Some(Self::SetIterator(set_iterator)), + HeapRootData::MapIterator(map_iterator) => Some(Self::MapIterator(map_iterator)), + HeapRootData::Generator(generator) => Some(Self::Generator(generator)), + HeapRootData::Module(module) => Some(Self::Module(module)), + HeapRootData::EmbedderObject(embedder_object) => { + Some(Self::EmbedderObject(embedder_object)) + } // Note: Do not use _ => Err(()) to make sure any added + // HeapRootData Value variants cause compile errors if not handled. + } + } +} + +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum ValueRootRepr { + Undefined = UNDEFINED_DISCRIMINANT, + Null = NULL_DISCRIMINANT, + Boolean(bool) = BOOLEAN_DISCRIMINANT, + SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, + Integer(SmallInteger) = INTEGER_DISCRIMINANT, + SmallF64(SmallF64) = FLOAT_DISCRIMINANT, + SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, + HeapRef(HeapRootRef) = 0x80, +} + impl HeapMarkAndSweep for Value { fn mark_values(&self, queues: &mut WorkQueues) { match self { diff --git a/nova_vm/src/engine.rs b/nova_vm/src/engine.rs index 0a15a0dea..8399e2912 100644 --- a/nova_vm/src/engine.rs +++ b/nova_vm/src/engine.rs @@ -3,9 +3,10 @@ // file, You can obtain one at https://mozilla.org/MPL/2.0/. mod bytecode; -pub mod local_value; pub mod register_value; +pub mod rootable; pub mod small_f64; pub mod small_integer; pub(crate) use bytecode::*; +pub use rootable::{Global, Scoped}; diff --git a/nova_vm/src/engine/local_value.rs b/nova_vm/src/engine/local_value.rs deleted file mode 100644 index 127cb86d3..000000000 --- a/nova_vm/src/engine/local_value.rs +++ /dev/null @@ -1,324 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at https://mozilla.org/MPL/2.0/. - -use std::marker::PhantomData; - -use small_string::SmallString; - -use crate::{ - ecmascript::{ - execution::Agent, - types::{ - bigint::SmallBigInt, BigInt, IntoObject, IntoValue, Number, String, Symbol, Value, - BOOLEAN_DISCRIMINANT, FLOAT_DISCRIMINANT, INTEGER_DISCRIMINANT, NULL_DISCRIMINANT, - SMALL_BIGINT_DISCRIMINANT, SMALL_STRING_DISCRIMINANT, UNDEFINED_DISCRIMINANT, - }, - }, - SmallInteger, -}; - -use super::small_f64::SmallF64; - -/// # Scoped JavaScript Value. -/// -/// This holds either an on-stack primitive JavaScript Value, or an index to a -/// scoped heap-allocated Value. This type is intended for cheap rooting of -/// JavaScript Values that need to be used after calling into functions that -/// may trigger garbage collection. -#[derive(Debug, Clone, Copy)] -#[repr(transparent)] -pub struct Local> { - inner: LocalInner, - _marker: PhantomData, -} - -#[derive(Debug, Clone, Copy)] -#[repr(u8)] -enum LocalInner { - /// ### [6.1.1 The Undefined Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-undefined-type) - Undefined = UNDEFINED_DISCRIMINANT, - - /// ### [6.1.2 The Null Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-null-type) - Null = NULL_DISCRIMINANT, - - /// ### [6.1.3 The Boolean Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-boolean-type) - Boolean(bool) = BOOLEAN_DISCRIMINANT, - - /// ### [6.1.4 The String Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type) - /// - /// 7-byte UTF-8 string on the stack. End of the string is determined by - /// the first 0xFF byte in the data. UTF-16 indexing is calculated on - /// demand from the data. - SmallString(SmallString) = SMALL_STRING_DISCRIMINANT, - - /// ### [6.1.6.1 The Number Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type) - /// - /// 53-bit signed integer on the stack. - Integer(SmallInteger) = INTEGER_DISCRIMINANT, - /// ### [6.1.6.1 The Number Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type) - /// - /// 56-bit f64 on the stack. The missing byte is a zero least significant - /// byte. - SmallF64(SmallF64) = FLOAT_DISCRIMINANT, - - /// ### [6.1.6.2 The BigInt Type](https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type) - /// - /// 56-bit signed integer on the stack. - SmallBigInt(SmallBigInt) = SMALL_BIGINT_DISCRIMINANT, - - /// ### Scoped Value - /// - /// Value stored in the current scope stack. - ScopedValue(u32) = 0x80, -} - -impl Local { - pub fn get(self, agent: &Agent) -> Value { - match self.inner { - LocalInner::Undefined => Value::Undefined, - LocalInner::Null => Value::Null, - LocalInner::Boolean(bool) => Value::Boolean(bool), - LocalInner::SmallString(small_string) => Value::SmallString(small_string), - LocalInner::Integer(small_integer) => Value::Integer(small_integer), - LocalInner::SmallF64(small_f64) => Value::SmallF64(small_f64), - LocalInner::SmallBigInt(small_big_int) => Value::SmallBigInt(small_big_int), - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure() - }; - value - } - } - } -} - -impl Local { - pub fn get(self, agent: &Agent) -> String { - match self.inner { - LocalInner::SmallString(small_string) => String::SmallString(small_string), - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure() - }; - let Value::String(string) = value else { - unreachable!() - }; - String::String(string) - } - _ => unreachable!(), - } - } -} - -impl Local { - pub fn get(self, agent: &Agent) -> Number { - match self.inner { - LocalInner::Integer(integer) => Number::Integer(integer), - LocalInner::SmallF64(float) => Number::SmallF64(float), - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure() - }; - let Value::Number(number) = value else { - unreachable!() - }; - Number::Number(number) - } - _ => unreachable!(), - } - } -} - -impl Local { - pub fn get(self, agent: &Agent) -> BigInt { - match self.inner { - LocalInner::SmallBigInt(small_bigint) => BigInt::SmallBigInt(small_bigint), - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure() - }; - let Value::BigInt(bigint) = value else { - unreachable!() - }; - BigInt::BigInt(bigint) - } - _ => unreachable!(), - } - } -} - -impl Local { - pub fn get(self, agent: &Agent) -> Symbol { - match self.inner { - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure() - }; - let Value::Symbol(value) = value else { - unreachable!() - }; - value - } - _ => unreachable!(), - } - } -} - -impl> Local { - pub fn get(self, agent: &Agent) -> T { - match self.inner { - LocalInner::ScopedValue(index) => { - let Some(&value) = agent.stack_values.borrow().get(index as usize) else { - handle_bound_check_failure(); - }; - let Ok(value) = T::try_from(value) else { - unreachable!() - }; - value - } - _ => unreachable!(), - } - } -} - -impl Value { - pub fn root(self, agent: &Agent) -> Local { - let inner = match self { - Value::Undefined => LocalInner::Undefined, - Value::Null => LocalInner::Null, - Value::Boolean(bool) => LocalInner::Boolean(bool), - Value::SmallString(small_string) => LocalInner::SmallString(small_string), - Value::Integer(small_integer) => LocalInner::Integer(small_integer), - Value::SmallF64(small_f64) => LocalInner::SmallF64(small_f64), - Value::SmallBigInt(small_big_int) => LocalInner::SmallBigInt(small_big_int), - _ => { - let stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - agent.stack_values.borrow_mut().push(self); - LocalInner::ScopedValue(index) - } - }; - - Local { - inner, - _marker: PhantomData, - } - } -} - -impl String { - pub fn root(self, agent: &Agent) -> Local { - let inner = match self { - String::SmallString(small_string) => LocalInner::SmallString(small_string), - String::String(string) => { - let stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - agent.stack_values.borrow_mut().push(Value::String(string)); - LocalInner::ScopedValue(index) - } - }; - - Local { - inner, - _marker: PhantomData, - } - } -} - -impl Number { - pub fn root(self, agent: &Agent) -> Local { - let inner = match self { - Number::Integer(number) => LocalInner::Integer(number), - Number::SmallF64(float) => LocalInner::SmallF64(float), - Number::Number(number) => { - let stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - agent.stack_values.borrow_mut().push(Value::Number(number)); - LocalInner::ScopedValue(index) - } - }; - - Local { - inner, - _marker: PhantomData, - } - } -} - -impl BigInt { - pub fn root(self, agent: &Agent) -> Local { - let inner = match self { - BigInt::SmallBigInt(small_bigint) => LocalInner::SmallBigInt(small_bigint), - BigInt::BigInt(bigint) => { - let stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - agent.stack_values.borrow_mut().push(Value::BigInt(bigint)); - LocalInner::ScopedValue(index) - } - }; - - Local { - inner, - _marker: PhantomData, - } - } -} - -impl Symbol { - pub fn root(self, agent: &Agent) -> Local { - let stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - agent.stack_values.borrow_mut().push(Value::Symbol(self)); - let inner = LocalInner::ScopedValue(index); - - Local { - inner, - _marker: PhantomData, - } - } -} - -pub trait ObjectScopeRoot -where - Self: Sized + IntoObject + TryFrom, -{ - fn root(self, agent: &Agent) -> Local { - let value = self.into_value(); - let mut stack_values = agent.stack_values.borrow_mut(); - let Ok(index) = u32::try_from(stack_values.len()) else { - handle_index_overflow(); - }; - stack_values.push(value); - let inner = LocalInner::ScopedValue(index); - - Local { - inner, - _marker: PhantomData, - } - } -} - -impl ObjectScopeRoot for T where T: Sized + IntoObject + TryFrom {} - -#[cold] -#[inline(never)] -fn handle_index_overflow() -> ! { - panic!("Local Values stack overflowed"); -} - -#[cold] -#[inline(never)] -fn handle_bound_check_failure() -> ! { - panic!("Attempted to access dropped Local Value") -} diff --git a/nova_vm/src/engine/rootable.rs b/nova_vm/src/engine/rootable.rs new file mode 100644 index 000000000..5e3614706 --- /dev/null +++ b/nova_vm/src/engine/rootable.rs @@ -0,0 +1,495 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +mod global; +mod scoped; + +use private::RootableSealed; + +#[cfg(feature = "date")] +use crate::ecmascript::builtins::date::Date; +#[cfg(feature = "shared-array-buffer")] +use crate::ecmascript::builtins::shared_array_buffer::SharedArrayBuffer; +#[cfg(feature = "array-buffer")] +use crate::ecmascript::builtins::{data_view::DataView, ArrayBuffer}; +#[cfg(feature = "weak-refs")] +use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set::WeakSet}; +#[cfg(feature = "date")] +use crate::ecmascript::types::DATE_DISCRIMINANT; +#[cfg(feature = "shared-array-buffer")] +use crate::ecmascript::types::SHARED_ARRAY_BUFFER_DISCRIMINANT; +#[cfg(feature = "array-buffer")] +use crate::ecmascript::types::{ + ARRAY_BUFFER_DISCRIMINANT, BIGINT_64_ARRAY_DISCRIMINANT, BIGUINT_64_ARRAY_DISCRIMINANT, + DATA_VIEW_DISCRIMINANT, FLOAT_32_ARRAY_DISCRIMINANT, FLOAT_64_ARRAY_DISCRIMINANT, + INT_16_ARRAY_DISCRIMINANT, INT_32_ARRAY_DISCRIMINANT, INT_8_ARRAY_DISCRIMINANT, + UINT_16_ARRAY_DISCRIMINANT, UINT_32_ARRAY_DISCRIMINANT, UINT_8_ARRAY_DISCRIMINANT, + UINT_8_CLAMPED_ARRAY_DISCRIMINANT, +}; +#[cfg(feature = "weak-refs")] +use crate::ecmascript::types::{ + WEAK_MAP_DISCRIMINANT, WEAK_REF_DISCRIMINANT, WEAK_SET_DISCRIMINANT, +}; +#[cfg(feature = "array-buffer")] +use crate::heap::indexes::TypedArrayIndex; +use crate::{ + ecmascript::{ + builtins::{ + bound_function::BoundFunction, + embedder_object::EmbedderObject, + error::Error, + finalization_registry::FinalizationRegistry, + generator_objects::Generator, + indexed_collections::array_objects::array_iterator_objects::array_iterator::ArrayIterator, + keyed_collections::{ + map_objects::map_iterator_objects::map_iterator::MapIterator, + set_objects::set_iterator_objects::set_iterator::SetIterator, + }, + map::Map, + module::Module, + primitive_objects::PrimitiveObject, + promise::Promise, + promise_objects::promise_abstract_operations::promise_resolving_functions::BuiltinPromiseResolvingFunction, + proxy::Proxy, + regexp::RegExp, + set::Set, + Array, BuiltinConstructorFunction, BuiltinFunction, ECMAScriptFunction, + }, + types::{ + bigint::HeapBigInt, HeapNumber, HeapString, OrdinaryObject, Symbol, + ARGUMENTS_DISCRIMINANT, ARRAY_DISCRIMINANT, ARRAY_ITERATOR_DISCRIMINANT, + ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, ASYNC_ITERATOR_DISCRIMINANT, + BIGINT_DISCRIMINANT, BOUND_FUNCTION_DISCRIMINANT, + BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, BUILTIN_FUNCTION_DISCRIMINANT, + BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, + BUILTIN_PROMISE_COLLECTOR_FUNCTION_DISCRIMINANT, + BUILTIN_PROMISE_RESOLVING_FUNCTION_DISCRIMINANT, BUILTIN_PROXY_REVOKER_FUNCTION, + ECMASCRIPT_FUNCTION_DISCRIMINANT, EMBEDDER_OBJECT_DISCRIMINANT, ERROR_DISCRIMINANT, + FINALIZATION_REGISTRY_DISCRIMINANT, GENERATOR_DISCRIMINANT, ITERATOR_DISCRIMINANT, + MAP_DISCRIMINANT, MAP_ITERATOR_DISCRIMINANT, MODULE_DISCRIMINANT, NUMBER_DISCRIMINANT, + OBJECT_DISCRIMINANT, PROMISE_DISCRIMINANT, PROXY_DISCRIMINANT, REGEXP_DISCRIMINANT, + SET_DISCRIMINANT, SET_ITERATOR_DISCRIMINANT, STRING_DISCRIMINANT, SYMBOL_DISCRIMINANT, + }, + }, + heap::HeapMarkAndSweep, +}; + +mod private { + #[cfg(feature = "date")] + use crate::ecmascript::builtins::date::Date; + #[cfg(feature = "shared-array-buffer")] + use crate::ecmascript::builtins::shared_array_buffer::SharedArrayBuffer; + #[cfg(feature = "array-buffer")] + use crate::ecmascript::builtins::{data_view::DataView, typed_array::TypedArray, ArrayBuffer}; + #[cfg(feature = "weak-refs")] + use crate::ecmascript::builtins::{weak_map::WeakMap, weak_ref::WeakRef, weak_set::WeakSet}; + use crate::ecmascript::{ + builtins::{ + bound_function::BoundFunction, + embedder_object::EmbedderObject, + error::Error, + finalization_registry::FinalizationRegistry, + generator_objects::Generator, + indexed_collections::array_objects::array_iterator_objects::array_iterator::ArrayIterator, + keyed_collections::{ + map_objects::map_iterator_objects::map_iterator::MapIterator, + set_objects::set_iterator_objects::set_iterator::SetIterator, + }, + map::Map, + module::Module, + primitive_objects::PrimitiveObject, + promise::Promise, + promise_objects::promise_abstract_operations::promise_resolving_functions::BuiltinPromiseResolvingFunction, + proxy::Proxy, + regexp::RegExp, + set::Set, + Array, BuiltinConstructorFunction, BuiltinFunction, ECMAScriptFunction, + }, + types::{ + BigInt, Function, Number, Numeric, Object, OrdinaryObject, Primitive, String, Symbol, + Value, + }, + }; + + /// Marker trait to make Rootable not implementable outside of nova_vm. + pub trait RootableSealed {} + impl RootableSealed for Array {} + #[cfg(feature = "array-buffer")] + impl RootableSealed for ArrayBuffer {} + impl RootableSealed for ArrayIterator {} + impl RootableSealed for BigInt {} + impl RootableSealed for BoundFunction {} + impl RootableSealed for BuiltinConstructorFunction {} + impl RootableSealed for BuiltinFunction {} + impl RootableSealed for BuiltinPromiseResolvingFunction {} + #[cfg(feature = "array-buffer")] + impl RootableSealed for DataView {} + #[cfg(feature = "date")] + impl RootableSealed for Date {} + impl RootableSealed for ECMAScriptFunction {} + impl RootableSealed for EmbedderObject {} + impl RootableSealed for Error {} + impl RootableSealed for FinalizationRegistry {} + impl RootableSealed for Function {} + impl RootableSealed for Generator {} + impl RootableSealed for Map {} + impl RootableSealed for MapIterator {} + impl RootableSealed for Module {} + impl RootableSealed for Number {} + impl RootableSealed for Numeric {} + impl RootableSealed for Object {} + impl RootableSealed for OrdinaryObject {} + impl RootableSealed for Primitive {} + impl RootableSealed for PrimitiveObject {} + impl RootableSealed for Promise {} + impl RootableSealed for Proxy {} + impl RootableSealed for RegExp {} + impl RootableSealed for Set {} + impl RootableSealed for SetIterator {} + #[cfg(feature = "shared-array-buffer")] + impl RootableSealed for SharedArrayBuffer {} + impl RootableSealed for String {} + impl RootableSealed for Symbol {} + #[cfg(feature = "array-buffer")] + impl RootableSealed for TypedArray {} + impl RootableSealed for Value {} + #[cfg(feature = "weak-refs")] + impl RootableSealed for WeakMap {} + #[cfg(feature = "weak-refs")] + impl RootableSealed for WeakRef {} + #[cfg(feature = "weak-refs")] + impl RootableSealed for WeakSet {} +} + +pub use global::Global; +pub use scoped::Scoped; + +pub trait Rootable: std::fmt::Debug + Copy + RootableSealed { + type RootRepr: Sized + std::fmt::Debug; + + /// Convert a rootable value to a root representation directly if the value + /// does not need to be rooted, or return its heap root representation as + /// the error value. + fn to_root_repr(value: Self) -> Result; + + /// Convert a rootable value's root representation to the value type + /// directly if it didn't need to be rooted in the first place, or return + /// its heap root reference as the error value. + fn from_root_repr(value: &Self::RootRepr) -> Result; + + /// Convert a heap root reference to a root representation. + fn from_heap_ref(heap_ref: HeapRootRef) -> Self::RootRepr; + + /// Convert the rooted type's heap data value to the type itself. A failure + /// to convert indicates that the heap is corrupted or the value's root + /// representation was misused and points to a reused heap root data slot. + fn from_heap_data(heap_data: HeapRootData) -> Option; +} + +/// Internal type that enables rooting any heap-allocated type mentioned here. +/// +/// This is used by Global and Local references. Adding a variant here requires +/// also implementing `TryFrom for T` and +/// `TryFrom for InnerHeapRef` to handle conversions to-and-from heap +/// reference format, and finally implementing `trait Rootable` to define the +/// root representation of the type. +/// +/// For a type that always refers to the heap, the root representation should +/// simply be `InnerHeapRef`. Types that have stack-value representations can +/// define their own root representation enum that switches between stack +/// values and the `InnerHeapRef` representation. +#[derive(Debug, Clone, Copy)] +#[repr(u8)] +pub enum HeapRootData { + // First the Value variants: This list should match 1-to-1 the list in + // value.rs, but with the + String(HeapString) = STRING_DISCRIMINANT, + Symbol(Symbol) = SYMBOL_DISCRIMINANT, + Number(HeapNumber) = NUMBER_DISCRIMINANT, + BigInt(HeapBigInt) = BIGINT_DISCRIMINANT, + Object(OrdinaryObject) = OBJECT_DISCRIMINANT, + BoundFunction(BoundFunction) = BOUND_FUNCTION_DISCRIMINANT, + BuiltinFunction(BuiltinFunction) = BUILTIN_FUNCTION_DISCRIMINANT, + ECMAScriptFunction(ECMAScriptFunction) = ECMASCRIPT_FUNCTION_DISCRIMINANT, + BuiltinGeneratorFunction = BUILTIN_GENERATOR_FUNCTION_DISCRIMINANT, + BuiltinConstructorFunction(BuiltinConstructorFunction) = + BUILTIN_CONSTRUCTOR_FUNCTION_DISCRIMINANT, + BuiltinPromiseResolvingFunction(BuiltinPromiseResolvingFunction) = + BUILTIN_PROMISE_RESOLVING_FUNCTION_DISCRIMINANT, + BuiltinPromiseCollectorFunction = BUILTIN_PROMISE_COLLECTOR_FUNCTION_DISCRIMINANT, + BuiltinProxyRevokerFunction = BUILTIN_PROXY_REVOKER_FUNCTION, + PrimitiveObject(PrimitiveObject), + Arguments(OrdinaryObject) = ARGUMENTS_DISCRIMINANT, + Array(Array) = ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + ArrayBuffer(ArrayBuffer) = ARRAY_BUFFER_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + DataView(DataView) = DATA_VIEW_DISCRIMINANT, + #[cfg(feature = "date")] + Date(Date) = DATE_DISCRIMINANT, + Error(Error) = ERROR_DISCRIMINANT, + FinalizationRegistry(FinalizationRegistry) = FINALIZATION_REGISTRY_DISCRIMINANT, + Map(Map) = MAP_DISCRIMINANT, + Promise(Promise) = PROMISE_DISCRIMINANT, + Proxy(Proxy) = PROXY_DISCRIMINANT, + RegExp(RegExp) = REGEXP_DISCRIMINANT, + Set(Set) = SET_DISCRIMINANT, + #[cfg(feature = "shared-array-buffer")] + SharedArrayBuffer(SharedArrayBuffer) = SHARED_ARRAY_BUFFER_DISCRIMINANT, + #[cfg(feature = "weak-refs")] + WeakMap(WeakMap) = WEAK_MAP_DISCRIMINANT, + #[cfg(feature = "weak-refs")] + WeakRef(WeakRef) = WEAK_REF_DISCRIMINANT, + #[cfg(feature = "weak-refs")] + WeakSet(WeakSet) = WEAK_SET_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Int8Array(TypedArrayIndex) = INT_8_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Uint8Array(TypedArrayIndex) = UINT_8_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Uint8ClampedArray(TypedArrayIndex) = UINT_8_CLAMPED_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Int16Array(TypedArrayIndex) = INT_16_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Uint16Array(TypedArrayIndex) = UINT_16_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Int32Array(TypedArrayIndex) = INT_32_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Uint32Array(TypedArrayIndex) = UINT_32_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + BigInt64Array(TypedArrayIndex) = BIGINT_64_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + BigUint64Array(TypedArrayIndex) = BIGUINT_64_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Float32Array(TypedArrayIndex) = FLOAT_32_ARRAY_DISCRIMINANT, + #[cfg(feature = "array-buffer")] + Float64Array(TypedArrayIndex) = FLOAT_64_ARRAY_DISCRIMINANT, + AsyncFromSyncIterator = ASYNC_FROM_SYNC_ITERATOR_DISCRIMINANT, + AsyncIterator = ASYNC_ITERATOR_DISCRIMINANT, + Iterator = ITERATOR_DISCRIMINANT, + ArrayIterator(ArrayIterator) = ARRAY_ITERATOR_DISCRIMINANT, + SetIterator(SetIterator) = SET_ITERATOR_DISCRIMINANT, + MapIterator(MapIterator) = MAP_ITERATOR_DISCRIMINANT, + Generator(Generator) = GENERATOR_DISCRIMINANT, + Module(Module) = MODULE_DISCRIMINANT, + EmbedderObject(EmbedderObject) = EMBEDDER_OBJECT_DISCRIMINANT, + // Non-Value types go here. If the 128 variants here are not enough, we can + // eventually take into use leftover "on-stack" discriminants but that has + // to be done carefully, accepting that Value's TryFrom must + // not accept those "recirculated" values. + // + // The order here shouldn't be important at all, feel free to eg. keep + // these in alphabetical order. +} + +/// Internal type that is used to refer from user-controlled memory (stack or +/// heap) into the Agent heap, indexing into some root list within. The exact +/// root list being referred to is determined by the wrapping type. Locals +/// refer to the locals list, globals refer to the corresponding Realm's +/// globals list. +/// +/// ### Usage note +/// +/// This type should never appear inside the heap and should never be used +/// as-is. It only make sense within some root list referring type, +/// specifically `Local` and `Global`, and then those types should never +/// appear within the heap directly. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct HeapRootRef(u32); + +impl HeapRootRef { + #[inline] + pub(crate) fn from_index(index: usize) -> Self { + let Ok(index) = u32::try_from(index) else { + handle_heap_ref_overflow() + }; + Self(index) + } + + #[inline] + pub(crate) fn to_index(self) -> usize { + self.0 as usize + } +} + +#[cold] +#[inline(never)] +fn handle_heap_ref_overflow() -> ! { + panic!("Heap references overflowed"); +} + +impl HeapMarkAndSweep for HeapRootData { + fn mark_values(&self, queues: &mut crate::heap::WorkQueues) { + match self { + HeapRootData::String(heap_string) => heap_string.mark_values(queues), + HeapRootData::Symbol(symbol) => symbol.mark_values(queues), + HeapRootData::Number(heap_number) => heap_number.mark_values(queues), + HeapRootData::BigInt(heap_big_int) => heap_big_int.mark_values(queues), + HeapRootData::Object(ordinary_object) => ordinary_object.mark_values(queues), + HeapRootData::BoundFunction(bound_function) => bound_function.mark_values(queues), + HeapRootData::BuiltinFunction(builtin_function) => builtin_function.mark_values(queues), + HeapRootData::ECMAScriptFunction(ecmascript_function) => { + ecmascript_function.mark_values(queues) + } + HeapRootData::BuiltinGeneratorFunction => todo!(), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function) => { + builtin_constructor_function.mark_values(queues) + } + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + builtin_promise_resolving_function.mark_values(queues) + } + HeapRootData::BuiltinPromiseCollectorFunction => todo!(), + HeapRootData::BuiltinProxyRevokerFunction => todo!(), + HeapRootData::PrimitiveObject(primitive_object) => primitive_object.mark_values(queues), + HeapRootData::Arguments(ordinary_object) => ordinary_object.mark_values(queues), + HeapRootData::Array(array) => array.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::ArrayBuffer(array_buffer) => array_buffer.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::DataView(data_view) => data_view.mark_values(queues), + #[cfg(feature = "date")] + HeapRootData::Date(date) => date.mark_values(queues), + HeapRootData::Error(error) => error.mark_values(queues), + HeapRootData::FinalizationRegistry(finalization_registry) => { + finalization_registry.mark_values(queues) + } + HeapRootData::Map(map) => map.mark_values(queues), + HeapRootData::Promise(promise) => promise.mark_values(queues), + HeapRootData::Proxy(proxy) => proxy.mark_values(queues), + HeapRootData::RegExp(reg_exp) => reg_exp.mark_values(queues), + HeapRootData::Set(set) => set.mark_values(queues), + #[cfg(feature = "shared-array-buffer")] + HeapRootData::SharedArrayBuffer(shared_array_buffer) => { + shared_array_buffer.mark_values(queues) + } + #[cfg(feature = "weak-refs")] + HeapRootData::WeakMap(weak_map) => weak_map.mark_values(queues), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakRef(weak_ref) => weak_ref.mark_values(queues), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakSet(weak_set) => weak_set.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Int8Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8ClampedArray(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Int16Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint16Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Int32Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint32Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::BigInt64Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::BigUint64Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Float32Array(base_index) => base_index.mark_values(queues), + #[cfg(feature = "array-buffer")] + HeapRootData::Float64Array(base_index) => base_index.mark_values(queues), + HeapRootData::AsyncFromSyncIterator => todo!(), + HeapRootData::AsyncIterator => todo!(), + HeapRootData::Iterator => todo!(), + HeapRootData::ArrayIterator(array_iterator) => array_iterator.mark_values(queues), + HeapRootData::SetIterator(set_iterator) => set_iterator.mark_values(queues), + HeapRootData::MapIterator(map_iterator) => map_iterator.mark_values(queues), + HeapRootData::Generator(generator) => generator.mark_values(queues), + HeapRootData::Module(module) => module.mark_values(queues), + HeapRootData::EmbedderObject(embedder_object) => embedder_object.mark_values(queues), + } + } + + fn sweep_values(&mut self, compactions: &crate::heap::CompactionLists) { + match self { + HeapRootData::String(heap_string) => heap_string.sweep_values(compactions), + HeapRootData::Symbol(symbol) => symbol.sweep_values(compactions), + HeapRootData::Number(heap_number) => heap_number.sweep_values(compactions), + HeapRootData::BigInt(heap_big_int) => heap_big_int.sweep_values(compactions), + HeapRootData::Object(ordinary_object) => ordinary_object.sweep_values(compactions), + HeapRootData::BoundFunction(bound_function) => bound_function.sweep_values(compactions), + HeapRootData::BuiltinFunction(builtin_function) => { + builtin_function.sweep_values(compactions) + } + HeapRootData::ECMAScriptFunction(ecmascript_function) => { + ecmascript_function.sweep_values(compactions) + } + HeapRootData::BuiltinGeneratorFunction => todo!(), + HeapRootData::BuiltinConstructorFunction(builtin_constructor_function) => { + builtin_constructor_function.sweep_values(compactions) + } + HeapRootData::BuiltinPromiseResolvingFunction(builtin_promise_resolving_function) => { + builtin_promise_resolving_function.sweep_values(compactions) + } + HeapRootData::BuiltinPromiseCollectorFunction => todo!(), + HeapRootData::BuiltinProxyRevokerFunction => todo!(), + HeapRootData::PrimitiveObject(primitive_object) => { + primitive_object.sweep_values(compactions) + } + HeapRootData::Arguments(ordinary_object) => ordinary_object.sweep_values(compactions), + HeapRootData::Array(array) => array.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::ArrayBuffer(array_buffer) => array_buffer.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::DataView(data_view) => data_view.sweep_values(compactions), + #[cfg(feature = "date")] + HeapRootData::Date(date) => date.sweep_values(compactions), + HeapRootData::Error(error) => error.sweep_values(compactions), + HeapRootData::FinalizationRegistry(finalization_registry) => { + finalization_registry.sweep_values(compactions) + } + HeapRootData::Map(map) => map.sweep_values(compactions), + HeapRootData::Promise(promise) => promise.sweep_values(compactions), + HeapRootData::Proxy(proxy) => proxy.sweep_values(compactions), + HeapRootData::RegExp(reg_exp) => reg_exp.sweep_values(compactions), + HeapRootData::Set(set) => set.sweep_values(compactions), + #[cfg(feature = "shared-array-buffer")] + HeapRootData::SharedArrayBuffer(shared_array_buffer) => { + shared_array_buffer.sweep_values(compactions) + } + #[cfg(feature = "weak-refs")] + HeapRootData::WeakMap(weak_map) => weak_map.sweep_values(compactions), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakRef(weak_ref) => weak_ref.sweep_values(compactions), + #[cfg(feature = "weak-refs")] + HeapRootData::WeakSet(weak_set) => weak_set.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Int8Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint8ClampedArray(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Int16Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint16Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Int32Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Uint32Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::BigInt64Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::BigUint64Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Float32Array(base_index) => base_index.sweep_values(compactions), + #[cfg(feature = "array-buffer")] + HeapRootData::Float64Array(base_index) => base_index.sweep_values(compactions), + HeapRootData::AsyncFromSyncIterator => todo!(), + HeapRootData::AsyncIterator => todo!(), + HeapRootData::Iterator => todo!(), + HeapRootData::ArrayIterator(array_iterator) => array_iterator.sweep_values(compactions), + HeapRootData::SetIterator(set_iterator) => set_iterator.sweep_values(compactions), + HeapRootData::MapIterator(map_iterator) => map_iterator.sweep_values(compactions), + HeapRootData::Generator(generator) => generator.sweep_values(compactions), + HeapRootData::Module(module) => module.sweep_values(compactions), + HeapRootData::EmbedderObject(embedder_object) => { + embedder_object.sweep_values(compactions) + } + } + } +} diff --git a/nova_vm/src/engine/rootable/global.rs b/nova_vm/src/engine/rootable/global.rs new file mode 100644 index 000000000..5961da04f --- /dev/null +++ b/nova_vm/src/engine/rootable/global.rs @@ -0,0 +1,106 @@ +use std::marker::PhantomData; + +use crate::{ + ecmascript::execution::Agent, + engine::rootable::{HeapRootRef, Rootable}, +}; + +/// # Global heap root +/// +/// This type roots a heap-allocated JavaScript engine value until explicitly +/// released. A rooted value cannot be garbage collected, so accessing the +/// rooted value is always safe. The Global can be thought of as a unique +/// pointer to a heap allocation in system programming languages. As long as +/// the pointer lives, the memory on the heap will not be released. +#[derive(Debug, PartialEq)] +pub struct Global(T::RootRepr, PhantomData); + +impl Global { + /// Root the given value into a Global, keeping it from being garbage + /// collected until the Global is explicitly released. + #[must_use] + pub fn new(agent: &Agent, value: T) -> Self { + let value = match T::to_root_repr(value) { + Ok(stack_repr) => { + // The value doesn't need rooting. + return Self(stack_repr, PhantomData); + } + Err(heap_data) => heap_data, + }; + let mut globals = agent.heap.globals.borrow_mut(); + let reused_index = globals.iter_mut().enumerate().find_map(|(index, entry)| { + if entry.is_none() { + *entry = Some(value); + Some(index) + } else { + None + } + }); + let heap_ref = if let Some(reused_index) = reused_index { + HeapRootRef::from_index(reused_index) + } else { + let next_index = globals.len(); + globals.push(Some(value)); + HeapRootRef::from_index(next_index) + }; + Self(T::from_heap_ref(heap_ref), Default::default()) + } + + /// Take the rooted value from inside this Global, releasing it in the + /// process. Using the Global is not possible after this call. + pub fn take(self, agent: &Agent) -> T { + let heap_ref = match T::from_root_repr(&self.0) { + Ok(value) => { + // The value didn't need rooting + return value; + } + Err(heap_ref) => heap_ref, + }; + // Leave a `None` in the index and return the value + let heap_data = agent + .heap + .globals + .borrow_mut() + .get_mut(heap_ref.to_index()) + .unwrap() + .take() + .unwrap(); + let Some(value) = T::from_heap_data(heap_data) else { + panic!("Invalid Global returned different type than expected"); + }; + value + } + + /// Access the rooted value from inside this Global without releasing the + /// Global. + pub fn get(&self, agent: &mut Agent) -> T { + let heap_ref = match T::from_root_repr(&self.0) { + Ok(value) => { + // The value didn't need rooting + return value; + } + Err(heap_ref) => heap_ref, + }; + let heap_data = *agent + .heap + .globals + .borrow_mut() + .get_mut(heap_ref.to_index()) + .unwrap() + .as_ref() + .unwrap(); + let Some(value) = T::from_heap_data(heap_data) else { + panic!("Invalid Global returned different type than expected"); + }; + value + } + + /// Create a clone of this Global. Cloning a global means that both the + /// original Global and the cloned one must be explicitly released before + /// the rooted value can be garbage collected. + #[must_use] + pub fn clone(&self, agent: &mut Agent) -> Self { + let value = self.get(agent); + Self::new(agent, value) + } +} diff --git a/nova_vm/src/engine/rootable/scoped.rs b/nova_vm/src/engine/rootable/scoped.rs new file mode 100644 index 000000000..1d8e8405f --- /dev/null +++ b/nova_vm/src/engine/rootable/scoped.rs @@ -0,0 +1,82 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. + +use std::marker::PhantomData; + +use crate::{ + ecmascript::execution::Agent, + engine::rootable::{HeapRootRef, Rootable}, +}; + +/// # Scoped heap root +/// +/// This type roots a heap-allocated JavaScript engine value for the duration +/// of the current JavaScript call context, roughly corresponding to a native +/// call scope. Stack-allocated values avoid rooting. Rooted values cannot be +/// garbage collected, so accessing the rooted value is always safe within the +/// current call context. This type is intended for cheap rooting of JavaScript +/// Values that need to be used after calling into functions that may trigger +/// garbage collection. +#[derive(Debug, Clone, Copy)] +#[repr(transparent)] +pub struct Scoped { + inner: T::RootRepr, + _marker: PhantomData, +} + +impl Scoped { + pub fn new(agent: &Agent, value: T) -> Self { + let value = match T::to_root_repr(value) { + Ok(stack_repr) => { + // The value doesn't need rooting. + return Self { + inner: stack_repr, + _marker: PhantomData, + }; + } + Err(heap_data) => heap_data, + }; + let mut stack_refs = agent.stack_refs.borrow_mut(); + let next_index = stack_refs.len(); + stack_refs.push(value); + Self { + inner: T::from_heap_ref(HeapRootRef::from_index(next_index)), + _marker: PhantomData, + } + } + + pub fn get(self, agent: &Agent) -> T { + match T::from_root_repr(&self.inner) { + Ok(value) => value, + Err(heap_root_ref) => { + let Some(&heap_data) = agent.stack_refs.borrow().get(heap_root_ref.to_index()) + else { + handle_bound_check_failure() + }; + let Some(value) = T::from_heap_data(heap_data) else { + handle_invalid_local_conversion() + }; + value + } + } + } +} + +#[cold] +#[inline(never)] +fn handle_invalid_local_conversion() -> ! { + panic!("Attempted to convert mismatched Local"); +} + +#[cold] +#[inline(never)] +fn handle_index_overflow() -> ! { + panic!("Locals stack overflowed"); +} + +#[cold] +#[inline(never)] +fn handle_bound_check_failure() -> ! { + panic!("Attempted to access dropped Local") +} diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 2f10ce9dd..48a8a9ee8 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -9,17 +9,16 @@ pub(crate) mod heap_gc; pub mod indexes; mod object_entry; -use std::ops::Index; +use std::{cell::RefCell, ops::Index}; pub(crate) use self::heap_constants::{ intrinsic_function_count, intrinsic_object_count, intrinsic_primitive_object_count, IntrinsicConstructorIndexes, IntrinsicFunctionIndexes, IntrinsicObjectIndexes, - IntrinsicPrimitiveObjectIndexes, WellKnownSymbolIndexes, + IntrinsicPrimitiveObjectIndexes, WellKnownSymbolIndexes, LAST_WELL_KNOWN_SYMBOL_INDEX, }; #[cfg(test)] pub(crate) use self::heap_constants::{ LAST_INTRINSIC_CONSTRUCTOR_INDEX, LAST_INTRINSIC_FUNCTION_INDEX, LAST_INTRINSIC_OBJECT_INDEX, - LAST_WELL_KNOWN_SYMBOL_INDEX, }; pub(crate) use self::object_entry::{ObjectEntry, ObjectEntryPropertyDescriptor}; use self::{ @@ -55,14 +54,6 @@ use crate::{ promise_resolving_functions::PromiseResolvingFunctionHeapData, }, }, - map::data::MapHeapData, - module::data::ModuleHeapData, - primitive_objects::PrimitiveObjectHeapData, - promise::data::PromiseHeapData, - proxy::data::ProxyHeapData, - set::data::SetHeapData, - }, - builtins::{ embedder_object::data::EmbedderObjectHeapData, error::ErrorHeapData, finalization_registry::data::FinalizationRegistryHeapData, @@ -71,25 +62,29 @@ use crate::{ map_objects::map_iterator_objects::map_iterator::MapIteratorHeapData, set_objects::set_iterator_objects::set_iterator::SetIteratorHeapData, }, + map::data::MapHeapData, + module::data::ModuleHeapData, + primitive_objects::PrimitiveObjectHeapData, + promise::data::PromiseHeapData, + proxy::data::ProxyHeapData, + regexp::RegExpHeapData, + set::data::SetHeapData, ArrayHeapData, }, execution::{Environments, Realm, RealmIdentifier}, - scripts_and_modules::source_code::SourceCodeHeapData, scripts_and_modules::{ module::ModuleIdentifier, script::{Script, ScriptIdentifier}, + source_code::SourceCodeHeapData, }, types::{ - bigint::HeapBigInt, BuiltinConstructorHeapData, HeapNumber, HeapString, OrdinaryObject, - BUILTIN_STRINGS_LIST, - }, - types::{ - BigIntHeapData, BoundFunctionHeapData, BuiltinFunctionHeapData, - ECMAScriptFunctionHeapData, NumberHeapData, Object, ObjectHeapData, String, - StringHeapData, SymbolHeapData, Value, + bigint::HeapBigInt, BigIntHeapData, BoundFunctionHeapData, BuiltinConstructorHeapData, + BuiltinFunctionHeapData, ECMAScriptFunctionHeapData, HeapNumber, HeapString, + NumberHeapData, Object, ObjectHeapData, OrdinaryObject, String, StringHeapData, + SymbolHeapData, BUILTIN_STRINGS_LIST, }, }, - engine::ExecutableHeapData, + engine::{rootable::HeapRootData, ExecutableHeapData}, }; pub(crate) use heap_bits::{CompactionLists, HeapMarkAndSweep, WorkQueues}; @@ -120,7 +115,7 @@ pub struct Heap { pub(crate) executables: Vec, pub finalization_registrys: Vec>, pub generators: Vec>, - pub globals: Vec>, + pub(crate) globals: RefCell>>, pub maps: Vec>, pub map_iterators: Vec>, pub numbers: Vec>, @@ -218,7 +213,7 @@ impl Heap { source_codes: Vec::with_capacity(0), finalization_registrys: Vec::with_capacity(0), generators: Vec::with_capacity(1024), - globals: Vec::with_capacity(1024), + globals: RefCell::new(Vec::with_capacity(1024)), maps: Vec::with_capacity(128), map_iterators: Vec::with_capacity(128), modules: Vec::with_capacity(0), diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index c1ce13609..8d8255d58 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -72,7 +72,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option]) { let Agent { heap, execution_context_stack, - stack_values, + stack_refs, vm_stack, options: _, symbol_id: _, @@ -91,7 +91,7 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option]) { execution_context_stack.iter().for_each(|ctx| { ctx.mark_values(&mut queues); }); - stack_values + stack_refs .borrow() .iter() .for_each(|value| value.mark_values(&mut queues)); @@ -99,15 +99,21 @@ pub fn heap_gc(agent: &mut Agent, root_realms: &mut [Option]) { unsafe { vm_ptr.as_ref() }.mark_values(&mut queues); }); let mut last_filled_global_value = None; - heap.globals.iter().enumerate().for_each(|(i, &value)| { - if let Some(value) = value { - value.mark_values(&mut queues); - last_filled_global_value = Some(i); - } - }); + heap.globals + .borrow() + .iter() + .enumerate() + .for_each(|(i, &value)| { + if let Some(value) = value { + value.mark_values(&mut queues); + last_filled_global_value = Some(i); + } + }); // Remove as many `None` global values without moving any `Some(Value)` values. if let Some(last_filled_global_value) = last_filled_global_value { - heap.globals.drain(last_filled_global_value + 1..); + heap.globals + .borrow_mut() + .drain(last_filled_global_value + 1..); } queues.strings.extend( @@ -987,7 +993,7 @@ fn sweep(agent: &mut Agent, bits: &HeapBits, root_realms: &mut [Option