diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index ec9cbd7e..f1206817 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -2,10 +2,13 @@ mod array; mod bigint; mod boolean; mod date; +mod element_array; mod error; mod function; +mod heap_bits; mod heap_constants; -mod heap_trace; +mod heap_gc; +pub(crate) mod indexes; mod math; mod number; mod object; @@ -18,13 +21,18 @@ use self::{ bigint::{initialize_bigint_heap, BigIntHeapData}, boolean::initialize_boolean_heap, date::{initialize_date_heap, DateHeapData}, + element_array::{ + ElementArray2Pow10, ElementArray2Pow12, ElementArray2Pow16, ElementArray2Pow24, + ElementArray2Pow32, ElementArray2Pow4, ElementArray2Pow6, ElementArray2Pow8, ElementArrays, + ElementsVector, + }, error::{initialize_error_heap, ErrorHeapData}, function::{initialize_function_heap, FunctionHeapData, JsBindingFunction}, heap_constants::{ BuiltinObjectIndexes, FIRST_CONSTRUCTOR_INDEX, LAST_BUILTIN_OBJECT_INDEX, LAST_WELL_KNOWN_SYMBOL_INDEX, }, - heap_trace::HeapTrace, + indexes::{FunctionIndex, NumberIndex, ObjectIndex, StringIndex}, math::initialize_math_object, number::{initialize_number_heap, NumberHeapData}, object::{ @@ -35,11 +43,14 @@ use self::{ symbol::{initialize_symbol_heap, SymbolHeapData}, }; use crate::value::Value; -use std::cell::Cell; use wtf8::Wtf8; #[derive(Debug)] pub struct Heap { + /// ElementsArrays is where all element arrays live; + /// Element arrays are static arrays of Values plus + /// a HashMap of possible property descriptors. + pub(crate) elements: ElementArrays, pub(crate) arrays: Vec>, pub(crate) bigints: Vec>, pub(crate) errors: Vec>, @@ -53,12 +64,19 @@ pub struct Heap { pub(crate) symbols: Vec>, } -fn stop_the_world() {} -fn start_the_world() {} - impl Heap { pub fn new() -> Heap { let mut heap = Heap { + elements: ElementArrays { + e2pow4: ElementArray2Pow4::with_capacity(1024), + e2pow6: ElementArray2Pow6::with_capacity(1024), + e2pow8: ElementArray2Pow8::default(), + e2pow10: ElementArray2Pow10::default(), + e2pow12: ElementArray2Pow12::default(), + e2pow16: ElementArray2Pow16::default(), + e2pow24: ElementArray2Pow24::default(), + e2pow32: ElementArray2Pow32::default(), + }, arrays: Vec::with_capacity(1024), bigints: Vec::with_capacity(1024), errors: Vec::with_capacity(1024), @@ -121,28 +139,28 @@ impl Heap { heap } - pub(crate) fn alloc_string(&mut self, message: &str) -> u32 { + pub(crate) fn alloc_string(&mut self, message: &str) -> StringIndex { let found = self.strings.iter().position(|opt| { opt.as_ref() .map_or(false, |data| data.data == Wtf8::from_str(message)) }); if let Some(idx) = found { - return idx as u32; + return StringIndex::from_index(idx); } let data = StringHeapData::from_str(message); let found = self.strings.iter().position(|opt| opt.is_none()); if let Some(idx) = found { self.strings[idx].replace(data); - idx as u32 + StringIndex::from_index(idx) } else { self.strings.push(Some(data)); - self.strings.len() as u32 + StringIndex::last(&self.strings) } } - pub(crate) fn alloc_number(&mut self, number: f64) -> u32 { + pub(crate) fn alloc_number(&mut self, number: f64) -> NumberIndex { self.numbers.push(Some(NumberHeapData::new(number))); - self.numbers.len() as u32 + NumberIndex::last(&self.numbers) } pub(crate) fn create_function( @@ -151,395 +169,82 @@ impl Heap { length: u8, uses_arguments: bool, binding: JsBindingFunction, - ) -> u32 { + ) -> FunctionIndex { + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(self, "length"), + PropertyDescriptor::roxh(Value::SmiU(length as u32)), + ), + ObjectEntry::new( + PropertyKey::from_str(self, "name"), + PropertyDescriptor::roxh(name), + ), + ]; + let (keys, values): (ElementsVector, ElementsVector) = + self.elements.create_object_entries(entries); let func_object_data = ObjectHeapData { _extensible: true, - bits: HeapBits::new(), - entries: vec![ - ObjectEntry::new( - PropertyKey::from_str(self, "length"), - PropertyDescriptor::roxh(Value::SmiU(length as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(self, "name"), - PropertyDescriptor::roxh(name), - ), - ], - prototype: PropertyDescriptor::roh(Value::Object( - BuiltinObjectIndexes::FunctionPrototypeIndex as u32, - )), + keys, + values, + prototype: Value::Object(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), }; self.objects.push(Some(func_object_data)); let func_data = FunctionHeapData { binding, - bits: HeapBits::new(), bound: None, length, - object_index: self.objects.len() as u32, + object_index: ObjectIndex::last(&self.objects), uses_arguments, visible: None, }; + let index = FunctionIndex::from_index(self.functions.len()); self.functions.push(Some(func_data)); - self.functions.len() as u32 + index } - pub(crate) fn create_object(&mut self, entries: Vec) -> u32 { + pub(crate) fn create_object(&mut self, entries: Vec) -> ObjectIndex { + let (keys, values) = self.elements.create_object_entries(entries); let object_data = ObjectHeapData { _extensible: true, - bits: HeapBits::new(), - entries, - prototype: PropertyDescriptor::roh(Value::Object( - BuiltinObjectIndexes::ObjectPrototypeIndex as u32, - )), + keys, + values, + prototype: Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), }; self.objects.push(Some(object_data)); - self.objects.len() as u32 + ObjectIndex::last(&self.objects) } - pub(crate) fn create_null_object(&mut self, entries: Vec) -> u32 { + pub(crate) fn create_null_object(&mut self, entries: Vec) -> ObjectIndex { + let (keys, values) = self.elements.create_object_entries(entries); let object_data = ObjectHeapData { _extensible: true, - bits: HeapBits::new(), - entries, - prototype: PropertyDescriptor::roh(Value::Null), + keys, + values, + prototype: Value::Null, }; self.objects.push(Some(object_data)); - self.objects.len() as u32 - } - - fn partial_trace(&mut self) -> () { - // TODO: Consider roots count - for global in self.globals.iter() { - global.trace(self); - } - for error in self.errors.iter() { - let Some(data) = error else { - continue; - }; - let marked = data.bits.marked.take(); - data.bits.marked.set(marked); - if !marked { - continue; - } - let dirty = data.bits.dirty.take(); - data.bits.dirty.set(dirty); - if dirty { - error.trace(self); - } - } - for function in self.functions.iter() { - let Some(data) = function else { - continue; - }; - let marked = data.bits.marked.take(); - data.bits.marked.set(marked); - if !marked { - continue; - } - let dirty = data.bits.dirty.take(); - data.bits.dirty.set(dirty); - if dirty { - function.trace(self); - } - } - for object in self.objects.iter() { - let Some(data) = object else { - continue; - }; - let marked = data.bits.marked.take(); - data.bits.marked.set(marked); - if !marked { - continue; - } - let dirty = data.bits.dirty.take(); - data.bits.dirty.set(dirty); - if dirty { - object.trace(self); - } - } - for symbol in self.symbols.iter() { - let Some(data) = symbol else { - continue; - }; - let marked = data.bits.marked.take(); - data.bits.marked.set(marked); - if !marked { - continue; - } - let dirty = data.bits.dirty.take(); - data.bits.dirty.set(dirty); - if dirty { - symbol.trace(self); - } - } - stop_the_world(); - // Repeat above tracing to check for mutations that happened while we were tracing. - for object in self.objects.iter_mut() { - let Some(data) = object else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = object.take(); - } - } - for string in self.strings.iter_mut() { - let Some(data) = string else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = string.take(); - } - } - for symbol in self.symbols.iter_mut() { - let Some(data) = symbol else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = symbol.take(); - } - } - for number in self.numbers.iter_mut() { - let Some(data) = number else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = number.take(); - } - } - for bigint in self.bigints.iter_mut() { - let Some(data) = bigint else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = bigint.take(); - } - } - while self.objects.last().is_none() { - self.objects.pop(); - } - while self.strings.last().is_none() { - self.strings.pop(); - } - while self.symbols.last().is_none() { - self.symbols.pop(); - } - while self.numbers.last().is_none() { - self.numbers.pop(); - } - while self.bigints.last().is_none() { - self.bigints.pop(); - } - start_the_world(); - } - - fn complete_trace(&mut self) -> () { - // TODO: Consider roots count - for error in self.errors.iter() { - let Some(data) = error else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for function in self.functions.iter() { - let Some(data) = function else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for object in self.objects.iter() { - let Some(data) = object else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for string in self.strings.iter() { - let Some(data) = string else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for symbol in self.symbols.iter() { - let Some(data) = symbol else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for number in self.numbers.iter() { - let Some(data) = number else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for bigint in self.bigints.iter() { - let Some(data) = bigint else { - continue; - }; - data.bits.marked.set(false); - data.bits.dirty.set(false); - } - for global in self.globals.iter() { - global.trace(self); - } - stop_the_world(); - // Trace from dirty objects and symbols. - for object in self.objects.iter_mut() { - let Some(data) = object else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = object.take(); - } - } - for string in self.strings.iter_mut() { - let Some(data) = string else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = string.take(); - } - } - for symbol in self.symbols.iter_mut() { - let Some(data) = symbol else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = symbol.take(); - } - } - for number in self.numbers.iter_mut() { - let Some(data) = number else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = number.take(); - } - } - for bigint in self.bigints.iter_mut() { - let Some(data) = bigint else { - continue; - }; - let marked = data.bits.marked.replace(true); - if !marked { - let _ = bigint.take(); - } - } - while self.objects.last().is_none() { - self.objects.pop(); - } - while self.strings.last().is_none() { - self.strings.pop(); - } - while self.symbols.last().is_none() { - self.symbols.pop(); - } - while self.numbers.last().is_none() { - self.numbers.pop(); - } - while self.bigints.last().is_none() { - self.bigints.pop(); - } - start_the_world(); + ObjectIndex::last(&self.objects) } -} -impl HeapTrace for Value { - fn trace(&self, heap: &Heap) { - match self { - &Value::Error(idx) => heap.errors[idx as usize].trace(heap), - &Value::Function(idx) => heap.functions[idx as usize].trace(heap), - &Value::HeapBigInt(idx) => heap.bigints[idx as usize].trace(heap), - &Value::HeapNumber(idx) => heap.numbers[idx as usize].trace(heap), - &Value::HeapString(idx) => heap.strings[idx as usize].trace(heap), - &Value::Object(idx) => heap.objects[idx as usize].trace(heap), - &Value::Symbol(idx) => heap.symbols[idx as usize].trace(heap), - _ => {} - } - } - - fn root(&self, heap: &Heap) { - match self { - &Value::Error(idx) => heap.errors[idx as usize].root(heap), - &Value::Function(idx) => heap.functions[idx as usize].root(heap), - &Value::HeapBigInt(idx) => heap.bigints[idx as usize].root(heap), - &Value::HeapNumber(idx) => heap.numbers[idx as usize].root(heap), - &Value::HeapString(idx) => heap.strings[idx as usize].root(heap), - &Value::Object(idx) => heap.objects[idx as usize].root(heap), - &Value::Symbol(idx) => heap.symbols[idx as usize].root(heap), - _ => {} - } - } - - fn unroot(&self, heap: &Heap) { - match self { - &Value::Error(idx) => heap.errors[idx as usize].unroot(heap), - &Value::Function(idx) => heap.functions[idx as usize].unroot(heap), - &Value::HeapBigInt(idx) => heap.bigints[idx as usize].unroot(heap), - &Value::HeapNumber(idx) => heap.numbers[idx as usize].unroot(heap), - &Value::HeapString(idx) => heap.strings[idx as usize].unroot(heap), - &Value::Object(idx) => heap.objects[idx as usize].unroot(heap), - &Value::Symbol(idx) => heap.symbols[idx as usize].unroot(heap), - _ => {} - } - } - - fn finalize(&mut self, _heap: &Heap) { - unreachable!("Finalize should never be called on a Value in stack"); - } -} - -// TODO: Change to using vectors of u8 bitfields for mark and dirty bits. -#[derive(Debug)] -pub struct HeapBits { - marked: Cell, - _weak_marked: Cell, - dirty: Cell, - // TODO: Consider removing roots entirely and only using globals. - // Roots are useful for stack allocated Values, as they can just - // mark their holding of the value. But they're not particularly great - // from a GC standpoint, probably. - roots: Cell, -} - -impl HeapBits { - pub fn new() -> Self { - HeapBits { - marked: Cell::new(false), - _weak_marked: Cell::new(false), - dirty: Cell::new(false), - roots: Cell::new(0), - } - } - - fn root(&self) { - let roots = self.roots.replace(1); - assert!(roots != u8::MAX); - self.roots.replace(roots + 1); - } - - fn unroot(&self) { - let roots = self.roots.replace(1); - assert!(roots != 0); - self.roots.replace(roots - 1); + pub(crate) fn insert_builtin_object( + &mut self, + index: BuiltinObjectIndexes, + extensible: bool, + prototype: Value, + entries: Vec, + ) -> ObjectIndex { + let (keys, values) = self.elements.create_object_entries(entries); + let object_data = ObjectHeapData { + _extensible: extensible, + keys, + values, + prototype, + }; + self.objects[index as usize] = Some(object_data); + ObjectIndex::last(&self.objects) } } -unsafe impl Sync for HeapBits {} - #[test] fn init_heap() { let heap = Heap::new(); diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index aff90c98..aa876b62 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -1,44 +1,24 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + Heap, PropertyDescriptor, }, value::{JsResult, Value}, }; use super::{ + element_array::ElementsVector, function::FunctionHeapData, heap_constants::WellKnownSymbolIndexes, - heap_trace::HeapTrace, + indexes::{FunctionIndex, ObjectIndex}, object::{ObjectEntry, PropertyKey}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct ArrayHeapData { - pub(super) bits: HeapBits, - pub(super) object_index: u32, + pub(super) object_index: ObjectIndex, // TODO: Use SmallVec<[Value; 4]> - pub(super) elements: Vec>, -} - -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - heap.objects[self.as_ref().unwrap().object_index as usize].trace(heap); - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } + pub(super) elements: ElementsVector, } pub fn initialize_array_heap(heap: &mut Heap) { @@ -59,136 +39,140 @@ pub fn initialize_array_heap(heap: &mut Heap) { let to_sorted_key = PropertyKey::from_str(heap, "toSorted"); let to_spliced_key = PropertyKey::from_str(heap, "toSpliced"); let values_key = PropertyKey::from_str(heap, "values"); - heap.objects[BuiltinObjectIndexes::ArrayConstructorIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "from", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "isArray", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "of", 0, true, array_todo), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::ArrayPrototypeIndex.into(), + ), + ObjectEntry::new( + PropertyKey::Symbol(WellKnownSymbolIndexes::Species.into()), + PropertyDescriptor::ReadOnly { + get: heap.create_function(species_function_name, 0, false, array_species), + enumerable: false, + configurable: true, + }, + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ArrayConstructorIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry(heap, "from", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "isArray", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "of", 0, true, array_todo), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::ArrayPrototypeIndex as u32, - ), - ObjectEntry::new( - PropertyKey::Symbol(WellKnownSymbolIndexes::Species as u32), - PropertyDescriptor::ReadOnly { - get: heap.create_function(species_function_name, 0, false, array_species), - enumerable: false, - configurable: true, - }, - ), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::ArrayConstructorIndex) as usize] = + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::ArrayConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::ArrayConstructorIndex as u32, + object_index: BuiltinObjectIndexes::ArrayConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: array_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::ArrayPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "at", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "concat", 1, true, array_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::ArrayConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "copyWithin", 2, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "entries", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "every", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "fill", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "filter", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "find", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "findIndex", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "findLast", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "findLastIndex", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "flat", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "flatMap", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "forEach", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "includes", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "indexOf", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "join", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "keys", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "lastIndexOf", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "map", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "pop", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "push", 1, true, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "reduce", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "reduceRight", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "reverse", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "shift", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "slice", 2, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "some", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "sort", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "splice", 2, true, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "toReversed", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "toSorted", 1, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "toSpliced", 2, true, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "unshift", 1, true, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "values", 0, false, array_todo), + ObjectEntry::new_prototype_function_entry(heap, "with", 2, false, array_todo), + // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.iterator]", + WellKnownSymbolIndexes::Iterator.into(), + 0, + false, + array_todo, + ), + ObjectEntry::new( + PropertyKey::Symbol(WellKnownSymbolIndexes::Unscopables.into()), + PropertyDescriptor::roxh(Value::Object(heap.create_object(vec![ + ObjectEntry::new(at_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new( + copy_within_key, + PropertyDescriptor::rwx(Value::Boolean(true)), + ), + ObjectEntry::new(entries_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new(fill_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new(find_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new( + find_index_key, + PropertyDescriptor::rwx(Value::Boolean(true)), + ), + ObjectEntry::new(find_last_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new( + find_last_index_key, + PropertyDescriptor::rwx(Value::Boolean(true)), + ), + ObjectEntry::new(flat_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new(flat_map_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new(includes_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new(keys_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new( + to_reversed_key, + PropertyDescriptor::rwx(Value::Boolean(true)), + ), + ObjectEntry::new(to_sorted_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ObjectEntry::new( + to_spliced_key, + PropertyDescriptor::rwx(Value::Boolean(true)), + ), + ObjectEntry::new(values_key, PropertyDescriptor::rwx(Value::Boolean(true))), + ]))), + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ArrayPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry(heap, "at", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "concat", 1, true, array_todo), - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::ArrayConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry(heap, "copyWithin", 2, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "entries", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "every", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "fill", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "filter", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "find", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "findIndex", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "findLast", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "findLastIndex", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "flat", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "flatMap", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "forEach", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "includes", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "indexOf", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "join", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "keys", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "lastIndexOf", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "map", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "pop", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "push", 1, true, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "reduce", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "reduceRight", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "reverse", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "shift", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "slice", 2, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "some", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "sort", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "splice", 2, true, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "toReversed", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "toSorted", 1, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "toSpliced", 2, true, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "unshift", 1, true, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "values", 0, false, array_todo), - ObjectEntry::new_prototype_function_entry(heap, "with", 2, false, array_todo), - // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.iterator]", - WellKnownSymbolIndexes::Iterator as u32, - 0, - false, - array_todo, - ), - ObjectEntry::new( - PropertyKey::Symbol(WellKnownSymbolIndexes::Unscopables as u32), - PropertyDescriptor::roxh(Value::Object(heap.create_object(vec![ - ObjectEntry::new(at_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new( - copy_within_key, - PropertyDescriptor::rwx(Value::Boolean(true)), - ), - ObjectEntry::new(entries_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new(fill_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new(find_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new( - find_index_key, - PropertyDescriptor::rwx(Value::Boolean(true)), - ), - ObjectEntry::new(find_last_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new( - find_last_index_key, - PropertyDescriptor::rwx(Value::Boolean(true)), - ), - ObjectEntry::new(flat_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new(flat_map_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new(includes_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new(keys_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new( - to_reversed_key, - PropertyDescriptor::rwx(Value::Boolean(true)), - ), - ObjectEntry::new(to_sorted_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ObjectEntry::new( - to_spliced_key, - PropertyDescriptor::rwx(Value::Boolean(true)), - ), - ObjectEntry::new(values_key, PropertyDescriptor::rwx(Value::Boolean(true))), - ]))), - ), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn array_constructor_binding(_heap: &mut Heap, _this: Value, _args: &[Value]) -> JsResult { - Ok(Value::Function(0)) + Ok(Value::Function(FunctionIndex::from_index(0))) } fn array_species(_heap: &mut Heap, this: Value, _args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index 5eddbb70..9231e6c3 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -1,17 +1,15 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - FunctionHeapData, Heap, HeapBits, ObjectEntry, ObjectHeapData, PropertyDescriptor, - PropertyKey, + FunctionHeapData, Heap, ObjectEntry, PropertyDescriptor, PropertyKey, }, value::{JsResult, Value}, }; -use super::heap_trace::HeapTrace; +use super::indexes::{ErrorIndex, ObjectIndex}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct BigIntHeapData { - pub(super) bits: HeapBits, pub(super) sign: bool, pub(super) len: u32, pub(super) parts: Box<[u64]>, @@ -31,101 +29,74 @@ impl BigIntHeapData { } } -impl HeapTrace for Option { - fn trace(&self, _heap: &Heap) {} - - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_bigint_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::BigintConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry( - heap, - "asIntN", - 2, - false, - bigint_as_int_n, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "asUintN", - 2, - false, - bigint_as_uint_n, - ), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::BigintPrototypeIndex as u32, - ), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::BigintConstructorIndex) as usize] = + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "asIntN", 2, false, bigint_as_int_n), + ObjectEntry::new_prototype_function_entry(heap, "asUintN", 2, false, bigint_as_uint_n), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::BigintPrototypeIndex.into(), + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::BigintConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::BigintConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: heap.objects.len() as u32, + object_index: ObjectIndex::last(&heap.objects), length: 1, uses_arguments: false, bound: None, visible: None, binding: bigint_constructor, }); - heap.objects[BuiltinObjectIndexes::BigintPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::BigintConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry( + heap, + "toLocaleString", + 0, + false, + bigint_prototype_to_locale_string, + ), + ObjectEntry::new_prototype_function_entry( + heap, + "toString", + 0, + false, + bigint_prototype_to_string, + ), + ObjectEntry::new_prototype_function_entry( + heap, + "valueOf", + 0, + false, + bigint_prototype_value_of, + ), + // @@ToStringTag + // ObjectEntry { key: PropertyKey::Symbol(), PropertyDescriptor } + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::BigintPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::BigintConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry( - heap, - "toLocaleString", - 0, - false, - bigint_prototype_to_locale_string, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "toString", - 0, - false, - bigint_prototype_to_string, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "valueOf", - 0, - false, - bigint_prototype_value_of, - ), - // @@ToStringTag - // ObjectEntry { key: PropertyKey::Symbol(), PropertyDescriptor } - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn bigint_constructor(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult { if !this.is_undefined() { // TODO: Throw TypeError - return Err(Value::Error(0)); + return Err(Value::Error(ErrorIndex::from_index(0))); } else { return Ok(Value::SmallBigInt(3)); } diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index e4e11d94..22103013 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -1,7 +1,7 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - FunctionHeapData, HeapBits, ObjectHeapData, PropertyDescriptor, + FunctionHeapData, PropertyDescriptor, }, value::{JsResult, Value}, }; @@ -12,39 +12,42 @@ use super::{ }; pub fn initialize_boolean_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::BooleanConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::BooleanPrototypeIndex as u32, - )], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::BooleanConstructorIndex) as usize] = + let entries = vec![ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::BooleanPrototypeIndex.into(), + )]; + heap.insert_builtin_object( + BuiltinObjectIndexes::BooleanConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::BooleanConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::BooleanConstructorIndex as u32, + object_index: BuiltinObjectIndexes::BooleanConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: boolean_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::BooleanPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::BooleanConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, boolean_todo), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, boolean_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::BooleanPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::BooleanConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, boolean_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, boolean_todo), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn boolean_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index e89545f6..c11d49ed 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -3,7 +3,7 @@ use std::time::SystemTime; use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + Heap, PropertyDescriptor, }, value::{JsResult, Value}, }; @@ -11,157 +11,110 @@ use crate::{ use super::{ function::FunctionHeapData, heap_constants::WellKnownSymbolIndexes, - heap_trace::HeapTrace, + indexes::{FunctionIndex, ObjectIndex}, object::{ObjectEntry, PropertyKey}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct DateHeapData { - pub(super) bits: HeapBits, - pub(super) object_index: u32, + pub(super) object_index: ObjectIndex, pub(super) _date: SystemTime, } -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - heap.objects[self.as_ref().unwrap().object_index as usize].trace(heap); - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_date_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::DateConstructorIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "now", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "parse", 1, false, date_todo), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::DatePrototypeIndex.into(), + ), + ObjectEntry::new_prototype_function_entry(heap, "UTC", 7, false, date_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::DateConstructorIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry(heap, "now", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "parse", 1, false, date_todo), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::DatePrototypeIndex as u32, - ), - ObjectEntry::new_prototype_function_entry(heap, "UTC", 7, false, date_todo), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::DateConstructorIndex) as usize] = + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::DateConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::DateConstructorIndex as u32, + object_index: BuiltinObjectIndexes::DateConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: date_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::DatePrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::DateConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "getDate", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getDay", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getFullYear", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getHours", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getMilliseconds", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getMinutes", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getMonth", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getSeconds", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getTime", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getTimezoneOffset", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCDate", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCDay", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCFullYear", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCHours", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMilliseconds", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMinutes", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMonth", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "getUTCSeconds", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setDate", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setFullYear", 3, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setHours", 4, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setMilliseconds", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setMinutes", 3, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setMonth", 2, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setSeconds", 2, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setTime", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCDate", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCFullYear", 3, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCHours", 4, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMilliseconds", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMinutes", 3, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMonth", 2, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "setUTCSeconds", 2, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toDateString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toJSON", 1, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleDateString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleTimeString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toTimeString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "toUTCString", 0, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, date_todo), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.toPrimitive]", + WellKnownSymbolIndexes::ToPrimitive.into(), + 1, + false, + date_todo, + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::DatePrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::DateConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry(heap, "getDate", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getDay", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getFullYear", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getHours", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getMilliseconds", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getMinutes", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getMonth", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getSeconds", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getTime", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "getTimezoneOffset", - 0, - false, - date_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "getUTCDate", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getUTCDay", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getUTCFullYear", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getUTCHours", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "getUTCMilliseconds", - 0, - false, - date_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "getUTCMinutes", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getUTCMonth", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "getUTCSeconds", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setDate", 1, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setFullYear", 3, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setHours", 4, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setMilliseconds", 1, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setMinutes", 3, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setMonth", 2, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setSeconds", 2, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setTime", 1, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setUTCDate", 1, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setUTCFullYear", 3, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setUTCHours", 4, false, date_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "setUTCMilliseconds", - 1, - false, - date_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "setUTCMinutes", 3, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setUTCMonth", 2, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "setUTCSeconds", 2, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "toDateString", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "toJSON", 1, false, date_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "toLocaleDateString", - 0, - false, - date_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "toLocaleTimeString", - 0, - false, - date_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "toTimeString", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "toUTCString", 0, false, date_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, date_todo), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.toPrimitive]", - WellKnownSymbolIndexes::ToPrimitive as u32, - 1, - false, - date_todo, - ), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn date_constructor_binding(_heap: &mut Heap, _this: Value, _args: &[Value]) -> JsResult { - Ok(Value::Function(0)) + Ok(Value::Function(FunctionIndex::from_index(0))) } fn date_todo(_heap: &mut Heap, _this: Value, _args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs new file mode 100644 index 00000000..ca37239d --- /dev/null +++ b/nova_vm/src/heap/element_array.rs @@ -0,0 +1,825 @@ +use super::{ + indexes::{ElementIndex, FunctionIndex}, + object::{ObjectEntry, PropertyDescriptor, PropertyKey}, +}; +use crate::value::Value; +use core::panic; +use std::{collections::HashMap, mem::MaybeUninit, num::NonZeroU16}; + +#[derive(Debug, Clone, Copy)] +pub(crate) enum ElementArrayKey { + /// up to 16 elements + E4, + /// up to 64 elements + E6, + /// up to 256 elements + E8, + /// up to 1024 elements + E10, + /// up to 4096 elements + E12, + /// up to 65536 elements + E16, + /// up to 16777216 elements + E24, + /// up to 4294967296 elements + E32, +} + +impl From for ElementArrayKey { + fn from(value: usize) -> Self { + if value <= usize::pow(2, 4) { + ElementArrayKey::E4 + } else if value <= usize::pow(2, 6) { + ElementArrayKey::E6 + } else if value <= usize::pow(2, 8) { + ElementArrayKey::E8 + } else if value <= usize::pow(2, 10) { + ElementArrayKey::E10 + } else if value <= usize::pow(2, 12) { + ElementArrayKey::E12 + } else if value <= usize::pow(2, 16) { + ElementArrayKey::E16 + } else if value <= usize::pow(2, 24) { + ElementArrayKey::E24 + } else if value <= usize::pow(2, 32) { + ElementArrayKey::E32 + } else { + panic!("Elements array length over 2 ** 32"); + } + } +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct ElementsVector { + pub(crate) elements_index: ElementIndex, + pub(crate) cap: ElementArrayKey, + pub(crate) len: u32, +} + +#[derive(Debug, Clone, Copy)] +pub enum ElementDescriptor { + /// ```js + /// { value, writable: true, enumerable: true, configurable: true } + /// ``` + WritableEnumerableConfigurableData, + /// ```js + /// { value, writable: true, enumerable: true, configurable: false } + /// ``` + WritableEnumerableUnconfigurableData, + /// ```js + /// { value, writable: true, enumerable: false, configurable: true } + /// ``` + WritableUnenumerableConfigurableData, + /// ```js + /// { value, writable: true, enumerable: false, configurable: false } + /// ``` + WritableUnenumerableUnconfigurableData, + /// ```js + /// { value, writable: false, enumerable: true, configurable: true } + /// ``` + ReadOnlyEnumerableConfigurableData, + /// ```js + /// { value, writable: false, enumerable: true, configurable: false } + /// ``` + ReadOnlyEnumerableUnconfigurableData, + /// ```js + /// { value, writable: false, enumerable: false, configurable: true } + /// ``` + ReadOnlyUnenumerableConfigurableData, + /// ```js + /// { value, writable: false, enumerable: false, configurable: false } + /// ``` + ReadOnlyUnenumerableUnconfigurableData, + // TODO: Is { enumerable, configurable } actually a real case or is that just in the spec? + // If it is then a NoReadNoWrite*umerable*onfigurable set of descriptors is needed + /// ```js + /// { get, enumerable: true, configurable: true } + /// ``` + ReadOnlyEnumerableConfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { get, enumerable: true, configurable: false } + /// ``` + ReadOnlyEnumerableUnconfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { get, enumerable: false, configurable: true } + /// ``` + ReadOnlyUnenumerableConfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { get, enumerable: false, configurable: false } + /// ``` + ReadOnlyUnenumerableUnconfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { set, enumerable: true, configurable: true } + /// ``` + WriteOnlyEnumerableConfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { set, enumerable: true, configurable: false } + /// ``` + WriteOnlyEnumerableUnconfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { set, enumerable: false, configurable: true } + /// ``` + WriteOnlyUnenumerableConfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { set, enumerable: false, configurable: false } + /// ``` + WriteOnlyUnenumerableUnconfigurableAccessor(u8, NonZeroU16), + /// ```js + /// { get, set, enumerable: true, configurable: true } + /// ``` + ReadWriteEnumerableConfigurableAccessor(u8, u8, NonZeroU16, NonZeroU16), + /// ```js + /// { get, set, enumerable: true, configurable: false } + /// ``` + ReadWriteEnumerableUnconfigurableAccessor(u8, u8, NonZeroU16, NonZeroU16), + /// ```js + /// { get, set, enumerable: false, configurable: true } + /// ``` + ReadWriteUnenumerableConfigurableAccessor(u8, u8, NonZeroU16, NonZeroU16), + /// ```js + /// { get, set, enumerable: false, configurable: false } + /// ``` + ReadWriteUnenumerableUnconfigurableAccessor(u8, u8, NonZeroU16, NonZeroU16), +} + +impl ElementDescriptor { + pub fn from_property_descriptor( + desc: PropertyDescriptor, + ) -> (Option, Option) { + match desc { + PropertyDescriptor::Data { + value, + writable, + enumerable, + configurable, + } => match (writable, enumerable, configurable) { + (true, true, true) => (None, Some(value)), + (true, true, false) => ( + Some(ElementDescriptor::WritableEnumerableUnconfigurableData), + Some(value), + ), + (true, false, true) => ( + Some(ElementDescriptor::WritableUnenumerableConfigurableData), + Some(value), + ), + (true, false, false) => ( + Some(ElementDescriptor::WritableUnenumerableUnconfigurableData), + Some(value), + ), + (false, true, true) => ( + Some(ElementDescriptor::ReadOnlyEnumerableConfigurableData), + Some(value), + ), + (false, true, false) => ( + Some(ElementDescriptor::ReadOnlyEnumerableUnconfigurableData), + Some(value), + ), + (false, false, true) => ( + Some(ElementDescriptor::ReadOnlyUnenumerableConfigurableData), + Some(value), + ), + (false, false, false) => ( + Some(ElementDescriptor::ReadOnlyUnenumerableUnconfigurableData), + Some(value), + ), + }, + PropertyDescriptor::Blocked { .. } => unreachable!(), + PropertyDescriptor::ReadOnly { + get, + enumerable, + configurable, + } => { + let get = get.into_u32(); + let top = (get >> 16) as u8; + let bottom = NonZeroU16::new(get as u16).unwrap(); + match (enumerable, configurable) { + (true, true) => ( + Some(ElementDescriptor::ReadOnlyEnumerableConfigurableAccessor( + top, bottom, + )), + None, + ), + (true, false) => ( + Some(ElementDescriptor::ReadOnlyEnumerableUnconfigurableAccessor( + top, bottom, + )), + None, + ), + (false, true) => ( + Some(ElementDescriptor::ReadOnlyUnenumerableConfigurableAccessor( + top, bottom, + )), + None, + ), + (false, false) => ( + Some( + ElementDescriptor::ReadOnlyUnenumerableUnconfigurableAccessor( + top, bottom, + ), + ), + None, + ), + } + } + PropertyDescriptor::WriteOnly { + set, + enumerable, + configurable, + } => { + let set = set.into_u32(); + let top = (set >> 16) as u8; + let bottom = NonZeroU16::new(set as u16).unwrap(); + match (enumerable, configurable) { + (true, true) => ( + Some(ElementDescriptor::WriteOnlyEnumerableConfigurableAccessor( + top, bottom, + )), + None, + ), + (true, false) => ( + Some( + ElementDescriptor::WriteOnlyEnumerableUnconfigurableAccessor( + top, bottom, + ), + ), + None, + ), + (false, true) => ( + Some( + ElementDescriptor::WriteOnlyUnenumerableConfigurableAccessor( + top, bottom, + ), + ), + None, + ), + (false, false) => ( + Some( + ElementDescriptor::WriteOnlyUnenumerableUnconfigurableAccessor( + top, bottom, + ), + ), + None, + ), + } + } + PropertyDescriptor::ReadWrite { + get, + set, + enumerable, + configurable, + } => { + let get = get.into_u32(); + let get_top = (get >> 16) as u8; + let get_bottom = NonZeroU16::new(get as u16).unwrap(); + let set = set.into_u32(); + let set_top = (set >> 16) as u8; + let set_bottom = NonZeroU16::new(set as u16).unwrap(); + match (enumerable, configurable) { + (true, true) => ( + Some(ElementDescriptor::ReadWriteEnumerableConfigurableAccessor( + get_top, set_top, get_bottom, set_bottom, + )), + None, + ), + (true, false) => ( + Some( + ElementDescriptor::ReadWriteEnumerableUnconfigurableAccessor( + get_top, set_top, get_bottom, set_bottom, + ), + ), + None, + ), + (false, true) => ( + Some( + ElementDescriptor::ReadWriteUnenumerableConfigurableAccessor( + get_top, set_top, get_bottom, set_bottom, + ), + ), + None, + ), + (false, false) => ( + Some( + ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor( + get_top, set_top, get_bottom, set_bottom, + ), + ), + None, + ), + } + } + } + } + + pub fn getter_index(&self) -> Option { + match self { + ElementDescriptor::ReadOnlyEnumerableConfigurableAccessor(get_top, get_bottom) + | ElementDescriptor::ReadOnlyEnumerableUnconfigurableAccessor(get_top, get_bottom) + | ElementDescriptor::ReadOnlyUnenumerableConfigurableAccessor(get_top, get_bottom) + | ElementDescriptor::ReadOnlyUnenumerableUnconfigurableAccessor(get_top, get_bottom) + | ElementDescriptor::ReadWriteEnumerableConfigurableAccessor( + get_top, + _, + get_bottom, + _, + ) + | ElementDescriptor::ReadWriteEnumerableUnconfigurableAccessor( + get_top, + _, + get_bottom, + _, + ) + | ElementDescriptor::ReadWriteUnenumerableConfigurableAccessor( + get_top, + _, + get_bottom, + _, + ) + | ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor( + get_top, + _, + get_bottom, + _, + ) => Some(FunctionIndex::from_u32( + (*get_top as u32) << 16 + get_bottom.get() as u32, + )), + _ => None, + } + } + + pub fn setter_index(&self) -> Option { + match self { + ElementDescriptor::WriteOnlyEnumerableConfigurableAccessor(set_top, set_bottom) + | ElementDescriptor::WriteOnlyEnumerableUnconfigurableAccessor(set_top, set_bottom) + | ElementDescriptor::WriteOnlyUnenumerableConfigurableAccessor(set_top, set_bottom) + | ElementDescriptor::WriteOnlyUnenumerableUnconfigurableAccessor(set_top, set_bottom) + | ElementDescriptor::ReadWriteEnumerableConfigurableAccessor( + _, + set_top, + _, + set_bottom, + ) + | ElementDescriptor::ReadWriteEnumerableUnconfigurableAccessor( + _, + set_top, + _, + set_bottom, + ) + | ElementDescriptor::ReadWriteUnenumerableConfigurableAccessor( + _, + set_top, + _, + set_bottom, + ) + | ElementDescriptor::ReadWriteUnenumerableUnconfigurableAccessor( + _, + set_top, + _, + set_bottom, + ) => Some(FunctionIndex::from_u32( + (*set_top as u32) << 16 + set_bottom.get() as u32, + )), + _ => None, + } + } +} + +/// Element arrays of up to 16 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow4 { + pub(crate) values: Vec; usize::pow(2, 4)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow4 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow4 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 64 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow6 { + pub(crate) values: Vec; usize::pow(2, 6)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow6 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow6 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 256 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow8 { + pub(crate) values: Vec; usize::pow(2, 8)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow8 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow8 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 1024 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow10 { + pub(crate) values: Vec; usize::pow(2, 10)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow10 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow10 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 4096 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow12 { + pub(crate) values: Vec; usize::pow(2, 12)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow12 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow12 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 65536 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow16 { + pub(crate) values: Vec; usize::pow(2, 16)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow16 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow16 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 16777216 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow24 { + pub(crate) values: Vec; usize::pow(2, 24)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow24 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow24 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +/// Element arrays of up to 4294967296 elements +#[derive(Debug)] +pub(crate) struct ElementArray2Pow32 { + pub(crate) values: Vec; usize::pow(2, 32)]>>, + pub(crate) descriptors: HashMap>, +} + +impl Default for ElementArray2Pow32 { + fn default() -> Self { + Self { + values: Default::default(), + descriptors: Default::default(), + } + } +} + +impl ElementArray2Pow32 { + pub fn with_capacity(cap: usize) -> Self { + Self { + values: Vec::with_capacity(cap), + descriptors: Default::default(), + } + } +} + +#[derive(Debug)] +pub(crate) struct ElementArrays { + /// up to 16 elements + pub(crate) e2pow4: ElementArray2Pow4, + /// up to 64 elements + pub(crate) e2pow6: ElementArray2Pow6, + /// up to 256 elements + pub(crate) e2pow8: ElementArray2Pow8, + /// up to 1024 elements + pub(crate) e2pow10: ElementArray2Pow10, + /// up to 4096 elements + pub(crate) e2pow12: ElementArray2Pow12, + /// up to 65536 elements + pub(crate) e2pow16: ElementArray2Pow16, + /// up to 16777216 elements + pub(crate) e2pow24: ElementArray2Pow24, + /// up to 4294967296 elements + pub(crate) e2pow32: ElementArray2Pow32, +} + +impl ElementArrays { + fn push_with_key( + &mut self, + key: ElementArrayKey, + vector: Vec>, + desciptors: Option>, + ) -> ElementIndex { + match key { + ElementArrayKey::E4 => { + self.e2pow4.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow4.values.set_len(self.e2pow4.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 4)]>, + >(self.e2pow4.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(&vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow4.values); + if let Some(descriptors) = desciptors { + self.e2pow4.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E6 => { + self.e2pow6.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow6.values.set_len(self.e2pow6.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 6)]>, + >(self.e2pow6.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow6.values); + if let Some(descriptors) = desciptors { + self.e2pow6.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E8 => { + self.e2pow8.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow8.values.set_len(self.e2pow8.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 8)]>, + >(self.e2pow8.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow8.values); + if let Some(descriptors) = desciptors { + self.e2pow8.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E10 => { + self.e2pow10.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow10.values.set_len(self.e2pow10.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 10)]>, + >(self.e2pow10.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow10.values); + if let Some(descriptors) = desciptors { + self.e2pow10.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E12 => { + self.e2pow12.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow12.values.set_len(self.e2pow12.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 12)]>, + >(self.e2pow12.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow12.values); + if let Some(descriptors) = desciptors { + self.e2pow12.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E16 => { + self.e2pow16.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow16.values.set_len(self.e2pow16.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 12)]>, + >(self.e2pow16.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow16.values); + if let Some(descriptors) = desciptors { + self.e2pow16.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E24 => { + self.e2pow24.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow24.values.set_len(self.e2pow24.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 12)]>, + >(self.e2pow24.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow24.values); + if let Some(descriptors) = desciptors { + self.e2pow24.descriptors.insert(index, descriptors); + } + index + } + ElementArrayKey::E32 => { + self.e2pow32.values.reserve(1); + // SAFETY: We reserved an extra slot successfully. + unsafe { + self.e2pow32.values.set_len(self.e2pow32.values.len() + 1); + let last = std::mem::transmute::< + _, + &mut MaybeUninit<[Option; usize::pow(2, 12)]>, + >(self.e2pow32.values.last_mut().unwrap()); + let length = vector.len(); + let last_slice = last.assume_init_mut().as_mut_slice(); + last_slice[0..length].copy_from_slice(vector.as_slice()); + last_slice[vector.len()..].fill(None) + }; + let index = ElementIndex::last(&self.e2pow32.values); + if let Some(descriptors) = desciptors { + self.e2pow32.descriptors.insert(index, descriptors); + } + index + } + } + } + + pub(crate) fn create_object_entries( + &mut self, + mut entries: Vec, + ) -> (ElementsVector, ElementsVector) { + let length = entries.len(); + let mut keys: Vec> = Vec::with_capacity(length); + let mut values: Vec> = Vec::with_capacity(length); + let mut descriptors: Option> = None; + entries.drain(..).enumerate().for_each(|(index, entry)| { + let ObjectEntry { key, value } = entry; + let (maybe_descriptor, maybe_value) = + ElementDescriptor::from_property_descriptor(value); + let key = match key { + PropertyKey::SmallAsciiString(data) => Value::StackString(data), + PropertyKey::Smi(data) => Value::Smi(data), + PropertyKey::String(data) => Value::HeapString(data), + PropertyKey::Symbol(data) => Value::Symbol(data), + }; + keys.push(Some(key)); + values.push(maybe_value); + if let Some(descriptor) = maybe_descriptor { + if descriptors.is_none() { + descriptors = Some(Default::default()); + } + descriptors + .as_mut() + .unwrap() + .insert(index as u32, descriptor); + } + }); + let cap = ElementArrayKey::from(length); + let len = length as u32; + let key_elements_index = self.push_with_key(cap, keys, None); + let value_elements_index = self.push_with_key(cap, values, descriptors); + ( + ElementsVector { + elements_index: key_elements_index, + cap, + len, + }, + ElementsVector { + elements_index: value_elements_index, + cap, + len, + }, + ) + } +} diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index c4027107..d13ba4c3 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -1,88 +1,71 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + Heap, PropertyDescriptor, }, value::{JsResult, Value}, }; use super::{ function::FunctionHeapData, - heap_trace::HeapTrace, + indexes::{FunctionIndex, ObjectIndex}, object::{ObjectEntry, PropertyKey}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct ErrorHeapData { - pub(super) bits: HeapBits, - pub(super) object_index: u32, + pub(super) object_index: ObjectIndex, // TODO: stack? name? } -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - heap.objects[self.as_ref().unwrap().object_index as usize].trace(heap); - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_error_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::ErrorConstructorIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::ErrorPrototypeIndex.into(), + )]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ErrorConstructorIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::ErrorPrototypeIndex as u32, - )], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::ErrorConstructorIndex) as usize] = + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::ErrorConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::ErrorConstructorIndex as u32, + object_index: BuiltinObjectIndexes::ErrorConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: error_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::ErrorPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::ErrorConstructorIndex, + ))), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "name"), + PropertyDescriptor::rwx(Value::EmptyString), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "name"), + PropertyDescriptor::rwx(Value::new_string(heap, "Error")), + ), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, error_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ErrorPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::ErrorConstructorIndex, - ))), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "name"), - PropertyDescriptor::rwx(Value::EmptyString), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "name"), - PropertyDescriptor::rwx(Value::new_string(heap, "Error")), - ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, error_todo), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn error_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::Function(0)) + Ok(Value::Function(FunctionIndex::from_index(0))) } fn error_todo(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 851feb3d..d7d6bc46 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -1,23 +1,22 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + Heap, PropertyDescriptor, }, value::{JsResult, Value}, }; use super::{ heap_constants::WellKnownSymbolIndexes, - heap_trace::HeapTrace, + indexes::{FunctionIndex, ObjectIndex}, object::{ObjectEntry, PropertyKey}, }; pub type JsBindingFunction = fn(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct FunctionHeapData { - pub(super) bits: HeapBits, - pub(super) object_index: u32, + pub(super) object_index: ObjectIndex, pub(super) length: u8, pub(super) uses_arguments: bool, pub(super) bound: Option>, @@ -26,85 +25,60 @@ pub(crate) struct FunctionHeapData { // TODO: Should name be here as an "internal slot" of sorts? } -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - heap.objects[self.as_ref().unwrap().object_index as usize].trace(heap); - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_function_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::FunctionConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::FunctionPrototypeIndex as u32, - )], - )); + let entries = vec![ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )]; + heap.insert_builtin_object( + BuiltinObjectIndexes::FunctionConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); heap.functions - [get_constructor_index(BuiltinObjectIndexes::FunctionConstructorIndex) as usize] = + [get_constructor_index(BuiltinObjectIndexes::FunctionConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::FunctionConstructorIndex as u32, + object_index: BuiltinObjectIndexes::FunctionConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: function_constructor_binding, }); + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "apply", 2, false, function_todo), + ObjectEntry::new_prototype_function_entry(heap, "bind", 1, true, function_todo), + ObjectEntry::new_prototype_function_entry(heap, "call", 1, true, function_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::FunctionConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, function_todo), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "hasInstance", + WellKnownSymbolIndexes::HasInstance.into(), + 1, + false, + function_todo, + ), + ]; // NOTE: According to ECMAScript spec https://tc39.es/ecma262/#sec-properties-of-the-function-prototype-object // the %Function.prototype% object should itself be a function that always returns undefined. This is not // upheld here and we probably do not care. It's seemingly the only prototype that is a function. - heap.objects[BuiltinObjectIndexes::FunctionPrototypeIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry(heap, "apply", 2, false, function_todo), - ObjectEntry::new_prototype_function_entry(heap, "bind", 1, true, function_todo), - ObjectEntry::new_prototype_function_entry(heap, "call", 1, true, function_todo), - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::FunctionConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry( - heap, - "toString", - 0, - false, - function_todo, - ), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "hasInstance", - WellKnownSymbolIndexes::HasInstance as u32, - 1, - false, - function_todo, - ), - ], - )); + heap.insert_builtin_object( + BuiltinObjectIndexes::FunctionPrototypeIndex, + true, + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn function_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::Function(0)) + Ok(Value::Function(FunctionIndex::from_index(0))) } fn function_todo(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs new file mode 100644 index 00000000..e9113919 --- /dev/null +++ b/nova_vm/src/heap/heap_bits.rs @@ -0,0 +1,157 @@ +use std::sync::atomic::AtomicBool; + +use crate::value::Value; + +use super::{ + indexes::{ + ArrayIndex, BigIntIndex, DateIndex, ElementIndex, ErrorIndex, FunctionIndex, NumberIndex, + ObjectIndex, RegExpIndex, StringIndex, SymbolIndex, + }, + Heap, +}; + +pub(crate) struct HeapBits { + pub e_2_4: Box<[AtomicBool]>, + pub e_2_6: Box<[AtomicBool]>, + pub e_2_8: Box<[AtomicBool]>, + pub e_2_10: Box<[AtomicBool]>, + pub e_2_12: Box<[AtomicBool]>, + pub e_2_16: Box<[AtomicBool]>, + pub e_2_24: Box<[AtomicBool]>, + pub e_2_32: Box<[AtomicBool]>, + pub arrays: Box<[AtomicBool]>, + pub bigints: Box<[AtomicBool]>, + pub errors: Box<[AtomicBool]>, + pub functions: Box<[AtomicBool]>, + pub dates: Box<[AtomicBool]>, + pub numbers: Box<[AtomicBool]>, + pub objects: Box<[AtomicBool]>, + pub regexps: Box<[AtomicBool]>, + pub strings: Box<[AtomicBool]>, + pub symbols: Box<[AtomicBool]>, +} + +pub(crate) struct WorkQueues { + pub e_2_4: Vec, + pub e_2_6: Vec, + pub e_2_8: Vec, + pub e_2_10: Vec, + pub e_2_12: Vec, + pub e_2_16: Vec, + pub e_2_24: Vec, + pub e_2_32: Vec, + pub arrays: Vec, + pub bigints: Vec, + pub errors: Vec, + pub functions: Vec, + pub dates: Vec, + pub numbers: Vec, + pub objects: Vec, + pub regexps: Vec, + pub strings: Vec, + pub symbols: Vec, +} + +impl HeapBits { + pub(crate) fn new(heap: &Heap) -> Self { + Self { + e_2_4: Vec::with_capacity(heap.elements.e2pow4.values.len()).into_boxed_slice(), + e_2_6: Vec::with_capacity(heap.elements.e2pow6.values.len()).into_boxed_slice(), + e_2_8: Vec::with_capacity(heap.elements.e2pow8.values.len()).into_boxed_slice(), + e_2_10: Vec::with_capacity(heap.elements.e2pow10.values.len()).into_boxed_slice(), + e_2_12: Vec::with_capacity(heap.elements.e2pow12.values.len()).into_boxed_slice(), + e_2_16: Vec::with_capacity(heap.elements.e2pow16.values.len()).into_boxed_slice(), + e_2_24: Vec::with_capacity(heap.elements.e2pow24.values.len()).into_boxed_slice(), + e_2_32: Vec::with_capacity(heap.elements.e2pow32.values.len()).into_boxed_slice(), + arrays: Vec::with_capacity(heap.arrays.len()).into_boxed_slice(), + bigints: Vec::with_capacity(heap.bigints.len()).into_boxed_slice(), + errors: Vec::with_capacity(heap.errors.len()).into_boxed_slice(), + functions: Vec::with_capacity(heap.functions.len()).into_boxed_slice(), + dates: Vec::with_capacity(heap.dates.len()).into_boxed_slice(), + numbers: Vec::with_capacity(heap.numbers.len()).into_boxed_slice(), + objects: Vec::with_capacity(heap.objects.len()).into_boxed_slice(), + regexps: Vec::with_capacity(heap.regexps.len()).into_boxed_slice(), + strings: Vec::with_capacity(heap.strings.len()).into_boxed_slice(), + symbols: Vec::with_capacity(heap.symbols.len()).into_boxed_slice(), + } + } +} + +impl WorkQueues { + pub(crate) fn new(heap: &Heap) -> Self { + Self { + e_2_4: Vec::with_capacity(heap.elements.e2pow4.values.len() / 4), + e_2_6: Vec::with_capacity(heap.elements.e2pow6.values.len() / 4), + e_2_8: Vec::with_capacity(heap.elements.e2pow8.values.len() / 4), + e_2_10: Vec::with_capacity(heap.elements.e2pow10.values.len() / 4), + e_2_12: Vec::with_capacity(heap.elements.e2pow12.values.len() / 4), + e_2_16: Vec::with_capacity(heap.elements.e2pow16.values.len() / 4), + e_2_24: Vec::with_capacity(heap.elements.e2pow24.values.len() / 4), + e_2_32: Vec::with_capacity(heap.elements.e2pow32.values.len() / 4), + arrays: Vec::with_capacity(heap.arrays.len() / 4), + bigints: Vec::with_capacity(heap.bigints.len() / 4), + errors: Vec::with_capacity(heap.errors.len() / 4), + functions: Vec::with_capacity(heap.functions.len() / 4), + dates: Vec::with_capacity(heap.dates.len() / 4), + numbers: Vec::with_capacity(heap.numbers.len() / 4), + objects: Vec::with_capacity(heap.objects.len() / 4), + regexps: Vec::with_capacity(heap.regexps.len() / 4), + strings: Vec::with_capacity(heap.strings.len() / 4), + symbols: Vec::with_capacity(heap.symbols.len() / 4), + } + } + + pub(crate) fn push_value(&mut self, value: Value) { + match value { + Value::Array(idx) => self.arrays.push(idx), + Value::BigIntObject(_) => todo!(), + Value::BooleanObject(idx) => todo!(), + Value::Boolean(_) => {} + Value::Date(idx) => self.dates.push(idx), + Value::EmptyString => {} + Value::Error(idx) => self.errors.push(idx), + Value::Function(idx) => todo!(), + Value::HeapBigInt(idx) => self.bigints.push(idx), + Value::HeapNumber(idx) => self.numbers.push(idx), + Value::HeapString(idx) => self.strings.push(idx), + Value::Infinity => {} + Value::NaN => {} + Value::NegativeInfinity => {} + Value::NegativeZero => {} + Value::Null => {} + Value::NumberObject(_) => todo!(), + Value::Object(idx) => self.objects.push(idx), + Value::RegExp(idx) => self.regexps.push(idx), + Value::StackString(_) => {} + Value::SmallBigInt(_) => {} + Value::SmallBigIntU(_) => {} + Value::Smi(_) => {} + Value::SmiU(_) => {} + Value::StringObject(_) => todo!(), + Value::Symbol(idx) => self.symbols.push(idx), + Value::SymbolObject(_) => todo!(), + Value::Undefined => {} + } + } + + pub(crate) fn is_empty(&self) -> bool { + self.e_2_4.is_empty() + && self.e_2_6.is_empty() + && self.e_2_8.is_empty() + && self.e_2_10.is_empty() + && self.e_2_12.is_empty() + && self.e_2_16.is_empty() + && self.e_2_24.is_empty() + && self.e_2_32.is_empty() + && self.arrays.is_empty() + && self.bigints.is_empty() + && self.errors.is_empty() + && self.functions.is_empty() + && self.dates.is_empty() + && self.numbers.is_empty() + && self.objects.is_empty() + && self.regexps.is_empty() + && self.strings.is_empty() + && self.symbols.is_empty() + } +} diff --git a/nova_vm/src/heap/heap_constants.rs b/nova_vm/src/heap/heap_constants.rs index f4921ce7..d3c53417 100644 --- a/nova_vm/src/heap/heap_constants.rs +++ b/nova_vm/src/heap/heap_constants.rs @@ -8,6 +8,9 @@ // | First the list of built-in prototypes and non-prototypal objects | // +==================================================================+ +use super::indexes::{FunctionIndex, ObjectIndex, SymbolIndex}; + +#[derive(Debug, Clone, Copy)] #[repr(u32)] pub enum BuiltinObjectIndexes { // Fundamental objects @@ -131,13 +134,28 @@ pub enum BuiltinObjectIndexes { ProxyConstructorIndex, } +impl Into for BuiltinObjectIndexes { + fn into(self) -> ObjectIndex { + ObjectIndex::from_u32_index(self as u32) + } +} + +impl Into for BuiltinObjectIndexes { + fn into(self) -> FunctionIndex { + // We do not allow more than 16 777 216 functions to exist. + assert!(self as u32 <= u32::pow(2, 24)); + FunctionIndex::from_u32_index(self as u32) + } +} + pub const LAST_BUILTIN_OBJECT_INDEX: u32 = BuiltinObjectIndexes::ProxyConstructorIndex as u32; pub const FIRST_CONSTRUCTOR_INDEX: u32 = BuiltinObjectIndexes::ObjectConstructorIndex as u32; -pub const fn get_constructor_index(object_index: BuiltinObjectIndexes) -> u32 { - object_index as u32 - FIRST_CONSTRUCTOR_INDEX +pub const fn get_constructor_index(object_index: BuiltinObjectIndexes) -> FunctionIndex { + FunctionIndex::from_u32_index(object_index as u32 - FIRST_CONSTRUCTOR_INDEX) } +#[derive(Debug, Clone, Copy)] #[repr(u32)] pub enum WellKnownSymbolIndexes { AsyncIterator, @@ -155,4 +173,10 @@ pub enum WellKnownSymbolIndexes { Unscopables, } +impl Into for WellKnownSymbolIndexes { + fn into(self) -> SymbolIndex { + SymbolIndex::from_u32_index(self as u32) + } +} + pub const LAST_WELL_KNOWN_SYMBOL_INDEX: u32 = WellKnownSymbolIndexes::Unscopables as u32; diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs new file mode 100644 index 00000000..1f5b0114 --- /dev/null +++ b/nova_vm/src/heap/heap_gc.rs @@ -0,0 +1,496 @@ +use std::sync::atomic::Ordering; + +use crate::value::Value; + +use super::{ + element_array::ElementArrayKey, + heap_bits::{HeapBits, WorkQueues}, + indexes::{ + ArrayIndex, DateIndex, ElementIndex, ErrorIndex, FunctionIndex, ObjectIndex, RegExpIndex, + StringIndex, SymbolIndex, + }, + ElementsVector, Heap, +}; + +pub(crate) fn heap_gc(heap: &mut Heap) { + let bits = HeapBits::new(heap); + let mut queues = WorkQueues::new(heap); + + heap.globals.iter().for_each(|&value| { + queues.push_value(value); + }); + + while !queues.is_empty() { + let mut arrays: Box<[ArrayIndex]> = queues.arrays.drain(..).collect(); + arrays.sort(); + arrays.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.arrays.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let heap_data = heap.arrays.get(index).unwrap().as_ref().unwrap(); + queues.push_value(Value::Object(heap_data.object_index)); + let ElementsVector { + elements_index, + cap, + .. + } = &heap_data.elements; + match cap { + ElementArrayKey::E4 => queues.e_2_4.push(*elements_index), + ElementArrayKey::E6 => queues.e_2_6.push(*elements_index), + ElementArrayKey::E8 => queues.e_2_8.push(*elements_index), + ElementArrayKey::E10 => queues.e_2_10.push(*elements_index), + ElementArrayKey::E12 => queues.e_2_12.push(*elements_index), + ElementArrayKey::E16 => queues.e_2_16.push(*elements_index), + ElementArrayKey::E24 => queues.e_2_24.push(*elements_index), + ElementArrayKey::E32 => queues.e_2_32.push(*elements_index), + } + } + }); + let mut errors: Box<[ErrorIndex]> = queues.errors.drain(..).collect(); + errors.sort(); + errors.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.errors.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let data = heap.errors.get(index).unwrap().as_ref().unwrap(); + queues.objects.push(data.object_index); + } + }); + let mut functions: Box<[FunctionIndex]> = queues.functions.drain(..).collect(); + functions.sort(); + functions.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.functions.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let data = heap.functions.get(index).unwrap().as_ref().unwrap(); + queues.objects.push(data.object_index); + if let Some(bound) = &data.bound { + bound.iter().for_each(|&value| { + queues.push_value(value); + }) + } + if let Some(visible) = &data.visible { + visible.iter().for_each(|&value| { + queues.push_value(value); + }) + } + } + }); + let mut dates: Box<[DateIndex]> = queues.dates.drain(..).collect(); + dates.sort(); + dates.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.dates.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let data = heap.dates.get(index).unwrap().as_ref().unwrap(); + queues.objects.push(data.object_index); + } + }); + let mut objects: Box<[ObjectIndex]> = queues.objects.drain(..).collect(); + objects.sort(); + objects.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.objects.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let heap_data = heap.objects.get(index).unwrap().as_ref().unwrap(); + let ElementsVector { + elements_index, + cap, + .. + } = &heap_data.keys; + match cap { + ElementArrayKey::E4 => queues.e_2_4.push(*elements_index), + ElementArrayKey::E6 => queues.e_2_6.push(*elements_index), + ElementArrayKey::E8 => queues.e_2_8.push(*elements_index), + ElementArrayKey::E10 => queues.e_2_10.push(*elements_index), + ElementArrayKey::E12 => queues.e_2_12.push(*elements_index), + ElementArrayKey::E16 => queues.e_2_16.push(*elements_index), + ElementArrayKey::E24 => queues.e_2_24.push(*elements_index), + ElementArrayKey::E32 => queues.e_2_32.push(*elements_index), + } + let ElementsVector { + elements_index, + cap, + .. + } = &heap_data.values; + match cap { + ElementArrayKey::E4 => queues.e_2_4.push(*elements_index), + ElementArrayKey::E6 => queues.e_2_6.push(*elements_index), + ElementArrayKey::E8 => queues.e_2_8.push(*elements_index), + ElementArrayKey::E10 => queues.e_2_10.push(*elements_index), + ElementArrayKey::E12 => queues.e_2_12.push(*elements_index), + ElementArrayKey::E16 => queues.e_2_16.push(*elements_index), + ElementArrayKey::E24 => queues.e_2_24.push(*elements_index), + ElementArrayKey::E32 => queues.e_2_32.push(*elements_index), + } + } + }); + let mut regexps: Box<[RegExpIndex]> = queues.regexps.drain(..).collect(); + regexps.sort(); + regexps.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.regexps.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let data = heap.regexps.get(index).unwrap().as_ref().unwrap(); + queues.objects.push(data.object_index); + } + }); + let mut strings: Box<[StringIndex]> = queues.strings.drain(..).collect(); + strings.sort(); + strings.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.strings.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + } + }); + let mut symbols: Box<[SymbolIndex]> = queues.symbols.drain(..).collect(); + symbols.sort(); + symbols.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.symbols.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + let data = heap.symbols.get(index).unwrap().as_ref().unwrap(); + if let Some(string_index) = data.descriptor { + queues.push_value(Value::HeapString(string_index)); + } + } + }); + let mut e_2_4: Box<[ElementIndex]> = queues.e_2_4.drain(..).collect(); + e_2_4.sort(); + e_2_4.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_4.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow4 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_6: Box<[ElementIndex]> = queues.e_2_6.drain(..).collect(); + e_2_6.sort(); + e_2_6.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_6.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow6 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_8: Box<[ElementIndex]> = queues.e_2_8.drain(..).collect(); + e_2_8.sort(); + e_2_8.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_8.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow8 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_10: Box<[ElementIndex]> = queues.e_2_10.drain(..).collect(); + e_2_10.sort(); + e_2_10.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_10.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow10 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_12: Box<[ElementIndex]> = queues.e_2_12.drain(..).collect(); + e_2_12.sort(); + e_2_12.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_12.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow12 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_16: Box<[ElementIndex]> = queues.e_2_16.drain(..).collect(); + e_2_16.sort(); + e_2_16.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_16.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow16 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_24: Box<[ElementIndex]> = queues.e_2_24.drain(..).collect(); + e_2_24.sort(); + e_2_24.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_24.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow24 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + let mut e_2_32: Box<[ElementIndex]> = queues.e_2_32.drain(..).collect(); + e_2_32.sort(); + e_2_32.iter().for_each(|&idx| { + let index = idx.into_index(); + if let Some(marked) = bits.e_2_32.get(index) { + if marked.load(Ordering::Acquire) { + // Already marked, ignore + return; + } + marked.store(true, Ordering::Relaxed); + heap.elements + .e2pow32 + .values + .get(index) + .unwrap() + .unwrap() + .iter() + .for_each(|&value| { + if let Some(value) = value { + queues.push_value(value) + } + }); + } + }); + } + + sweep(heap, &bits); +} + +fn sweep(heap: &mut Heap, bits: &HeapBits) { + bits.e_2_4.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow4.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_6.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow6.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_8.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow8.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_10.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow10.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_12.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow12.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_16.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow16.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_24.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow24.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.e_2_32.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.elements.e2pow32.values.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.arrays.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.arrays.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.bigints.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.bigints.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.dates.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.dates.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.errors.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.errors.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.functions.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.functions.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.numbers.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.numbers.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.objects.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.objects.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.regexps.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.regexps.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.strings.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.strings.get_mut(index).unwrap(); + reference.take(); + } + }); + bits.symbols.iter().enumerate().for_each(|(index, bit)| { + if !bit.load(Ordering::Acquire) { + let reference = heap.symbols.get_mut(index).unwrap(); + reference.take(); + } + }); +} diff --git a/nova_vm/src/heap/heap_trace.rs b/nova_vm/src/heap/heap_trace.rs deleted file mode 100644 index 9b256970..00000000 --- a/nova_vm/src/heap/heap_trace.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::Heap; - -pub trait HeapTrace { - fn trace(&self, heap: &Heap); - - fn root(&self, heap: &Heap); - - fn unroot(&self, heap: &Heap); - - fn finalize(&mut self, heap: &Heap); -} diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs new file mode 100644 index 00000000..7472b552 --- /dev/null +++ b/nova_vm/src/heap/indexes.rs @@ -0,0 +1,140 @@ +use crate::value::Value; + +use super::{ + array::ArrayHeapData, bigint::BigIntHeapData, date::DateHeapData, error::ErrorHeapData, + function::FunctionHeapData, number::NumberHeapData, object::ObjectHeapData, + regexp::RegExpHeapData, string::StringHeapData, symbol::SymbolHeapData, +}; +use core::fmt::Debug; +use std::hash::{Hash, Hasher}; +use std::{marker::PhantomData, mem::size_of, num::NonZeroU32}; + +/// A struct containing a non-zero index into an array or +/// vector of `T`s. Due to the non-zero value, the offset +/// in the vector is offset by one. +/// +/// This index implies a tracing reference count from this +/// struct to T at the given index. +pub(crate) struct BaseIndex(NonZeroU32, PhantomData); + +const _INDEX_SIZE_IS_U32: () = assert!(size_of::>() == size_of::()); +const _OPTION_INDEX_SIZE_IS_U32: () = + assert!(size_of::>>() == size_of::()); + +impl Debug for BaseIndex { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + assert!(self.0.get() != 0); + (&self.0.get() - 1).fmt(f) + } +} + +impl Clone for BaseIndex { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for BaseIndex {} + +impl PartialEq for BaseIndex { + fn eq(&self, other: &Self) -> bool { + self.0 == other.0 + } + + fn ne(&self, other: &Self) -> bool { + !self.eq(other) + } +} + +impl Eq for BaseIndex {} + +impl PartialOrd for BaseIndex { + fn partial_cmp(&self, other: &Self) -> Option { + self.0.partial_cmp(&other.0) + } +} + +impl Ord for BaseIndex { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.0.cmp(&other.0) + } +} + +impl Hash for BaseIndex { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} + +impl BaseIndex { + pub const fn into_index(self) -> usize { + self.0.get() as usize - 1 + } + + pub const fn into_u32_index(self) -> u32 { + self.0.get() as u32 - 1 + } + + pub const fn into_usize(self) -> usize { + self.0.get() as usize + } + + pub const fn into_u32(self) -> u32 { + self.0.get() as u32 + } + + pub const fn from_index(value: usize) -> Self { + let value = value as u32; + assert!(value != u32::MAX); + // SAFETY: Number is not max value and will not overflow to zero. + // This check is done manually to allow const context. + Self( + unsafe { NonZeroU32::new_unchecked(value as u32 + 1) }, + PhantomData, + ) + } + + pub const fn from_u32_index(value: u32) -> Self { + let value = value as u32; + assert!(value != u32::MAX); + // SAFETY: Number is not max value and will not overflow to zero. + // This check is done manually to allow const context. + Self(unsafe { NonZeroU32::new_unchecked(value + 1) }, PhantomData) + } + + pub const fn from_usize(value: usize) -> Self { + let value = value as u32; + assert!(value != 0); + // SAFETY: Number is not zero. + // This check is done manually to allow const context. + Self( + unsafe { NonZeroU32::new_unchecked(value as u32) }, + PhantomData, + ) + } + + pub const fn from_u32(value: u32) -> Self { + let value = value as u32; + assert!(value != 0); + // SAFETY: Number is not zero. + // This check is done manually to allow const context. + Self(unsafe { NonZeroU32::new_unchecked(value) }, PhantomData) + } + + pub fn last(vec: &Vec>) -> Self { + assert!(vec.len() > 0); + Self::from_index(vec.len()) + } +} + +pub(crate) type ArrayIndex = BaseIndex; +pub(crate) type BigIntIndex = BaseIndex; +pub(crate) type DateIndex = BaseIndex; +pub(crate) type ErrorIndex = BaseIndex; +pub(crate) type FunctionIndex = BaseIndex; +pub(crate) type NumberIndex = BaseIndex; +pub(crate) type ObjectIndex = BaseIndex; +pub(crate) type RegExpIndex = BaseIndex; +pub(crate) type StringIndex = BaseIndex; +pub(crate) type SymbolIndex = BaseIndex; +pub(crate) type ElementIndex = BaseIndex<[Option]>; diff --git a/nova_vm/src/heap/math.rs b/nova_vm/src/heap/math.rs index 39e1c750..c8905ad0 100644 --- a/nova_vm/src/heap/math.rs +++ b/nova_vm/src/heap/math.rs @@ -60,7 +60,7 @@ pub(super) fn initialize_math_object(heap: &mut Heap) { ObjectEntry::new_frozen_entry(heap, "SQRT1_2", sqrt1_2), ObjectEntry::new_frozen_entry(heap, "SQRT2", sqrt2), ObjectEntry::new( - PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag as u32), + PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag.into()), PropertyDescriptor::roxh(Value::new_string(heap, "Math")), ), abs, diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index 11305c6b..2792aa85 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -1,30 +1,25 @@ use std::vec; use super::{ - heap_trace::HeapTrace, object::{ObjectEntry, PropertyKey}, Heap, }; use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - FunctionHeapData, HeapBits, ObjectHeapData, PropertyDescriptor, + FunctionHeapData, PropertyDescriptor, }, value::{JsResult, Value}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct NumberHeapData { - pub(super) bits: HeapBits, pub(super) data: f64, } impl NumberHeapData { pub(super) fn new(data: f64) -> NumberHeapData { - NumberHeapData { - bits: HeapBits::new(), - data, - } + NumberHeapData { data } } pub(crate) fn value(&self) -> f64 { @@ -32,120 +27,87 @@ impl NumberHeapData { } } -impl HeapTrace for Option { - fn trace(&self, _heap: &Heap) {} - - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_number_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::NumberConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "EPSILON"), - PropertyDescriptor::roh(Value::from_f64(heap, f64::EPSILON)), - ), - ObjectEntry::new_prototype_function_entry(heap, "isFinite", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "isInteger", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "isNan", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "isSafeInteger", - 1, - false, - number_todo, - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "MAX_SAFE_INTEGER"), - PropertyDescriptor::roh(Value::from_f64(heap, 9007199254740991.0)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "MAX_VALUE"), - PropertyDescriptor::roh(Value::from_f64(heap, f64::MAX)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "MIN_SAFE_INTEGER"), - PropertyDescriptor::roh(Value::from_f64(heap, -9007199254740991.0)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "MIN_VALUE"), - PropertyDescriptor::roh(Value::from_f64(heap, f64::MIN)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "NaN"), - PropertyDescriptor::roh(Value::NaN), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "NEGATIVE_INFINITY"), - PropertyDescriptor::roh(Value::NegativeInfinity), - ), - ObjectEntry::new_prototype_function_entry( - heap, - "parseFloat", - 1, - false, - number_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "parseInt", 2, false, number_todo), - ObjectEntry::new( - PropertyKey::from_str(heap, "POSITIVE_INFINITY"), - PropertyDescriptor::roh(Value::Infinity), - ), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::NumberPrototypeIndex as u32, - ), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::NumberConstructorIndex) as usize] = + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "EPSILON"), + PropertyDescriptor::roh(Value::from_f64(heap, f64::EPSILON)), + ), + ObjectEntry::new_prototype_function_entry(heap, "isFinite", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "isInteger", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "isNan", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "isSafeInteger", 1, false, number_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "MAX_SAFE_INTEGER"), + PropertyDescriptor::roh(Value::from_f64(heap, 9007199254740991.0)), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "MAX_VALUE"), + PropertyDescriptor::roh(Value::from_f64(heap, f64::MAX)), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "MIN_SAFE_INTEGER"), + PropertyDescriptor::roh(Value::from_f64(heap, -9007199254740991.0)), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "MIN_VALUE"), + PropertyDescriptor::roh(Value::from_f64(heap, f64::MIN)), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "NaN"), + PropertyDescriptor::roh(Value::NaN), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "NEGATIVE_INFINITY"), + PropertyDescriptor::roh(Value::NegativeInfinity), + ), + ObjectEntry::new_prototype_function_entry(heap, "parseFloat", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "parseInt", 2, false, number_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "POSITIVE_INFINITY"), + PropertyDescriptor::roh(Value::Infinity), + ), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::NumberPrototypeIndex.into(), + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::NumberConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::NumberConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::NumberConstructorIndex as u32, + object_index: BuiltinObjectIndexes::NumberConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: number_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::NumberPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::NumberConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "toPrecision", 1, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, number_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::NumberPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::NumberConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "toLocaleString", - 0, - false, - number_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "toPrecision", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, number_todo), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn number_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 6bdbd8d0..8ab5c754 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -2,18 +2,22 @@ use crate::{ heap::{ function::JsBindingFunction, heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - heap_trace::HeapTrace, - FunctionHeapData, Heap, HeapBits, + FunctionHeapData, Heap, }, stack_string::StackString, - value::{FunctionIndex, JsResult, StringIndex, SymbolIndex, Value}, + value::{JsResult, Value}, +}; +use std::{fmt::Debug, vec}; + +use super::{ + element_array::ElementsVector, + indexes::{FunctionIndex, ObjectIndex, StringIndex, SymbolIndex}, }; -use std::fmt::Debug; #[derive(Debug)] pub struct ObjectEntry { - key: PropertyKey, - value: PropertyDescriptor, + pub(crate) key: PropertyKey, + pub(crate) value: PropertyDescriptor, } impl ObjectEntry { @@ -43,7 +47,7 @@ impl ObjectEntry { pub(crate) fn new_prototype_symbol_function_entry( heap: &mut Heap, name: &str, - symbol_index: u32, + symbol_index: SymbolIndex, length: u8, uses_arguments: bool, binding: JsBindingFunction, @@ -55,7 +59,7 @@ impl ObjectEntry { ObjectEntry { key, value } } - pub(crate) fn new_constructor_prototype_entry(heap: &mut Heap, idx: u32) -> Self { + pub(crate) fn new_constructor_prototype_entry(heap: &mut Heap, idx: ObjectIndex) -> Self { ObjectEntry { key: PropertyKey::from_str(heap, "prototype"), value: PropertyDescriptor::Data { @@ -125,7 +129,7 @@ pub enum PropertyDescriptor { impl PropertyDescriptor { #[inline(always)] - pub const fn prototype_slot(idx: u32) -> Self { + pub const fn prototype_slot(idx: ObjectIndex) -> Self { Self::Data { value: Value::Object(idx), writable: false, @@ -219,9 +223,8 @@ impl PropertyDescriptor { } } -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct ObjectHeapData { - pub(crate) bits: HeapBits, pub(crate) _extensible: bool, // TODO: It's probably not necessary to have a whole data descriptor here. // A prototype can only be set to be null or an object, meaning that most of the @@ -229,14 +232,19 @@ pub(crate) struct ObjectHeapData { // We could possibly do with just a `Option` but it would cause issues // with functions and possible other special object cases we want to track with partially // separate heap fields later down the line. - pub(crate) prototype: PropertyDescriptor, - pub(crate) entries: Vec, + pub(crate) prototype: Value, + pub(crate) keys: ElementsVector, + pub(crate) values: ElementsVector, } impl ObjectHeapData { - pub fn new(extensible: bool, prototype: PropertyDescriptor, entries: Vec) -> Self { + pub fn new( + extensible: bool, + prototype: Value, + keys: ElementsVector, + values: ElementsVector, + ) -> Self { Self { - bits: HeapBits::new(), _extensible: extensible, // TODO: Number, Boolean, etc. objects exist. These can all be // modeled with their own heap vector or alternatively by adding @@ -245,232 +253,112 @@ impl ObjectHeapData { // Number objects etc the field is Some(Value). // TODO: Move prototype and key vector into shapes prototype, - // TODO: Separate entries into key and value vectors - // TODO: Use SmallVec<[T; 4]> - entries, - } - } -} - -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - let data = self.as_ref().unwrap(); - let dirty = data.bits.dirty.replace(false); - let marked = data.bits.marked.replace(true); - if marked && !dirty { - // Do not keep recursing into already-marked heap values. - return; + // TODO: Consider using SmallVec<[Option; 3]> or such? + keys, + values, } - match &data.prototype { - PropertyDescriptor::Data { value, .. } => value.trace(heap), - PropertyDescriptor::Blocked { .. } => {} - PropertyDescriptor::ReadOnly { get, .. } => { - heap.functions[*get as usize].trace(heap); - } - PropertyDescriptor::WriteOnly { set, .. } => { - heap.functions[*set as usize].trace(heap); - } - PropertyDescriptor::ReadWrite { get, set, .. } => { - heap.functions[*get as usize].trace(heap); - heap.functions[*set as usize].trace(heap); - } - } - for reference in data.entries.iter() { - match reference.key { - PropertyKey::SmallAsciiString(_) | PropertyKey::Smi(_) => {} - PropertyKey::String(idx) => heap.strings[idx as usize].trace(heap), - PropertyKey::Symbol(idx) => heap.symbols[idx as usize].trace(heap), - } - match &reference.value { - PropertyDescriptor::Data { value, .. } => value.trace(heap), - PropertyDescriptor::Blocked { .. } => {} - PropertyDescriptor::ReadOnly { get, .. } => { - heap.functions[*get as usize].trace(heap); - } - PropertyDescriptor::WriteOnly { set, .. } => { - heap.functions[*set as usize].trace(heap); - } - PropertyDescriptor::ReadWrite { get, set, .. } => { - heap.functions[*get as usize].trace(heap); - heap.functions[*set as usize].trace(heap); - } - } - } - } - - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); } } pub fn initialize_object_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::ObjectConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new_prototype_function_entry(heap, "assign", 1, true, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "create", 2, false, object_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "defineProperties", - 2, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "defineProperty", - 3, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "entries", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "freeze", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "fromEntries", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "getOwnPropertyDescriptor", - 2, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "getOwnPropertyDescriptors", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "getOwnPropertyNames", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "getOwnPropertySymbols", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "getPrototypeOf", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "hasOwn", 2, false, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "is", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "isExtensible", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "isFrozen", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "isSealed", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "keys", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "preventExtensions", - 1, - false, - object_todo, - ), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::ObjectPrototypeIndex as u32, - ), - ObjectEntry::new_prototype_function_entry(heap, "seal", 1, false, object_todo), - ObjectEntry::new_prototype_function_entry( - heap, - "setPrototypeOf", - 2, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "values", 1, false, object_todo), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::ObjectConstructorIndex) as usize] = + let entries = vec![ + ObjectEntry::new_prototype_function_entry(heap, "assign", 1, true, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "create", 2, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "defineProperties", 2, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "defineProperty", 3, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "entries", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "freeze", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "fromEntries", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry( + heap, + "getOwnPropertyDescriptor", + 2, + false, + object_todo, + ), + ObjectEntry::new_prototype_function_entry( + heap, + "getOwnPropertyDescriptors", + 1, + false, + object_todo, + ), + ObjectEntry::new_prototype_function_entry( + heap, + "getOwnPropertyNames", + 1, + false, + object_todo, + ), + ObjectEntry::new_prototype_function_entry( + heap, + "getOwnPropertySymbols", + 1, + false, + object_todo, + ), + ObjectEntry::new_prototype_function_entry(heap, "getPrototypeOf", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "hasOwn", 2, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "is", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "isExtensible", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "isFrozen", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "isSealed", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "keys", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "preventExtensions", 1, false, object_todo), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + ), + ObjectEntry::new_prototype_function_entry(heap, "seal", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "setPrototypeOf", 2, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "values", 1, false, object_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ObjectConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::ObjectConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::ObjectConstructorIndex as u32, + object_index: BuiltinObjectIndexes::ObjectConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: object_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::ObjectConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::roh(Value::Null), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::ObjectConstructorIndex, - ))), - ), - ObjectEntry::new_prototype_function_entry( - heap, - "hasOwnProperty", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "isPrototypeOf", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "propertyIsEnumerable", - 1, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry( - heap, - "toLocaleString", - 0, - false, - object_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, object_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, object_todo), - ], - )); + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::ObjectConstructorIndex, + ))), + ), + ObjectEntry::new_prototype_function_entry(heap, "hasOwnProperty", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "isPrototypeOf", 1, false, object_todo), + ObjectEntry::new_prototype_function_entry( + heap, + "propertyIsEnumerable", + 1, + false, + object_todo, + ), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, object_todo), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, object_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::ObjectConstructorIndex, + true, + Value::Null, + entries, + ); } fn object_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::Object(0)) + Ok(Value::Object(ObjectIndex::from_index(0))) } fn object_todo(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index 40e3193f..c23cd1d7 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -1,7 +1,7 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + Heap, PropertyDescriptor, }, value::{JsResult, Value}, }; @@ -9,129 +9,111 @@ use crate::{ use super::{ function::FunctionHeapData, heap_constants::WellKnownSymbolIndexes, - heap_trace::HeapTrace, + indexes::{FunctionIndex, ObjectIndex}, object::{ObjectEntry, PropertyKey}, }; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct RegExpHeapData { - pub(super) bits: HeapBits, - pub(super) object_index: u32, + pub(super) object_index: ObjectIndex, // pub(super) _regex: RegExp, } -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - heap.objects[self.as_ref().unwrap().object_index as usize].trace(heap); - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_regexp_heap(heap: &mut Heap) { let species_function_name = Value::new_string(heap, "get [Symbol.species]"); - heap.objects[BuiltinObjectIndexes::RegExpConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::RegExpPrototypeIndex as u32, - ), - ObjectEntry::new( - PropertyKey::Symbol(WellKnownSymbolIndexes::Species as u32), - PropertyDescriptor::ReadOnly { - get: heap.create_function(species_function_name, 0, false, regexp_species), - enumerable: false, - configurable: true, - }, - ), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::RegExpConstructorIndex) as usize] = + let entries = vec![ + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::RegExpPrototypeIndex.into(), + ), + ObjectEntry::new( + PropertyKey::Symbol(WellKnownSymbolIndexes::Species.into()), + PropertyDescriptor::ReadOnly { + get: heap.create_function(species_function_name, 0, false, regexp_species), + enumerable: false, + configurable: true, + }, + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::RegExpConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::RegExpConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::RegExpConstructorIndex as u32, + object_index: BuiltinObjectIndexes::RegExpConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: regexp_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::RegExpPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::RegExpConstructorIndex, + ))), + ), + // TODO: Write out all the getters + ObjectEntry::new_prototype_function_entry(heap, "exec", 1, false, regexp_todo), + // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.match]", + WellKnownSymbolIndexes::Match.into(), + 1, + false, + regexp_todo, + ), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.matchAll]", + WellKnownSymbolIndexes::MatchAll.into(), + 1, + false, + regexp_todo, + ), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.replace]", + WellKnownSymbolIndexes::Replace.into(), + 2, + false, + regexp_todo, + ), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.search]", + WellKnownSymbolIndexes::Search.into(), + 1, + false, + regexp_todo, + ), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.split]", + WellKnownSymbolIndexes::Split.into(), + 2, + false, + regexp_todo, + ), + ObjectEntry::new_prototype_function_entry(heap, "test", 1, false, regexp_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, regexp_todo), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::RegExpPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::RegExpConstructorIndex, - ))), - ), - // TODO: Write out all the getters - ObjectEntry::new_prototype_function_entry(heap, "exec", 1, false, regexp_todo), - // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.match]", - WellKnownSymbolIndexes::Match as u32, - 1, - false, - regexp_todo, - ), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.matchAll]", - WellKnownSymbolIndexes::MatchAll as u32, - 1, - false, - regexp_todo, - ), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.replace]", - WellKnownSymbolIndexes::Replace as u32, - 2, - false, - regexp_todo, - ), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.search]", - WellKnownSymbolIndexes::Search as u32, - 1, - false, - regexp_todo, - ), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.split]", - WellKnownSymbolIndexes::Split as u32, - 2, - false, - regexp_todo, - ), - ObjectEntry::new_prototype_function_entry(heap, "test", 1, false, regexp_todo), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, regexp_todo), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn regexp_constructor_binding(_heap: &mut Heap, _this: Value, _args: &[Value]) -> JsResult { - Ok(Value::Function(0)) + Ok(Value::Function(FunctionIndex::from_index(0))) } fn regexp_species(_heap: &mut Heap, this: Value, _args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 78b7a330..8d8a8814 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -1,23 +1,20 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, - heap_trace::HeapTrace, - FunctionHeapData, Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + FunctionHeapData, Heap, }, value::{JsResult, Value}, }; use wtf8::Wtf8Buf; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct StringHeapData { - pub(crate) bits: HeapBits, pub(crate) data: Wtf8Buf, } impl StringHeapData { pub fn from_str(str: &str) -> Self { StringHeapData { - bits: HeapBits::new(), data: Wtf8Buf::from_str(str), } } @@ -28,48 +25,31 @@ impl StringHeapData { } } -impl HeapTrace for Option { - fn trace(&self, _heap: &Heap) {} - - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_string_heap(heap: &mut Heap) { - heap.objects[BuiltinObjectIndexes::StringConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - // TODO: Methods and properties - Vec::with_capacity(0), - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::StringConstructorIndex) as usize] = + heap.insert_builtin_object( + BuiltinObjectIndexes::StringConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + // TODO: Methods and properties + Vec::with_capacity(0), + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::StringConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::StringConstructorIndex as u32, + object_index: BuiltinObjectIndexes::StringConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: string_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::StringPrototypeIndex as usize] = Some(ObjectHeapData::new( + heap.insert_builtin_object( + BuiltinObjectIndexes::StringPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), // TODO: Methods and properties Vec::with_capacity(0), - )); + ); } fn string_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index bab3daf2..86a5c575 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -1,235 +1,194 @@ use crate::{ heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes, WellKnownSymbolIndexes}, - heap_trace::HeapTrace, - FunctionHeapData, Heap, HeapBits, ObjectHeapData, PropertyDescriptor, + FunctionHeapData, Heap, PropertyDescriptor, }, - value::{JsResult, StringIndex, Value}, + value::{JsResult, Value}, }; -use super::object::{ObjectEntry, PropertyKey}; +use super::{ + indexes::{FunctionIndex, StringIndex, SymbolIndex}, + object::{ObjectEntry, PropertyKey}, +}; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub(crate) struct SymbolHeapData { - pub(super) bits: HeapBits, pub(super) descriptor: Option, } -impl HeapTrace for Option { - fn trace(&self, heap: &Heap) { - assert!(self.is_some()); - if let Some(idx) = self.as_ref().unwrap().descriptor { - heap.strings[idx as usize].trace(heap); - } - } - fn root(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.root(); - } - - fn unroot(&self, _heap: &Heap) { - assert!(self.is_some()); - self.as_ref().unwrap().bits.unroot(); - } - - fn finalize(&mut self, _heap: &Heap) { - self.take(); - } -} - pub fn initialize_symbol_heap(heap: &mut Heap) { // AsyncIterator heap.symbols[WellKnownSymbolIndexes::AsyncIterator as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.asyncIterator")), }); // HasInstance heap.symbols[WellKnownSymbolIndexes::HasInstance as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.hasInstance")), }); // IsConcatSpreadable heap.symbols[WellKnownSymbolIndexes::IsConcatSpreadable as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.isConcatSpreadable")), }); // Iterator heap.symbols[WellKnownSymbolIndexes::Iterator as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.iterator")), }); // Match heap.symbols[WellKnownSymbolIndexes::Match as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.match")), }); // MatchAll heap.symbols[WellKnownSymbolIndexes::MatchAll as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.matchAll")), }); // Replace heap.symbols[WellKnownSymbolIndexes::Replace as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.replace")), }); // Search heap.symbols[WellKnownSymbolIndexes::Search as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.search")), }); // Species heap.symbols[WellKnownSymbolIndexes::Species as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.species")), }); // Split heap.symbols[WellKnownSymbolIndexes::Split as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.split")), }); // ToPrimitive heap.symbols[WellKnownSymbolIndexes::ToPrimitive as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.toPrimitive")), }); // ToStringTag heap.symbols[WellKnownSymbolIndexes::ToStringTag as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.toStringTag")), }); // Unscopables heap.symbols[WellKnownSymbolIndexes::Unscopables as usize] = Some(SymbolHeapData { - bits: HeapBits::new(), descriptor: Some(heap.alloc_string("Symbol.unscopables")), }); - heap.objects[BuiltinObjectIndexes::SymbolConstructorIndex as usize] = - Some(ObjectHeapData::new( - true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::FunctionPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "asyncIterator"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::AsyncIterator as u32, - )), - ), - ObjectEntry::new_prototype_function_entry(heap, "for", 1, false, symbol_todo), - ObjectEntry::new( - PropertyKey::from_str(heap, "hasInstance"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::HasInstance as u32, - )), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "isConcatSpreadable"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::IsConcatSpreadable as u32, - )), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "iterator"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Iterator as u32)), - ), - ObjectEntry::new_prototype_function_entry(heap, "keyFor", 1, false, symbol_todo), - ObjectEntry::new( - PropertyKey::from_str(heap, "Match"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Match as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "MatchAll"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::MatchAll as u32)), - ), - ObjectEntry::new_constructor_prototype_entry( - heap, - BuiltinObjectIndexes::SymbolPrototypeIndex as u32, - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "Replace"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Replace as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "Search"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Search as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "Species"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Species as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "Split"), - PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Split as u32)), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "ToPrimitive"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::ToPrimitive as u32, - )), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "ToStringTag"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::ToStringTag as u32, - )), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "Unscopables"), - PropertyDescriptor::roh(Value::Symbol( - WellKnownSymbolIndexes::Unscopables as u32, - )), - ), - ], - )); - heap.functions[get_constructor_index(BuiltinObjectIndexes::SymbolConstructorIndex) as usize] = + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "asyncIterator"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::AsyncIterator.into())), + ), + ObjectEntry::new_prototype_function_entry(heap, "for", 1, false, symbol_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "hasInstance"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::HasInstance.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "isConcatSpreadable"), + PropertyDescriptor::roh(Value::Symbol( + WellKnownSymbolIndexes::IsConcatSpreadable.into(), + )), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "iterator"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Iterator.into())), + ), + ObjectEntry::new_prototype_function_entry(heap, "keyFor", 1, false, symbol_todo), + ObjectEntry::new( + PropertyKey::from_str(heap, "Match"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Match.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "MatchAll"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::MatchAll.into())), + ), + ObjectEntry::new_constructor_prototype_entry( + heap, + BuiltinObjectIndexes::SymbolPrototypeIndex.into(), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "Replace"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Replace.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "Search"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Search.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "Species"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Species.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "Split"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Split.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "ToPrimitive"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::ToPrimitive.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "ToStringTag"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::ToStringTag.into())), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "Unscopables"), + PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Unscopables.into())), + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::SymbolConstructorIndex, + true, + Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + entries, + ); + heap.functions + [get_constructor_index(BuiltinObjectIndexes::SymbolConstructorIndex).into_index()] = Some(FunctionHeapData { - bits: HeapBits::new(), - object_index: BuiltinObjectIndexes::SymbolConstructorIndex as u32, + object_index: BuiltinObjectIndexes::SymbolConstructorIndex.into(), length: 1, uses_arguments: false, bound: None, visible: None, binding: symbol_constructor_binding, }); - heap.objects[BuiltinObjectIndexes::SymbolPrototypeIndex as usize] = Some(ObjectHeapData::new( + let entries = vec![ + ObjectEntry::new( + PropertyKey::from_str(heap, "constructor"), + PropertyDescriptor::rwx(Value::Function(get_constructor_index( + BuiltinObjectIndexes::SymbolConstructorIndex, + ))), + ), + ObjectEntry::new( + PropertyKey::from_str(heap, "description"), + // TODO: create description getter function + PropertyDescriptor::ReadOnly { + get: FunctionIndex::from_index(0), + enumerable: false, + configurable: true, + }, + ), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, symbol_todo), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, symbol_todo), + ObjectEntry::new_prototype_symbol_function_entry( + heap, + "[Symbol.toPrimitive]", + WellKnownSymbolIndexes::ToPrimitive.into(), + 1, + false, + symbol_todo, + ), + ObjectEntry::new( + PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag.into()), + PropertyDescriptor::roxh(Value::new_string(heap, "Symbol")), + ), + ]; + heap.insert_builtin_object( + BuiltinObjectIndexes::SymbolPrototypeIndex, true, - PropertyDescriptor::prototype_slot(BuiltinObjectIndexes::ObjectPrototypeIndex as u32), - vec![ - ObjectEntry::new( - PropertyKey::from_str(heap, "constructor"), - PropertyDescriptor::rwx(Value::Function(get_constructor_index( - BuiltinObjectIndexes::SymbolConstructorIndex, - ))), - ), - ObjectEntry::new( - PropertyKey::from_str(heap, "description"), - // TODO: create description getter function - PropertyDescriptor::ReadOnly { - get: 0, - enumerable: false, - configurable: true, - }, - ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, symbol_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, symbol_todo), - ObjectEntry::new_prototype_symbol_function_entry( - heap, - "[Symbol.toPrimitive]", - WellKnownSymbolIndexes::ToPrimitive as u32, - 1, - false, - symbol_todo, - ), - ObjectEntry::new( - PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag as u32), - PropertyDescriptor::roxh(Value::new_string(heap, "Symbol")), - ), - ], - )); + Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + entries, + ); } fn symbol_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::Symbol(0)) + Ok(Value::Symbol(SymbolIndex::from_index(0))) } fn symbol_todo(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { diff --git a/nova_vm/src/lib.rs b/nova_vm/src/lib.rs index 422922e9..9ad9ea63 100644 --- a/nova_vm/src/lib.rs +++ b/nova_vm/src/lib.rs @@ -15,6 +15,8 @@ use oxc_ast::{ use std::fmt::Debug; use value::{JsResult, Value}; +use crate::heap::indexes::StringIndex; + /// https://tc39.es/ecma262/multipage/ecmascript-data-types-and-values.html#sec-ecmascript-language-types #[derive(Debug, Clone, Copy, PartialEq)] pub enum Type { @@ -113,7 +115,7 @@ impl<'a> VM<'a> { } Instruction::LoadString => { let addr = *iter.next().unwrap() as usize; - memory[addr] = Value::HeapString(*iter.next().unwrap() as u32); + memory[addr] = Value::HeapString(StringIndex::from_u32(*iter.next().unwrap())); } Instruction::CopyValue => { let addr = *iter.next().unwrap() as usize; @@ -310,7 +312,7 @@ impl<'a> VM<'a> { self.instructions.push(Instruction::LoadString.into()); self.instructions.push(addr); - self.instructions.push(string_idx as u32); + self.instructions.push(string_idx.into_u32()); } Expression::ParenthesizedExpression(data) => { return self.build_expr(addr, &data.expression, env); diff --git a/nova_vm/src/value.rs b/nova_vm/src/value.rs index 9d700094..d0f57700 100644 --- a/nova_vm/src/value.rs +++ b/nova_vm/src/value.rs @@ -1,19 +1,17 @@ -use crate::{heap::Heap, stack_string::StackString, Type, VM}; +use crate::{ + heap::{ + indexes::{ + ArrayIndex, BigIntIndex, DateIndex, ErrorIndex, FunctionIndex, NumberIndex, + ObjectIndex, RegExpIndex, StringIndex, SymbolIndex, + }, + Heap, + }, + stack_string::StackString, + Type, VM, +}; use std::{fmt::Debug, mem::size_of}; -// TODO(@aapoalas): Use transparent struct (u32)'s to ensure proper indexing. -pub type ArrayIndex = u32; -pub type BigIntIndex = u32; -pub type DateIndex = u32; -pub type ErrorIndex = u32; -pub type FunctionIndex = u32; -pub type NumberIndex = u32; -pub type ObjectIndex = u32; -pub type RegExpIndex = u32; -pub type StringIndex = u32; -pub type SymbolIndex = u32; - -#[derive(Clone)] +#[derive(Clone, Copy)] #[repr(u8)] pub enum Value { Array(ArrayIndex), @@ -123,13 +121,13 @@ impl Value { Value::SmallBigInt(that) | Value::Smi(that), ) => *this == *that as u32, (&Value::HeapBigInt(x), &Value::HeapNumber(y)) => { - let big_int = &vm.heap.bigints[x as usize]; - let number = &vm.heap.numbers[y as usize]; + let big_int = &vm.heap.bigints[x.into_index()]; + let number = &vm.heap.numbers[y.into_index()]; big_int.as_ref().unwrap().try_into_f64() == Some(number.as_ref().unwrap().value()) } (&Value::HeapNumber(x), &Value::HeapBigInt(y)) => { - let big_int = &vm.heap.bigints[y as usize]; - let number = &vm.heap.numbers[x as usize]; + let big_int = &vm.heap.bigints[y.into_index()]; + let number = &vm.heap.numbers[x.into_index()]; big_int.as_ref().unwrap().try_into_f64() == Some(number.as_ref().unwrap().value()) } (Value::HeapNumber(_), Value::HeapString(_)) => todo!("use ToNumber() intrinsics"), @@ -184,8 +182,8 @@ impl Value { (Value::HeapNumber(n1), Value::HeapNumber(n2)) => { n1 == n2 - || vm.heap.numbers[*n1 as usize].as_ref().unwrap().value() - == vm.heap.numbers[*n2 as usize].as_ref().unwrap().value() + || vm.heap.numbers[n1.into_index()].as_ref().unwrap().value() + == vm.heap.numbers[n2.into_index()].as_ref().unwrap().value() } // https://tc39.es/ecma262/multipage/abstract-operations.html#sec-samevaluenonnumber @@ -193,8 +191,8 @@ impl Value { (Value::HeapBigInt(n1), Value::HeapBigInt(n2)) => n1 == n2, (Value::HeapString(s1), Value::HeapString(s2)) => { s1 == s2 - || vm.heap.strings[*s1 as usize].as_ref().unwrap().data - == vm.heap.strings[*s2 as usize].as_ref().unwrap().data + || vm.heap.strings[s1.into_index()].as_ref().unwrap().data + == vm.heap.strings[s2.into_index()].as_ref().unwrap().data } (Value::Boolean(b1), Value::Boolean(b2)) => b1 == b2, // TODO: implement x is y procedures @@ -263,7 +261,7 @@ impl Value { pub fn try_into_f64(&self, vm: &mut VM) -> JsResult { match self { - &Value::HeapNumber(n) => Ok(vm.heap.numbers[n as usize].as_ref().unwrap().value()), + &Value::HeapNumber(n) => Ok(vm.heap.numbers[n.into_index()].as_ref().unwrap().value()), &Value::Smi(n) => Ok(n as f64), &Value::SmiU(n) => Ok(n as f64), Value::Infinity => Ok(f64::INFINITY),