From c339f75e0ab9f36860a6c66e9b252b7da60e7a20 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 10:39:48 -0500 Subject: [PATCH 01/32] feat: engine setup This is greatly inspired by Kiesel and SerenetyOS's LibJS JavaScript engines. --- nova_cli/src/main.rs | 20 +- nova_vm/Cargo.toml | 9 +- nova_vm/src/builtins.rs | 6 + nova_vm/src/builtins/array.rs | 25 + nova_vm/src/builtins/builtin_function.rs | 96 ++++ nova_vm/src/dyn_ObjectHeapData | 40 -- nova_vm/src/execution.rs | 13 + nova_vm/src/execution/agent.rs | 44 ++ nova_vm/src/execution/default_host_hooks.rs | 15 + nova_vm/src/execution/environments.rs | 25 + .../environments/declarative_environment.rs | 29 ++ .../environments/function_environment.rs | 37 ++ .../environments/global_environment.rs | 23 + .../environments/object_environment.rs | 16 + .../environments/private_environment.rs | 12 + nova_vm/src/execution/execution_context.rs | 42 ++ nova_vm/src/execution/realm.rs | 27 + nova_vm/src/execution/realm/intrinsics.rs | 144 ++++++ nova_vm/src/heap.rs | 71 +-- nova_vm/src/heap/array.rs | 5 +- nova_vm/src/heap/bigint.rs | 46 +- nova_vm/src/heap/boolean.rs | 3 +- nova_vm/src/heap/date.rs | 3 +- nova_vm/src/heap/element_array.rs | 8 +- nova_vm/src/heap/error.rs | 7 +- nova_vm/src/heap/function.rs | 3 +- nova_vm/src/heap/heap_bits.rs | 30 +- nova_vm/src/heap/heap_gc.rs | 4 +- nova_vm/src/heap/indexes.rs | 2 +- nova_vm/src/heap/math.rs | 4 +- nova_vm/src/heap/number.rs | 13 +- nova_vm/src/heap/object.rs | 19 +- nova_vm/src/heap/regexp.rs | 5 +- nova_vm/src/heap/string.rs | 10 +- nova_vm/src/heap/symbol.rs | 5 +- nova_vm/src/language.rs | 4 + nova_vm/src/language/bytecode.rs | 7 + nova_vm/src/language/bytecode/executable.rs | 70 +++ nova_vm/src/language/bytecode/instructions.rs | 198 ++++++++ nova_vm/src/language/bytecode/vm.rs | 60 +++ nova_vm/src/language/script.rs | 158 ++++++ nova_vm/src/lib.rs | 475 +----------------- nova_vm/src/small_integer.rs | 53 ++ nova_vm/src/small_string.rs | 45 ++ nova_vm/src/stack_string.rs | 95 ---- nova_vm/src/types.rs | 17 + nova_vm/src/types/language.rs | 3 + nova_vm/src/types/language/value.rs | 167 ++++++ nova_vm/src/types/spec.rs | 3 + nova_vm/src/types/spec/bytecode.rs | 7 + nova_vm/src/types/spec/reference.rs | 66 +++ nova_vm/src/value.rs | 368 -------------- 52 files changed, 1555 insertions(+), 1102 deletions(-) create mode 100644 nova_vm/src/builtins.rs create mode 100644 nova_vm/src/builtins/array.rs create mode 100644 nova_vm/src/builtins/builtin_function.rs delete mode 100644 nova_vm/src/dyn_ObjectHeapData create mode 100644 nova_vm/src/execution.rs create mode 100644 nova_vm/src/execution/agent.rs create mode 100644 nova_vm/src/execution/default_host_hooks.rs create mode 100644 nova_vm/src/execution/environments.rs create mode 100644 nova_vm/src/execution/environments/declarative_environment.rs create mode 100644 nova_vm/src/execution/environments/function_environment.rs create mode 100644 nova_vm/src/execution/environments/global_environment.rs create mode 100644 nova_vm/src/execution/environments/object_environment.rs create mode 100644 nova_vm/src/execution/environments/private_environment.rs create mode 100644 nova_vm/src/execution/execution_context.rs create mode 100644 nova_vm/src/execution/realm.rs create mode 100644 nova_vm/src/execution/realm/intrinsics.rs create mode 100644 nova_vm/src/language.rs create mode 100644 nova_vm/src/language/bytecode.rs create mode 100644 nova_vm/src/language/bytecode/executable.rs create mode 100644 nova_vm/src/language/bytecode/instructions.rs create mode 100644 nova_vm/src/language/bytecode/vm.rs create mode 100644 nova_vm/src/language/script.rs create mode 100644 nova_vm/src/small_integer.rs create mode 100644 nova_vm/src/small_string.rs delete mode 100644 nova_vm/src/stack_string.rs create mode 100644 nova_vm/src/types.rs create mode 100644 nova_vm/src/types/language.rs create mode 100644 nova_vm/src/types/language/value.rs create mode 100644 nova_vm/src/types/spec.rs create mode 100644 nova_vm/src/types/spec/bytecode.rs create mode 100644 nova_vm/src/types/spec/reference.rs diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index 04ae9b6e..bb221fa8 100644 --- a/nova_cli/src/main.rs +++ b/nova_cli/src/main.rs @@ -1,5 +1,5 @@ use clap::{Parser as ClapParser, Subcommand}; -use nova_vm::{heap::Heap, VM}; +use nova_vm::heap::Heap; use oxc_ast::SourceType; use oxc_parser::Parser; @@ -47,16 +47,16 @@ fn main() -> Result<(), Box> { let parser = Parser::new(&allocator, &file, source_type.with_typescript(false)); let result = parser.parse(); - let mut vm = VM { - source: &file, - pc: 0, - instructions: Vec::new(), - heap: Heap::new(), - }; + // let mut vm = VM { + // source: &file, + // pc: 0, + // instructions: Vec::new(), + // heap: Heap::new(), + // }; - vm.load_program(result.program); - println!("{:?}", vm.instructions); - vm.interpret(); + // vm.load_program(result.program); + // println!("{:?}", vm.instructions); + // vm.interpret(); } } diff --git a/nova_vm/Cargo.toml b/nova_vm/Cargo.toml index bf852d5f..ee98d66c 100644 --- a/nova_vm/Cargo.toml +++ b/nova_vm/Cargo.toml @@ -8,5 +8,10 @@ edition = "2021" [dependencies] gc = { version = "0.4", features = ["derive"] } wtf8 = "0.1" -oxc_parser = "0.0.6" -oxc_ast = "0.0.6" +oxc_parser = "0.0.7" +oxc_span = "0.0.7" +oxc_ast = "0.0.7" +oxc_allocator = "0.0.7" +oxc_diagnostics = "0.0.7" +num-bigint-dig = "0.8" +small_vec = "0.1" diff --git a/nova_vm/src/builtins.rs b/nova_vm/src/builtins.rs new file mode 100644 index 00000000..a999825a --- /dev/null +++ b/nova_vm/src/builtins.rs @@ -0,0 +1,6 @@ +mod array; +mod builtin_function; + +pub use builtin_function::{ + create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, +}; diff --git a/nova_vm/src/builtins/array.rs b/nova_vm/src/builtins/array.rs new file mode 100644 index 00000000..f7336d2a --- /dev/null +++ b/nova_vm/src/builtins/array.rs @@ -0,0 +1,25 @@ +use super::{create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs}; +use crate::{ + execution::{Agent, JsResult, Realm}, + types::{Object, Value}, +}; + +struct ArrayConstructor; + +impl Builtin for ArrayConstructor { + fn create(realm: &mut Realm) -> Object { + let object = create_builtin_function( + &mut realm.agent.clone().borrow_mut(), + Behaviour::Regular(Self::behaviour), + BuiltinFunctionArgs::new(1, "Array", realm), + ); + + object + } +} + +impl ArrayConstructor { + fn behaviour(agent: &mut Agent, value: Value, arguments: ArgumentsList) -> JsResult { + todo!(); + } +} diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs new file mode 100644 index 00000000..3e823cc4 --- /dev/null +++ b/nova_vm/src/builtins/builtin_function.rs @@ -0,0 +1,96 @@ +use crate::{ + execution::{Agent, JsResult, Realm}, + types::{Object, Value}, +}; + +#[derive(Debug)] +pub struct ArgumentsList; + +type RegularFn = fn(&mut Agent, Value, ArgumentsList) -> JsResult; +type ConstructorFn = fn(&mut Agent, Value, ArgumentsList, Option) -> JsResult; + +#[derive(Debug)] +pub enum Behaviour { + Regular(RegularFn), + Constructor(ConstructorFn), +} + +pub trait Builtin { + fn create(realm: &mut Realm) -> Object; +} + +#[derive(Debug, Default)] +pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> { + pub length: u32, + pub name: &'static str, + pub realm: Option<&'a mut Realm<'ctx, 'host>>, + pub prototype: Option, + pub prefix: Option, +} + +impl<'a, 'ctx, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { + pub fn new(length: u32, name: &'static str, realm: &'a mut Realm<'ctx, 'host>) -> Self { + Self { + length, + name, + realm: Some(realm), + ..Default::default() + } + } +} + +/// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ) +/// https://tc39.es/ecma262/#sec-createbuiltinfunction +pub fn create_builtin_function<'ctx, 'host: 'ctx>( + agent: &mut Agent<'ctx, 'host>, + behaviour: Behaviour, + args: BuiltinFunctionArgs<'_, 'ctx, 'host>, +) -> Object { + // 1. If realm is not present, set realm to the current Realm Record. + let realm = args.realm.unwrap(); // TODO: load record + + // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. + let prototype = args + .prototype + .unwrap_or_else(|| realm.intrinsics.function_prototype()); + + // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 + // requires for the built-in function object that is about to be created. + // 4. Append to internalSlotsList the elements of additionalInternalSlotsList. + + // 5. Let func be a new built-in function object that, when called, performs the action + // described by behaviour using the provided arguments as the values of the corresponding + // parameters specified by behaviour. The new function object has internal slots whose names + // are the elements of internalSlotsList, and an [[InitialName]] internal slot. + + // 10. Perform SetFunctionLength(func, length). + + // 11. If prefix is not present, then + // a. Perform SetFunctionName(func, name). + // 12. Else, + // a. Perform SetFunctionName(func, name, prefix). + + // 13. Return func. + todo!(); +} + +pub fn define_builtin_function<'ctx, 'host: 'ctx>( + object: Object, + name: &'static str, + behaviour: RegularFn, + length: u32, + realm: &'ctx mut Realm<'ctx, 'host>, +) { + let agent_mut = realm.agent.clone(); + let mut agent = agent_mut.borrow_mut(); + + let function = create_builtin_function( + &mut agent, + Behaviour::Regular(behaviour), + BuiltinFunctionArgs::new(length, name, realm), + ); + + define_builtin_property(object, name, Value::from(function)); +} + +pub fn define_builtin_property(object: Object, name: &'static str, value: Value) {} diff --git a/nova_vm/src/dyn_ObjectHeapData b/nova_vm/src/dyn_ObjectHeapData deleted file mode 100644 index ab8d3603..00000000 --- a/nova_vm/src/dyn_ObjectHeapData +++ /dev/null @@ -1,40 +0,0 @@ -pub trait ObjectHeapData: Trace + Debug { - fn get_prototype_of(&self, heap: &mut Heap) -> JsResult>; - fn set_prototype_of(&self, heap: &mut Heap, prototype: Option) -> JsResult; - fn is_extensible(&self, heap: &mut Heap) -> JsResult; - fn prevent_extensions(&self, heap: &mut Heap) -> JsResult; - fn get_own_property( - &self, - heap: &mut Heap, - key: PropertyKey, - ) -> JsResult>; - fn define_own_property( - &self, - heap: &mut Heap, - key: PropertyKey, - descriptor: PropertyDescriptor, - ) -> JsResult; - fn has_property(&self, heap: &mut Heap, key: PropertyKey) -> JsResult; - fn get(&self, heap: &mut Heap, key: PropertyKey, receiver: &Value) -> JsResult; - fn set( - &self, - heap: &mut Heap, - key: PropertyKey, - value: Value, - receiver: &Value, - ) -> JsResult; - fn delete(&self, heap: &mut Heap, key: PropertyKey) -> JsResult; - fn own_property_keys(&self, heap: &mut Heap) -> JsResult>; - - // Tracing helpers - fn get_strong_references(&self) -> Vec; - fn get_weak_references(&self) -> Vec; -} - -pub trait FunctionHeapData: ObjectHeapData { - fn call(&self, this: &Value, args: &[Value]) -> JsResult; -} - -pub trait ConstructorHeapData: FunctionHeapData { - fn construct(&self, args: &[Value], target: ObjectIndex) -> JsResult; -} \ No newline at end of file diff --git a/nova_vm/src/execution.rs b/nova_vm/src/execution.rs new file mode 100644 index 00000000..c0362db7 --- /dev/null +++ b/nova_vm/src/execution.rs @@ -0,0 +1,13 @@ +pub mod agent; +mod default_host_hooks; +mod environments; +mod execution_context; +mod realm; + +pub use agent::{Agent, JsResult}; +pub use environments::{ + DeclarativeEnvironment, Environment, FunctionEnvironment, GlobalEnvironment, ObjectEnvironment, + PrivateEnvironment, +}; +pub use execution_context::{ECMAScriptCode, ExecutionContext, ScriptOrModule}; +pub use realm::Realm; diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs new file mode 100644 index 00000000..e34d42c1 --- /dev/null +++ b/nova_vm/src/execution/agent.rs @@ -0,0 +1,44 @@ +use super::{ExecutionContext, Realm}; +use crate::{ + types::{Object, Symbol, Value}, + Heap, +}; +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub struct Options { + pub disable_gc: bool, + pub print_ast: bool, + pub print_bytecode: bool, +} + +pub type JsResult = std::result::Result; + +// #[derive(Debug)] +// pub struct PreAllocated; + +#[derive(Debug)] +pub struct HostHooks { + pub host_ensure_can_compile_strings: fn(callee_realm: &mut Realm) -> JsResult<()>, + pub host_has_source_text_available: fn(func: Object) -> bool, +} + +/// 9.7 Agents +/// https://tc39.es/ecma262/#sec-agents +#[derive(Debug)] +pub struct Agent<'ctx, 'host> { + pub heap: Heap, + pub options: Options, + // pre_allocated: PreAllocated, + pub exception: Option, + pub symbol_id: usize, + pub global_symbol_registry: HashMap<&'static str, Symbol>, + pub host_hooks: HostHooks, + pub execution_context_stack: Vec>, +} + +impl Agent<'_, '_> { + pub fn current_realm(&self) -> &mut Realm { + todo!() + } +} diff --git a/nova_vm/src/execution/default_host_hooks.rs b/nova_vm/src/execution/default_host_hooks.rs new file mode 100644 index 00000000..33916ab4 --- /dev/null +++ b/nova_vm/src/execution/default_host_hooks.rs @@ -0,0 +1,15 @@ +use super::{JsResult, Realm}; +use crate::types::Object; + +/// 19.2.1.2 HostEnsureCanCompileStrings ( calleeRealm ) +/// https://tc39.es/ecma262/#sec-hostensurecancompilestrings +pub fn host_ensure_can_compile_strings(_: &mut Realm) -> JsResult<()> { + Ok(()) +} + +/// 20.2.5 HostHasSourceTextAvailable ( func ) +/// https://tc39.es/ecma262/#sec-hosthassourcetextavailable +pub fn host_has_source_text_available(_: Object) -> bool { + // The default implementation of HostHasSourceTextAvailable is to return true. + return true; +} diff --git a/nova_vm/src/execution/environments.rs b/nova_vm/src/execution/environments.rs new file mode 100644 index 00000000..c7a2ee5c --- /dev/null +++ b/nova_vm/src/execution/environments.rs @@ -0,0 +1,25 @@ +//! 9.1 Environment Records +//! https://tc39.es/ecma262/#sec-environment-records + +pub mod declarative_environment; +pub mod function_environment; +pub mod global_environment; +pub mod object_environment; +pub mod private_environment; + +pub use declarative_environment::DeclarativeEnvironment; +pub use function_environment::FunctionEnvironment; +pub use global_environment::GlobalEnvironment; +pub use object_environment::ObjectEnvironment; +pub use private_environment::PrivateEnvironment; +use std::{cell::RefCell, rc::Rc}; + +/// 9.1.1 The Environment Record Type Hierarchy +/// https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy +#[derive(Debug)] +pub enum Environment { + DeclarativeEnvironment(Rc>), + ObjectEnvironment(Rc>), + FunctionEnvironment(Rc>), + GlobalEnvironment(Rc>), +} diff --git a/nova_vm/src/execution/environments/declarative_environment.rs b/nova_vm/src/execution/environments/declarative_environment.rs new file mode 100644 index 00000000..c1e75df0 --- /dev/null +++ b/nova_vm/src/execution/environments/declarative_environment.rs @@ -0,0 +1,29 @@ +use super::Environment; +use crate::types::Value; +use std::collections::HashMap; + +/// 9.1.1.1 Declarative Environment Records +/// https://tc39.es/ecma262/#sec-declarative-environment-records +#[derive(Debug)] +pub struct DeclarativeEnvironment { + pub outer_env: Option, + pub bindings: HashMap<&'static str, Binding>, +} + +#[derive(Debug)] +pub struct Binding { + pub value: Option, + pub strict: bool, + pub mutable: bool, + pub deletable: bool, +} + +impl DeclarativeEnvironment { + /// 9.1.1.1.1 HasBinding ( N ) + /// https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n + pub fn has_binding(self, name: &str) -> bool { + // 1. If envRec has a binding for N, return true. + // 2. Return false. + return self.bindings.contains_key(name); + } +} diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs new file mode 100644 index 00000000..2347ffc3 --- /dev/null +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -0,0 +1,37 @@ +use super::{DeclarativeEnvironment, Environment}; +use crate::types::Value; +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug)] +pub enum ThisBindingStatus { + Lexical, + Initialized, + Uninitialized, +} + +#[derive(Debug)] +struct ECMAScriptFunction; + +/// 9.1.1.3 Function Environment Records +/// https://tc39.es/ecma262/#sec-function-environment-records +#[derive(Debug)] +pub struct FunctionEnvironment { + /// [[ThisValue]] + this_value: Value, + + /// [[ThisBindingStatus]] + this_binding_status: ThisBindingStatus, + + /// [[FunctionObject]] + function_object: ECMAScriptFunction, + + /// [[NewTarget]] + new_target: Option, + + /// [[OuterEnv]] + outer_env: Option, + + // NOTE: This is how we implement the spec's inheritance of function + // environments. + declarative_environment: Rc>, +} diff --git a/nova_vm/src/execution/environments/global_environment.rs b/nova_vm/src/execution/environments/global_environment.rs new file mode 100644 index 00000000..4f1d07da --- /dev/null +++ b/nova_vm/src/execution/environments/global_environment.rs @@ -0,0 +1,23 @@ +use super::{DeclarativeEnvironment, Environment, ObjectEnvironment}; +use crate::types::Object; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +/// 9.1.1.4 Global Environment Records +/// https://tc39.es/ecma262/#sec-global-environment-records +#[derive(Debug)] +pub struct GlobalEnvironment { + // [[ObjectRecord]] + object_record: Rc>, + + /// [[GlobalThisValue]] + global_this_value: Object, + + /// [[DeclarativeRecord]] + declarative_record: Rc>, + + /// [[VarNames]] + var_names: HashMap<&'static str, ()>, + + /// [[OuterEnv]] + outer_env: Option, +} diff --git a/nova_vm/src/execution/environments/object_environment.rs b/nova_vm/src/execution/environments/object_environment.rs new file mode 100644 index 00000000..2200038f --- /dev/null +++ b/nova_vm/src/execution/environments/object_environment.rs @@ -0,0 +1,16 @@ +use super::Environment; +use crate::types::Object; + +/// 9.1.1.2 Object Environment Records +/// https://tc39.es/ecma262/#sec-object-environment-records +#[derive(Debug)] +pub struct ObjectEnvironment { + /// [[BindingObject]] + binding_object: Object, + + /// [[IsWithEnvironment]] + is_with_environment: bool, + + /// [[OuterEnv]] + outer_env: Option, +} diff --git a/nova_vm/src/execution/environments/private_environment.rs b/nova_vm/src/execution/environments/private_environment.rs new file mode 100644 index 00000000..9fd9b48e --- /dev/null +++ b/nova_vm/src/execution/environments/private_environment.rs @@ -0,0 +1,12 @@ +use std::{cell::RefCell, collections::HashMap, rc::Rc}; + +/// 9.2 PrivateEnvironment Records +/// https://tc39.es/ecma262/#sec-privateenvironment-records +#[derive(Debug)] +pub struct PrivateEnvironment { + /// [[OuterPrivateEnvironment]] + outer_private_environment: Option>>, + + /// [[Names]] + names: HashMap<&'static str, ()>, // TODO: Implement private names +} diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs new file mode 100644 index 00000000..4e7e4e38 --- /dev/null +++ b/nova_vm/src/execution/execution_context.rs @@ -0,0 +1,42 @@ +use super::{Environment, PrivateEnvironment}; +use crate::{execution::Realm, language::Script, types::*}; +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug)] +pub struct Module; + +#[derive(Debug)] +pub enum ScriptOrModule<'ctx, 'host> { + Script(&'ctx mut Script<'ctx, 'host>), + Module(Rc>), +} + +#[derive(Debug)] +pub struct ECMAScriptCode { + /// LexicalEnvironment + pub lexical_environment: Environment, + + /// VariableEnvironment + pub variable_environment: Environment, + + /// PrivateEnvironment + pub private_environment: Option>>, +} + +/// 9.4 Execution Contexts +/// https://tc39.es/ecma262/#sec-execution-contexts +#[derive(Debug)] +pub struct ExecutionContext<'ctx, 'host> { + /// Function + pub function: Option, + + /// Realm + pub realm: Rc>>, + + /// ScriptOrModule + pub script_or_module: Option>, + + /// ECMAScript code execution contexts have the additional state components listed in Table 26. + /// https://tc39.es/ecma262/#ecmascript-code-execution-context + pub ecmascript_code: Option, +} diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs new file mode 100644 index 00000000..c2d12cb0 --- /dev/null +++ b/nova_vm/src/execution/realm.rs @@ -0,0 +1,27 @@ +mod intrinsics; + +use super::{Agent, GlobalEnvironment}; +use crate::types::Object; +use intrinsics::Intrinsics; +use std::{any::Any, cell::RefCell, rc::Rc}; + +/// 9.3 Realms +/// https://tc39.es/ecma262/#sec-code-realms +#[derive(Debug)] +pub struct Realm<'ctx, 'host> { + pub agent: Rc>>, + + // rng: Xoroshiro128, + /// [[Intrinsics]] + pub intrinsics: Intrinsics<'ctx, 'host>, + + /// [[GlobalObject]] + pub global_object: Object, + + /// [[GlobalEnv]] + pub global_env: Rc>, + + /// [[HostDefined]] + pub host_defined: Option>>, + // TODO: [[TemplateMap]], [[LoadedModules]] +} diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs new file mode 100644 index 00000000..7782ed35 --- /dev/null +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -0,0 +1,144 @@ +use super::Realm; +use crate::types::Object; +use std::{cell::RefCell, rc::Rc}; + +#[derive(Debug)] +pub struct Intrinsics<'ctx, 'host> { + pub realm: Rc>>, + + // Not stored as top-level properties so we can have methods of the same names + pub lazy_intrinsics: LazyIntrinsics, +} + +macro_rules! lazy_intrinsic { + ($name: ident $ptr: ty) => { + pub fn $name(&mut self) -> Object { + let intrinsic = &mut self.lazy_intrinsics.$name; + + if let Some(intrinsic) = intrinsic { + intrinsic + } else { + } + } + }; +} + +impl Intrinsics<'_, '_> { + pub fn function_prototype(&mut self) -> Object { + todo!() + } +} + +#[derive(Debug)] +pub struct LazyIntrinsics { + /// %Array% + pub array: Option, + + /// %Array.prototype% + pub array_prototype_prototype: Option, + + /// %BigInt% + pub big_int: Option, + + /// %BigInt.prototype% + pub big_int_prototype: Option, + + /// %Boolean% + pub boolean: Option, + + /// %Boolean.prototype% + pub boolean_prototype: Option, + + /// %Error% + pub error: Option, + + /// %Error.prototype% + pub error_prototype: Option, + + /// %eval% + pub eval: Option, + + /// %EvalError% + pub eval_error: Option, + + /// %EvalError.prototype% + pub eval_error_prototype: Option, + + /// %Function% + pub function: Option, + + /// %Function.prototype% + pub function_prototype: Option, + + /// %isFinite% + pub is_finite: Option, + + /// %isNaN% + pub is_nan: Option, + + /// %Math% + pub math: Option, + + /// %Number% + pub number: Option, + + /// %Number.prototype% + pub number_prototype: Option, + + /// %Object% + pub object: Option, + + /// %Object.prototype% + pub object_prototype: Option, + + /// %Object.prototype.toString% + pub object_prototype_to_string: Option, + + /// %RangeError% + pub range_error: Option, + + /// %RangeError.prototype% + pub range_error_prototype: Option, + + /// %ReferenceError% + pub reference_error: Option, + + /// %ReferenceError.prototype% + pub reference_error_prototype: Option, + + /// %Reflect% + pub reflect: Option, + + /// %String% + pub string: Option, + + /// %String.prototype% + pub string_prototype: Option, + + /// %Symbol% + pub symbol: Option, + + /// %Symbol.prototype% + pub symbol_prototype: Option, + + /// %SyntaxError% + pub syntax_error: Option, + + /// %SyntaxError.prototype% + pub syntax_error_prototype: Option, + + /// %ThrowTypeError% + pub throw_type_error: Option, + + /// %TypeError% + pub type_error: Option, + + /// %TypeError.prototype% + pub type_error_prototype: Option, + + /// %URIError% + pub uri_error: Option, + + /// %URIError.prototype% + pub uri_error_prototype: Option, +} diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index f1206817..77eff022 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -42,7 +42,7 @@ use self::{ string::{initialize_string_heap, StringHeapData}, symbol::{initialize_symbol_heap, SymbolHeapData}, }; -use crate::value::Value; +use crate::types::Value; use wtf8::Wtf8; #[derive(Debug)] @@ -64,6 +64,34 @@ pub struct Heap { pub(crate) symbols: Vec>, } +/// Creates a [`Value`] from the given data. Allocating the data is **not** +/// guaranteed. +pub trait CreateHeapData { + fn create(&mut self, data: T) -> Value; +} + +impl CreateHeapData for Heap { + fn create(&mut self, data: f64) -> Value { + if let Ok(value) = Value::try_from(data) { + value + } else { + let id = self.alloc_number(data); + Value::Number(id) + } + } +} + +impl CreateHeapData<&str> for Heap { + fn create(&mut self, data: &str) -> Value { + if let Ok(value) = Value::try_from(data) { + value + } else { + let id = self.alloc_string(data); + Value::String(id) + } + } +} + impl Heap { pub fn new() -> Heap { let mut heap = Heap { @@ -100,41 +128,18 @@ impl Heap { heap.functions.push(None); } } - initialize_object_heap(&mut heap); - initialize_function_heap(&mut heap); + initialize_array_heap(&mut heap); + initialize_bigint_heap(&mut heap); initialize_boolean_heap(&mut heap); - initialize_symbol_heap(&mut heap); + initialize_date_heap(&mut heap); initialize_error_heap(&mut heap); - initialize_number_heap(&mut heap); - initialize_bigint_heap(&mut heap); + initialize_function_heap(&mut heap); initialize_math_object(&mut heap); - initialize_date_heap(&mut heap); - initialize_string_heap(&mut heap); + initialize_number_heap(&mut heap); + initialize_object_heap(&mut heap); initialize_regexp_heap(&mut heap); - initialize_array_heap(&mut heap); - // initialize_typedarray_heap(&mut heap); - // initialize_map_heap(&mut heap); - // initialize_set_heap(&mut heap); - // initialize_weak_map_heap(&mut heap); - // initialize_weak_set_heap(&mut heap); - // initialize_array_buffer_heap(&mut heap); - // initialize_shared_array_buffer_heap(&mut heap); - // initialize_data_view_heap(&mut heap); - // initialize_json_heap(&mut heap); - // initialize_atomics_heap(&mut heap); - // initialize_weak_ref_heap(&mut heap); - // initialize_finalization_registry_heap(&mut heap); - // initialize_iterator_heap(&mut heap); - // initialize_async_iterator_heap(&mut heap); - // initialize_promise_heap(&mut heap); - // initialize_generator_function_heap(&mut heap); - // initialize_async_generator_function_heap(&mut heap); - // initialize_generator_heap(&mut heap); - // initialize_async_generator_heap(&mut heap); - // initialize_async_function_heap(&mut heap); - // initialize_reflect_heap(&mut heap); - // initialize_proxy_heap(&mut heap); - // initialize_module_heap(&mut heap); + initialize_string_heap(&mut heap); + initialize_symbol_heap(&mut heap); heap } @@ -173,7 +178,7 @@ impl Heap { let entries = vec![ ObjectEntry::new( PropertyKey::from_str(self, "length"), - PropertyDescriptor::roxh(Value::SmiU(length as u32)), + PropertyDescriptor::roxh(Value::from(length)), ), ObjectEntry::new( PropertyKey::from_str(self, "name"), diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index aa876b62..9281fcb7 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ @@ -22,7 +23,7 @@ pub(crate) struct ArrayHeapData { } pub fn initialize_array_heap(heap: &mut Heap) { - let species_function_name = Value::new_string(heap, "get [Symbol.species]"); + let species_function_name = Value::from_str(heap, "get [Symbol.species]"); let at_key = PropertyKey::from_str(heap, "at"); let copy_within_key = PropertyKey::from_str(heap, "copyWithin"); let entries_key = PropertyKey::from_str(heap, "entries"); diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index 9231e6c3..13c0392f 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -1,31 +1,22 @@ +use super::indexes::ObjectIndex; use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, ObjectEntry, PropertyDescriptor, PropertyKey, }, - value::{JsResult, Value}, + types::Value, }; - -use super::indexes::{ErrorIndex, ObjectIndex}; +use num_bigint_dig::BigInt; #[derive(Debug, Clone)] pub(crate) struct BigIntHeapData { - pub(super) sign: bool, - pub(super) len: u32, - pub(super) parts: Box<[u64]>, + pub(super) data: BigInt, } impl BigIntHeapData { - pub(crate) fn len(&self) -> u32 { - self.len - } - pub(crate) fn try_into_f64(&self) -> Option { - if self.len == 1 { - Some(self.parts[0] as f64) - } else { - None - } + None } } @@ -94,20 +85,23 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { } fn bigint_constructor(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult { - if !this.is_undefined() { - // TODO: Throw TypeError - return Err(Value::Error(ErrorIndex::from_index(0))); - } else { - return Ok(Value::SmallBigInt(3)); - } + // if !this.is_undefined() { + // // TODO: Throw TypeError + // return Err(Value::Error(ErrorIndex::from_index(0))); + // } else { + // Ok(Value::SmallBigInt(3)) + // } + Ok(Value::Null) } fn bigint_as_int_n(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::SmallBigInt(3)) + // Ok(Value::SmallBigInt(3)) + Ok(Value::Null) } fn bigint_as_uint_n(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult { - Ok(Value::SmallBigIntU(3)) + // Ok(Value::SmallBigIntU(3)) + Ok(Value::Null) } fn bigint_prototype_to_locale_string( @@ -115,13 +109,13 @@ fn bigint_prototype_to_locale_string( this: Value, args: &[Value], ) -> JsResult { - Ok(Value::new_string(heap, "BigInt(3n)")) + Ok(Value::from_str(heap, "BigInt(3n)")) } fn bigint_prototype_to_string(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult { - Ok(Value::new_string(heap, "BigInt(3n)")) + Ok(Value::from_str(heap, "BigInt(3n)")) } fn bigint_prototype_value_of(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult { - Ok(Value::new_string(heap, "BigInt(3n)")) + Ok(Value::from_str(heap, "BigInt(3n)")) } diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index 22103013..31d9a058 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index c11d49ed..cfcbb6e6 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -1,11 +1,12 @@ use std::time::SystemTime; use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs index ca37239d..6ac743ca 100644 --- a/nova_vm/src/heap/element_array.rs +++ b/nova_vm/src/heap/element_array.rs @@ -2,7 +2,7 @@ use super::{ indexes::{ElementIndex, FunctionIndex}, object::{ObjectEntry, PropertyDescriptor, PropertyKey}, }; -use crate::value::Value; +use crate::types::Value; use core::panic; use std::{collections::HashMap, mem::MaybeUninit, num::NonZeroU16}; @@ -788,9 +788,9 @@ impl ElementArrays { 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::SmallString(data) => Value::SmallString(data), + PropertyKey::Smi(data) => Value::from(data), + PropertyKey::String(data) => Value::String(data), PropertyKey::Symbol(data) => Value::Symbol(data), }; keys.push(Some(key)); diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index d13ba4c3..bfb331b5 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ @@ -48,11 +49,11 @@ pub fn initialize_error_heap(heap: &mut Heap) { ), ObjectEntry::new( PropertyKey::from_str(heap, "name"), - PropertyDescriptor::rwx(Value::EmptyString), + PropertyDescriptor::rwx(Value::try_from("").unwrap()), ), ObjectEntry::new( PropertyKey::from_str(heap, "name"), - PropertyDescriptor::rwx(Value::new_string(heap, "Error")), + PropertyDescriptor::rwx(Value::from_str(heap, "Error")), ), ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, error_todo), ]; diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index d7d6bc46..1cafa13d 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index e9113919..0b0f3403 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -1,6 +1,6 @@ use std::sync::atomic::AtomicBool; -use crate::value::Value; +use crate::types::Value; use super::{ indexes::{ @@ -104,33 +104,27 @@ impl WorkQueues { 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::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::BigInt(idx) => self.bigints.push(idx), + Value::Number(idx) => self.numbers.push(idx), + Value::String(idx) => self.strings.push(idx), Value::Null => {} - Value::NumberObject(_) => todo!(), + // Value::NumberObject(_) => todo!(), Value::Object(idx) => self.objects.push(idx), Value::RegExp(idx) => self.regexps.push(idx), - Value::StackString(_) => {} + Value::SmallString(_) => {} Value::SmallBigInt(_) => {} - Value::SmallBigIntU(_) => {} - Value::Smi(_) => {} - Value::SmiU(_) => {} - Value::StringObject(_) => todo!(), + // Value::StringObject(_) => todo!(), Value::Symbol(idx) => self.symbols.push(idx), - Value::SymbolObject(_) => todo!(), + // Value::SymbolObject(_) => todo!(), Value::Undefined => {} + Value::Integer(_) => {} + Value::Float(_) => {} } } diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 1f5b0114..61a5d8a4 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -1,6 +1,6 @@ use std::sync::atomic::Ordering; -use crate::value::Value; +use crate::types::Value; use super::{ element_array::ElementArrayKey, @@ -183,7 +183,7 @@ pub(crate) fn heap_gc(heap: &mut Heap) { 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)); + queues.push_value(Value::String(string_index)); } } }); diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index 7472b552..90ebe47e 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -1,4 +1,4 @@ -use crate::value::Value; +use crate::types::Value; use super::{ array::ArrayHeapData, bigint::BigIntHeapData, date::DateHeapData, error::ErrorHeapData, diff --git a/nova_vm/src/heap/math.rs b/nova_vm/src/heap/math.rs index c8905ad0..eea06c25 100644 --- a/nova_vm/src/heap/math.rs +++ b/nova_vm/src/heap/math.rs @@ -1,4 +1,4 @@ -use crate::value::{JsResult, Value}; +use crate::{execution::JsResult, types::Value}; use super::{ heap_constants::WellKnownSymbolIndexes, @@ -61,7 +61,7 @@ pub(super) fn initialize_math_object(heap: &mut Heap) { ObjectEntry::new_frozen_entry(heap, "SQRT2", sqrt2), ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag.into()), - PropertyDescriptor::roxh(Value::new_string(heap, "Math")), + PropertyDescriptor::roxh(Value::from_str(heap, "Math")), ), abs, acos, diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index 2792aa85..e204a438 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -1,15 +1,14 @@ -use std::vec; - use super::{ object::{ObjectEntry, PropertyKey}, Heap, }; use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; #[derive(Debug, Clone, Copy)] @@ -55,17 +54,17 @@ pub fn initialize_number_heap(heap: &mut Heap) { ), ObjectEntry::new( PropertyKey::from_str(heap, "NaN"), - PropertyDescriptor::roh(Value::NaN), + PropertyDescriptor::roh(Value::from(f32::NAN)), ), ObjectEntry::new( PropertyKey::from_str(heap, "NEGATIVE_INFINITY"), - PropertyDescriptor::roh(Value::NegativeInfinity), + PropertyDescriptor::roh(Value::from(f32::NEG_INFINITY)), ), 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), + PropertyDescriptor::roh(Value::from(f32::INFINITY)), ), ObjectEntry::new_constructor_prototype_entry( heap, @@ -111,7 +110,7 @@ pub fn initialize_number_heap(heap: &mut Heap) { } fn number_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::SmiU(0)) + Ok(Value::from(0)) } fn number_todo(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 8ab5c754..125d113c 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -1,11 +1,12 @@ use crate::{ + execution::JsResult, heap::{ function::JsBindingFunction, heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, }, - stack_string::StackString, - value::{JsResult, Value}, + types::Value, + SmallString, }; use std::{fmt::Debug, vec}; @@ -34,9 +35,9 @@ impl ObjectEntry { ) -> Self { let key = PropertyKey::from_str(heap, name); let name = match key { - PropertyKey::SmallAsciiString(data) => Value::StackString(data.clone()), + PropertyKey::SmallString(data) => Value::SmallString(data.clone()), PropertyKey::Smi(_) => unreachable!("No prototype functions should have SMI names"), - PropertyKey::String(idx) => Value::HeapString(idx), + PropertyKey::String(idx) => Value::String(idx), PropertyKey::Symbol(idx) => Value::Symbol(idx), }; let func_index = heap.create_function(name, length, uses_arguments, binding); @@ -52,7 +53,7 @@ impl ObjectEntry { uses_arguments: bool, binding: JsBindingFunction, ) -> Self { - let name = Value::new_string(heap, name); + let name = Value::from_str(heap, name); let key = PropertyKey::Symbol(symbol_index); let func_index = heap.create_function(name, length, uses_arguments, binding); let value = PropertyDescriptor::roxh(Value::Function(func_index)); @@ -81,7 +82,7 @@ impl ObjectEntry { #[derive(Debug)] pub enum PropertyKey { - SmallAsciiString(StackString), + SmallString(SmallString), Smi(i32), String(StringIndex), Symbol(SymbolIndex), @@ -89,8 +90,8 @@ pub enum PropertyKey { impl PropertyKey { pub fn from_str(heap: &mut Heap, str: &str) -> Self { - if let Some(ascii_string) = StackString::try_from_str(str) { - PropertyKey::SmallAsciiString(ascii_string) + if let Ok(ascii_string) = SmallString::try_from(str) { + PropertyKey::SmallString(ascii_string) } else { PropertyKey::String(heap.alloc_string(str)) } @@ -226,7 +227,7 @@ impl PropertyDescriptor { #[derive(Debug, Clone, Copy)] pub(crate) struct ObjectHeapData { pub(crate) _extensible: bool, - // TODO: It's probably not necessary to have a whole data descriptor here. + // TODO: It's probably not necessary to have a whole Value here. // A prototype can only be set to be null or an object, meaning that most of the // possible Value options are impossible. // We could possibly do with just a `Option` but it would cause issues diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index c23cd1d7..42fe4d15 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ @@ -20,7 +21,7 @@ pub(crate) struct RegExpHeapData { } pub fn initialize_regexp_heap(heap: &mut Heap) { - let species_function_name = Value::new_string(heap, "get [Symbol.species]"); + let species_function_name = Value::from_str(heap, "get [Symbol.species]"); let entries = vec![ ObjectEntry::new_constructor_prototype_entry( heap, diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 8d8a8814..55169a80 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, }, - value::{JsResult, Value}, + types::Value, }; use wtf8::Wtf8Buf; @@ -18,11 +19,6 @@ impl StringHeapData { data: Wtf8Buf::from_str(str), } } - - pub fn len(&self) -> usize { - // TODO: We should return the UTF-16 length. - self.data.len() - } } pub fn initialize_string_heap(heap: &mut Heap) { @@ -53,5 +49,5 @@ pub fn initialize_string_heap(heap: &mut Heap) { } fn string_constructor_binding(heap: &mut Heap, _this: Value, args: &[Value]) -> JsResult { - Ok(Value::EmptyString) + Ok(Value::Null) } diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index 86a5c575..a263fe64 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -1,9 +1,10 @@ use crate::{ + execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes, WellKnownSymbolIndexes}, FunctionHeapData, Heap, PropertyDescriptor, }, - value::{JsResult, Value}, + types::Value, }; use super::{ @@ -176,7 +177,7 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { ), ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag.into()), - PropertyDescriptor::roxh(Value::new_string(heap, "Symbol")), + PropertyDescriptor::roxh(Value::from_str(heap, "Symbol")), ), ]; heap.insert_builtin_object( diff --git a/nova_vm/src/language.rs b/nova_vm/src/language.rs new file mode 100644 index 00000000..5dc24586 --- /dev/null +++ b/nova_vm/src/language.rs @@ -0,0 +1,4 @@ +mod bytecode; +mod script; + +pub use script::Script; diff --git a/nova_vm/src/language/bytecode.rs b/nova_vm/src/language/bytecode.rs new file mode 100644 index 00000000..b153f88a --- /dev/null +++ b/nova_vm/src/language/bytecode.rs @@ -0,0 +1,7 @@ +mod executable; +mod instructions; +mod vm; + +pub use executable::{Executable, IndexType, JumpIndex}; +pub use instructions::{Instr, Instruction, InstructionIter}; +pub use vm::Vm; diff --git a/nova_vm/src/language/bytecode/executable.rs b/nova_vm/src/language/bytecode/executable.rs new file mode 100644 index 00000000..ea9e3438 --- /dev/null +++ b/nova_vm/src/language/bytecode/executable.rs @@ -0,0 +1,70 @@ +use super::Instruction; +use crate::{types::Value, Heap}; +use oxc_span::Atom; + +pub type IndexType = u16; + +#[derive(Debug)] +pub struct Executable<'ctx> { + pub heap: &'ctx mut Heap, + pub instructions: Vec, + pub constants: Vec, + pub identifiers: Vec, + // TODO: function_expressions +} + +impl<'ctx> Executable<'ctx> { + pub fn add_instruction(&mut self, instruction: Instruction) { + self.instructions.push(instruction); + } + + pub fn add_constant(&mut self, constant: Value) { + self.constants.push(constant); + } + + pub fn add_identifier(&mut self, identifier: Atom) { + self.identifiers.push(identifier); + } + + pub fn add_instruction_with_constant(&mut self, instruction: Instruction, constant: Value) { + debug_assert!(instruction.has_constant_index()); + self.add_instruction(instruction); + self.add_constant(constant); + } + + pub fn add_instruction_with_identifier(&mut self, instruction: Instruction, identifier: Atom) { + debug_assert!(instruction.has_identifier_index()); + self.add_instruction(instruction); + self.add_identifier(identifier); + } + + pub fn add_index(&mut self, index: usize) { + assert!(index < IndexType::MAX as usize); + let bytes: [u8; 2] = unsafe { std::mem::transmute(index as IndexType) }; + self.instructions[index] = unsafe { std::mem::transmute(bytes[0]) }; + self.instructions[index + 1] = unsafe { std::mem::transmute(bytes[0]) }; + } + + pub fn add_jump_index(&mut self) -> JumpIndex { + self.add_index(0); + JumpIndex { + index: self.instructions.len() - std::mem::size_of::(), + } + } + + pub fn set_jump_target(&mut self, jump: JumpIndex, index: usize) { + assert!(index < IndexType::MAX as usize); + let bytes: [u8; 2] = unsafe { std::mem::transmute(index as IndexType) }; + self.instructions[jump.index] = unsafe { std::mem::transmute(bytes[0]) }; + self.instructions[jump.index + 1] = unsafe { std::mem::transmute(bytes[0]) }; + } + + pub fn set_jump_target_here(&mut self, jump: JumpIndex) { + self.set_jump_target(jump, self.instructions.len()); + } +} + +#[derive(Debug)] +pub struct JumpIndex { + pub index: usize, +} diff --git a/nova_vm/src/language/bytecode/instructions.rs b/nova_vm/src/language/bytecode/instructions.rs new file mode 100644 index 00000000..d81f8168 --- /dev/null +++ b/nova_vm/src/language/bytecode/instructions.rs @@ -0,0 +1,198 @@ +use super::IndexType; + +#[derive(Debug, Clone, Copy)] +#[non_exhaustive] +pub enum Instruction { + /// Store ApplyStringOrNumericBinaryOperator() as the result value. + ApplyStringOrNumericBinaryOperator, + /// Store ArrayCreate(0) as the result value. + ArrayCreate, + /// Set an array's value at the given index. + ArraySetValue, + /// Set the length property of an array to the given index. + ArraySetLength, + /// Apply bitwise NOT to the last value on the stack and store it as the result value. + BitwiseNot, + /// Create a catch binding for the given name and populate it with the stored exception. + CreateCatchBinding, + /// Apply the delete operation to the evaluated expression and set it as the result value. + Delete, + /// Store EvaluateCall() as the result value. + /// This instruction has the number of argument values that need to be popped from the stack + /// (last to first) as an argument, the values on the stack afterwards are the this value and + /// lastly the function to call. + EvaluateCall, + /// Store EvaluateNew() as the result value. + /// This instruction has the number of argument values that need to be popped from the stack + /// (last to first) as an argument, the value on the stack afterwards is the constructor to + /// call. + EvaluateNew, + /// Store EvaluatePropertyAccessWithExpressionKey() as the result value. + EvaluatePropertyAccessWithExpressionKey, + /// Store EvaluatePropertyAccessWithIdentifierKey() as the result value. + EvaluatePropertyAccessWithIdentifierKey, + /// Store GetValue() as the result value. + GetValue, + /// Compare the last two values on the stack using the '>' operator rules. + GreaterThan, + /// Compare the last two values on the stack using the '>=' operator rules. + GreaterThanEquals, + /// Store HasProperty() as the result value. + HasProperty, + /// Store InstanceofOperator() as the result value. + InstanceofOperator, + /// Store InstantiateArrowFunctionExpression() as the result value. + InstantiateArrowFunctionExpression, + /// Store InstantiateOrdinaryFunctionExpression() as the result value. + InstantiateOrdinaryFunctionExpression, + /// Store IsLooselyEqual() as the result value. + IsLooselyEqual, + /// Store IsStrictlyEqual() as the result value. + IsStrictlyEqual, + /// Jump to another instruction by setting the instruction pointer. + Jump, + /// Jump to one of two other instructions depending on whether the last value on the stack is + /// truthy or not. + JumpConditional, + /// Compare the last two values on the stack using the '<' operator rules. + LessThan, + /// Compare the last two values on the stack using the '<=' operator rules. + LessThanEquals, + /// Load the result value and add it to the stack. + Load, + /// Load a constant and add it to the stack. + LoadConstant, + /// Determine the this value for an upcoming evaluate_call instruction and add it to the stack. + LoadThisValue, + /// Apply logical NOT to the last value on the stack and store it as the result value. + LogicalNot, + /// Store OrdinaryObjectCreate(%Object.prototype%) as the result value. + ObjectCreate, + /// Set an object's property to the key/value pair from the last two values on the stack. + ObjectSetProperty, + /// Pop a jump target for uncaught exceptions + PopExceptionJumpTarget, + /// Pop the last stored reference. + PopReference, + /// Push a jump target for uncaught exceptions + PushExceptionJumpTarget, + /// Push the last evaluated reference, if any. + PushReference, + /// Call PutValue() with the last reference on the reference stack and the result value. + PutValue, + /// Store ResolveBinding() as the result value. + ResolveBinding, + /// Store ResolveThisBinding() as the result value. + ResolveThisBinding, + /// Rethrow the stored exception, if any. + RethrowExceptionIfAny, + /// Stop bytecode execution, indicating a return from the current function. + Return, + /// Store the last value from the stack as the result value. + Store, + /// Store a constant as the result value. + StoreConstant, + /// Throw the last value from the stack as an exception. + Throw, + /// Store ToNumber() as the result value. + ToNumber, + /// Store ToNumeric() as the result value. + ToNumeric, + /// Apply the typeof operation to the evaluated expression and set it as the result value. + Typeof, + /// Store Number::unaryMinus() / BigInt::unaryMinus() as the result value. + UnaryMinus, +} + +impl Instruction { + pub fn argument_count(self) -> u8 { + return match self { + Self::EvaluateCall + | Self::EvaluatePropertyAccessWithIdentifierKey + | Self::JumpConditional + | Self::ResolveBinding => 2, + Self::ApplyStringOrNumericBinaryOperator + | Self::ArraySetLength + | Self::ArraySetValue + | Self::CreateCatchBinding + | Self::EvaluateNew + | Self::EvaluatePropertyAccessWithExpressionKey + | Self::InstantiateArrowFunctionExpression + | Self::InstantiateOrdinaryFunctionExpression + | Self::Jump + | Self::LoadConstant + | Self::PushExceptionJumpTarget + | Self::StoreConstant => 1, + _ => 0, + }; + } + + pub fn has_constant_index(self) -> bool { + return match self { + Self::LoadConstant | Self::StoreConstant => true, + _ => false, + }; + } + + pub fn has_identifier_index(self) -> bool { + return match self { + Self::CreateCatchBinding + | Self::EvaluatePropertyAccessWithIdentifierKey + | Self::ResolveBinding => true, + _ => false, + }; + } + + pub fn has_function_expression_index(self) -> bool { + return match self { + Self::InstantiateArrowFunctionExpression + | Self::InstantiateOrdinaryFunctionExpression => true, + _ => false, + }; + } +} + +#[derive(Debug)] +pub struct Instr { + pub kind: Instruction, + pub args: [Option; 2], +} + +#[derive(Debug)] +pub struct InstructionIter<'a> { + instructions: &'a [Instruction], + index: usize, +} + +impl<'a> InstructionIter<'a> { + pub fn new(instructions: &'a [Instruction]) -> Self { + Self { + instructions, + index: 0, + } + } +} + +impl Iterator for InstructionIter<'_> { + type Item = Instr; + + fn next(&mut self) -> Option { + if self.index >= self.instructions.len() { + return None; + } + + let kind = self.instructions[self.index]; + self.index += 1; + + let mut args: [Option; 2] = [None, None]; + + for i in 0..kind.argument_count() as usize { + let bytes: &[IndexType] = + unsafe { std::mem::transmute(&self.instructions[self.index..]) }; + self.index += 2; + args[i] = Some(bytes[0]); + } + + Some(Instr { kind, args }) + } +} diff --git a/nova_vm/src/language/bytecode/vm.rs b/nova_vm/src/language/bytecode/vm.rs new file mode 100644 index 00000000..27694b40 --- /dev/null +++ b/nova_vm/src/language/bytecode/vm.rs @@ -0,0 +1,60 @@ +use oxc_span::Atom; + +use crate::{ + execution::Agent, + types::{Reference, Value}, +}; + +use super::{Executable, IndexType, Instruction}; + +#[derive(Debug)] +pub struct Vm<'ctx, 'host> { + agent: &'ctx mut Agent<'ctx, 'host>, + ip: usize, + stack: Vec, + reference_stack: Vec>, + exception_jump_target_stack: Vec, + result: Option, + exception: Option, + reference: Option, +} + +impl<'ctx, 'host> Vm<'ctx, 'host> { + pub fn new(agent: &'ctx mut Agent<'ctx, 'host>) -> Self { + Self { + agent, + ip: 0, + stack: Vec::with_capacity(32), + reference_stack: Vec::new(), + exception_jump_target_stack: Vec::new(), + result: None, + exception: None, + reference: None, + } + } + + fn fetch_instruction(&mut self, executable: &Executable) -> Option { + executable.instructions.get(self.ip).map(|kind| { + self.ip += 1; + *kind + }) + } + + fn fetch_constant(&mut self, executable: &Executable) -> Value { + let index = self.fetch_index(executable); + executable.constants[index as usize].clone() + } + + fn fetch_identifier(&mut self, executable: &Executable) -> Atom { + let index = self.fetch_index(executable); + executable.identifiers[index as usize].clone() + } + + fn fetch_index(&mut self, executable: &Executable) -> IndexType { + let bytes: [u8; std::mem::size_of::()] = [ + unsafe { std::mem::transmute(self.fetch_instruction(executable).unwrap()) }, + unsafe { std::mem::transmute(self.fetch_instruction(executable).unwrap()) }, + ]; + unsafe { std::mem::transmute(bytes) } + } +} diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs new file mode 100644 index 00000000..5866458c --- /dev/null +++ b/nova_vm/src/language/script.rs @@ -0,0 +1,158 @@ +use crate::{ + execution::{ECMAScriptCode, Environment, ExecutionContext, Realm, ScriptOrModule}, + types::Value, +}; +use oxc_allocator::Allocator; +use oxc_ast::ast::{BindingPatternKind, Declaration, Program, Statement}; +use oxc_parser::Parser; +use oxc_span::SourceType; +use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc}; + +pub type HostDefined<'ctx> = Option<&'ctx mut dyn Any>; + +/// 16.1.4 Script Records +/// https://tc39.es/ecma262/#sec-script-records +#[derive(Debug)] +pub struct Script<'ctx, 'host> { + /// [[Realm]] + pub realm: Rc>>, + + /// [[ECMAScriptCode]] + pub ecmascript_code: Rc>, + + // TODO: [[LoadedModules]] + /// [[HostDefined]] + pub host_defined: Option>, +} + +#[derive(Debug)] +pub enum ScriptOrErrors<'ctx, 'host> { + Script(Script<'ctx, 'host>), + Errors(Vec), +} + +impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { + /// 16.1.5 ParseScript ( sourceText, realm, hostDefined ) + /// https://tc39.es/ecma262/#sec-parse-script + pub fn parse( + allocator: &'host Allocator, + source_text: &'host str, + realm: Rc>>, + host_defined: Option>, + ) -> ScriptOrErrors<'ctx, 'host> { + // 1. Let script be ParseText(sourceText, Script). + // 2. If script is a List of errors, return script. + let parser = Parser::new(&allocator, source_text, SourceType::default()); + let script = parser.parse(); + + if script.errors.len() != 0 { + return ScriptOrErrors::Errors(script.errors); + } + + // 3. Return Script Record { + // [[Realm]]: realm, [[ECMAScriptCode]]: script, [[LoadedModules]]: « », [[HostDefined]]: hostDefined + // }. + ScriptOrErrors::Script(Self { + realm, + ecmascript_code: Rc::new(script.program), + host_defined, + }) + } + + /// 16.1.6 ScriptEvaluation ( scriptRecord ) + /// https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation + pub fn evaluate(&'ctx mut self) -> Value { + let ecmascript_code = self.ecmascript_code.clone(); + let realm = self.realm.clone(); + let agent = { + let realm = self.realm.borrow(); + realm.agent.clone() + }; + + // 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]]. + let global_env = { + let realm = self.realm.borrow(); + realm.global_env.clone() + }; + + // 2. Let scriptContext be a new ECMAScript code execution context. + let script_context = ExecutionContext { + // 3. Set the Function of scriptContext to null. + function: None, + + // 4. Set the Realm of scriptContext to scriptRecord.[[Realm]]. + realm, + + // 5. Set the ScriptOrModule of scriptContext to scriptRecord. + script_or_module: Some(ScriptOrModule::Script(self)), + + ecmascript_code: Some(ECMAScriptCode { + // 6. Set the VariableEnvironment of scriptContext to globalEnv. + variable_environment: Environment::GlobalEnvironment(global_env.clone()), + + // 7. Set the LexicalEnvironment of scriptContext to globalEnv. + lexical_environment: Environment::GlobalEnvironment(global_env.clone()), + + // 8. Set the PrivateEnvironment of scriptContext to null. + private_environment: None, + }), + }; + + // TODO: 9. Suspend the running execution context. + + // 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context. + agent + .borrow_mut() + .execution_context_stack + .push(script_context); + + // 11. Let script be scriptRecord.[[ECMAScriptCode]]. + let script = ecmascript_code.as_ref(); + + // TODO: 12. Let result be Completion(GlobalDeclarationInstantiation(script, globalEnv)). + // NOTE: This is totally ad-hoc for now. + let mut seen = HashMap::new(); + for variable_declaration in script.body.iter() { + match &variable_declaration { + Statement::Declaration(decl) => match decl { + Declaration::VariableDeclaration(decl) => { + if decl.kind.is_var() { + for decl in decl.declarations.iter() { + let var_name = match &decl.id.kind { + BindingPatternKind::BindingIdentifier(name) => { + name.name.as_str() + } + _ => continue, + }; + + if !seen.contains_key(var_name) { + // global_env.create_global_var_binding(agent, var_name, false); + _ = seen.insert(var_name, ()); + } + } + } + } + _ => {} + }, + _ => {} + } + } + + // 13. If result.[[Type]] is normal, then + // a. Set result to Completion(Evaluation of script). + // b. If result.[[Type]] is normal and result.[[Value]] is empty, then + // i. Set result to NormalCompletion(undefined). + + // 14. Suspend scriptContext and remove it from the execution context stack. + _ = agent.borrow_mut().execution_context_stack.pop(); + + // 15. Assert: The execution context stack is not empty. + debug_assert!(agent.borrow().execution_context_stack.len() > 0); + + // TODO: 16. Resume the context that is now on the top of the execution context stack as the + // running execution context. + + // 17. Return ? result. + todo!() + } +} diff --git a/nova_vm/src/lib.rs b/nova_vm/src/lib.rs index 9ad9ea63..5b3357cd 100644 --- a/nova_vm/src/lib.rs +++ b/nova_vm/src/lib.rs @@ -1,467 +1,10 @@ -#![feature(try_trait_v2)] - +pub mod builtins; +pub mod execution; pub mod heap; -mod stack_string; -pub mod value; -use heap::Heap; -use oxc_ast::{ - ast::{ - AssignmentOperator, AssignmentTarget, BinaryOperator, BindingPatternKind, Declaration, - Expression, LogicalOperator, Program, SimpleAssignmentTarget, Statement, - VariableDeclarator, - }, - syntax_directed_operations::PropName, -}; -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 { - BigInt, - Boolean, - Function, - Null, - Number, - Object, - String, - Symbol, - Undefined, -} - -#[repr(u32)] -pub enum Instruction { - LoadInteger, - LoadBoolean, - LoadNull, - LoadString, - - // [out] [in] - CopyValue, - - // [out] [left] [right] - Add, - Sub, - Mul, - Div, - Mod, - StrictEquality, - Equality, - StrictInequality, - Inequality, - - /// `[jump_rel]` - /// - /// Jumps to the relative instruction. - Jump, - - /// `[addr] [jump_rel]` - /// - /// If `addr` is a true when converted to a boolean, then the instruction - /// will skip `jump_rel` instructions forward. - Test, - - /// `[addr] [jump_rel]` - /// If `addr` is false when converted to a boolean, then the instruction - /// will skip `jump_rel` instructions forward. - TestNot, -} - -impl Into for Instruction { - fn into(self) -> u32 { - self as u32 - } -} - -pub struct VM<'a> { - pub source: &'a str, - pub instructions: Vec, - /// Program counter. - pub pc: u32, - pub heap: Heap, -} - -#[derive(Debug, Default)] -pub struct Env<'a> { - pub map: std::collections::HashMap<&'a str, u32>, - pub parent: Option>>, -} - -impl<'a> VM<'a> { - pub fn interpret(&mut self) -> JsResult<()> { - let mut memory = Vec::::with_capacity(self.pc as usize); - - for _ in 0..self.pc { - memory.push(Value::Undefined); - } - - let instructions = self.instructions.clone(); - let mut iter = instructions.iter(); - while let Some(leading) = iter.next() { - match unsafe { std::mem::transmute::(*leading) } { - Instruction::LoadInteger => { - let addr = *iter.next().unwrap() as usize; - memory[addr] = Value::from_i32(*iter.next().unwrap() as i32); - } - Instruction::LoadBoolean => { - let addr = *iter.next().unwrap() as usize; - memory[addr] = Value::Boolean(*iter.next().unwrap() == 1); - } - Instruction::LoadNull => { - let addr = *iter.next().unwrap() as usize; - memory[addr] = Value::Null; - } - Instruction::LoadString => { - let addr = *iter.next().unwrap() as usize; - memory[addr] = Value::HeapString(StringIndex::from_u32(*iter.next().unwrap())); - } - Instruction::CopyValue => { - let addr = *iter.next().unwrap() as usize; - memory[addr] = memory[*iter.next().unwrap() as usize].clone(); - } - Instruction::Add => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr].try_into_f64(self)?; - let right = &memory[*iter.next().unwrap() as usize].try_into_f64(self)?; - memory[addr] = Value::from_f64(&mut self.heap, left + right); - } - Instruction::Sub => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr].try_into_f64(self)?; - let right = &memory[*iter.next().unwrap() as usize].try_into_f64(self)?; - memory[addr] = Value::from_f64(&mut self.heap, left - right); - } - Instruction::Mul => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr].try_into_f64(self)?; - let right = &memory[*iter.next().unwrap() as usize].try_into_f64(self)?; - memory[addr] = Value::from_f64(&mut self.heap, left * right); - } - Instruction::Mod => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr].try_into_f64(self)?; - let right = &memory[*iter.next().unwrap() as usize].try_into_f64(self)?; - memory[addr] = Value::from_f64(&mut self.heap, left % right); - } - Instruction::Div => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr].try_into_f64(self)?; - let right = &memory[*iter.next().unwrap() as usize].try_into_f64(self)?; - memory[addr] = Value::from_f64(&mut self.heap, left / right); - } - Instruction::StrictEquality => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr]; - let right = &memory[*iter.next().unwrap() as usize]; - memory[addr] = Value::Boolean(left.is_strictly_equal(self, right)?); - } - Instruction::Equality => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr]; - let right = &memory[*iter.next().unwrap() as usize]; - memory[addr] = Value::Boolean(left.is_loosely_equal(self, right)?); - } - Instruction::StrictInequality => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr]; - let right = &memory[*iter.next().unwrap() as usize]; - memory[addr] = Value::Boolean(!left.is_strictly_equal(self, right)?); - } - Instruction::Inequality => { - let addr = *iter.next().unwrap() as usize; - let left = &memory[addr]; - let right = &memory[*iter.next().unwrap() as usize]; - memory[addr] = Value::Boolean(!left.is_loosely_equal(self, right)?); - } - Instruction::Test => { - let addr = *iter.next().unwrap() as usize; - let jump_rel = *iter.next().unwrap() as usize; - - if memory[addr].into_bool() { - _ = iter.nth(jump_rel); - }; - } - Instruction::TestNot => { - let addr = *iter.next().unwrap() as usize; - let jump_rel = *iter.next().unwrap() as usize; - - if !memory[addr].into_bool() { - _ = iter.nth(jump_rel); - }; - } - Instruction::Jump => { - let jump_rel = *iter.next().unwrap() as usize; - _ = iter.nth(jump_rel); - } - } - } - - println!("{:?}", memory.as_slice()); - - Ok(()) - } - - /// Builds the bytecode to run an expression. - /// - /// Assumes the memory location is valid to use throughout the evaluation as - /// a scratch. - fn build_expr(&mut self, addr: u32, expr: &Expression, env: &mut Env) { - match expr { - Expression::NullLiteral(_) => { - self.instructions.push(Instruction::LoadNull.into()); - self.instructions.push(addr); - } - Expression::BooleanLiteral(l) => { - self.instructions.push(Instruction::LoadBoolean.into()); - self.instructions.push(addr); - self.instructions.push(l.value.into()); - } - Expression::Identifier(ident) => { - // TODO: figure out how to return the ident's memory addr as - // an optimization - self.instructions.push(Instruction::CopyValue.into()); - self.instructions.push(addr); - // TODO: support recursive ident lookups - self.instructions - .push(*env.map.get(ident.name.as_str()).unwrap()); - } - Expression::NumberLiteral(num) => { - self.instructions.push(Instruction::LoadInteger.into()); - self.instructions.push(addr); - self.instructions - .push(unsafe { std::mem::transmute(*num.value.as_f32()) }); - } - Expression::LogicalExpression(expr) => match expr.operator { - LogicalOperator::And => { - self.build_expr(addr, &expr.left, env); - self.instructions.push(Instruction::Test.into()); - self.instructions.push(addr); - let jump_addr = self.instructions.len(); - self.instructions.push(0); - self.build_expr(addr, &expr.right, env); - self.instructions[jump_addr] = (self.instructions.len() - jump_addr) as u32; - } - LogicalOperator::Or => { - self.build_expr(addr, &expr.left, env); - self.instructions.push(Instruction::TestNot.into()); - self.instructions.push(addr); - let jump_addr = self.instructions.len(); - self.instructions.push(0); - self.build_expr(addr, &expr.right, env); - self.instructions[jump_addr] = (self.instructions.len() - jump_addr) as u32; - } - _ => panic!(), - }, - Expression::ConditionalExpression(cond) => { - self.build_expr(addr, &cond.test, env); - - self.instructions.push(Instruction::Test.into()); - self.instructions.push(addr); - let finish_idx = self.instructions.len(); - self.instructions.push(0); - - self.build_expr(addr, &cond.alternate, env); - - self.instructions.push(Instruction::Jump.into()); - let alternate_idx = self.instructions.len(); - self.instructions.push(0); - - self.instructions[finish_idx] = (self.instructions.len() - finish_idx - 2) as u32; - self.build_expr(addr, &cond.consequent, env); - - self.instructions[alternate_idx] = - (self.instructions.len() - alternate_idx - 2) as u32; - } - Expression::ObjectExpression(obj) => { - for prop in obj.properties.iter() { - prop.prop_name(); - } - } - Expression::BinaryExpression(binary_op) => { - macro_rules! binary_op { - ($name: ident) => {{ - let right = self.pc; - self.pc += 1; - - self.build_expr(addr, &binary_op.left, env); - self.build_expr(right, &binary_op.right, env); - - self.instructions.push(Instruction::$name.into()); - self.instructions.push(addr); - self.instructions.push(right); - }}; - } - - match binary_op.operator { - BinaryOperator::Addition => binary_op!(Add), - BinaryOperator::Subtraction => binary_op!(Sub), - BinaryOperator::Multiplication => binary_op!(Mul), - BinaryOperator::Remainder => binary_op!(Mod), - BinaryOperator::Division => binary_op!(Div), - BinaryOperator::StrictEquality => binary_op!(StrictEquality), - BinaryOperator::Equality => binary_op!(Equality), - BinaryOperator::StrictInequality => binary_op!(StrictInequality), - BinaryOperator::Inequality => binary_op!(Inequality), - _ => todo!(), - } - } - Expression::StringLiteral(s) => { - let string_idx = self.heap.alloc_string(&*s.value.as_str()); - - self.instructions.push(Instruction::LoadString.into()); - self.instructions.push(addr); - self.instructions.push(string_idx.into_u32()); - } - Expression::ParenthesizedExpression(data) => { - return self.build_expr(addr, &data.expression, env); - } - Expression::SequenceExpression(data) => { - for expr in data.expressions.iter() { - self.build_expr(addr, expr, env); - } - } - Expression::AssignmentExpression(s) => match s.operator { - AssignmentOperator::Assign => match &s.left { - AssignmentTarget::SimpleAssignmentTarget(target) => match target { - SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => { - let Some(addr) = env.map.get(ident.name.as_str()) else { - panic!("Unknown ident."); - }; - self.build_expr(*addr, &s.right, env); - } - _ => todo!(), - }, - _ => todo!(), - }, - _ => todo!(), - }, - Expression::ArrayExpression(_) => todo!(), - Expression::BigintLiteral(_) => todo!(), - Expression::RegExpLiteral(_) => todo!(), - Expression::TemplateLiteral(_) => todo!(), - Expression::MetaProperty(_) => todo!(), - Expression::Super(_) => todo!(), - Expression::ArrowFunctionExpression(_) => todo!(), - Expression::AwaitExpression(_) => todo!(), - Expression::CallExpression(_) => todo!(), - Expression::ChainExpression(_) => todo!(), - Expression::ClassExpression(_) => todo!(), - Expression::FunctionExpression(_) => todo!(), - Expression::ImportExpression(_) => todo!(), - Expression::MemberExpression(_) => todo!(), - Expression::NewExpression(_) => todo!(), - Expression::TaggedTemplateExpression(_) => todo!(), - Expression::ThisExpression(_) => todo!(), - Expression::UnaryExpression(_) => todo!(), - Expression::UpdateExpression(_) => todo!(), - Expression::YieldExpression(_) => todo!(), - Expression::PrivateInExpression(_) => todo!(), - // TypeScript and JSX not supported - Expression::JSXElement(_) - | Expression::JSXFragment(_) - | Expression::TSAsExpression(_) - | Expression::TSSatisfiesExpression(_) - | Expression::TSTypeAssertion(_) - | Expression::TSNonNullExpression(_) - | Expression::TSInstantiationExpression(_) => unreachable!(), - } - } - - pub fn build_stmt<'b>(&mut self, stmt: &'b Statement, env: &mut Env<'b>) { - match stmt { - Statement::Declaration(Declaration::VariableDeclaration(decl)) => { - for member in decl.declarations.as_slice() { - let member: &VariableDeclarator = member; - env.map.insert( - match &member.id.kind { - BindingPatternKind::BindingIdentifier(ident) => ident.name.as_str(), - _ => panic!(), - }, - self.pc, - ); - let addr = self.pc; - self.pc += 1; - - if let Some(expr) = &member.init { - self.build_expr(addr, expr, env); - } else { - todo!("Load undefined."); - } - } - } - Statement::Declaration(Declaration::FunctionDeclaration(_)) => todo!(), - Statement::Declaration(Declaration::ClassDeclaration(_)) => todo!(), - Statement::ExpressionStatement(expr) => { - self.build_expr(self.pc, &expr.expression, env); - } - Statement::BlockStatement(block) => { - for stmt in block.body.iter() { - self.build_stmt(&stmt, env); - } - } - Statement::IfStatement(s) => { - let addr = self.pc; - self.pc += 1; - - self.build_expr(addr, &s.test, env); - self.instructions.push(Instruction::Test.into()); - self.instructions.push(addr); - let consequent_idx = self.instructions.len(); - self.instructions.push(0); - - if let Some(alternate) = &s.alternate { - self.build_stmt(alternate, env); - } - - self.instructions.push(Instruction::Jump.into()); - let finish_idx = self.instructions.len(); - self.instructions.push(0); - - self.instructions[consequent_idx] = - (self.instructions.len() - consequent_idx - 2) as u32; - self.build_stmt(&s.consequent, env); - - self.instructions[finish_idx] = (self.instructions.len() - finish_idx - 2) as u32; - } - Statement::BreakStatement(_) => todo!(), - Statement::ContinueStatement(_) => todo!(), - Statement::DebuggerStatement(_) => todo!(), - Statement::DoWhileStatement(_) => todo!(), - Statement::EmptyStatement(_) => todo!(), - Statement::ForInStatement(_) => todo!(), - Statement::ForOfStatement(_) => todo!(), - Statement::ForStatement(_) => todo!(), - Statement::LabeledStatement(_) => todo!(), - Statement::ReturnStatement(_) => todo!(), - Statement::SwitchStatement(_) => todo!(), - Statement::ThrowStatement(_) => todo!(), - Statement::TryStatement(_) => todo!(), - Statement::WhileStatement(_) => todo!(), - Statement::WithStatement(_) => todo!(), - Statement::ModuleDeclaration(_) => todo!(), - // TypeScript not supported - Statement::Declaration(Declaration::TSTypeAliasDeclaration(_)) - | Statement::Declaration(Declaration::TSInterfaceDeclaration(_)) - | Statement::Declaration(Declaration::TSEnumDeclaration(_)) - | Statement::Declaration(Declaration::TSModuleDeclaration(_)) - | Statement::Declaration(Declaration::TSImportEqualsDeclaration(_)) => unreachable!(), - } - } - - pub fn load_program(&mut self, program: Program) { - let mut env = Env::default(); - for stmt in program.body.iter() { - self.build_stmt(stmt, &mut env); - } - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - fn foo() {} -} +pub mod language; +pub mod types; +pub use heap::Heap; +mod small_integer; +mod small_string; +pub use small_integer::SmallInteger; +pub use small_string::SmallString; diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs new file mode 100644 index 00000000..d19560ac --- /dev/null +++ b/nova_vm/src/small_integer.rs @@ -0,0 +1,53 @@ +/// 56-bit signed integer. +#[derive(Clone, Copy)] +pub struct SmallInteger { + data: [u8; 7], +} + +impl std::fmt::Debug for SmallInteger { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", Into::::into(*self)) + } +} + +impl SmallInteger { + pub const MIN: i64 = -(2 as i64).pow(56) / 2 + 1; + pub const MAX: i64 = (2 as i64).pow(56) / 2 - 1; + + pub(crate) fn from_i64_unchecked(value: i64) -> SmallInteger { + debug_assert!(value >= Self::MIN && value <= Self::MAX); + let mut data: [u8; 7] = [0, 0, 0, 0, 0, 0, 0]; + let bytes = i64::to_ne_bytes(value); + if cfg!(target_endian = "little") { + data.copy_from_slice(&bytes[0..7]); + } else { + data.copy_from_slice(&bytes[1..8]); + } + Self { data } + } +} + +impl TryFrom for SmallInteger { + type Error = (); + fn try_from(value: i64) -> Result { + if value >= Self::MIN && value <= Self::MAX { + Ok(Self::from_i64_unchecked(value)) + } else { + Err(()) + } + } +} + +impl Into for SmallInteger { + fn into(self) -> i64 { + let Self { data } = self; + let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; + let bytes_slice = &mut bytes.as_mut_slice()[0..7]; + bytes_slice.copy_from_slice(data.as_slice()); + if cfg!(target_endian = "big") { + bytes.copy_within(0..7, 1); + } + // SAFETY: The format is guaranteed to match `from_i64_unchecked`. + unsafe { std::mem::transmute(bytes) } + } +} diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs new file mode 100644 index 00000000..e51be0bd --- /dev/null +++ b/nova_vm/src/small_string.rs @@ -0,0 +1,45 @@ +#[derive(Clone, Copy)] +pub struct SmallString { + data: [u8; 7], +} + +impl std::fmt::Debug for SmallString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "\"{}\"", self.as_str()) + } +} + +impl SmallString { + pub fn len(&self) -> usize { + self.data.iter().position(|byte| *byte == 0).unwrap_or(7) + } + + pub fn as_bytes(&self) -> &[u8] { + &self.data[0..self.len()] + } + + pub fn as_str(&self) -> &str { + // SAFETY: Guaranteed to be valid UTF-8. + unsafe { std::str::from_utf8_unchecked(self.as_bytes()) } + } + + pub(crate) fn from_str_unchecked(value: &str) -> Self { + let len = value.len(); + assert!(len < 8); + let mut data: [u8; 7] = [0, 0, 0, 0, 0, 0, 0]; + let data_slice = &mut data.as_mut_slice()[0..len]; + data_slice.copy_from_slice(value.as_bytes()); + Self { data } + } +} + +impl TryFrom<&str> for SmallString { + type Error = (); + fn try_from(value: &str) -> Result { + if value.len() < 8 { + Ok(Self::from_str_unchecked(value)) + } else { + Err(()) + } + } +} diff --git a/nova_vm/src/stack_string.rs b/nova_vm/src/stack_string.rs deleted file mode 100644 index f6aaf25d..00000000 --- a/nova_vm/src/stack_string.rs +++ /dev/null @@ -1,95 +0,0 @@ -use std::fmt::Debug; - -/// Small UTF-8 string of up to 7 bytes in length -/// -/// The string is terminated after either the last non-null -/// byte or at the end of the byte slice. In essence -/// this is a mix between a CStr and a str. -#[derive(Copy, Clone)] -pub struct StackString { - bytes: [u8; 7], -} - -impl StackString { - // TODO: Need to get the length, and UTF-16 length, of the string. - pub fn utf16_len(&self) -> usize { - self.as_str().encode_utf16().count() - } - - pub fn byte_len(&self) -> usize { - // Find the last non-null character and add one to its index to get length. - self.bytes - .as_slice() - .iter() - .rev() - .position(|&x| x != 0) - .map_or(0, |i| 7 - i) - } - - pub fn as_str(&self) -> &str { - // SAFETY: Guaranteed to be ASCII, which is a subset of UTF-8. - unsafe { &std::str::from_utf8_unchecked(self.as_slice()) } - } - - #[inline(always)] - pub fn as_slice(&self) -> &[u8] { - &self.data().as_slice().split_at(self.byte_len()).0 - } - - #[inline(always)] - pub fn data(&self) -> &[u8; 7] { - return &self.bytes; - } - - // TODO: try_from_X should return Result. Option is smaller and we - // do not care about the reason why conversion failed so we prefer - // that but the method name should be changed. - pub(crate) fn try_from_str(string: &str) -> Option { - let string_bytes = string.as_bytes(); - if string_bytes.len() > 7 || string_bytes.last() == Some(&0) { - // We have only 7 bytes to work with, and we cannot tell apart - // UTF-8 strings that end with a null byte from our null - // terminator so we must fail to convert on those. - return None; - }; - let mut this = StackString { - bytes: [0, 0, 0, 0, 0, 0, 0], - }; - this.bytes - .as_mut_slice() - .split_at_mut(string_bytes.len()) - .0 - .copy_from_slice(string_bytes); - Some(this) - } -} - -impl Debug for StackString { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "\"{}\"", self.as_str()) - } -} - -#[test] -fn valid_stack_strings() { - assert!(StackString::try_from_str("").is_some()); - assert_eq!(StackString::try_from_str("").unwrap().byte_len(), 0); - assert!(StackString::try_from_str("asd").is_some()); - assert_eq!(StackString::try_from_str("asd").unwrap().byte_len(), 3); - assert!(StackString::try_from_str("asdasd").is_some()); - assert_eq!(StackString::try_from_str("asdasd").unwrap().byte_len(), 6); - assert!(StackString::try_from_str("asdasda").is_some()); - assert_eq!(StackString::try_from_str("asdasda").unwrap().byte_len(), 7); - assert!(StackString::try_from_str("asd76fd").is_some()); - assert_eq!(StackString::try_from_str("asd76fd").unwrap().byte_len(), 7); - assert!(StackString::try_from_str("💩").is_some()); - assert_eq!(StackString::try_from_str("💩").unwrap().byte_len(), 4); - assert!(StackString::try_from_str("asd\0foo").is_some()); - assert_eq!(StackString::try_from_str("asd\0foo").unwrap().byte_len(), 7); -} - -#[test] -fn not_valid_stack_strings() { - assert!(StackString::try_from_str("asd asd r 547 gdfg").is_none()); - assert!(StackString::try_from_str("asdfoo\0").is_none()); -} diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs new file mode 100644 index 00000000..f7f88309 --- /dev/null +++ b/nova_vm/src/types.rs @@ -0,0 +1,17 @@ +mod language; +mod spec; + +pub use language::Value; +pub use spec::{Base, Reference, ReferencedName}; + +impl From for Value { + fn from(value: Object) -> Self { + todo!() + } +} + +#[derive(Debug)] +pub struct Object; + +#[derive(Debug)] +pub struct Symbol; diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs new file mode 100644 index 00000000..f96f3e0c --- /dev/null +++ b/nova_vm/src/types/language.rs @@ -0,0 +1,3 @@ +mod value; + +pub use value::Value; diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs new file mode 100644 index 00000000..0a82c4d1 --- /dev/null +++ b/nova_vm/src/types/language/value.rs @@ -0,0 +1,167 @@ +use std::mem::size_of; + +use crate::{ + heap::indexes::{ + ArrayIndex, BigIntIndex, DateIndex, ErrorIndex, FunctionIndex, NumberIndex, ObjectIndex, + RegExpIndex, StringIndex, SymbolIndex, + }, + Heap, SmallInteger, SmallString, +}; + +/// 6.1 ECMAScript Language Types +/// https://tc39.es/ecma262/#sec-ecmascript-language-types +#[derive(Debug, Clone, Copy)] +pub enum Value { + /// 6.1.1 The Undefined Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-undefined-type + Undefined, + + /// 6.1.2 The Null Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-null-type + Null, + + /// 6.1.3 The Boolean Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-boolean-type + Boolean(bool), + + /// 6.1.4 The String Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type + String(StringIndex), + SmallString(SmallString), + + /// 6.1.5 The Symbol Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-symbol-type + Symbol(SymbolIndex), + + /// 6.1.6.1 The Number Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type + Number(NumberIndex), + Integer(SmallInteger), // 56-bit signed integer. + Float(f32), + + /// 6.1.6.2 The BigInt Type + /// https://tc39.es/ecma262/#sec-ecmascript-language-types-bigint-type + BigInt(BigIntIndex), + SmallBigInt(SmallInteger), + + /// 6.1.7 The Object Type + /// https://tc39.es/ecma262/#sec-object-type + Object(ObjectIndex), + + // Well-known object types + // Roughly corresponding to 6.1.7.4 Well-Known Intrinsic Objects + // https://tc39.es/ecma262/#sec-well-known-intrinsic-objects + Array(ArrayIndex), + Date(DateIndex), + Error(ErrorIndex), + Function(FunctionIndex), + RegExp(RegExpIndex), + // TODO: Implement primitive value objects, those useless things. + // BigIntObject(u32), + // BooleanObject(u32), + // NumberObject(u32), + // StringObject(u32), + // SymbolObject(u32), +} + +/// We want to guarantee that all handles to JS values are register sized. This assert must never be removed or broken. +const _VALUE_SIZE_IS_WORD: () = assert!(size_of::() == size_of::()); +// We may also want to keep Option register sized so that eg. holes in arrays do not start requiring extra bookkeeping. +const _OPTIONAL_VALUE_SIZE_IS_WORD: () = assert!(size_of::>() == size_of::()); + +impl Value { + pub fn from_str(heap: &mut Heap, message: &str) -> Value { + if let Ok(ascii_string) = SmallString::try_from(message) { + Value::SmallString(ascii_string) + } else { + Value::String(heap.alloc_string(message)) + } + } + + pub fn from_f64(heap: &mut Heap, value: f64) -> Value { + let is_int = value.fract() == 0.0; + if is_int { + if let Ok(data) = Value::try_from(value as i64) { + return data; + } + } + if value as f32 as f64 == value { + // TODO: Verify logic + Value::Float(value as f32) + } else { + Value::Number(heap.alloc_number(value)) + } + } + + pub const fn nan() -> Self { + Self::Float(f32::NAN) + } + + pub const fn infinity() -> Self { + Self::Float(f32::INFINITY) + } + + pub const fn neg_infinity() -> Self { + Self::Float(f32::NEG_INFINITY) + } +} + +impl From> for Value { + fn from(value: Option) -> Self { + value.unwrap_or(Value::Undefined) + } +} + +impl TryFrom<&str> for Value { + type Error = (); + fn try_from(value: &str) -> Result { + if let Ok(data) = value.try_into() { + Ok(Value::SmallString(data)) + } else { + Err(()) + } + } +} + +impl TryFrom for Value { + type Error = (); + fn try_from(value: f64) -> Result { + // TODO: verify logic + if value as f32 as f64 == value { + Ok(Value::Float(value as f32)) + } else { + Err(()) + } + } +} + +impl From for Value { + fn from(value: f32) -> Self { + Value::Float(value) + } +} + +impl TryFrom for Value { + type Error = (); + fn try_from(value: i64) -> Result { + Ok(Value::Integer(SmallInteger::try_from(value)?)) + } +} + +macro_rules! impl_value_from_n { + ($size: ty) => { + impl From<$size> for Value { + fn from(value: $size) -> Self { + let n: i64 = value.into(); + Value::Integer(SmallInteger::from_i64_unchecked(n)) + } + } + }; +} + +impl_value_from_n!(u8); +impl_value_from_n!(i8); +impl_value_from_n!(u16); +impl_value_from_n!(i16); +impl_value_from_n!(u32); +impl_value_from_n!(i32); diff --git a/nova_vm/src/types/spec.rs b/nova_vm/src/types/spec.rs new file mode 100644 index 00000000..f87bdbda --- /dev/null +++ b/nova_vm/src/types/spec.rs @@ -0,0 +1,3 @@ +mod reference; + +pub use reference::{Base, Reference, ReferencedName}; diff --git a/nova_vm/src/types/spec/bytecode.rs b/nova_vm/src/types/spec/bytecode.rs new file mode 100644 index 00000000..8d7db609 --- /dev/null +++ b/nova_vm/src/types/spec/bytecode.rs @@ -0,0 +1,7 @@ +mod executable; +mod instructions; +mod vm; + +pub use executable::{Executable, IndexType}; +pub use instructions::{Instruction, InstructionIter}; +pub use vm::Vm; diff --git a/nova_vm/src/types/spec/reference.rs b/nova_vm/src/types/spec/reference.rs new file mode 100644 index 00000000..0027e6fb --- /dev/null +++ b/nova_vm/src/types/spec/reference.rs @@ -0,0 +1,66 @@ +use crate::{ + execution::Environment, + types::{Symbol, Value}, +}; +use oxc_span::Atom; + +/// 6.2.5 The Reference Record Specification Type +/// https://tc39.es/ecma262/#sec-reference-record-specification-type +#[derive(Debug)] +pub struct Reference { + /// [[Base]] + pub base: Base, + + /// [[ReferencedName]] + pub referenced_name: ReferencedName, + + /// [[Strict]] + pub strict: bool, + + /// [[ThisValue]] + pub this_value: Option, +} + +impl Reference { + /// 6.2.5.1 IsPropertyReference ( V ) + /// https://tc39.es/ecma262/#sec-ispropertyreference + pub fn is_property_reference(self) -> bool { + match self.base { + // 1. if V.[[Base]] is unresolvable, return false. + Base::Unresolvable => false, + + // 2. If V.[[Base]] is an Environment Record, return false; otherwise return true. + Base::Environment(_) => false, + _ => true, + } + } + + /// 6.2.5.2 IsUnresolvableReference ( V ) + /// https://tc39.es/ecma262/#sec-isunresolvablereference + pub fn is_unresolvable_reference(self) -> bool { + // 1. If V.[[Base]] is unresolvable, return true; otherwise return false. + return matches!(self.base, Base::Unresolvable); + } + + /// 6.2.5.3 IsSuperReference ( V ) + /// https://tc39.es/ecma262/#sec-issuperreference + pub fn is_super_reference(self) -> bool { + // 1. If V.[[ThisValue]] is not empty, return true; otherwise return false. + return !matches!(self.this_value, None); + } +} + +#[derive(Debug)] +pub enum Base { + Value(Value), + Environment(Environment), + Unresolvable, +} + +#[derive(Debug)] +pub enum ReferencedName { + String(Atom), + Symbol(Symbol), + // TODO: implement private names + PrivateName, +} diff --git a/nova_vm/src/value.rs b/nova_vm/src/value.rs index d0f57700..e69de29b 100644 --- a/nova_vm/src/value.rs +++ b/nova_vm/src/value.rs @@ -1,368 +0,0 @@ -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}; - -#[derive(Clone, Copy)] -#[repr(u8)] -pub enum Value { - Array(ArrayIndex), - BigIntObject(u32), // TODO: Implement primitive value objects :( - BooleanObject(u32), // TODO: Implement primitive value objects :( - Boolean(bool), - Date(DateIndex), - EmptyString, - Error(ErrorIndex), - Function(FunctionIndex), - HeapBigInt(BigIntIndex), - HeapNumber(NumberIndex), - HeapString(StringIndex), - Infinity, - NaN, - NegativeInfinity, - NegativeZero, - Null, - NumberObject(u32), // TODO: Implement primitive value objects :( - Object(ObjectIndex), - RegExp(RegExpIndex), - StackString(StackString), - // TOO: Extend these to i56. - SmallBigInt(i32), - SmallBigIntU(u32), - // TODO: Extend these to i48 or even i56. - // i56 would provide safe integer operations - // superior to f64 but is no longer spec-compliant. - Smi(i32), - SmiU(u32), - StringObject(u32), // TODO: Implement primitive value objects :( - Symbol(SymbolIndex), - SymbolObject(u32), // TODO: Implement primitive value objects :( - Undefined, -} - -/// We want to guarantee that all handles to JS values are register sized. This assert must never be removed or broken. -const _VALUE_SIZE_IS_WORD: () = assert!(size_of::() == size_of::()); -// We may also want to keep Option register sized to allow returning it in some cases. -// This may not be possible in the long run and it may not be necessary as we might want to use Undefined instead. -const _OPTIONAL_VALUE_SIZE_IS_WORD: () = assert!(size_of::>() == size_of::()); - -impl Value { - pub fn new_string(heap: &mut Heap, message: &str) -> Value { - if let Some(ascii_string) = StackString::try_from_str(message) { - Value::StackString(ascii_string) - } else { - Value::HeapString(heap.alloc_string(message)) - } - } - - pub fn create_exception(heap: &mut Heap, message: &str) -> Value { - Value::HeapString(heap.alloc_string(message)) - } - - pub fn get_type(&self) -> Type { - match self { - Value::Boolean(_) => Type::Boolean, - Value::EmptyString | Value::HeapString(_) | Value::StackString(_) => Type::String, - Value::Function(_) => Type::Function, - Value::HeapNumber(_) - | Value::Infinity - | Value::NaN - | Value::NegativeInfinity - | Value::NegativeZero - | Value::Smi(_) - | Value::SmiU(_) => Type::Number, - Value::Null => Type::Null, - Value::Object(_) - | Value::Array(_) - | Value::BigIntObject(_) - | Value::BooleanObject(_) - | Value::Date(_) - | Value::Error(_) - | Value::NumberObject(_) - | Value::RegExp(_) - | Value::StringObject(_) - | Value::SymbolObject(_) => Type::Object, - Value::HeapBigInt(_) | Value::SmallBigInt(_) | Value::SmallBigIntU(_) => Type::BigInt, - Value::Symbol(_) => Type::Symbol, - Value::Undefined => Type::Undefined, - } - } - - /// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-islooselyequal - pub fn is_loosely_equal(&self, vm: &mut VM, other: &Value) -> JsResult { - if self.get_type() == other.get_type() { - return self.is_strictly_equal(vm, other); - } - - Ok(match (self, other) { - (Value::Null | Value::Undefined, Value::Null | Value::Undefined) => true, - ( - Value::SmallBigInt(this) | Value::Smi(this), - Value::SmallBigInt(that) | Value::Smi(that), - ) => this == that, - ( - Value::SmallBigIntU(this) | Value::SmiU(this), - Value::SmallBigIntU(that) | Value::SmiU(that), - ) => this == that, - ( - Value::SmallBigInt(this) | Value::Smi(this), - Value::SmallBigIntU(that) | Value::SmiU(that), - ) => *this as u32 == *that, - ( - Value::SmallBigIntU(this) | Value::SmiU(this), - Value::SmallBigInt(that) | Value::Smi(that), - ) => *this == *that as u32, - (&Value::HeapBigInt(x), &Value::HeapNumber(y)) => { - 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.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"), - (Value::HeapString(_), Value::HeapNumber(_)) => todo!("use ToNumber() intrinsics"), - (Value::HeapBigInt(_), Value::HeapString(_)) => { - todo!("use StringToBigInt() intrinsics") - } - (Value::HeapString(_), Value::HeapBigInt(_)) => other.is_loosely_equal(vm, self)?, - (Value::Boolean(_), _) => { - let self_as_f64 = self.try_into_f64(vm)?; - Value::from_f64(&mut vm.heap, self_as_f64).is_loosely_equal(vm, other)? - } - (_, Value::Boolean(_)) => { - let other_as_f64 = other.try_into_f64(vm)?; - Value::from_f64(&mut vm.heap, other_as_f64).is_loosely_equal(vm, self)? - } - ( - Value::HeapString(_) - | Value::HeapNumber(_) - | Value::HeapBigInt(_) - | Value::Symbol(_), - _, - ) => other.is_loosely_equal(vm, &self.to_primitive()?)?, - ( - Value::Object(_), - Value::HeapString(_) - | Value::HeapNumber(_) - | Value::HeapBigInt(_) - | Value::Symbol(_), - ) => self.to_primitive()?.is_loosely_equal(vm, other)?, - _ => false, - }) - } - - /// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-isstrictlyequal - pub fn is_strictly_equal(&self, vm: &VM, other: &Value) -> JsResult { - if self.get_type() != other.get_type() { - return Ok(false); - } - - Ok(match (self, other) { - (Value::SmiU(n1), Value::NegativeZero) | (Value::NegativeZero, Value::SmiU(n1)) => { - *n1 == 0 - } - (Value::Smi(n1) | Value::SmallBigInt(n1), Value::Smi(n2) | Value::SmallBigInt(n2)) => { - n1 == n2 - } - ( - Value::SmiU(n1) | Value::SmallBigIntU(n1), - Value::SmiU(n2) | Value::SmallBigIntU(n2), - ) => n1 == n2, - - (Value::HeapNumber(n1), Value::HeapNumber(n2)) => { - n1 == n2 - || 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 - (Value::Null | Value::Undefined, _) => true, - (Value::HeapBigInt(n1), Value::HeapBigInt(n2)) => n1 == n2, - (Value::HeapString(s1), Value::HeapString(s2)) => { - s1 == s2 - || 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 - (Value::Object(obj1), Value::Object(obj2)) => obj1 == obj2, - _ => false, - }) - } - - pub fn to_primitive(&self) -> JsResult { - Ok(Value::Null) - } - - /// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-toboolean - pub fn to_boolean(&self) -> Value { - match self { - &Value::Boolean(b) => Value::Boolean(b), - &Value::SmiU(n) => Value::Boolean(n == 0), - Value::Null | Value::EmptyString | Value::NaN | Value::NegativeZero => { - Value::Boolean(false) - } - _ => Value::Boolean(true), - } - } - - /// https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tonumber - pub fn to_number(&self, _vm: &mut VM) -> JsResult { - Ok(match self { - Value::HeapNumber(_) - | Value::Smi(_) - | Value::SmiU(_) - | Value::Infinity - | Value::NegativeInfinity - | Value::NegativeZero => self.clone(), - Value::Function(_) - | Value::Symbol(_) - | Value::HeapBigInt(_) - | Value::SmallBigInt(_) - | Value::SmallBigIntU(_) => todo!("type error"), - Value::Undefined | Value::NaN => Value::NaN, - Value::Null | Value::Boolean(false) | Value::EmptyString => Value::SmiU(0), - Value::Boolean(true) => Value::SmiU(1), - Value::StackString(_) | Value::HeapString(_) => todo!("parse number from string"), - Value::Object(_) | Value::Error(_) => todo!("call valueOf"), - _ => todo!("implement primitive value objects :("), - }) - } - - pub fn from_f64(heap: &mut Heap, value: f64) -> Value { - let is_int = value.fract() == 0.0; - if value.is_nan() { - Value::NaN - } else if value.is_infinite() { - if value.is_sign_positive() { - Value::Infinity - } else { - Value::NegativeInfinity - } - } else if !is_int || value > u32::MAX as f64 || value < i32::MIN as f64 { - Value::HeapNumber(heap.alloc_number(value)) - } else if value.is_sign_positive() { - Value::SmiU(value as u32) - } else { - Value::Smi(value as i32) - } - } - - pub fn try_into_f64(&self, vm: &mut VM) -> JsResult { - match self { - &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), - Value::NegativeInfinity => Ok(f64::NEG_INFINITY), - Value::NegativeZero => Ok(0.), - Value::Undefined | Value::NaN => Ok(f64::NAN), - Value::Function(_) - | Value::Symbol(_) - | Value::HeapBigInt(_) - | Value::SmallBigInt(_) - | Value::SmallBigIntU(_) => todo!("type error"), - Value::Null | Value::Boolean(false) | Value::EmptyString => Ok(0.), - Value::Boolean(true) => Ok(1.), - Value::StackString(_) | Value::HeapString(_) => todo!("parse number from string"), - Value::Object(_) => todo!("call valueOf"), - _ => todo!("sigh"), - } - } - - pub fn into_bool(&self) -> bool { - match self { - &Value::Boolean(b) => b, - &Value::SmiU(n) => n == 0, - Value::Null | Value::EmptyString | Value::NaN | Value::NegativeZero => false, - _ => true, - } - } - - pub fn from_u32(value: u32) -> Value { - Value::SmiU(value) - } - - pub fn from_i32(value: i32) -> Value { - if value >= 0 { - Value::from_u32(value as u32) - } else { - Value::Smi(value) - } - } - - pub fn is_undefined(&self) -> bool { - match self { - Value::Undefined => true, - _ => false, - } - } - - pub fn is_null(&self) -> bool { - match self { - Value::Null => true, - _ => false, - } - } - - pub fn is_number(&self) -> bool { - match self { - Value::Smi(_) => true, - Value::SmiU(_) => true, - Value::NaN => true, - Value::Infinity => true, - Value::NegativeInfinity => true, - Value::NegativeZero => true, - Value::HeapNumber(_) => true, - _ => false, - } - } -} - -pub type JsResult = std::result::Result; - -impl Debug for Value { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Value::Array(arg0) => f.debug_tuple("Array").field(arg0).finish(), - Value::BigIntObject(arg0) => f.debug_tuple("BigIntObject").field(arg0).finish(), - Value::Boolean(arg0) => f.debug_tuple("Boolean").field(arg0).finish(), - Value::BooleanObject(arg0) => f.debug_tuple("BooleanObject").field(arg0).finish(), - Value::EmptyString => write!(f, "EmptyString"), - Value::Date(arg0) => f.debug_tuple("Date").field(arg0).finish(), - Value::Error(arg0) => f.debug_tuple("Error").field(arg0).finish(), - Value::Function(arg0) => f.debug_tuple("Function").field(arg0).finish(), - Value::HeapBigInt(arg0) => f.debug_tuple("BigInt").field(arg0).finish(), - Value::HeapNumber(arg0) => f.debug_tuple("Number").field(arg0).finish(), - Value::HeapString(arg0) => f.debug_tuple("String").field(arg0).finish(), - Value::Infinity => write!(f, "Infinity"), - Value::NaN => write!(f, "NaN"), - Value::NegativeInfinity => write!(f, "-Infinity"), - Value::NegativeZero => write!(f, "-0"), - Value::Null => write!(f, "Null"), - Value::NumberObject(arg0) => f.debug_tuple("NumberObject").field(arg0).finish(), - Value::Object(arg0) => f.debug_tuple("Object").field(arg0).finish(), - Value::RegExp(arg0) => f.debug_tuple("RegExp").field(arg0).finish(), - Value::StackString(arg0) => f.debug_tuple("SmallAsciiString").field(arg0).finish(), - Value::SmallBigInt(arg0) => f.debug_tuple("SmallBigInt").field(arg0).finish(), - Value::SmallBigIntU(arg0) => f.debug_tuple("SmallBigIntU").field(arg0).finish(), - Value::Smi(arg0) => f.debug_tuple("Smi").field(arg0).finish(), - Value::SmiU(arg0) => f.debug_tuple("SmiU").field(arg0).finish(), - Value::StringObject(arg0) => f.debug_tuple("StringObject").field(arg0).finish(), - Value::Symbol(arg0) => f.debug_tuple("Symbol").field(arg0).finish(), - Value::SymbolObject(arg0) => f.debug_tuple("SymbolObject").field(arg0).finish(), - Value::Undefined => write!(f, "Undefined"), - } - } -} From 3da9393f08dc9c79972c9649bdea78715c237f78 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 11:16:24 -0500 Subject: [PATCH 02/32] fix small string --- nova_vm/src/small_string.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index e51be0bd..278c68d3 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -25,7 +25,7 @@ impl SmallString { pub(crate) fn from_str_unchecked(value: &str) -> Self { let len = value.len(); - assert!(len < 8); + assert!(len < 8 && !value.as_bytes().contains(&0)); let mut data: [u8; 7] = [0, 0, 0, 0, 0, 0, 0]; let data_slice = &mut data.as_mut_slice()[0..len]; data_slice.copy_from_slice(value.as_bytes()); @@ -36,7 +36,7 @@ impl SmallString { impl TryFrom<&str> for SmallString { type Error = (); fn try_from(value: &str) -> Result { - if value.len() < 8 { + if value.len() < 8 && !value.as_bytes().contains(&0) { Ok(Self::from_str_unchecked(value)) } else { Err(()) From 17157430e93958db67767872b0b6f339233cf321 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 11:52:23 -0500 Subject: [PATCH 03/32] fix small string Co-authored-by: Aapo Alasuutari --- nova_vm/src/small_string.rs | 77 ++++++++++++++++++++++++++++++------- 1 file changed, 63 insertions(+), 14 deletions(-) diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index 278c68d3..dbfad321 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -1,6 +1,6 @@ #[derive(Clone, Copy)] pub struct SmallString { - data: [u8; 7], + bytes: [u8; 7], } impl std::fmt::Debug for SmallString { @@ -11,35 +11,84 @@ impl std::fmt::Debug for SmallString { impl SmallString { pub fn len(&self) -> usize { - self.data.iter().position(|byte| *byte == 0).unwrap_or(7) + // Find the last non-null character and add one to its index to get length. + self.bytes + .as_slice() + .iter() + .rev() + .position(|&x| x != 0) + .map_or(0, |i| 7 - i) } + #[inline] + pub fn as_str(&self) -> &str { + // SAFETY: Guaranteed to be ASCII, which is a subset of UTF-8. + unsafe { &std::str::from_utf8_unchecked(self.as_bytes()) } + } + + #[inline] pub fn as_bytes(&self) -> &[u8] { - &self.data[0..self.len()] + &self.bytes.as_slice().split_at(self.len()).0 } - pub fn as_str(&self) -> &str { - // SAFETY: Guaranteed to be valid UTF-8. - unsafe { std::str::from_utf8_unchecked(self.as_bytes()) } + #[inline] + pub fn data(&self) -> &[u8; 7] { + return &self.bytes; } - pub(crate) fn from_str_unchecked(value: &str) -> Self { - let len = value.len(); - assert!(len < 8 && !value.as_bytes().contains(&0)); - let mut data: [u8; 7] = [0, 0, 0, 0, 0, 0, 0]; - let data_slice = &mut data.as_mut_slice()[0..len]; - data_slice.copy_from_slice(value.as_bytes()); - Self { data } + pub(crate) fn from_str_unchecked(string: &str) -> Self { + let string_bytes = string.as_bytes(); + + // We have only 7 bytes to work with, and we cannot tell apart + // UTF-8 strings that end with a null byte from our null + // terminator so we must fail to convert on those. + debug_assert!(string_bytes.len() < 8 && string_bytes.last() != Some(&0)); + + let mut bytes = [0, 0, 0, 0, 0, 0, 0]; + bytes + .as_mut_slice() + .split_at_mut(string_bytes.len()) + .0 + .copy_from_slice(string_bytes); + + Self { bytes } } } impl TryFrom<&str> for SmallString { type Error = (); fn try_from(value: &str) -> Result { - if value.len() < 8 && !value.as_bytes().contains(&0) { + // We have only 7 bytes to work with, and we cannot tell apart + // UTF-8 strings that end with a null byte from our null + // terminator so we must fail to convert on those. + if value.len() < 8 && value.as_bytes().last() != Some(&0) { Ok(Self::from_str_unchecked(value)) } else { Err(()) } } } + +#[test] +fn valid_stack_strings() { + assert!(SmallString::try_from("").is_ok()); + assert_eq!(SmallString::try_from("").unwrap().len(), 0); + assert!(SmallString::try_from("asd").is_ok()); + assert_eq!(SmallString::try_from("asd").unwrap().len(), 3); + assert!(SmallString::try_from("asdasd").is_ok()); + assert_eq!(SmallString::try_from("asdasd").unwrap().len(), 6); + assert!(SmallString::try_from("asdasda").is_ok()); + assert_eq!(SmallString::try_from("asdasda").unwrap().len(), 7); + assert!(SmallString::try_from("asd76fd").is_ok()); + assert_eq!(SmallString::try_from("asd76fd").unwrap().len(), 7); + assert!(SmallString::try_from("💩").is_ok()); + assert_eq!(SmallString::try_from("💩 ").unwrap().len(), 5); + assert!(SmallString::try_from("asd\0foo").is_ok()); + assert_eq!(SmallString::try_from("asd\0foo").unwrap().len(), 7); +} + +#[test] +fn not_valid_stack_strings() { + assert!(SmallString::try_from("asd asd r 547 gdfg").is_err()); + assert!(SmallString::try_from("asdfoo\0").is_err()); +} From fcf032178b453997f4a350ae44f0c5a4ada8c0e9 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 12:54:40 -0500 Subject: [PATCH 04/32] fix small integer Co-authored-by: Aapo Alasuutari --- nova_vm/src/small_integer.rs | 67 ++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 15 deletions(-) diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs index d19560ac..46df5fdc 100644 --- a/nova_vm/src/small_integer.rs +++ b/nova_vm/src/small_integer.rs @@ -1,5 +1,5 @@ /// 56-bit signed integer. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub struct SmallInteger { data: [u8; 7], } @@ -11,18 +11,23 @@ impl std::fmt::Debug for SmallInteger { } impl SmallInteger { - pub const MIN: i64 = -(2 as i64).pow(56) / 2 + 1; - pub const MAX: i64 = (2 as i64).pow(56) / 2 - 1; + pub const MIN: i64 = -(2 as i64).pow(53) / 2 + 1; + pub const MAX: i64 = (2 as i64).pow(53) / 2 - 1; pub(crate) fn from_i64_unchecked(value: i64) -> SmallInteger { debug_assert!(value >= Self::MIN && value <= Self::MAX); - let mut data: [u8; 7] = [0, 0, 0, 0, 0, 0, 0]; let bytes = i64::to_ne_bytes(value); - if cfg!(target_endian = "little") { - data.copy_from_slice(&bytes[0..7]); + + let data = if cfg!(target_endian = "little") { + [ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], + ] } else { - data.copy_from_slice(&bytes[1..8]); - } + [ + bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + ] + }; + Self { data } } } @@ -41,13 +46,45 @@ impl TryFrom for SmallInteger { impl Into for SmallInteger { fn into(self) -> i64 { let Self { data } = self; - let mut bytes: [u8; 8] = [0, 0, 0, 0, 0, 0, 0, 0]; - let bytes_slice = &mut bytes.as_mut_slice()[0..7]; - bytes_slice.copy_from_slice(data.as_slice()); - if cfg!(target_endian = "big") { - bytes.copy_within(0..7, 1); + + #[repr(u8)] + enum Repr { + Data([u8; 7]), + } + + // SAFETY: This matches the format on the endian platform. + let number: i64 = unsafe { std::mem::transmute(Repr::Data(data)) }; + + if cfg!(target_endian = "little") { + number >> 8 + } else { + number << 8 >> 8 } - // SAFETY: The format is guaranteed to match `from_i64_unchecked`. - unsafe { std::mem::transmute(bytes) } } } + +#[test] +fn valid_small_integers() { + assert_eq!(0i64, SmallInteger::try_from(0).unwrap().into()); + assert_eq!(5i64, SmallInteger::try_from(5).unwrap().into()); + assert_eq!(23i64, SmallInteger::try_from(23).unwrap().into()); + assert_eq!( + SmallInteger::MAX, + SmallInteger::try_from(SmallInteger::MAX).unwrap().into() + ); + + assert_eq!(-5i64, SmallInteger::try_from(-5).unwrap().into()); + assert_eq!(-59i64, SmallInteger::try_from(-59).unwrap().into()); + assert_eq!( + SmallInteger::MIN, + SmallInteger::try_from(SmallInteger::MIN).unwrap().into() + ); +} + +#[test] +fn invalid_small_integers() { + assert_eq!(SmallInteger::try_from(SmallInteger::MAX + 1), Err(())); + assert_eq!(SmallInteger::try_from(i64::MAX), Err(())); + assert_eq!(SmallInteger::try_from(SmallInteger::MIN - 1), Err(())); + assert_eq!(SmallInteger::try_from(i64::MIN), Err(())); +} From 86565b7d8b41863cba6565b646072278e8b4d06b Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 22:07:25 -0500 Subject: [PATCH 05/32] more work --- nova_vm/src/execution/agent.rs | 16 + nova_vm/src/execution/realm.rs | 2 +- nova_vm/src/heap.rs | 59 ++- nova_vm/src/small_integer.rs | 5 + nova_vm/src/small_string.rs | 4 +- nova_vm/src/types.rs | 2 +- nova_vm/src/types/language.rs | 6 + nova_vm/src/types/language/bigint.rs | 4 + nova_vm/src/types/language/number.rs | 663 +++++++++++++++++++++++++++ nova_vm/src/types/language/string.rs | 88 ++++ nova_vm/src/types/language/value.rs | 488 +++++++++++++++++++- 11 files changed, 1313 insertions(+), 24 deletions(-) create mode 100644 nova_vm/src/types/language/bigint.rs create mode 100644 nova_vm/src/types/language/number.rs create mode 100644 nova_vm/src/types/language/string.rs diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs index e34d42c1..005af431 100644 --- a/nova_vm/src/execution/agent.rs +++ b/nova_vm/src/execution/agent.rs @@ -41,4 +41,20 @@ impl Agent<'_, '_> { pub fn current_realm(&self) -> &mut Realm { todo!() } + + /// 5.2.3.2 Throw an Exception + /// https://tc39.es/ecma262/#sec-throw-an-exception + pub fn throw_exception(&mut self, kind: ExceptionType, message: &'static str) -> () { + todo!() + } +} + +#[derive(Debug)] +pub enum ExceptionType { + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + UriError, } diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index c2d12cb0..14a4827c 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -1,7 +1,7 @@ mod intrinsics; use super::{Agent, GlobalEnvironment}; -use crate::types::Object; +use crate::{types::Object, Heap}; use intrinsics::Intrinsics; use std::{any::Any, cell::RefCell, rc::Rc}; diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 77eff022..f33c1803 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -32,7 +32,7 @@ use self::{ BuiltinObjectIndexes, FIRST_CONSTRUCTOR_INDEX, LAST_BUILTIN_OBJECT_INDEX, LAST_WELL_KNOWN_SYMBOL_INDEX, }, - indexes::{FunctionIndex, NumberIndex, ObjectIndex, StringIndex}, + indexes::{BaseIndex, FunctionIndex, NumberIndex, ObjectIndex, StringIndex}, math::initialize_math_object, number::{initialize_number_heap, NumberHeapData}, object::{ @@ -42,7 +42,7 @@ use self::{ string::{initialize_string_heap, StringHeapData}, symbol::{initialize_symbol_heap, SymbolHeapData}, }; -use crate::types::Value; +use crate::types::{Number, String, Value}; use wtf8::Wtf8; #[derive(Debug)] @@ -64,34 +64,65 @@ pub struct Heap { pub(crate) symbols: Vec>, } -/// Creates a [`Value`] from the given data. Allocating the data is **not** -/// guaranteed. -pub trait CreateHeapData { - fn create(&mut self, data: T) -> Value; +pub trait CreateHeapData { + /// Creates a [`Value`] from the given data. Allocating the data is **not** + /// guaranteed. + fn create(&mut self, data: T) -> F; } -impl CreateHeapData for Heap { - fn create(&mut self, data: f64) -> Value { +pub trait GetHeapData<'a, T, F: 'a> { + fn get(&'a self, handle: BaseIndex) -> F; +} + +impl CreateHeapData for Heap { + fn create(&mut self, data: f64) -> Number { if let Ok(value) = Value::try_from(data) { - value + Number::new(value) + } else if data as f32 as f64 == data { + Number::new(Value::Float(data as f32)) } else { let id = self.alloc_number(data); - Value::Number(id) + Value::Number(id).try_into().unwrap() } } } -impl CreateHeapData<&str> for Heap { - fn create(&mut self, data: &str) -> Value { - if let Ok(value) = Value::try_from(data) { +impl<'a> GetHeapData<'a, NumberHeapData, f64> for Heap { + fn get(&'a self, id: NumberIndex) -> f64 { + self.numbers + .get(id.into_index()) + .as_ref() + .unwrap() + .as_ref() + .unwrap() + .data + } +} + +impl CreateHeapData<&str, String> for Heap { + fn create(&mut self, data: &str) -> String { + if let Ok(value) = String::try_from(data) { value } else { let id = self.alloc_string(data); - Value::String(id) + Value::String(id).try_into().unwrap() } } } +impl<'a> GetHeapData<'a, StringHeapData, &'a Wtf8> for Heap { + fn get(&'a self, id: StringIndex) -> &'a Wtf8 { + let data = self + .strings + .get(id.into_index()) + .as_ref() + .unwrap() + .as_ref() + .unwrap(); + &data.data.slice(0, data.data.len()) + } +} + impl Heap { pub fn new() -> Heap { let mut heap = Heap { diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs index 46df5fdc..c37a726a 100644 --- a/nova_vm/src/small_integer.rs +++ b/nova_vm/src/small_integer.rs @@ -14,6 +14,11 @@ impl SmallInteger { pub const MIN: i64 = -(2 as i64).pow(53) / 2 + 1; pub const MAX: i64 = (2 as i64).pow(53) / 2 - 1; + #[inline] + pub fn into_i64(self) -> i64 { + self.into() + } + pub(crate) fn from_i64_unchecked(value: i64) -> SmallInteger { debug_assert!(value >= Self::MIN && value <= Self::MAX); let bytes = i64::to_ne_bytes(value); diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index dbfad321..39a8ee9d 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -17,12 +17,12 @@ impl SmallString { .iter() .rev() .position(|&x| x != 0) - .map_or(0, |i| 7 - i) + .unwrap_or(7) } #[inline] pub fn as_str(&self) -> &str { - // SAFETY: Guaranteed to be ASCII, which is a subset of UTF-8. + // SAFETY: Guaranteed to be UTF-8. unsafe { &std::str::from_utf8_unchecked(self.as_bytes()) } } diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs index f7f88309..44eb1d78 100644 --- a/nova_vm/src/types.rs +++ b/nova_vm/src/types.rs @@ -1,7 +1,7 @@ mod language; mod spec; -pub use language::Value; +pub use language::{Number, String, Value}; pub use spec::{Base, Reference, ReferencedName}; impl From for Value { diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs index f96f3e0c..05e805eb 100644 --- a/nova_vm/src/types/language.rs +++ b/nova_vm/src/types/language.rs @@ -1,3 +1,9 @@ +mod bigint; +mod number; +mod string; mod value; +pub use bigint::BigInt; +pub use number::Number; +pub use string::String; pub use value::Value; diff --git a/nova_vm/src/types/language/bigint.rs b/nova_vm/src/types/language/bigint.rs new file mode 100644 index 00000000..9e8efb01 --- /dev/null +++ b/nova_vm/src/types/language/bigint.rs @@ -0,0 +1,4 @@ +use super::Value; + +#[derive(Clone, Copy)] +pub struct BigInt(Value); diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs new file mode 100644 index 00000000..9fdfabe4 --- /dev/null +++ b/nova_vm/src/types/language/number.rs @@ -0,0 +1,663 @@ +use super::Value; +use crate::{ + execution::{Agent, JsResult}, + heap::{CreateHeapData, GetHeapData}, + SmallInteger, +}; + +/// 6.1.6.1 The Number Type +/// https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type +#[derive(Clone, Copy)] +pub struct Number(Value); + +impl std::fmt::Debug for Number { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl From for Number { + fn from(value: SmallInteger) -> Self { + Number(Value::Integer(value)) + } +} + +impl From for Number { + fn from(value: i32) -> Self { + Number(Value::Integer(SmallInteger::from_i64_unchecked( + value as i64, + ))) + } +} + +impl From for Number { + fn from(value: i64) -> Self { + let n = value.min(SmallInteger::MAX).max(SmallInteger::MIN); + Number(Value::Integer(SmallInteger::from_i64_unchecked(n))) + } +} + +impl From for Number { + fn from(value: f32) -> Self { + Number(Value::Float(value)) + } +} + +impl TryFrom for Number { + type Error = (); + fn try_from(value: Value) -> Result { + if matches!( + value, + Value::Number(_) | Value::Integer(_) | Value::Float(_) + ) { + Ok(Number(value)) + } else { + Err(()) + } + } +} + +impl Number { + pub(crate) fn new(value: Value) -> Self { + debug_assert!(matches!( + value, + Value::Number(_) | Value::Integer(_) | Value::Float(_) + )); + Self(value) + } + + pub fn nan() -> Self { + Self::from(f32::NAN) + } + + pub fn neg_zero() -> Self { + Self::from(-0.0) + } + + pub fn pos_zero() -> Self { + Self::from(0) + } + + pub fn pos_inf() -> Self { + Self::from(f32::INFINITY) + } + + pub fn neg_inf() -> Self { + Self::from(f32::NEG_INFINITY) + } + + pub fn into_value(self) -> Value { + self.0 + } + + pub fn is_nan(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n).is_nan(), + Value::Integer(_) => false, + Value::Float(n) => n.is_nan(), + _ => unreachable!(), + } + } + + pub fn is_pos_zero(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n).is_sign_positive(), + Value::Integer(n) => 0i64 == n.into(), + Value::Float(n) => n.is_sign_positive(), + _ => unreachable!(), + } + } + + pub fn is_neg_zero(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n).is_sign_negative(), + Value::Integer(_) => false, + Value::Float(n) => n.is_sign_negative(), + _ => unreachable!(), + } + } + + pub fn is_pos_infinity(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n) == f64::INFINITY, + Value::Integer(_) => false, + Value::Float(n) => n == f32::INFINITY, + _ => unreachable!(), + } + } + + pub fn is_neg_infinity(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n) == f64::NEG_INFINITY, + Value::Integer(_) => false, + Value::Float(n) => n == f32::NEG_INFINITY, + _ => unreachable!(), + } + } + + pub fn is_finite(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n).is_finite(), + Value::Integer(_) => true, + Value::Float(n) => n.is_finite(), + _ => unreachable!(), + } + } + + pub fn is_nonzero(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => { + let n = agent.heap.get(n); + !n.is_sign_negative() && !n.is_sign_positive() + } + Value::Integer(_) => true, + Value::Float(n) => !n.is_sign_negative() && !n.is_sign_positive(), + _ => unreachable!(), + } + } + + /// https://tc39.es/ecma262/#eqn-truncate + /// + /// Guaranteed to be in the valid [`Value::Integer`] range. + pub fn truncate(self, agent: &mut Agent) -> i64 { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n).trunc() as i64, + Value::Integer(n) => n.into_i64(), + Value::Float(n) => n.trunc() as i64, + _ => unreachable!(), + } + } + + pub fn into_f64(self, agent: &Agent) -> f64 { + let x = self.into_value(); + + match x { + Value::Number(n) => agent.heap.get(n), + Value::Integer(n) => Into::::into(n) as f64, + Value::Float(n) => n as f64, + _ => unreachable!(), + } + } + + /// A minimal version of ObjectIs when you know the arguments are numbers. + pub fn is(self, agent: &mut Agent, y: Self) -> bool { + // TODO: Add in spec from Object.is pertaining to numbers. + let x = self.into_value(); + let y = y.into_value(); + + match (x, y) { + (Value::Number(x), Value::Number(y)) => agent.heap.get(x) == agent.heap.get(y), + (Value::Number(x), Value::Integer(y)) => agent.heap.get(x) == y.into_i64() as f64, + (Value::Number(x), Value::Float(y)) => agent.heap.get(x) == y as f64, + (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) == agent.heap.get(y), + (Value::Integer(x), Value::Integer(y)) => x.into_i64() == y.into_i64(), + (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) == y as f64, + (Value::Float(x), Value::Number(y)) => (x as f64) == agent.heap.get(y), + (Value::Float(x), Value::Integer(y)) => (x as f64) == y.into_i64() as f64, + (Value::Float(x), Value::Float(y)) => x == y, + _ => unreachable!(), + } + } + + pub fn is_odd_integer(self, agent: &mut Agent) -> bool { + let x = self.into_value(); + + match x { + Value::Number(n) => { + let n = agent.heap.get(n); + n % 1.0 == 0.0 && n % 2.0 == 0.0 + } + Value::Integer(n) => Into::::into(n) % 2 == 0, + Value::Float(n) => n % 1.0 == 0.0 && n % 2.0 == 0.0, + _ => unreachable!(), + } + } + + pub fn abs(self, agent: &mut Agent) -> Self { + let x = self.into_value(); + + match x { + Value::Number(n) => { + let n = agent.heap.get(n); + if n > 0.0 { + self + } else { + agent.heap.create(-n) + } + } + Value::Integer(n) => { + let n = n.into_i64(); + Number(Value::Integer(SmallInteger::from_i64_unchecked(n.abs()))) + } + Value::Float(n) => Number(Value::Float(n.abs())), + _ => unreachable!(), + } + } + + pub fn greater_than(self, agent: &mut Agent, y: Self) -> Value { + let x = self; + y.less_than(agent, x) + } + + /// 6.1.6.1.1 Number::unaryMinus ( x ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-unaryMinus + pub fn unary_minus(self, agent: &mut Agent) -> Self { + let x = self.into_value(); + + // 1. If x is NaN, return NaN. + // NOTE: Computers do this automatically. + + // 2. Return the result of negating x; that is, compute a Number with the same magnitude but opposite sign. + match x { + Value::Number(n) => agent.heap.create(-agent.heap.get(n)), + Value::Integer(n) => SmallInteger::from_i64_unchecked(-n.into_i64()).into(), + Value::Float(n) => (-n).into(), + _ => unreachable!(), + } + } + + /// 6.1.6.1.2 Number::bitwiseNOT ( x ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-bitwiseNOT + pub fn bitwise_not(self, agent: &mut Agent) -> JsResult { + let x = self.into_value(); + + // 1. Let oldValue be ! ToInt32(x). + let old_value = x.to_int32(agent)?; + + // 2. Return the result of applying bitwise complement to oldValue. The mathematical value of the result is exactly representable as a 32-bit two's complement bit string. + Ok(Number::from(!old_value)) + } + + /// 6.1.6.1.3 Number::exponentiate ( base, exponent ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-exponentiate + pub fn exponentiate(self, agent: &mut Agent, exponent: Self) -> Self { + let base = self; + + // 1. If exponent is NaN, return NaN. + if exponent.is_nan(agent) { + return Number::nan(); + } + + // 2. If exponent is either +0𝔽 or -0𝔽, return 1𝔽. + if exponent.is_pos_zero(agent) || exponent.is_neg_zero(agent) { + return Number::from(1); + } + + // 3. If base is NaN, return NaN. + if base.is_nan(agent) { + return Number::nan(); + } + + // 4. If base is +∞𝔽, then + if base.is_pos_infinity(agent) { + // a. If exponent > +0𝔽, return +∞𝔽. Otherwise, return +0𝔽. + return if exponent.greater_than(agent, Number::from(0)).is_true() { + Number::pos_inf() + } else { + Number::pos_zero() + }; + } + + // 5. If base is -∞𝔽, then + if base.is_neg_infinity(agent) { + // a. If exponent > +0𝔽, then + return if exponent.greater_than(agent, 0.into()).is_true() { + // i. If exponent is an odd integral Number, return -∞𝔽. Otherwise, return +∞𝔽. + if exponent.is_odd_integer(agent) { + Number::neg_inf() + } else { + Number::pos_inf() + } + } + // b. Else, + else { + // i. If exponent is an odd integral Number, return -0𝔽. Otherwise, return +0𝔽. + if exponent.is_odd_integer(agent) { + Number::neg_zero() + } else { + Number::pos_zero() + } + }; + } + + // 6. If base is +0𝔽, then + if base.is_pos_zero(agent) { + // a. If exponent > +0𝔽, return +0𝔽. Otherwise, return +∞𝔽. + return if exponent.greater_than(agent, Number::pos_zero()).is_true() { + Number::pos_zero() + } else { + Number::pos_inf() + }; + } + + // 7. If base is -0𝔽, then + if base.is_neg_zero(agent) { + // a. If exponent > +0𝔽, then + return if exponent.greater_than(agent, Number::pos_zero()).is_true() { + // i. If exponent is an odd integral Number, return -0𝔽. Otherwise, return +0𝔽. + if exponent.is_odd_integer(agent) { + Number::neg_zero() + } else { + Number::pos_zero() + } + } + // b. Else, + else { + // i. If exponent is an odd integral Number, return -∞𝔽. Otherwise, return +∞𝔽. + if exponent.is_odd_integer(agent) { + Number::neg_inf() + } else { + Number::pos_inf() + } + }; + } + + // 8. Assert: base is finite and is neither +0𝔽 nor -0𝔽. + debug_assert!(base.is_finite(agent) && base.is_nonzero(agent)); + + // 9. If exponent is +∞𝔽, then + if exponent.is_pos_infinity(agent) { + let base = base.abs(agent); + + // a. If abs(ℝ(base)) > 1, return +∞𝔽. + return if base.greater_than(agent, Number::from(1)).is_true() { + Number::pos_inf() + } + // b. If abs(ℝ(base)) = 1, return NaN. + else if base.is(agent, Number::from(1)) { + Number::nan() + } + // c. If abs(ℝ(base)) < 1, return +0𝔽. + else { + Number::pos_zero() + }; + } + + // 10. If exponent is -∞𝔽, then + if exponent.is_neg_infinity(agent) { + let base = base.into_f64(agent).abs(); + + // a. If abs(ℝ(base)) > 1, return +0𝔽. + return if base > 1.0 { + Number::pos_inf() + } + // b. If abs(ℝ(base)) = 1, return NaN. + else if base == 1.0 { + Number::nan() + } + // c. If abs(ℝ(base)) < 1, return +∞𝔽. + else { + Number::pos_inf() + }; + } + + // 11. Assert: exponent is finite and is neither +0𝔽 nor -0𝔽. + debug_assert!(exponent.is_finite(agent) && exponent.is_nonzero(agent)); + + // 12. If base < -0𝔽 and exponent is not an integral Number, return NaN. + if base.less_than(agent, Number::neg_zero()).is_true() && !exponent.is_odd_integer(agent) { + return Number::nan(); + } + + // 13. Return an implementation-approximated Number value representing the result of raising ℝ(base) to the ℝ(exponent) power. + agent + .heap + .create(base.into_f64(agent).powf(exponent.into_f64(agent))) + } + + // ... + + /// 6.1.6.1.12 Number::lessThan ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-lessThan + pub fn less_than(self, agent: &mut Agent, y: Self) -> Value { + let x = self; + + // 1. If x is NaN, return undefined. + if x.is_nan(agent) { + return Value::Undefined; + } + + // 2. If y is NaN, return undefined. + if y.is_nan(agent) { + return Value::Undefined; + } + + // 3. If x is y, return false. + if x.is(agent, y) { + return false.into(); + } + + // 4. If x is +0𝔽 and y is -0𝔽, return false. + if x.is_pos_zero(agent) && y.is_neg_zero(agent) { + return false.into(); + } + + // 5. If x is -0𝔽 and y is +0𝔽, return false. + if x.is_neg_zero(agent) && y.is_pos_zero(agent) { + return false.into(); + } + + // 6. If x is +∞𝔽, return false. + if x.is_pos_infinity(agent) { + return false.into(); + } + + // 7. If y is +∞𝔽, return true. + if y.is_pos_infinity(agent) { + return true.into(); + } + + // 8. If y is -∞𝔽, return false. + if y.is_neg_infinity(agent) { + return false.into(); + } + + // 9. If x is -∞𝔽, return true. + if x.is_neg_infinity(agent) { + return true.into(); + } + + // 10. Assert: x and y are finite and non-zero. + debug_assert!( + x.is_finite(agent) && x.is_nonzero(agent) && y.is_finite(agent) && y.is_nonzero(agent) + ); + + // 11. If ℝ(x) < ℝ(y), return true. Otherwise, return false. + Value::Boolean(match (x.into_value(), y.into_value()) { + (Value::Number(x), Value::Number(y)) => agent.heap.get(x) < agent.heap.get(y), + (Value::Number(x), Value::Integer(y)) => agent.heap.get(x) < y.into_i64() as f64, + (Value::Number(x), Value::Float(y)) => agent.heap.get(x) < y as f64, + (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) < agent.heap.get(y), + (Value::Integer(x), Value::Integer(y)) => x.into_i64() < y.into_i64(), + (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) < y as f64, + (Value::Float(x), Value::Number(y)) => (x as f64) < agent.heap.get(y), + (Value::Float(x), Value::Integer(y)) => (x as f64) < y.into_i64() as f64, + (Value::Float(x), Value::Float(y)) => x < y, + _ => unreachable!(), + }) + } + + /// 6.1.6.1.13 Number::equal ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-equal + pub fn equal(self, agent: &mut Agent, y: Self) -> bool { + let x = self; + + // 1. If x is NaN, return false. + if x.is_nan(agent) { + return false; + } + + // 2. If y is NaN, return false. + if y.is_nan(agent) { + return false; + } + + // 3. If x is y, return true. + if x.is(agent, y) { + return true; + } + + // 4. If x is +0𝔽 and y is -0𝔽, return true. + if x.is_pos_zero(agent) && y.is_neg_zero(agent) { + return true; + } + + // 5. If x is -0𝔽 and y is +0𝔽, return true. + if x.is_neg_zero(agent) && y.is_pos_zero(agent) { + return true; + } + + // 6. Return false. + return false; + } + + /// 6.1.6.1.14 Number::sameValue ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-sameValue + pub fn same_value(self, agent: &mut Agent, y: Self) -> bool { + let x = self; + + // 1. If x is NaN and y is NaN, return true. + if x.is_nan(agent) && y.is_nan(agent) { + return true; + } + + // 2. If x is +0𝔽 and y is -0𝔽, return false. + if x.is_pos_zero(agent) && y.is_neg_zero(agent) { + return false; + } + + // 3. If x is -0𝔽 and y is +0𝔽, return false. + if x.is_neg_zero(agent) && y.is_pos_zero(agent) { + return false; + } + + // 4. If x is y, return true. + if x.is(agent, y) { + return true; + } + + // 5. Return false. + return false; + } + + /// 6.1.6.1.15 Number::sameValueZero ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-sameValueZero + pub fn same_value_zero(self, agent: &mut Agent, y: Self) -> bool { + let x = self; + + // 1. If x is NaN and y is NaN, return true. + if x.is_nan(agent) && y.is_nan(agent) { + return true; + } + + // 2. If x is +0𝔽 and y is -0𝔽, return true. + if x.is_pos_zero(agent) && y.is_neg_zero(agent) { + return true; + } + + // 3. If x is -0𝔽 and y is +0𝔽, return true. + if x.is_neg_zero(agent) && y.is_pos_zero(agent) { + return true; + } + + // 4. If x is y, return true. + if x.is(agent, y) { + return true; + } + + // 5. Return false. + return false; + } + + /// 6.1.6.1.16 NumberBitwiseOp ( op, x, y ) + /// https://tc39.es/ecma262/#sec-numberbitwiseop + pub fn bitwise_op(self, agent: &mut Agent, op: BitwiseOp, y: Self) -> JsResult { + let x = self; + + // 1. Let lnum be ! ToInt32(x). + let lnum = x.into_value().to_int32(agent)?; + + // 2. Let rnum be ! ToInt32(y). + let rnum = y.into_value().to_int32(agent)?; + + // 3. Let lbits be the 32-bit two's complement bit string representing ℝ(lnum). + let lbits = lnum; + + // 4. Let rbits be the 32-bit two's complement bit string representing ℝ(rnum). + let rbits = rnum; + + let result = match op { + // 5. If op is &, then + BitwiseOp::And => { + // a. Let result be the result of applying the bitwise AND operation to lbits and rbits. + lbits & rbits + } + // 6. Else if op is ^, then + BitwiseOp::Xor => { + // a. Let result be the result of applying the bitwise exclusive OR (XOR) operation to lbits and rbits. + lbits ^ rbits + } + // 7. Else, + // a. Assert: op is |. + BitwiseOp::Or => { + // b. Let result be the result of applying the bitwise inclusive OR operation to lbits and rbits. + lbits | rbits + } + }; + + // 8. Return the Number value for the integer represented by the 32-bit two's complement bit string result. + Ok(Number::from(result)) + } + + /// 6.1.6.1.17 Number::bitwiseAND ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-bitwiseAND + pub fn bitwise_and(self, agent: &mut Agent, y: Self) -> JsResult { + let x = self; + + // 1. Return NumberBitwiseOp(&, x, y). + x.bitwise_op(agent, BitwiseOp::And, y) + } + + /// 6.1.6.1.18 Number::bitwiseXOR ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-bitwiseXOR + pub fn bitwise_xor(self, agent: &mut Agent, y: Self) -> JsResult { + let x = self; + + // 1. Return NumberBitwiseOp(^, x, y). + x.bitwise_op(agent, BitwiseOp::Xor, y) + } + + /// 6.1.6.1.19 Number::bitwiseOR ( x, y ) + /// https://tc39.es/ecma262/#sec-numeric-types-number-bitwiseOR + pub fn bitwise_or(self, agent: &mut Agent, y: Self) -> JsResult { + let x = self; + + // 1. Return NumberBitwiseOp(|, x, y). + x.bitwise_op(agent, BitwiseOp::Or, y) + } + + // ... +} + +#[derive(Debug, Clone, Copy)] +pub enum BitwiseOp { + And, + Xor, + Or, +} diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs new file mode 100644 index 00000000..ee467fde --- /dev/null +++ b/nova_vm/src/types/language/string.rs @@ -0,0 +1,88 @@ +use super::Value; +use crate::{execution::Agent, heap::GetHeapData, SmallString}; + +/// 6.1.4 The String Type +/// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type +#[derive(Debug)] +pub struct String(Value); + +impl TryFrom<&str> for String { + type Error = (); + fn try_from(value: &str) -> Result { + SmallString::try_from(value).map(|s| String::new(Value::SmallString(s))) + } +} + +impl TryFrom for String { + type Error = (); + fn try_from(value: Value) -> Result { + if matches!(value, Value::String(_) | Value::SmallString(_)) { + Ok(String(value)) + } else { + Err(()) + } + } +} + +impl String { + pub(crate) fn new(value: Value) -> Self { + matches!(value, Value::String(_) | Value::SmallString(_)); + Self(value) + } + + pub fn into_value(self) -> Value { + self.0 + } + + /// Byte length of the string. + pub fn len(self, agent: &Agent) -> usize { + let s = self.into_value(); + + match s { + Value::String(s) => agent.heap.get(s).len(), + Value::SmallString(s) => s.len(), + _ => unreachable!(), + } + } + + pub fn as_str<'a>(&'a self, agent: &'a Agent) -> Option<&'a str> { + match &self.0 { + Value::String(s) => agent.heap.get(*s).as_str(), + Value::SmallString(s) => Some(s.as_str()), + _ => unreachable!(), + } + } + + /// 6.1.4.1 StringIndexOf ( string, searchValue, fromIndex ) + /// https://tc39.es/ecma262/#sec-stringindexof + pub fn index_of(self, agent: &mut Agent, search_value: Self, from_index: i64) -> i64 { + // TODO: Figure out what we should do for invalid cases. + let string = self.as_str(agent).unwrap(); + let search_value = search_value.as_str(agent).unwrap(); + + // 1. Let len be the length of string. + let len = string.len() as i64; + + // 2. If searchValue is the empty String and fromIndex ≤ len, return fromIndex. + if len == 0 && from_index <= len { + return from_index as i64; + } + + // 3. Let searchLen be the length of searchValue. + let search_len = search_value.len() as i64; + + // 4. For each integer i such that fromIndex ≤ i ≤ len - searchLen, in ascending order, do + for i in from_index..=(len - search_len) as i64 { + // a. Let candidate be the substring of string from i to i + searchLen. + let candidate = &string[i as usize..(i + search_len) as usize]; + + // b. If candidate is searchValue, return i. + if candidate == search_value { + return i; + } + } + + // 5. Return -1. + -1 + } +} diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index 0a82c4d1..4b4ed73a 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -1,6 +1,7 @@ use std::mem::size_of; use crate::{ + execution::{Agent, JsResult}, heap::indexes::{ ArrayIndex, BigIntIndex, DateIndex, ErrorIndex, FunctionIndex, NumberIndex, ObjectIndex, RegExpIndex, StringIndex, SymbolIndex, @@ -8,6 +9,8 @@ use crate::{ Heap, SmallInteger, SmallString, }; +use super::{BigInt, Number}; + /// 6.1 ECMAScript Language Types /// https://tc39.es/ecma262/#sec-ecmascript-language-types #[derive(Debug, Clone, Copy)] @@ -69,6 +72,12 @@ const _VALUE_SIZE_IS_WORD: () = assert!(size_of::() == size_of::() // We may also want to keep Option register sized so that eg. holes in arrays do not start requiring extra bookkeeping. const _OPTIONAL_VALUE_SIZE_IS_WORD: () = assert!(size_of::>() == size_of::()); +#[derive(Debug, Clone, Copy)] +pub enum PreferredType { + String, + Number, +} + impl Value { pub fn from_str(heap: &mut Heap, message: &str) -> Value { if let Ok(ascii_string) = SmallString::try_from(message) { @@ -93,16 +102,483 @@ impl Value { } } - pub const fn nan() -> Self { - Self::Float(f32::NAN) + pub fn nan() -> Self { + Number::nan().into_value() + } + + pub fn infinity() -> Self { + Number::pos_inf().into_value() + } + + pub fn neg_infinity() -> Self { + Number::neg_inf().into_value() + } + + pub fn is_true(self) -> bool { + matches!(self, Value::Boolean(true)) + } + + pub fn is_false(self) -> bool { + matches!(self, Value::Boolean(false)) + } + + pub fn is_object(self) -> bool { + matches!(self, Value::Object(_) | Value::Array(_)) + } + + pub fn is_string(self) -> bool { + matches!(self, Value::String(_) | Value::SmallString(_)) + } + + pub fn is_boolean(self) -> bool { + // TODO: Check for Boolean object instance. + matches!(self, Value::Boolean(_)) + } + + pub fn is_null(self) -> bool { + matches!(self, Value::Null) + } + + pub fn is_undefined(self) -> bool { + matches!(self, Value::Undefined) + } + + pub fn is_pos_zero(self, agent: &mut Agent) -> bool { + Number::try_from(self) + .map(|n| n.is_pos_zero(agent)) + .unwrap_or(false) + } + + pub fn is_neg_zero(self, agent: &mut Agent) -> bool { + Number::try_from(self) + .map(|n| n.is_neg_zero(agent)) + .unwrap_or(false) + } + + pub fn is_nan(self, agent: &mut Agent) -> bool { + Number::try_from(self) + .map(|n| n.is_nan(agent)) + .unwrap_or(false) + } + + pub fn is_bigint(self) -> bool { + // TODO: Check for BigInt object instance. + matches!(self, Value::BigInt(_)) + } + + pub fn is_symbol(self) -> bool { + matches!(self, Value::Symbol(_)) + } + + /// 7.1.1 ToPrimitive ( input [ , preferredType ] ) + /// https://tc39.es/ecma262/#sec-toprimitive + pub fn to_primitive( + self, + agent: &mut Agent, + preferred_type: Option, + ) -> JsResult { + let input = self; + + // 1. If input is an Object, then + if input.is_object() { + // a. Let exoticToPrim be ? GetMethod(input, @@toPrimitive). + // b. If exoticToPrim is not undefined, then + // i. If preferredType is not present, then + // 1. Let hint be "default". + // ii. Else if preferredType is string, then + // 1. Let hint be "string". + // iii. Else, + // 1. Assert: preferredType is number. + // 2. Let hint be "number". + // iv. Let result be ? Call(exoticToPrim, input, « hint »). + // v. If result is not an Object, return result. + // vi. Throw a TypeError exception. + // c. If preferredType is not present, let preferredType be number. + // d. Return ? OrdinaryToPrimitive(input, preferredType). + todo!(); + } + + // 2. Return input. + Ok(input) + } + + /// 7.1.1.1 OrdinaryToPrimitive ( O, hint ) + /// https://tc39.es/ecma262/#sec-ordinarytoprimitive + pub fn ordinary_to_primitive(self, agent: &mut Agent, hint: PreferredType) -> JsResult { + // TODO: This takes in an object...so probably put it in Object. + let o = self; + + // 1. If hint is string, then + let method_names = if matches!(hint, PreferredType::String) { + // a. Let methodNames be « "toString", "valueOf" ». + &["toString", "valueOf"] + } + // 2. Else, + else { + // a. Let methodNames be « "valueOf", "toString" ». + &["valueOf", "toString"] + }; + + // TODO: 3. For each element name of methodNames, do + for name in method_names.iter() { + // a. Let method be ? Get(O, name). + // b. If IsCallable(method) is true, then + // i. Let result be ? Call(method, O). + // ii. If result is not an Object, return result. + // 4. Throw a TypeError exception. + } + + todo!() + } + + /// 7.1.2 ToBoolean ( argument ) + /// https://tc39.es/ecma262/#sec-toboolean + pub fn to_boolean(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. If argument is a Boolean, return argument. + if argument.is_boolean() { + return Ok(argument); + } + + // 2. If argument is one of undefined, null, +0𝔽, -0𝔽, NaN, 0ℤ, or the empty String, return false. + // TODO: checks for 0ℤ and empty String + if argument.is_undefined() + || argument.is_null() + || argument.is_pos_zero(agent) + || argument.is_neg_zero(agent) + || argument.is_nan(agent) + { + return Ok(false.into()); + } + + // 3. NOTE: This step is replaced in section B.3.6.1. + + // 4. Return true. + return Ok(true.into()); } - pub const fn infinity() -> Self { - Self::Float(f32::INFINITY) + /// 7.1.3 ToNumeric ( value ) + /// https://tc39.es/ecma262/#sec-tonumeric + pub fn to_numeric(self, agent: &mut Agent) -> JsResult { + let value = self; + + // 1. Let primValue be ? ToPrimitive(value, number). + let prim_value = value.to_primitive(agent, Some(PreferredType::Number))?; + + // 2. If primValue is a BigInt, return primValue. + if prim_value.is_bigint() { + return Ok(prim_value); + } + + // 3. Return ? ToNumber(primValue). + prim_value.to_number(agent).map(|n| n.into_value()) } - pub const fn neg_infinity() -> Self { - Self::Float(f32::NEG_INFINITY) + /// 7.1.4 ToNumber ( argument ) + /// https://tc39.es/ecma262/#sec-tonumber + pub fn to_number(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. If argument is a Number, return argument. + if let Ok(argument) = Number::try_from(argument) { + return Ok(argument); + } + + // 2. If argument is either a Symbol or a BigInt, throw a TypeError exception. + if argument.is_symbol() || argument.is_bigint() { + todo!(); + } + + // 3. If argument is undefined, return NaN. + if argument.is_undefined() { + return Ok(Number::nan()); + } + + // 4. If argument is either null or false, return +0𝔽. + if argument.is_null() || argument.is_false() { + return Ok(Number::from(0)); + } + + // 5. If argument is true, return 1𝔽. + if argument.is_true() { + return Ok(Number::from(1)); + } + + // 6. If argument is a String, return StringToNumber(argument). + if argument.is_string() { + todo!(); + } + + // 7. Assert: argument is an Object. + debug_assert!(argument.is_object()); + + // 8. Let primValue be ? ToPrimitive(argument, number). + let prim_value = argument.to_primitive(agent, Some(PreferredType::Number))?; + + // 9. Assert: primValue is not an Object. + debug_assert!(!prim_value.is_object()); + + // 10. Return ? ToNumber(primValue). + prim_value.to_number(agent) + } + + /// 7.1.5 ToIntegerOrInfinity ( argument ) + /// https://tc39.es/ecma262/#sec-tointegerorinfinity + // TODO: Should we add another [`Value`] newtype for IntegerOrInfinity? + pub fn to_integer_or_infinty(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is one of NaN, +0𝔽, or -0𝔽, return 0. + if number.is_nan(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(Number::pos_zero()); + } + + // 3. If number is +∞𝔽, return +∞. + if number.is_pos_infinity(agent) { + return Ok(Number::pos_inf()); + } + + // 4. If number is -∞𝔽, return -∞. + if number.is_neg_infinity(agent) { + return Ok(Number::neg_inf()); + } + + // 5. Return truncate(ℝ(number)). + Ok(Number::from(number.truncate(agent))) + } + + /// 7.1.6 ToInt32 ( argument ) + /// https://tc39.es/ecma262/#sec-toint32 + pub fn to_int32(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int32bit be int modulo 2^32. + let int32bit = int % 2i64.pow(32); + + // 5. If int32bit ≥ 2^31, return 𝔽(int32bit - 2^32); otherwise return 𝔽(int32bit). + Ok(if int32bit >= 2i64.pow(32) { + int32bit - 2i64.pow(32) + } else { + int32bit + } as i32) + } + + /// 7.1.7 ToUint32 ( argument ) + /// https://tc39.es/ecma262/#sec-touint32 + pub fn to_uint32(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int32bit be int modulo 2^32. + let int32bit = int % 2i64.pow(32); + + // 5. Return 𝔽(int32bit). + Ok(int32bit as u32) + } + + /// 7.1.8 ToInt16 ( argument ) + /// https://tc39.es/ecma262/#sec-toint16 + pub fn to_int16(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int16bit be int modulo 2^16. + let int16bit = int % 2i64.pow(16); + + // 5. If int16bit ≥ 2^15, return 𝔽(int16bit - 2^16); otherwise return 𝔽(int16bit). + Ok(if int16bit >= 2i64.pow(15) { + int16bit - 2i64.pow(16) + } else { + int16bit + } as i16) + } + + /// 7.1.9 ToUint16 ( argument ) + /// https://tc39.es/ecma262/#sec-touint16 + pub fn to_uint16(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int16bit be int modulo 2^16. + let int16bit = int % 2i64.pow(16); + + // Return 𝔽(int16bit). + Ok(int16bit as i16) + } + + /// 7.1.10 ToInt8 ( argument ) + /// https://tc39.es/ecma262/#sec-toint8 + pub fn to_int8(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int8bit be int modulo 2^8. + let int8bit = int % 2i64.pow(8); + + // 5. If int8bit ≥ 2^7, return 𝔽(int8bit - 2^8); otherwise return 𝔽(int8bit). + Ok(if int8bit >= 2i64.pow(7) { + int8bit - 2i64.pow(8) + } else { + int8bit + } as i8) + } + + /// 7.1.11 ToUint8 ( argument ) + /// https://tc39.es/ecma262/#sec-touint8 + pub fn to_uint8(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is not finite or number is either +0𝔽 or -0𝔽, return +0𝔽. + if !number.is_finite(agent) || number.is_pos_zero(agent) || number.is_neg_zero(agent) { + return Ok(0); + } + + // 3. Let int be truncate(ℝ(number)). + let int = number.truncate(agent); + + // 4. Let int8bit be int modulo 2^8. + let int8bit = int % 2i64.pow(8); + + // 5. Return 𝔽(int8bit). + Ok(int8bit as u8) + } + + /// 7.1.12 ToUint8Clamp ( argument ) + /// https://tc39.es/ecma262/#sec-touint8clamp + pub fn to_uint8_clamp(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let number be ? ToNumber(argument). + let number = argument.to_number(agent)?; + + // 2. If number is NaN, return +0𝔽. + if number.is_nan(agent) { + return Ok(0); + } + + // 3. Let mv be the extended mathematical value of number. + // TODO: Is there a better way? + let mv = number.into_f64(agent); + + // 4. Let clamped be the result of clamping mv between 0 and 255. + let clamped = mv.clamp(0.0, 255.0); + + // 5. Let f be floor(clamped). + let f = clamped.floor(); + + Ok( + // 6. If clamped < f + 0.5, return 𝔽(f). + if clamped < f + 0.5 { + f as u8 + } + // 7. If clamped > f + 0.5, return 𝔽(f + 1). + else if clamped > f + 0.5 { + f as u8 + 1 + } + // 8. If f is even, return 𝔽(f). Otherwise, return 𝔽(f + 1). + else if f % 2.0 == 0.0 { + f as u8 + } else { + f as u8 + 1 + }, + ) + } + + /// 7.1.13 ToBigInt ( argument ) + /// https://tc39.es/ecma262/#sec-tobigint + pub fn to_big_int(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // 1. Let prim be ? ToPrimitive(argument, number). + let prim = argument.to_primitive(agent, Some(PreferredType::Number))?; + + // 2. Return the value that prim corresponds to in Table 12. + todo!() + } + + /// 7.1.17 ToString ( argument ) + /// https://tc39.es/ecma262/#sec-tostring + pub fn to_string(self, agent: &mut Agent) -> JsResult { + let argument = self; + + // TODO: 1. If argument is a String, return argument. + // 2. If argument is a Symbol, throw a TypeError exception. + // 3. If argument is undefined, return "undefined". + // 4. If argument is null, return "null". + // 5. If argument is true, return "true". + // 6. If argument is false, return "false". + // 7. If argument is a Number, return Number::toString(argument, 10). + // 8. If argument is a BigInt, return BigInt::toString(argument, 10). + // 9. Assert: argument is an Object. + // 10. Let primValue be ? ToPrimitive(argument, string). + // 11. Assert: primValue is not an Object. + // 12. Return ? ToString(primValue). + + todo!() + } +} + +impl From for Value { + fn from(value: bool) -> Self { + Value::Boolean(value) } } From 5859a2f62b1a1daf36ab6b84e08a3d15de7cfcce Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 22:11:36 -0500 Subject: [PATCH 06/32] little empty string bonus --- nova_vm/src/types/language/value.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index 4b4ed73a..3dd4b6d5 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -170,6 +170,14 @@ impl Value { matches!(self, Value::Symbol(_)) } + pub fn is_empty_string(self) -> bool { + if let Value::SmallString(s) = self { + s.len() == 0 + } else { + false + } + } + /// 7.1.1 ToPrimitive ( input [ , preferredType ] ) /// https://tc39.es/ecma262/#sec-toprimitive pub fn to_primitive( @@ -242,12 +250,13 @@ impl Value { } // 2. If argument is one of undefined, null, +0𝔽, -0𝔽, NaN, 0ℤ, or the empty String, return false. - // TODO: checks for 0ℤ and empty String + // TODO: checks for 0ℤ if argument.is_undefined() || argument.is_null() || argument.is_pos_zero(agent) || argument.is_neg_zero(agent) || argument.is_nan(agent) + || argument.is_empty_string() { return Ok(false.into()); } From 6158f72c40eda0f5ed408a7487bfe6bdafd26eda Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Mon, 24 Jul 2023 22:39:57 -0500 Subject: [PATCH 07/32] bank error in your favor. inherit $500 --- nova_vm/src/execution/agent.rs | 9 ++- nova_vm/src/execution/realm.rs | 2 + nova_vm/src/types.rs | 5 +- nova_vm/src/types/language.rs | 2 + nova_vm/src/types/language/number.rs | 85 ++++++++++++++++++++-------- nova_vm/src/types/language/object.rs | 4 ++ nova_vm/src/types/language/string.rs | 8 ++- 7 files changed, 81 insertions(+), 34 deletions(-) create mode 100644 nova_vm/src/types/language/object.rs diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs index 005af431..f3550716 100644 --- a/nova_vm/src/execution/agent.rs +++ b/nova_vm/src/execution/agent.rs @@ -3,7 +3,7 @@ use crate::{ types::{Object, Symbol, Value}, Heap, }; -use std::collections::HashMap; +use std::{cell::RefCell, collections::HashMap, rc::Rc}; #[derive(Debug, Default)] pub struct Options { @@ -27,7 +27,6 @@ pub struct HostHooks { /// https://tc39.es/ecma262/#sec-agents #[derive(Debug)] pub struct Agent<'ctx, 'host> { - pub heap: Heap, pub options: Options, // pre_allocated: PreAllocated, pub exception: Option, @@ -37,9 +36,9 @@ pub struct Agent<'ctx, 'host> { pub execution_context_stack: Vec>, } -impl Agent<'_, '_> { - pub fn current_realm(&self) -> &mut Realm { - todo!() +impl<'ctx, 'host> Agent<'ctx, 'host> { + pub fn current_realm(&self) -> Rc>> { + self.execution_context_stack.last().unwrap().realm.clone() } /// 5.2.3.2 Throw an Exception diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 14a4827c..749b403f 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -9,6 +9,8 @@ use std::{any::Any, cell::RefCell, rc::Rc}; /// https://tc39.es/ecma262/#sec-code-realms #[derive(Debug)] pub struct Realm<'ctx, 'host> { + pub heap: Heap, + pub agent: Rc>>, // rng: Xoroshiro128, diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs index 44eb1d78..0ddfa807 100644 --- a/nova_vm/src/types.rs +++ b/nova_vm/src/types.rs @@ -1,7 +1,7 @@ mod language; mod spec; -pub use language::{Number, String, Value}; +pub use language::{Number, Object, String, Value}; pub use spec::{Base, Reference, ReferencedName}; impl From for Value { @@ -10,8 +10,5 @@ impl From for Value { } } -#[derive(Debug)] -pub struct Object; - #[derive(Debug)] pub struct Symbol; diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs index 05e805eb..f923d449 100644 --- a/nova_vm/src/types/language.rs +++ b/nova_vm/src/types/language.rs @@ -1,9 +1,11 @@ mod bigint; mod number; +mod object; mod string; mod value; pub use bigint::BigInt; pub use number::Number; +pub use object::Object; pub use string::String; pub use value::Value; diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 9fdfabe4..01f66224 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -94,7 +94,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n).is_nan(), + Value::Number(n) => agent.current_realm().borrow().heap.get(n).is_nan(), Value::Integer(_) => false, Value::Float(n) => n.is_nan(), _ => unreachable!(), @@ -105,7 +105,12 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n).is_sign_positive(), + Value::Number(n) => agent + .current_realm() + .borrow() + .heap + .get(n) + .is_sign_positive(), Value::Integer(n) => 0i64 == n.into(), Value::Float(n) => n.is_sign_positive(), _ => unreachable!(), @@ -116,7 +121,12 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n).is_sign_negative(), + Value::Number(n) => agent + .current_realm() + .borrow() + .heap + .get(n) + .is_sign_negative(), Value::Integer(_) => false, Value::Float(n) => n.is_sign_negative(), _ => unreachable!(), @@ -127,7 +137,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n) == f64::INFINITY, + Value::Number(n) => agent.current_realm().borrow().heap.get(n) == f64::INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::INFINITY, _ => unreachable!(), @@ -138,7 +148,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n) == f64::NEG_INFINITY, + Value::Number(n) => agent.current_realm().borrow().heap.get(n) == f64::NEG_INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::NEG_INFINITY, _ => unreachable!(), @@ -149,7 +159,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n).is_finite(), + Value::Number(n) => agent.current_realm().borrow().heap.get(n).is_finite(), Value::Integer(_) => true, Value::Float(n) => n.is_finite(), _ => unreachable!(), @@ -161,7 +171,7 @@ impl Number { match x { Value::Number(n) => { - let n = agent.heap.get(n); + let n = agent.current_realm().borrow().heap.get(n); !n.is_sign_negative() && !n.is_sign_positive() } Value::Integer(_) => true, @@ -177,7 +187,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n).trunc() as i64, + Value::Number(n) => agent.current_realm().borrow().heap.get(n).trunc() as i64, Value::Integer(n) => n.into_i64(), Value::Float(n) => n.trunc() as i64, _ => unreachable!(), @@ -188,7 +198,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.heap.get(n), + Value::Number(n) => agent.current_realm().borrow().heap.get(n), Value::Integer(n) => Into::::into(n) as f64, Value::Float(n) => n as f64, _ => unreachable!(), @@ -202,13 +212,24 @@ impl Number { let y = y.into_value(); match (x, y) { - (Value::Number(x), Value::Number(y)) => agent.heap.get(x) == agent.heap.get(y), - (Value::Number(x), Value::Integer(y)) => agent.heap.get(x) == y.into_i64() as f64, - (Value::Number(x), Value::Float(y)) => agent.heap.get(x) == y as f64, - (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) == agent.heap.get(y), + (Value::Number(x), Value::Number(y)) => { + agent.current_realm().borrow().heap.get(x) + == agent.current_realm().borrow().heap.get(y) + } + (Value::Number(x), Value::Integer(y)) => { + agent.current_realm().borrow().heap.get(x) == y.into_i64() as f64 + } + (Value::Number(x), Value::Float(y)) => { + agent.current_realm().borrow().heap.get(x) == y as f64 + } + (Value::Integer(x), Value::Number(y)) => { + (x.into_i64() as f64) == agent.current_realm().borrow().heap.get(y) + } (Value::Integer(x), Value::Integer(y)) => x.into_i64() == y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) == y as f64, - (Value::Float(x), Value::Number(y)) => (x as f64) == agent.heap.get(y), + (Value::Float(x), Value::Number(y)) => { + (x as f64) == agent.current_realm().borrow().heap.get(y) + } (Value::Float(x), Value::Integer(y)) => (x as f64) == y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x == y, _ => unreachable!(), @@ -220,7 +241,7 @@ impl Number { match x { Value::Number(n) => { - let n = agent.heap.get(n); + let n = agent.current_realm().borrow().heap.get(n); n % 1.0 == 0.0 && n % 2.0 == 0.0 } Value::Integer(n) => Into::::into(n) % 2 == 0, @@ -234,11 +255,11 @@ impl Number { match x { Value::Number(n) => { - let n = agent.heap.get(n); + let n = agent.current_realm().borrow().heap.get(n); if n > 0.0 { self } else { - agent.heap.create(-n) + agent.current_realm().borrow_mut().heap.create(-n) } } Value::Integer(n) => { @@ -265,7 +286,12 @@ impl Number { // 2. Return the result of negating x; that is, compute a Number with the same magnitude but opposite sign. match x { - Value::Number(n) => agent.heap.create(-agent.heap.get(n)), + Value::Number(n) => { + let realm = agent.current_realm(); + let mut realm = realm.borrow_mut(); + let value = realm.heap.get(n); + realm.heap.create(-value) + } Value::Integer(n) => SmallInteger::from_i64_unchecked(-n.into_i64()).into(), Value::Float(n) => (-n).into(), _ => unreachable!(), @@ -417,6 +443,8 @@ impl Number { // 13. Return an implementation-approximated Number value representing the result of raising ℝ(base) to the ℝ(exponent) power. agent + .current_realm() + .borrow_mut() .heap .create(base.into_f64(agent).powf(exponent.into_f64(agent))) } @@ -480,13 +508,24 @@ impl Number { // 11. If ℝ(x) < ℝ(y), return true. Otherwise, return false. Value::Boolean(match (x.into_value(), y.into_value()) { - (Value::Number(x), Value::Number(y)) => agent.heap.get(x) < agent.heap.get(y), - (Value::Number(x), Value::Integer(y)) => agent.heap.get(x) < y.into_i64() as f64, - (Value::Number(x), Value::Float(y)) => agent.heap.get(x) < y as f64, - (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) < agent.heap.get(y), + (Value::Number(x), Value::Number(y)) => { + agent.current_realm().borrow().heap.get(x) + < agent.current_realm().borrow().heap.get(y) + } + (Value::Number(x), Value::Integer(y)) => { + agent.current_realm().borrow().heap.get(x) < y.into_i64() as f64 + } + (Value::Number(x), Value::Float(y)) => { + agent.current_realm().borrow().heap.get(x) < y as f64 + } + (Value::Integer(x), Value::Number(y)) => { + (x.into_i64() as f64) < agent.current_realm().borrow().heap.get(y) + } (Value::Integer(x), Value::Integer(y)) => x.into_i64() < y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) < y as f64, - (Value::Float(x), Value::Number(y)) => (x as f64) < agent.heap.get(y), + (Value::Float(x), Value::Number(y)) => { + (x as f64) < agent.current_realm().borrow().heap.get(y) + } (Value::Float(x), Value::Integer(y)) => (x as f64) < y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x < y, _ => unreachable!(), diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs new file mode 100644 index 00000000..7b05d0d7 --- /dev/null +++ b/nova_vm/src/types/language/object.rs @@ -0,0 +1,4 @@ +use super::Value; + +#[derive(Debug, Clone, Copy)] +pub struct Object(Value); diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index ee467fde..3436ae33 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -39,7 +39,7 @@ impl String { let s = self.into_value(); match s { - Value::String(s) => agent.heap.get(s).len(), + Value::String(s) => agent.current_realm().borrow().heap.get(s).len(), Value::SmallString(s) => s.len(), _ => unreachable!(), } @@ -47,7 +47,11 @@ impl String { pub fn as_str<'a>(&'a self, agent: &'a Agent) -> Option<&'a str> { match &self.0 { - Value::String(s) => agent.heap.get(*s).as_str(), + // SAFETY: The immutable reference to the Agent ensures no mutable + // access to the realm. + Value::String(s) => unsafe { + std::mem::transmute(agent.current_realm().borrow().heap.get(*s).as_str()) + }, Value::SmallString(s) => Some(s.as_str()), _ => unreachable!(), } From a5d47e7d5d4f06fef36168ac6d08f745d6d67e3d Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Tue, 25 Jul 2023 15:44:31 -0500 Subject: [PATCH 08/32] press CTRL+S to save --- nova_vm/src/builtins.rs | 9 +- nova_vm/src/builtins/array.rs | 18 +- nova_vm/src/builtins/builtin_function.rs | 47 ++--- nova_vm/src/builtins/ecmascript_function.rs | 63 +++++++ nova_vm/src/builtins/number.rs | 34 ++++ nova_vm/src/builtins/ordinary.rs | 18 ++ nova_vm/src/execution/environments.rs | 2 +- nova_vm/src/execution/execution_context.rs | 4 +- nova_vm/src/execution/realm.rs | 8 +- nova_vm/src/execution/realm/intrinsics.rs | 176 +++++++++++------- nova_vm/src/heap.rs | 27 ++- nova_vm/src/heap/array.rs | 6 +- nova_vm/src/heap/bigint.rs | 6 +- nova_vm/src/heap/boolean.rs | 6 +- nova_vm/src/heap/date.rs | 6 +- nova_vm/src/heap/error.rs | 6 +- nova_vm/src/heap/function.rs | 12 +- nova_vm/src/heap/heap_gc.rs | 20 +- nova_vm/src/heap/number.rs | 6 +- nova_vm/src/heap/object.rs | 6 +- nova_vm/src/heap/regexp.rs | 6 +- nova_vm/src/heap/string.rs | 6 +- nova_vm/src/heap/symbol.rs | 6 +- nova_vm/src/language/script.rs | 4 +- nova_vm/src/types.rs | 4 +- nova_vm/src/types/language.rs | 4 +- nova_vm/src/types/language/function.rs | 25 +++ nova_vm/src/types/language/object.rs | 41 ++++ nova_vm/src/types/language/object/data.rs | 10 + .../types/language/object/internal_methods.rs | 70 +++++++ .../src/types/language/object/property_key.rs | 4 + nova_vm/src/types/spec.rs | 2 + nova_vm/src/types/spec/property_descriptor.rs | 95 ++++++++++ 33 files changed, 607 insertions(+), 150 deletions(-) create mode 100644 nova_vm/src/builtins/ecmascript_function.rs create mode 100644 nova_vm/src/builtins/number.rs create mode 100644 nova_vm/src/builtins/ordinary.rs create mode 100644 nova_vm/src/types/language/function.rs create mode 100644 nova_vm/src/types/language/object/data.rs create mode 100644 nova_vm/src/types/language/object/internal_methods.rs create mode 100644 nova_vm/src/types/language/object/property_key.rs create mode 100644 nova_vm/src/types/spec/property_descriptor.rs diff --git a/nova_vm/src/builtins.rs b/nova_vm/src/builtins.rs index a999825a..860649f9 100644 --- a/nova_vm/src/builtins.rs +++ b/nova_vm/src/builtins.rs @@ -1,6 +1,13 @@ mod array; mod builtin_function; +mod ecmascript_function; +mod number; +pub mod ordinary; +pub use array::ArrayConstructor; pub use builtin_function::{ - create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, + create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, + RegularFn as JsFunction, }; +pub use ecmascript_function::ECMAScriptFunction; +pub use number::NumberConstructor; diff --git a/nova_vm/src/builtins/array.rs b/nova_vm/src/builtins/array.rs index f7336d2a..46cba49d 100644 --- a/nova_vm/src/builtins/array.rs +++ b/nova_vm/src/builtins/array.rs @@ -1,25 +1,31 @@ -use super::{create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs}; +use super::{ + builtin_function::define_builtin_function, create_builtin_function, ArgumentsList, Behaviour, + Builtin, BuiltinFunctionArgs, +}; use crate::{ execution::{Agent, JsResult, Realm}, types::{Object, Value}, }; -struct ArrayConstructor; +pub struct ArrayConstructor; impl Builtin for ArrayConstructor { - fn create(realm: &mut Realm) -> Object { + fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { let object = create_builtin_function( - &mut realm.agent.clone().borrow_mut(), Behaviour::Regular(Self::behaviour), BuiltinFunctionArgs::new(1, "Array", realm), ); - object + Ok(object.into_object()) } } impl ArrayConstructor { - fn behaviour(agent: &mut Agent, value: Value, arguments: ArgumentsList) -> JsResult { + fn behaviour( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + ) -> JsResult { todo!(); } } diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index 3e823cc4..ece6cca4 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -1,13 +1,13 @@ use crate::{ execution::{Agent, JsResult, Realm}, - types::{Object, Value}, + types::{Function, Object, Value}, }; #[derive(Debug)] -pub struct ArgumentsList; +pub struct ArgumentsList<'a>(&'a [Value]); -type RegularFn = fn(&mut Agent, Value, ArgumentsList) -> JsResult; -type ConstructorFn = fn(&mut Agent, Value, ArgumentsList, Option) -> JsResult; +pub type RegularFn = fn(&mut Agent, Value, ArgumentsList<'_>) -> JsResult; +type ConstructorFn = fn(&mut Agent, Value, ArgumentsList<'_>, Option) -> JsResult; #[derive(Debug)] pub enum Behaviour { @@ -16,7 +16,7 @@ pub enum Behaviour { } pub trait Builtin { - fn create(realm: &mut Realm) -> Object; + fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult; } #[derive(Debug, Default)] @@ -28,7 +28,7 @@ pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> { pub prefix: Option, } -impl<'a, 'ctx, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { +impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { pub fn new(length: u32, name: &'static str, realm: &'a mut Realm<'ctx, 'host>) -> Self { Self { length, @@ -41,18 +41,19 @@ impl<'a, 'ctx, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { /// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ) /// https://tc39.es/ecma262/#sec-createbuiltinfunction -pub fn create_builtin_function<'ctx, 'host: 'ctx>( - agent: &mut Agent<'ctx, 'host>, +pub fn create_builtin_function<'a, 'b: 'a>( behaviour: Behaviour, - args: BuiltinFunctionArgs<'_, 'ctx, 'host>, -) -> Object { + args: BuiltinFunctionArgs<'a, 'b, 'b>, +) -> Function { // 1. If realm is not present, set realm to the current Realm Record. let realm = args.realm.unwrap(); // TODO: load record // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. - let prototype = args - .prototype - .unwrap_or_else(|| realm.intrinsics.function_prototype()); + let prototype = if let Some(prototype) = args.prototype { + prototype + } else { + realm.intrinsics().function() + }; // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 // requires for the built-in function object that is about to be created. @@ -74,23 +75,27 @@ pub fn create_builtin_function<'ctx, 'host: 'ctx>( todo!(); } -pub fn define_builtin_function<'ctx, 'host: 'ctx>( +pub fn define_builtin_function<'a>( object: Object, name: &'static str, behaviour: RegularFn, length: u32, - realm: &'ctx mut Realm<'ctx, 'host>, -) { - let agent_mut = realm.agent.clone(); - let mut agent = agent_mut.borrow_mut(); - + realm: &'a mut Realm<'a, 'a>, +) -> JsResult<()> { let function = create_builtin_function( - &mut agent, Behaviour::Regular(behaviour), BuiltinFunctionArgs::new(length, name, realm), ); - define_builtin_property(object, name, Value::from(function)); + Ok(()) } pub fn define_builtin_property(object: Object, name: &'static str, value: Value) {} + +pub fn todo_builtin(agent: &mut Agent, _: Value, _: ArgumentsList) -> JsResult { + agent.throw_exception( + crate::execution::agent::ExceptionType::SyntaxError, + "TODO: Builtin not implemented.", + ); + Err(()) +} diff --git a/nova_vm/src/builtins/ecmascript_function.rs b/nova_vm/src/builtins/ecmascript_function.rs new file mode 100644 index 00000000..2c32aa3b --- /dev/null +++ b/nova_vm/src/builtins/ecmascript_function.rs @@ -0,0 +1,63 @@ +use std::{cell::RefCell, rc::Rc}; + +use oxc_ast::ast::{FormalParameters, FunctionBody}; + +use crate::{ + execution::{Environment, PrivateEnvironment, Realm, ScriptOrModule}, + types::Object, +}; + +#[derive(Debug, Clone, Copy)] +pub enum ConstructorKind { + Base, + Derived, +} + +#[derive(Debug, Clone, Copy)] +pub enum ThisMode { + Lexical, + Strict, + Global, +} + +/// 10.2 ECMAScript Function Objects +/// https://tc39.es/ecma262/#sec-ecmascript-function-objects +#[derive(Debug, Clone)] +pub struct ECMAScriptFunction<'ctx, 'host> { + /// [[Environment]] + pub environment: Environment, + + /// [[PrivateEnvironment]] + pub private_environment: Option>>, + + /// [[FormalParameters]] + pub formal_parameters: &'host FormalParameters<'host>, + + /// [[ECMAScriptCode]] + pub ecmascript_code: &'host FunctionBody<'host>, + + /// [[ConstructorKind]] + pub constructor_kind: ConstructorKind, + + /// [[Realm]] + pub realm: Rc>>, + + /// [[ScriptOrModule]] + pub script_or_module: ScriptOrModule<'ctx, 'host>, + + /// [[ThisMode]] + pub this_mode: ThisMode, + + /// [[Strict]] + pub strict: bool, + + /// [[HomeObject]] + pub home_object: Option, + + /// [[SourceText]] + pub source_text: &'host str, + + // TODO: [[Fields]], [[PrivateMethods]], [[ClassFieldInitializerName]] + /// [[IsClassConstructor]] + pub is_class_constructor: bool, +} diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs new file mode 100644 index 00000000..bbed2511 --- /dev/null +++ b/nova_vm/src/builtins/number.rs @@ -0,0 +1,34 @@ +use super::{ + builtin_function::define_builtin_function, create_builtin_function, todo_builtin, + ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, +}; +use crate::{ + execution::{Agent, JsResult, Realm}, + types::{Object, Value}, +}; + +pub struct NumberConstructor; + +impl Builtin for NumberConstructor { + fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { + let object = create_builtin_function( + Behaviour::Regular(NumberConstructor::behaviour), + BuiltinFunctionArgs::new(1, "Array", realm), + ) + .into_object(); + + define_builtin_function(object, "isFinite", todo_builtin, 1, realm)?; + + Ok(object) + } +} + +impl NumberConstructor { + fn behaviour( + agent: &mut Agent, + this_value: Value, + arguments: ArgumentsList, + ) -> JsResult { + todo!(); + } +} diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs new file mode 100644 index 00000000..9e221309 --- /dev/null +++ b/nova_vm/src/builtins/ordinary.rs @@ -0,0 +1,18 @@ +use crate::{ + execution::{Agent, JsResult}, + types::Object, +}; + +/// 10.1.1 [[GetPrototypeOf]] ( ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof +fn get_prototype_of(agent: &mut Agent, object: Object) -> Option { + // 1. Return OrdinaryGetPrototypeOf(O). + return ordinary_get_prototype_of(agent, object); +} + +/// 10.1.1.1 OrdinaryGetPrototypeOf ( O ) +/// https://tc39.es/ecma262/#sec-ordinarygetprototypeof +pub fn ordinary_get_prototype_of(agent: &mut Agent, object: Object) -> Option { + // 1. Return O.[[Prototype]]. + return object.prototype(agent); +} diff --git a/nova_vm/src/execution/environments.rs b/nova_vm/src/execution/environments.rs index c7a2ee5c..dfffc310 100644 --- a/nova_vm/src/execution/environments.rs +++ b/nova_vm/src/execution/environments.rs @@ -16,7 +16,7 @@ use std::{cell::RefCell, rc::Rc}; /// 9.1.1 The Environment Record Type Hierarchy /// https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Environment { DeclarativeEnvironment(Rc>), ObjectEnvironment(Rc>), diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index 4e7e4e38..9ce50452 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -5,9 +5,9 @@ use std::{cell::RefCell, rc::Rc}; #[derive(Debug)] pub struct Module; -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ScriptOrModule<'ctx, 'host> { - Script(&'ctx mut Script<'ctx, 'host>), + Script(Rc>>), Module(Rc>), } diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 749b403f..cb8c20a0 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -15,7 +15,7 @@ pub struct Realm<'ctx, 'host> { // rng: Xoroshiro128, /// [[Intrinsics]] - pub intrinsics: Intrinsics<'ctx, 'host>, + // pub intrinsics: Intrinsics<'ctx, 'host>, /// [[GlobalObject]] pub global_object: Object, @@ -27,3 +27,9 @@ pub struct Realm<'ctx, 'host> { pub host_defined: Option>>, // TODO: [[TemplateMap]], [[LoadedModules]] } + +impl<'ctx, 'host> Realm<'ctx, 'host> { + pub fn intrinsics<'a>(&'a self) -> Intrinsics<'a, 'ctx, 'host> { + Intrinsics { realm: self } + } +} diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index 7782ed35..acd8dac9 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -1,144 +1,194 @@ use super::Realm; use crate::types::Object; -use std::{cell::RefCell, rc::Rc}; #[derive(Debug)] -pub struct Intrinsics<'ctx, 'host> { - pub realm: Rc>>, - - // Not stored as top-level properties so we can have methods of the same names - pub lazy_intrinsics: LazyIntrinsics, -} - -macro_rules! lazy_intrinsic { - ($name: ident $ptr: ty) => { - pub fn $name(&mut self) -> Object { - let intrinsic = &mut self.lazy_intrinsics.$name; - - if let Some(intrinsic) = intrinsic { - intrinsic - } else { - } - } - }; +pub struct Intrinsics<'a, 'ctx, 'host> { + pub realm: &'a Realm<'ctx, 'host>, } -impl Intrinsics<'_, '_> { - pub fn function_prototype(&mut self) -> Object { +impl Intrinsics<'_, '_, '_> { + /// %Array% + pub fn array(&self) -> Object { todo!() } -} - -#[derive(Debug)] -pub struct LazyIntrinsics { - /// %Array% - pub array: Option, /// %Array.prototype% - pub array_prototype_prototype: Option, + pub fn array_prototype_prototype(&self) -> Object { + todo!() + } /// %BigInt% - pub big_int: Option, + pub fn big_int(&self) -> Object { + todo!() + } /// %BigInt.prototype% - pub big_int_prototype: Option, + pub fn big_int_prototype(&self) -> Object { + todo!() + } /// %Boolean% - pub boolean: Option, + pub fn boolean(&self) -> Object { + todo!() + } /// %Boolean.prototype% - pub boolean_prototype: Option, + pub fn boolean_prototype(&self) -> Object { + todo!() + } /// %Error% - pub error: Option, + pub fn error(&self) -> Object { + todo!() + } /// %Error.prototype% - pub error_prototype: Option, + pub fn error_prototype(&self) -> Object { + todo!() + } /// %eval% - pub eval: Option, + pub fn eval(&self) -> Object { + todo!() + } /// %EvalError% - pub eval_error: Option, + pub fn eval_error(&self) -> Object { + todo!() + } /// %EvalError.prototype% - pub eval_error_prototype: Option, + pub fn eval_error_prototype(&self) -> Object { + todo!() + } /// %Function% - pub function: Option, + pub fn function(&self) -> Object { + todo!() + } /// %Function.prototype% - pub function_prototype: Option, + pub fn function_prototype(&self) -> Object { + todo!() + } /// %isFinite% - pub is_finite: Option, + pub fn is_finite(&self) -> Object { + todo!() + } /// %isNaN% - pub is_nan: Option, + pub fn is_nan(&self) -> Object { + todo!() + } /// %Math% - pub math: Option, + pub fn math(&self) -> Object { + todo!() + } /// %Number% - pub number: Option, + pub fn number(&self) -> Object { + todo!() + } /// %Number.prototype% - pub number_prototype: Option, + pub fn number_prototype(&self) -> Object { + todo!() + } /// %Object% - pub object: Option, + pub fn object(&self) -> Object { + todo!() + } /// %Object.prototype% - pub object_prototype: Option, + pub fn object_prototype(&self) -> Object { + todo!() + } /// %Object.prototype.toString% - pub object_prototype_to_string: Option, + pub fn object_prototype_to_string(&self) -> Object { + todo!() + } /// %RangeError% - pub range_error: Option, + pub fn range_error(&self) -> Object { + todo!() + } /// %RangeError.prototype% - pub range_error_prototype: Option, + pub fn range_error_prototype(&self) -> Object { + todo!() + } /// %ReferenceError% - pub reference_error: Option, + pub fn reference_error(&self) -> Object { + todo!() + } /// %ReferenceError.prototype% - pub reference_error_prototype: Option, + pub fn reference_error_prototype(&self) -> Object { + todo!() + } /// %Reflect% - pub reflect: Option, + pub fn reflect(&self) -> Object { + todo!() + } /// %String% - pub string: Option, + pub fn string(&self) -> Object { + todo!() + } /// %String.prototype% - pub string_prototype: Option, + pub fn string_prototype(&self) -> Object { + todo!() + } /// %Symbol% - pub symbol: Option, + pub fn symbol(&self) -> Object { + todo!() + } /// %Symbol.prototype% - pub symbol_prototype: Option, + pub fn symbol_prototype(&self) -> Object { + todo!() + } /// %SyntaxError% - pub syntax_error: Option, + pub fn syntax_error(&self) -> Object { + todo!() + } /// %SyntaxError.prototype% - pub syntax_error_prototype: Option, + pub fn syntax_error_prototype(&self) -> Object { + todo!() + } /// %ThrowTypeError% - pub throw_type_error: Option, + pub fn throw_type_error(&self) -> Object { + todo!() + } /// %TypeError% - pub type_error: Option, + pub fn type_error(&self) -> Object { + todo!() + } /// %TypeError.prototype% - pub type_error_prototype: Option, + pub fn type_error_prototype(&self) -> Object { + todo!() + } /// %URIError% - pub uri_error: Option, + pub fn uri_error(&self) -> Object { + todo!() + } /// %URIError.prototype% - pub uri_error_prototype: Option, + pub fn uri_error_prototype(&self) -> Object { + todo!() + } } diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index f33c1803..95d989bf 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -42,7 +42,7 @@ use self::{ string::{initialize_string_heap, StringHeapData}, symbol::{initialize_symbol_heap, SymbolHeapData}, }; -use crate::types::{Number, String, Value}; +use crate::types::{Function, Number, String, Value}; use wtf8::Wtf8; #[derive(Debug)] @@ -123,6 +123,25 @@ impl<'a> GetHeapData<'a, StringHeapData, &'a Wtf8> for Heap { } } +impl CreateHeapData for Heap { + fn create(&mut self, data: FunctionHeapData) -> Function { + let id = self.functions.len(); + self.functions.push(Some(data)); + Function::new(Value::Function(FunctionIndex::from_index(id))) + } +} + +impl<'a> GetHeapData<'a, FunctionHeapData, &'a FunctionHeapData> for Heap { + fn get(&'a self, id: FunctionIndex) -> &'a FunctionHeapData { + self.functions + .get(id.into_index()) + .as_ref() + .unwrap() + .as_ref() + .unwrap() + } +} + impl Heap { pub fn new() -> Heap { let mut heap = Heap { @@ -227,11 +246,11 @@ impl Heap { self.objects.push(Some(func_object_data)); let func_data = FunctionHeapData { binding, - bound: None, + // bound: None, length, object_index: ObjectIndex::last(&self.objects), - uses_arguments, - visible: None, + // uses_arguments, + // visible: None, }; let index = FunctionIndex::from_index(self.functions.len()); self.functions.push(Some(func_data)); diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index 9281fcb7..b8b4936b 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -68,9 +68,9 @@ pub fn initialize_array_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::ArrayConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: array_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index 13c0392f..fa80f2eb 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -40,9 +40,9 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: ObjectIndex::last(&heap.objects), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: bigint_constructor, }); let entries = vec![ diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index 31d9a058..26c622ce 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -28,9 +28,9 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::BooleanConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: boolean_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index cfcbb6e6..cbded91d 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -43,9 +43,9 @@ pub fn initialize_date_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::DateConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: date_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index bfb331b5..f1700e40 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -35,9 +35,9 @@ pub fn initialize_error_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::ErrorConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: error_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 1cafa13d..9ecdd73e 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -19,10 +19,10 @@ pub type JsBindingFunction = fn(heap: &mut Heap, this: Value, args: &[Value]) -> pub(crate) struct FunctionHeapData { pub(super) object_index: ObjectIndex, pub(super) length: u8, - pub(super) uses_arguments: bool, - pub(super) bound: Option>, - pub(super) visible: Option>, pub(super) binding: JsBindingFunction, + // pub(super) uses_arguments: bool, + // pub(super) bound: Option>, + // pub(super) visible: Option>, // TODO: Should name be here as an "internal slot" of sorts? } @@ -42,9 +42,9 @@ pub fn initialize_function_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::FunctionConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: function_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 61a5d8a4..ac221911 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -76,16 +76,16 @@ pub(crate) fn heap_gc(heap: &mut Heap) { 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); - }) - } + // 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(); diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index e204a438..ecb69806 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -82,9 +82,9 @@ pub fn initialize_number_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::NumberConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: number_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 125d113c..66a13224 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -325,9 +325,9 @@ pub fn initialize_object_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::ObjectConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: object_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index 42fe4d15..245eff4e 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -47,9 +47,9 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::RegExpConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: regexp_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 55169a80..128d3e33 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -34,9 +34,9 @@ pub fn initialize_string_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::StringConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: string_constructor_binding, }); heap.insert_builtin_object( diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index a263fe64..10df66fb 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -144,9 +144,9 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { Some(FunctionHeapData { object_index: BuiltinObjectIndexes::SymbolConstructorIndex.into(), length: 1, - uses_arguments: false, - bound: None, - visible: None, + // uses_arguments: false, + // bound: None, + // visible: None, binding: symbol_constructor_binding, }); let entries = vec![ diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 5866458c..65713309 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -61,7 +61,7 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { /// 16.1.6 ScriptEvaluation ( scriptRecord ) /// https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation - pub fn evaluate(&'ctx mut self) -> Value { + pub fn evaluate(self) -> Value { let ecmascript_code = self.ecmascript_code.clone(); let realm = self.realm.clone(); let agent = { @@ -84,7 +84,7 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { realm, // 5. Set the ScriptOrModule of scriptContext to scriptRecord. - script_or_module: Some(ScriptOrModule::Script(self)), + script_or_module: Some(ScriptOrModule::Script(Rc::new(RefCell::new(self)))), ecmascript_code: Some(ECMAScriptCode { // 6. Set the VariableEnvironment of scriptContext to globalEnv. diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs index 0ddfa807..ee4945a1 100644 --- a/nova_vm/src/types.rs +++ b/nova_vm/src/types.rs @@ -1,8 +1,8 @@ mod language; mod spec; -pub use language::{Number, Object, String, Value}; -pub use spec::{Base, Reference, ReferencedName}; +pub use language::{Function, Number, Object, String, Value}; +pub use spec::{Base, PropertyDescriptor, Reference, ReferencedName}; impl From for Value { fn from(value: Object) -> Self { diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs index f923d449..de08617d 100644 --- a/nova_vm/src/types/language.rs +++ b/nova_vm/src/types/language.rs @@ -1,11 +1,13 @@ mod bigint; +mod function; mod number; mod object; mod string; mod value; pub use bigint::BigInt; +pub use function::Function; pub use number::Number; -pub use object::Object; +pub use object::{Object, ObjectData}; pub use string::String; pub use value::Value; diff --git a/nova_vm/src/types/language/function.rs b/nova_vm/src/types/language/function.rs new file mode 100644 index 00000000..edaa8502 --- /dev/null +++ b/nova_vm/src/types/language/function.rs @@ -0,0 +1,25 @@ +use super::{Object, Value}; + +/// https://tc39.es/ecma262/#function-object +#[derive(Clone, Copy)] +pub struct Function(Value); + +impl std::fmt::Debug for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.0) + } +} + +impl Function { + pub(crate) fn new(value: Value) -> Self { + Self(value) + } + + pub fn into_value(self) -> Value { + self.0 + } + + pub fn into_object(self) -> Object { + Object::new(self.into_value()) + } +} diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 7b05d0d7..74d36e5f 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -1,4 +1,45 @@ +mod data; +mod internal_methods; +mod property_key; + +use crate::{execution::Agent, heap::GetHeapData}; + use super::Value; +pub use data::ObjectData; +pub use internal_methods::InternalMethods; +pub use property_key::PropertyKey; +/// 6.1.7 The Object Type +/// https://tc39.es/ecma262/#sec-object-type #[derive(Debug, Clone, Copy)] pub struct Object(Value); + +impl Object { + pub(crate) fn new(value: Value) -> Self { + Self(value) + } + + pub fn into_value(self) -> Value { + self.0 + } + + pub fn prototype(self, agent: &mut Agent) -> Option { + let realm = agent.current_realm(); + let object = self.into_value(); + + match object { + // Value::Object(object) => { + // let object = realm.heap.get(object); + // } + // Value::ArrayObject(array) => { + // let array = realm.heap.get(array); + // } + Value::Function(function) => { + // let function = realm.heap.get(function); + // function.binding; + todo!() + } + _ => unreachable!(), + } + } +} diff --git a/nova_vm/src/types/language/object/data.rs b/nova_vm/src/types/language/object/data.rs new file mode 100644 index 00000000..4ab49cd6 --- /dev/null +++ b/nova_vm/src/types/language/object/data.rs @@ -0,0 +1,10 @@ +use super::Object; + +#[derive(Debug)] +pub struct ObjectData { + /// [[Prototype]] + pub prototype: Option, + + /// [[Extensible]] + pub extensible: bool, +} diff --git a/nova_vm/src/types/language/object/internal_methods.rs b/nova_vm/src/types/language/object/internal_methods.rs new file mode 100644 index 00000000..bc53cbd9 --- /dev/null +++ b/nova_vm/src/types/language/object/internal_methods.rs @@ -0,0 +1,70 @@ +use super::{Object, PropertyKey}; +use crate::{ + builtins::ArgumentsList, + execution::JsResult, + types::{PropertyDescriptor, Value}, +}; + +pub type GetPrototypeOf = fn(object: Object) -> JsResult; +pub type SetPrototypeOf = fn(object: Object, prototype: Option) -> JsResult; +pub type IsExtensible = fn(object: Object) -> JsResult; +pub type PreventExtensions = fn(object: Object) -> JsResult; +pub type GetOwnProperty = fn(object: Object, property_key: PropertyKey) -> JsResult<()>; +pub type DefineOwnProperty = fn( + object: Object, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, +) -> JsResult; +pub type HasProperty = fn(object: Object, property_key: PropertyKey) -> JsResult; +pub type Get = fn(object: Object, property_key: PropertyKey, receiver: Value) -> JsResult; +pub type Set = + fn(object: Object, property_key: PropertyKey, value: Value, receiver: Value) -> JsResult; +pub type Delete = fn(object: Object, property_key: PropertyKey) -> JsResult; +pub type OwnPropertyKeys = fn(object: Object) -> JsResult>; +pub type Call = + fn(object: Object, this_value: Value, arguments_list: ArgumentsList) -> JsResult; +pub type Construct = fn(object: Object, arguments_list: ArgumentsList) -> JsResult; + +/// 6.1.7.2 Object Internal Methods and Internal Slots +/// https://tc39.es/ecma262/#sec-object-internal-methods-and-internal-slots +#[derive(Debug, Clone)] +pub struct InternalMethods { + /// [[GetPrototypeOf]] + pub get_prototype_of: GetPrototypeOf, + + /// [[SetPrototypeOf]] + pub set_prototype_of: SetPrototypeOf, + + /// [[IsExtensible]] + pub is_extensible: IsExtensible, + + /// [[PreventExtensions]] + pub prevent_extensions: PreventExtensions, + + /// [[GetOwnProperty]] + pub get_own_property: GetOwnProperty, + + /// [[DefineOwnProperty]] + pub define_own_property: DefineOwnProperty, + + /// [[HasProperty]] + pub has_property: HasProperty, + + /// [[Get]] + pub get: Get, + + /// [[Set]] + pub set: Set, + + /// [[Delete]] + pub delete: Delete, + + /// [[OwnPropertyKeys]] + pub own_property_keys: OwnPropertyKeys, + + /// [[Call]] + pub call: Option, + + /// [[Construct]] + pub construct: Option, +} diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs new file mode 100644 index 00000000..e8d7e424 --- /dev/null +++ b/nova_vm/src/types/language/object/property_key.rs @@ -0,0 +1,4 @@ +use crate::types::Value; + +#[derive(Debug, Clone, Copy)] +pub struct PropertyKey(Value); diff --git a/nova_vm/src/types/spec.rs b/nova_vm/src/types/spec.rs index f87bdbda..f3e7960e 100644 --- a/nova_vm/src/types/spec.rs +++ b/nova_vm/src/types/spec.rs @@ -1,3 +1,5 @@ +mod property_descriptor; mod reference; +pub use property_descriptor::PropertyDescriptor; pub use reference::{Base, Reference, ReferencedName}; diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs new file mode 100644 index 00000000..6df57bf8 --- /dev/null +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -0,0 +1,95 @@ +use crate::{ + execution::{Agent, JsResult}, + types::{Object, Value}, +}; + +/// 6.2.6 The Property Descriptor Specification Type +/// https://tc39.es/ecma262/#sec-property-descriptor-specification-type +#[derive(Debug)] +pub struct PropertyDescriptor { + /// [[Value]] + pub value: Option, + + /// [[Writable]] + pub writable: Option, + + /// [[Get]] + pub get: Option, + + /// [[Set]] + pub set: Option, + + /// [[Enumerable]] + pub enumerable: Option, + + /// [[Configurable]] + pub configurable: Option, +} + +impl PropertyDescriptor { + /// 6.2.6.1 IsAccessorDescriptor ( Desc ) + /// https://tc39.es/ecma262/#sec-isaccessordescriptor + pub fn is_accessor_descriptor(&self) -> bool { + // 1. If Desc is undefined, return false. + match (self.get, self.set) { + // 2. If Desc has a [[Get]] field, return true. + // 3. If Desc has a [[Set]] field, return true. + (Some(_), Some(_)) => true, + + // 4. Return false. + _ => false, + } + } + + /// 6.2.6.2 IsDataDescriptor ( Desc ) + /// https://tc39.es/ecma262/#sec-isdatadescriptor + pub fn is_data_descriptor(&self) -> bool { + // 1. If Desc is undefined, return false. + match (self.value, self.writable) { + // 2. If Desc has a [[Value]] field, return true. + // 3. If Desc has a [[Writable]] field, return true. + (Some(_), Some(_)) => true, + // 4. Return false. + _ => false, + } + } + + /// 6.2.6.3 IsGenericDescriptor ( Desc ) + /// https://tc39.es/ecma262/#sec-isgenericdescriptor + pub fn is_generic_descriptor(&self) -> bool { + // 1. If Desc is undefined, return false. + // 2. If IsAccessorDescriptor(Desc) is true, return false. + // 3. If IsDataDescriptor(Desc) is true, return false. + // 4. Return true. + !self.is_accessor_descriptor() && !self.is_data_descriptor() + } + + /// 6.2.6.4 FromPropertyDescriptor ( Desc ) + /// https://tc39.es/ecma262/#sec-frompropertydescriptor + pub fn from_property_descriptor(&self, agent: &mut Agent) -> JsResult { + let realm = agent.current_realm(); + let realm = realm.borrow_mut(); + + // 1. If Desc is undefined, return undefined. + + // 2. Let obj be OrdinaryObjectCreate(%Object.prototype%). + // 3. Assert: obj is an extensible ordinary object with no own properties. + + // 4. If Desc has a [[Value]] field, then + // a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]). + + // 5. If Desc has a [[Writable]] field, then + + // 6. If Desc has a [[Get]] field, then + // a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]). + // 7. If Desc has a [[Set]] field, then + // a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]). + // 8. If Desc has an [[Enumerable]] field, then + // a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]). + + // 9. If Desc has a [[Configurable]] field, then + // a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]). + // 10. Return obj. + todo!() + } +} From ebe57e84e38b496283e502cb876f7eac9b89053c Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Wed, 26 Jul 2023 13:00:41 -0500 Subject: [PATCH 09/32] get builtin flow working mid --- nova_vm/src/builtins.rs | 2 +- nova_vm/src/builtins/builtin_function.rs | 56 +++-- nova_vm/src/builtins/ecmascript_function.rs | 30 ++- nova_vm/src/builtins/number.rs | 164 +++++++++++++- nova_vm/src/builtins/ordinary.rs | 204 +++++++++++++++++- nova_vm/src/execution.rs | 2 +- nova_vm/src/execution/realm.rs | 13 +- nova_vm/src/execution/realm/intrinsics.rs | 169 +++++++++------ nova_vm/src/heap.rs | 55 +++-- nova_vm/src/heap/array.rs | 87 ++++---- nova_vm/src/heap/bigint.rs | 30 +-- nova_vm/src/heap/boolean.rs | 6 +- nova_vm/src/heap/date.rs | 93 ++++---- nova_vm/src/heap/error.rs | 4 +- nova_vm/src/heap/function.rs | 19 +- nova_vm/src/heap/heap_constants.rs | 8 +- nova_vm/src/heap/math.rs | 70 +++--- nova_vm/src/heap/number.rs | 26 +-- nova_vm/src/heap/object.rs | 101 +++------ nova_vm/src/heap/regexp.rs | 15 +- nova_vm/src/heap/string.rs | 2 +- nova_vm/src/heap/symbol.rs | 11 +- nova_vm/src/types.rs | 2 +- nova_vm/src/types/language.rs | 2 +- nova_vm/src/types/language/object.rs | 109 ++++++++-- .../types/language/object/internal_methods.rs | 52 +++-- .../src/types/language/object/property_key.rs | 44 +++- nova_vm/src/types/language/value.rs | 87 +++++++- nova_vm/src/types/spec/property_descriptor.rs | 2 +- 29 files changed, 1054 insertions(+), 411 deletions(-) diff --git a/nova_vm/src/builtins.rs b/nova_vm/src/builtins.rs index 860649f9..a840bcf3 100644 --- a/nova_vm/src/builtins.rs +++ b/nova_vm/src/builtins.rs @@ -7,7 +7,7 @@ pub mod ordinary; pub use array::ArrayConstructor; pub use builtin_function::{ create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, - RegularFn as JsFunction, + ConstructorFn, RegularFn as JsFunction, RegularFn, }; pub use ecmascript_function::ECMAScriptFunction; pub use number::NumberConstructor; diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index ece6cca4..b48cdc0a 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -1,15 +1,17 @@ use crate::{ - execution::{Agent, JsResult, Realm}, - types::{Function, Object, Value}, + execution::{Agent, Intrinsics, JsResult, Realm}, + heap::CreateHeapData, + types::{Function, Object, PropertyDescriptor, Value}, }; #[derive(Debug)] pub struct ArgumentsList<'a>(&'a [Value]); pub type RegularFn = fn(&mut Agent, Value, ArgumentsList<'_>) -> JsResult; -type ConstructorFn = fn(&mut Agent, Value, ArgumentsList<'_>, Option) -> JsResult; +pub type ConstructorFn = + fn(&mut Agent, Value, ArgumentsList<'_>, Option) -> JsResult; -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] pub enum Behaviour { Regular(RegularFn), Constructor(ConstructorFn), @@ -22,14 +24,14 @@ pub trait Builtin { #[derive(Debug, Default)] pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> { pub length: u32, - pub name: &'static str, + pub name: &'a str, pub realm: Option<&'a mut Realm<'ctx, 'host>>, pub prototype: Option, pub prefix: Option, } impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { - pub fn new(length: u32, name: &'static str, realm: &'a mut Realm<'ctx, 'host>) -> Self { + pub fn new(length: u32, name: &'a str, realm: &'a mut Realm<'ctx, 'host>) -> Self { Self { length, name, @@ -49,12 +51,11 @@ pub fn create_builtin_function<'a, 'b: 'a>( let realm = args.realm.unwrap(); // TODO: load record // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. - let prototype = if let Some(prototype) = args.prototype { - prototype - } else { - realm.intrinsics().function() - }; + let prototype = args + .prototype + .unwrap_or_else(Intrinsics::function_prototype); + // TODO: Steps 3-4 // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 // requires for the built-in function object that is about to be created. // 4. Append to internalSlotsList the elements of additionalInternalSlotsList. @@ -63,24 +64,39 @@ pub fn create_builtin_function<'a, 'b: 'a>( // described by behaviour using the provided arguments as the values of the corresponding // parameters specified by behaviour. The new function object has internal slots whose names // are the elements of internalSlotsList, and an [[InitialName]] internal slot. - + // 6. Set func.[[Prototype]] to prototype. + // 7. Set func.[[Extensible]] to true. + // 8. Set func.[[Realm]] to realm. + // NOTE: Heap data is implicitly attached to the Realm so I don't think + // this matters. + let object = realm + .heap + .create_object_with_prototype(prototype.into_value()); + + // 9. Set func.[[InitialName]] to null. + // TODO: This is non-standard. + let initial_name = realm.heap.create(args.name).into_value(); // 10. Perform SetFunctionLength(func, length). + let length = args.length as u8; + // TODO: Actually set behaviour somewhere + let func = realm.heap.create_function(initial_name, length, false); + // TODO: Steps 11-12 // 11. If prefix is not present, then // a. Perform SetFunctionName(func, name). // 12. Else, // a. Perform SetFunctionName(func, name, prefix). // 13. Return func. - todo!(); + Function::new(Value::Function(func)) } -pub fn define_builtin_function<'a>( +pub fn define_builtin_function<'a, 'b>( object: Object, - name: &'static str, + name: &'a str, behaviour: RegularFn, length: u32, - realm: &'a mut Realm<'a, 'a>, + realm: &'a mut Realm<'b, 'b>, ) -> JsResult<()> { let function = create_builtin_function( Behaviour::Regular(behaviour), @@ -90,7 +106,13 @@ pub fn define_builtin_function<'a>( Ok(()) } -pub fn define_builtin_property(object: Object, name: &'static str, value: Value) {} +pub fn define_builtin_property( + object: Object, + name: &'static str, + descriptor: PropertyDescriptor, +) -> JsResult<()> { + Ok(()) +} pub fn todo_builtin(agent: &mut Agent, _: Value, _: ArgumentsList) -> JsResult { agent.throw_exception( diff --git a/nova_vm/src/builtins/ecmascript_function.rs b/nova_vm/src/builtins/ecmascript_function.rs index 2c32aa3b..433edbe8 100644 --- a/nova_vm/src/builtins/ecmascript_function.rs +++ b/nova_vm/src/builtins/ecmascript_function.rs @@ -3,8 +3,8 @@ use std::{cell::RefCell, rc::Rc}; use oxc_ast::ast::{FormalParameters, FunctionBody}; use crate::{ - execution::{Environment, PrivateEnvironment, Realm, ScriptOrModule}, - types::Object, + execution::{Agent, Environment, JsResult, PrivateEnvironment, Realm, ScriptOrModule}, + types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, }; #[derive(Debug, Clone, Copy)] @@ -61,3 +61,29 @@ pub struct ECMAScriptFunction<'ctx, 'host> { /// [[IsClassConstructor]] pub is_class_constructor: bool, } + +impl Object { + /// 10.2.10 SetFunctionLength ( F, length ) + /// https://tc39.es/ecma262/#sec-setfunctionlength + pub fn set_function_length(self, agent: &mut Agent, length: i64) -> JsResult<()> { + let function = self; + + // TODO: 1. Assert: F is an extensible object that does not have a "length" own property. + + // 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }). + function.define_property_or_throw( + agent, + PropertyKey::try_from(Value::try_from("length").unwrap()).unwrap(), + PropertyDescriptor { + value: Some(Number::try_from(length).unwrap().into_value()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(true), + ..Default::default() + }, + )?; + + // 3. Return unused. + Ok(()) + } +} diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index bbed2511..fffd8bf7 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -1,23 +1,175 @@ use super::{ - builtin_function::define_builtin_function, create_builtin_function, todo_builtin, - ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, + builtin_function::{define_builtin_function, define_builtin_property}, + create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, }; use crate::{ - execution::{Agent, JsResult, Realm}, - types::{Object, Value}, + execution::{Agent, Intrinsics, JsResult, Realm}, + heap::{BuiltinObjectIndexes, CreateHeapData}, + types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, + SmallInteger, }; pub struct NumberConstructor; impl Builtin for NumberConstructor { fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { - let object = create_builtin_function( + let object: Object = create_builtin_function( Behaviour::Regular(NumberConstructor::behaviour), - BuiltinFunctionArgs::new(1, "Array", realm), + BuiltinFunctionArgs { + length: 1, + name: "Number", + realm: Some(realm), + prototype: Some(Intrinsics::function_prototype()), + ..Default::default() + }, ) .into_object(); + // 21.1.2.1 Number.EPSILON + // https://tc39.es/ecma262/#sec-number.epsilon + define_builtin_property( + object, + "EPSILON", + PropertyDescriptor { + value: Some(realm.heap.create(f64::EPSILON).into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.6 Number.MAX_SAFE_INTEGER + // https://tc39.es/ecma262/#sec-number.max_safe_integer + define_builtin_property( + object, + "MAX_SAFE_INTEGER", + PropertyDescriptor { + value: Some(Number::from(SmallInteger::MAX).into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.7 Number.MAX_VALUE + // https://tc39.es/ecma262/#sec-number.max_value + define_builtin_property( + object, + "MAX_VALUE", + PropertyDescriptor { + value: Some(realm.heap.create(f64::MAX).into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.8 Number.MIN_SAFE_INTEGER + // https://tc39.es/ecma262/#sec-number.min_safe_integer + define_builtin_property( + object, + "MIN_SAFE_INTEGER", + PropertyDescriptor { + value: Some(Number::from(SmallInteger::MIN).into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.8 Number.MIN_VALUE + // https://tc39.es/ecma262/#sec-number.min_value + define_builtin_property( + object, + "MIN_VALUE", + PropertyDescriptor { + value: Some(realm.heap.create(f64::MIN).into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.10 Number.NaN + // https://tc39.es/ecma262/#sec-number.nan + define_builtin_property( + object, + "NaN", + PropertyDescriptor { + value: Some(Number::nan().into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.11 Number.NEGATIVE_INFINITY + // https://tc39.es/ecma262/#sec-number.negative_infinity + define_builtin_property( + object, + "NEGATIVE_INFINITY", + PropertyDescriptor { + value: Some(Number::neg_inf().into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.2.14 Number.POSITIVE_INFINITY + // https://tc39.es/ecma262/#sec-number.positive_infinity + define_builtin_property( + object, + "POSITIVE_INFINITY", + PropertyDescriptor { + value: Some(Number::pos_inf().into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + define_builtin_function(object, "isFinite", todo_builtin, 1, realm)?; + define_builtin_function(object, "isNaN", todo_builtin, 1, realm)?; + define_builtin_function(object, "isSafeInteger", todo_builtin, 1, realm)?; + define_builtin_function(object, "parseFloat", todo_builtin, 1, realm)?; + define_builtin_function(object, "parseInt", todo_builtin, 2, realm)?; + + // 21.1.2.15 Number.prototype + // https://tc39.es/ecma262/#sec-number.prototype + define_builtin_property( + object, + "prototype", + PropertyDescriptor { + value: Some(Intrinsics::number_prototype().into()), + writable: Some(false), + enumerable: Some(false), + configurable: Some(false), + ..Default::default() + }, + )?; + + // 21.1.3.1 Number.prototype.constructor + // https://tc39.es/ecma262/#sec-number.prototype.constructor + define_builtin_property( + Intrinsics::number_prototype(), + "constructor", + PropertyDescriptor { + value: Some(object.into_value()), + writable: Some(true), + enumerable: Some(false), + configurable: Some(true), + ..Default::default() + }, + )?; Ok(object) } diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index 9e221309..aaca0be6 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -1,18 +1,216 @@ use crate::{ execution::{Agent, JsResult}, - types::Object, + types::{InternalMethods, Object, PropertyDescriptor, PropertyKey, Value}, +}; + +use super::ArgumentsList; + +/// 10.1 Ordinary Object Internal Methods and Internal Slots +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots +pub static METHODS: InternalMethods = InternalMethods { + get_prototype_of, + set_prototype_of, + is_extensible, + prevent_extensions, + get_own_property, + define_own_property, + has_property, + get, + set, + delete, + own_property_keys, + call: Some(call), + construct: Some(construct), }; /// 10.1.1 [[GetPrototypeOf]] ( ) /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getprototypeof fn get_prototype_of(agent: &mut Agent, object: Object) -> Option { // 1. Return OrdinaryGetPrototypeOf(O). - return ordinary_get_prototype_of(agent, object); + ordinary_get_prototype_of(agent, object) } /// 10.1.1.1 OrdinaryGetPrototypeOf ( O ) /// https://tc39.es/ecma262/#sec-ordinarygetprototypeof pub fn ordinary_get_prototype_of(agent: &mut Agent, object: Object) -> Option { // 1. Return O.[[Prototype]]. - return object.prototype(agent); + object.prototype(agent) +} + +/// 10.1.2 [[SetPrototypeOf]] ( V ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-setprototypeof-v +fn set_prototype_of( + agent: &mut Agent, + object: Object, + prototype: Option, +) -> JsResult { + // 1. Return OrdinarySetPrototypeOf(O, V). + return ordinary_set_prototype_of(agent, object, prototype); +} + +/// 10.1.2.1 OrdinarySetPrototypeOf ( O, V ) +/// https://tc39.es/ecma262/#sec-ordinarysetprototypeof +pub fn ordinary_set_prototype_of( + agent: &mut Agent, + object: Object, + prototype: Option, +) -> JsResult { + // 1. Let current be O.[[Prototype]]. + let current = object.prototype(agent); + + // 2. If SameValue(V, current) is true, return true. + match (prototype, current) { + (Some(prototype), Some(current)) + if prototype + .into_value() + .same_value(agent, current.into_value()) => + { + return Ok(true) + } + (None, None) => return Ok(true), + _ => {} + } + + // 3. Let extensible be O.[[Extensible]]. + let extensible = object.extensible(agent); + + // 4. If extensible is false, return false. + if !extensible { + return Ok(false); + } + + // 5. Let p be V. + let mut parent_prototype_outer = prototype; + + // 6. Let done be false. + // 7. Repeat, while done is false, + while let Some(parent_prototype) = parent_prototype_outer { + // a. If p is null, then + // i. Set done to true. + + // b. Else if SameValue(p, O) is true, then + if parent_prototype + .into_value() + .same_value(agent, object.into_value()) + { + // i. Return false. + return Ok(false); + } + + // c. Else, + // i. If p.[[GetPrototypeOf]] is not the ordinary object internal method defined in 10.1.1, + // set done to true. + if parent_prototype.internal_methods(agent).get_prototype_of != get_prototype_of { + break; + } + + // ii. Else, set p to p.[[Prototype]]. + parent_prototype_outer = parent_prototype.prototype(agent); + } + + // 8. Set O.[[Prototype]] to V. + object.set_prototype(agent, parent_prototype_outer); + + // 9. Return true. + Ok(true) +} + +/// 10.1.3 [[IsExtensible]] ( ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible +fn is_extensible(agent: &mut Agent, object: Object) -> JsResult { + // 1. Return OrdinaryIsExtensible(O). + Ok(ordinary_is_extensible(agent, object)) +} + +/// 10.1.3.1 OrdinaryIsExtensible ( O ) +/// https://tc39.es/ecma262/#sec-ordinaryisextensible +pub fn ordinary_is_extensible(agent: &mut Agent, object: Object) -> bool { + // 1. Return O.[[Extensible]]. + todo!() +} + +/// 10.1.4 [[PreventExtensions]] ( ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions +fn prevent_extensions(agent: &mut Agent, object: Object) -> JsResult { + // 1. Return OrdinaryPreventExtensions(O). + Ok(ordinary_prevent_extensions(agent, object)) +} + +/// 10.1.4.1 OrdinaryPreventExtensions ( O ) +/// https://tc39.es/ecma262/#sec-ordinarypreventextensions +pub fn ordinary_prevent_extensions(agent: &mut Agent, object: Object) -> bool { + // 1. Set O.[[Extensible]] to false. + todo!(); + + // 2. Return true. + return true; +} + +pub fn get_own_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, +) -> JsResult<()> { + todo!() +} + +pub fn define_own_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, +) -> bool { + todo!() +} + +pub fn has_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, +) -> JsResult { + todo!() +} + +pub fn get( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + receiver: Value, +) -> JsResult { + todo!() +} + +pub fn set( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + value: Value, + receiver: Value, +) -> JsResult { + todo!() +} + +pub fn delete(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult { + todo!() +} + +pub fn own_property_keys(agent: &mut Agent, object: Object) -> JsResult> { + todo!() +} + +pub fn call( + agent: &mut Agent, + object: Object, + this_value: Value, + arguments_list: ArgumentsList, +) -> JsResult { + todo!() +} + +pub fn construct( + agent: &mut Agent, + object: Object, + arguments_list: ArgumentsList, +) -> JsResult { + todo!() } diff --git a/nova_vm/src/execution.rs b/nova_vm/src/execution.rs index c0362db7..6f33692c 100644 --- a/nova_vm/src/execution.rs +++ b/nova_vm/src/execution.rs @@ -10,4 +10,4 @@ pub use environments::{ PrivateEnvironment, }; pub use execution_context::{ECMAScriptCode, ExecutionContext, ScriptOrModule}; -pub use realm::Realm; +pub use realm::{Intrinsics, Realm}; diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index cb8c20a0..1f1333d5 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -2,7 +2,7 @@ mod intrinsics; use super::{Agent, GlobalEnvironment}; use crate::{types::Object, Heap}; -use intrinsics::Intrinsics; +pub use intrinsics::Intrinsics; use std::{any::Any, cell::RefCell, rc::Rc}; /// 9.3 Realms @@ -13,10 +13,9 @@ pub struct Realm<'ctx, 'host> { pub agent: Rc>>, - // rng: Xoroshiro128, - /// [[Intrinsics]] - // pub intrinsics: Intrinsics<'ctx, 'host>, + // NOTE: We will need an rng here at some point. + // NOTE: [[Intrinsics]] are statically known via the [`Intrinsics`] struct. /// [[GlobalObject]] pub global_object: Object, @@ -27,9 +26,3 @@ pub struct Realm<'ctx, 'host> { pub host_defined: Option>>, // TODO: [[TemplateMap]], [[LoadedModules]] } - -impl<'ctx, 'host> Realm<'ctx, 'host> { - pub fn intrinsics<'a>(&'a self) -> Intrinsics<'a, 'ctx, 'host> { - Intrinsics { realm: self } - } -} diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index acd8dac9..b48abe33 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -1,194 +1,235 @@ -use super::Realm; -use crate::types::Object; +use crate::{ + heap::BuiltinObjectIndexes, + types::{Object, Value}, +}; -#[derive(Debug)] -pub struct Intrinsics<'a, 'ctx, 'host> { - pub realm: &'a Realm<'ctx, 'host>, -} +// TODO: We should probably consider lazily loading intrinsics. This would +// contain a mutable reference to [`Realm`] and be created via a +// `Realm::intrinsic()` method to guarantee safety. + +pub struct Intrinsics; -impl Intrinsics<'_, '_, '_> { +impl Intrinsics { /// %Array% - pub fn array(&self) -> Object { - todo!() + pub fn array() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ArrayConstructorIndex.into(), + )) } /// %Array.prototype% - pub fn array_prototype_prototype(&self) -> Object { - todo!() + pub fn array_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ArrayPrototypeIndex.into(), + )) } /// %BigInt% - pub fn big_int(&self) -> Object { - todo!() + pub fn big_int() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::BigintConstructorIndex.into(), + )) } /// %BigInt.prototype% - pub fn big_int_prototype(&self) -> Object { - todo!() + pub fn big_int_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::BigintPrototypeIndex.into(), + )) } /// %Boolean% - pub fn boolean(&self) -> Object { - todo!() + pub fn boolean() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::BooleanConstructorIndex.into(), + )) } /// %Boolean.prototype% - pub fn boolean_prototype(&self) -> Object { - todo!() + pub fn boolean_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::BooleanPrototypeIndex.into(), + )) } /// %Error% - pub fn error(&self) -> Object { - todo!() + pub fn error() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ErrorConstructorIndex.into(), + )) } /// %Error.prototype% - pub fn error_prototype(&self) -> Object { - todo!() + pub fn error_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ErrorPrototypeIndex.into(), + )) } /// %eval% - pub fn eval(&self) -> Object { + pub fn eval() -> Object { todo!() } /// %EvalError% - pub fn eval_error(&self) -> Object { - todo!() + pub fn eval_error() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ArrayConstructorIndex.into(), + )) } /// %EvalError.prototype% - pub fn eval_error_prototype(&self) -> Object { + pub fn eval_error_prototype() -> Object { todo!() } /// %Function% - pub fn function(&self) -> Object { - todo!() + pub fn function() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::FunctionConstructorIndex.into(), + )) } /// %Function.prototype% - pub fn function_prototype(&self) -> Object { - todo!() + pub fn function_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )) } /// %isFinite% - pub fn is_finite(&self) -> Object { + pub fn is_finite() -> Object { todo!() } /// %isNaN% - pub fn is_nan(&self) -> Object { + pub fn is_nan() -> Object { todo!() } /// %Math% - pub fn math(&self) -> Object { - todo!() + pub fn math() -> Object { + Object::new(Value::Object(BuiltinObjectIndexes::MathObjectIndex.into())) } /// %Number% - pub fn number(&self) -> Object { - todo!() + pub fn number() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::NumberConstructorIndex.into(), + )) } /// %Number.prototype% - pub fn number_prototype(&self) -> Object { - todo!() + pub fn number_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::NumberPrototypeIndex.into(), + )) } /// %Object% - pub fn object(&self) -> Object { - todo!() + pub fn object() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ObjectConstructorIndex.into(), + )) } /// %Object.prototype% - pub fn object_prototype(&self) -> Object { - todo!() + pub fn object_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )) } /// %Object.prototype.toString% - pub fn object_prototype_to_string(&self) -> Object { + pub fn object_prototype_to_string() -> Object { todo!() } /// %RangeError% - pub fn range_error(&self) -> Object { + pub fn range_error() -> Object { todo!() } /// %RangeError.prototype% - pub fn range_error_prototype(&self) -> Object { + pub fn range_error_prototype() -> Object { todo!() } /// %ReferenceError% - pub fn reference_error(&self) -> Object { + pub fn reference_error() -> Object { todo!() } /// %ReferenceError.prototype% - pub fn reference_error_prototype(&self) -> Object { + pub fn reference_error_prototype() -> Object { todo!() } /// %Reflect% - pub fn reflect(&self) -> Object { + pub fn reflect() -> Object { todo!() } /// %String% - pub fn string(&self) -> Object { - todo!() + pub fn string() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::StringConstructorIndex.into(), + )) } /// %String.prototype% - pub fn string_prototype(&self) -> Object { - todo!() + pub fn string_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::StringPrototypeIndex.into(), + )) } /// %Symbol% - pub fn symbol(&self) -> Object { - todo!() + pub fn symbol() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::SymbolConstructorIndex.into(), + )) } /// %Symbol.prototype% - pub fn symbol_prototype(&self) -> Object { - todo!() + pub fn symbol_prototype() -> Object { + Object::new(Value::Object( + BuiltinObjectIndexes::SymbolPrototypeIndex.into(), + )) } /// %SyntaxError% - pub fn syntax_error(&self) -> Object { + pub fn syntax_error() -> Object { todo!() } /// %SyntaxError.prototype% - pub fn syntax_error_prototype(&self) -> Object { + pub fn syntax_error_prototype() -> Object { todo!() } /// %ThrowTypeError% - pub fn throw_type_error(&self) -> Object { + pub fn throw_type_error() -> Object { todo!() } /// %TypeError% - pub fn type_error(&self) -> Object { + pub fn type_error() -> Object { todo!() } /// %TypeError.prototype% - pub fn type_error_prototype(&self) -> Object { + pub fn type_error_prototype() -> Object { todo!() } /// %URIError% - pub fn uri_error(&self) -> Object { + pub fn uri_error() -> Object { todo!() } /// %URIError.prototype% - pub fn uri_error_prototype(&self) -> Object { + pub fn uri_error_prototype() -> Object { todo!() } } diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 95d989bf..66e4bc62 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -16,6 +16,8 @@ mod regexp; mod string; mod symbol; +pub(crate) use self::heap_constants::BuiltinObjectIndexes; + use self::{ array::{initialize_array_heap, ArrayHeapData}, bigint::{initialize_bigint_heap, BigIntHeapData}, @@ -27,10 +29,9 @@ use self::{ ElementsVector, }, error::{initialize_error_heap, ErrorHeapData}, - function::{initialize_function_heap, FunctionHeapData, JsBindingFunction}, + function::{initialize_function_heap, FunctionHeapData}, heap_constants::{ - BuiltinObjectIndexes, FIRST_CONSTRUCTOR_INDEX, LAST_BUILTIN_OBJECT_INDEX, - LAST_WELL_KNOWN_SYMBOL_INDEX, + FIRST_CONSTRUCTOR_INDEX, LAST_BUILTIN_OBJECT_INDEX, LAST_WELL_KNOWN_SYMBOL_INDEX, }, indexes::{BaseIndex, FunctionIndex, NumberIndex, ObjectIndex, StringIndex}, math::initialize_math_object, @@ -42,7 +43,7 @@ use self::{ string::{initialize_string_heap, StringHeapData}, symbol::{initialize_symbol_heap, SymbolHeapData}, }; -use crate::types::{Function, Number, String, Value}; +use crate::types::{Function, Number, Object, String, Value}; use wtf8::Wtf8; #[derive(Debug)] @@ -125,9 +126,8 @@ impl<'a> GetHeapData<'a, StringHeapData, &'a Wtf8> for Heap { impl CreateHeapData for Heap { fn create(&mut self, data: FunctionHeapData) -> Function { - let id = self.functions.len(); self.functions.push(Some(data)); - Function::new(Value::Function(FunctionIndex::from_index(id))) + Function::new(Value::Function(FunctionIndex::last(&self.functions))) } } @@ -142,6 +142,24 @@ impl<'a> GetHeapData<'a, FunctionHeapData, &'a FunctionHeapData> for Heap { } } +impl CreateHeapData for Heap { + fn create(&mut self, data: ObjectHeapData) -> Object { + self.objects.push(Some(data)); + Object::new(Value::Object(ObjectIndex::last(&self.objects))) + } +} + +impl<'a> GetHeapData<'a, ObjectHeapData, &'a ObjectHeapData> for Heap { + fn get(&'a self, id: ObjectIndex) -> &'a ObjectHeapData { + self.objects + .get(id.into_index()) + .as_ref() + .unwrap() + .as_ref() + .unwrap() + } +} + impl Heap { pub fn new() -> Heap { let mut heap = Heap { @@ -223,7 +241,7 @@ impl Heap { name: Value, length: u8, uses_arguments: bool, - binding: JsBindingFunction, + // behaviour: Behaviour, ) -> FunctionIndex { let entries = vec![ ObjectEntry::new( @@ -238,19 +256,20 @@ impl Heap { let (keys, values): (ElementsVector, ElementsVector) = self.elements.create_object_entries(entries); let func_object_data = ObjectHeapData { - _extensible: true, + extensible: true, keys, values, prototype: Value::Object(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), }; self.objects.push(Some(func_object_data)); let func_data = FunctionHeapData { - binding, + // behaviour, // bound: None, length, object_index: ObjectIndex::last(&self.objects), // uses_arguments, // visible: None, + initial_name: Value::Null, }; let index = FunctionIndex::from_index(self.functions.len()); self.functions.push(Some(func_data)); @@ -260,7 +279,7 @@ impl Heap { 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, + extensible: true, keys, values, prototype: Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), @@ -272,7 +291,7 @@ impl Heap { 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, + extensible: true, keys, values, prototype: Value::Null, @@ -281,6 +300,18 @@ impl Heap { ObjectIndex::last(&self.objects) } + pub(crate) fn create_object_with_prototype(&mut self, prototype: Value) -> ObjectIndex { + let (keys, values) = self.elements.create_object_entries(vec![]); + let object_data = ObjectHeapData { + extensible: true, + keys, + values, + prototype, + }; + self.objects.push(Some(object_data)); + ObjectIndex::last(&self.objects) + } + pub(crate) fn insert_builtin_object( &mut self, index: BuiltinObjectIndexes, @@ -290,7 +321,7 @@ impl Heap { ) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(entries); let object_data = ObjectHeapData { - _extensible: extensible, + extensible, keys, values, prototype, diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index b8b4936b..05b02700 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -41,9 +41,9 @@ pub fn initialize_array_heap(heap: &mut Heap) { let to_spliced_key = PropertyKey::from_str(heap, "toSpliced"); let values_key = PropertyKey::from_str(heap, "values"); 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_prototype_function_entry(heap, "from", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isArray", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "of", 0, true), ObjectEntry::new_constructor_prototype_entry( heap, BuiltinObjectIndexes::ArrayPrototypeIndex.into(), @@ -51,7 +51,7 @@ pub fn initialize_array_heap(heap: &mut Heap) { ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::Species.into()), PropertyDescriptor::ReadOnly { - get: heap.create_function(species_function_name, 0, false, array_species), + get: heap.create_function(species_function_name, 0, false), enumerable: false, configurable: true, }, @@ -71,53 +71,53 @@ pub fn initialize_array_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: array_constructor_binding, + initial_name: Value::Null, }); 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_prototype_function_entry(heap, "at", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "concat", 1, true), 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), + ObjectEntry::new_prototype_function_entry(heap, "copyWithin", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "entries", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "every", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "fill", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "filter", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "find", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "findIndex", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "findLast", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "findLastIndex", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "flat", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "flatMap", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "forEach", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "includes", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "indexOf", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "join", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "keys", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "lastIndexOf", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "map", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "pop", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "push", 1, true), + ObjectEntry::new_prototype_function_entry(heap, "reduce", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "reduceRight", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "reverse", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "shift", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "slice", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "some", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "sort", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "splice", 2, true), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toReversed", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toSorted", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toSpliced", 2, true), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "unshift", 1, true), + ObjectEntry::new_prototype_function_entry(heap, "values", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "with", 2, false), // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -125,7 +125,6 @@ pub fn initialize_array_heap(heap: &mut Heap) { WellKnownSymbolIndexes::Iterator.into(), 0, false, - array_todo, ), ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::Unscopables.into()), diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index fa80f2eb..86679db1 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -22,8 +22,8 @@ impl BigIntHeapData { pub fn initialize_bigint_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "asIntN", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "asUintN", 2, false), ObjectEntry::new_constructor_prototype_entry( heap, BuiltinObjectIndexes::BigintPrototypeIndex.into(), @@ -43,7 +43,7 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: bigint_constructor, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -52,27 +52,9 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { 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, - ), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), // @@ToStringTag // ObjectEntry { key: PropertyKey::Symbol(), PropertyDescriptor } ]; diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index 26c622ce..56703978 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -31,7 +31,7 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: boolean_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -40,8 +40,8 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { BuiltinObjectIndexes::BooleanConstructorIndex, ))), ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, boolean_todo), - ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false, boolean_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::BooleanPrototypeIndex, diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index cbded91d..2ee8b467 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -24,13 +24,13 @@ pub(crate) struct DateHeapData { pub fn initialize_date_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "now", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "parse", 1, false), ObjectEntry::new_constructor_prototype_entry( heap, BuiltinObjectIndexes::DatePrototypeIndex.into(), ), - ObjectEntry::new_prototype_function_entry(heap, "UTC", 7, false, date_todo), + ObjectEntry::new_prototype_function_entry(heap, "UTC", 7, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::DateConstructorIndex, @@ -46,7 +46,7 @@ pub fn initialize_date_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: date_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -55,55 +55,54 @@ pub fn initialize_date_heap(heap: &mut Heap) { 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_function_entry(heap, "getDate", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getDay", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getFullYear", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getHours", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getMilliseconds", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getMinutes", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getMonth", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getSeconds", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getTime", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getTimezoneOffset", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCDate", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCDay", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCFullYear", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCHours", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMilliseconds", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMinutes", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCMonth", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "getUTCSeconds", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "setDate", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setFullYear", 3, false), + ObjectEntry::new_prototype_function_entry(heap, "setHours", 4, false), + ObjectEntry::new_prototype_function_entry(heap, "setMilliseconds", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setMinutes", 3, false), + ObjectEntry::new_prototype_function_entry(heap, "setMonth", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "setSeconds", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "setTime", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCDate", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCFullYear", 3, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCHours", 4, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMilliseconds", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMinutes", 3, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCMonth", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "setUTCSeconds", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "toDateString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toJSON", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleDateString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleTimeString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toTimeString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toUTCString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), ObjectEntry::new_prototype_symbol_function_entry( heap, "[Symbol.toPrimitive]", WellKnownSymbolIndexes::ToPrimitive.into(), 1, false, - date_todo, ), ]; heap.insert_builtin_object( diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index f1700e40..05c0d3f6 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -38,7 +38,7 @@ pub fn initialize_error_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: error_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -55,7 +55,7 @@ pub fn initialize_error_heap(heap: &mut Heap) { PropertyKey::from_str(heap, "name"), PropertyDescriptor::rwx(Value::from_str(heap, "Error")), ), - ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false, error_todo), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::ErrorPrototypeIndex, diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 9ecdd73e..627de74d 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -1,4 +1,5 @@ use crate::{ + builtins::Behaviour, execution::JsResult, heap::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, @@ -13,13 +14,14 @@ use super::{ object::{ObjectEntry, PropertyKey}, }; -pub type JsBindingFunction = fn(heap: &mut Heap, this: Value, args: &[Value]) -> JsResult; - #[derive(Debug, Clone)] pub(crate) struct FunctionHeapData { pub(super) object_index: ObjectIndex, pub(super) length: u8, - pub(super) binding: JsBindingFunction, + pub(crate) initial_name: Value, + // pub(crate) behaviour: Behaviour, + // TODO: Should we create a `BoundFunctionHeapData` for an exotic object + // that allows setting fields and other deoptimizations? // pub(super) uses_arguments: bool, // pub(super) bound: Option>, // pub(super) visible: Option>, @@ -45,26 +47,25 @@ pub fn initialize_function_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: function_constructor_binding, + initial_name: Value::Null, }); 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_prototype_function_entry(heap, "apply", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "bind", 1, true), + ObjectEntry::new_prototype_function_entry(heap, "call", 1, true), 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_function_entry(heap, "toString", 0, false), 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 diff --git a/nova_vm/src/heap/heap_constants.rs b/nova_vm/src/heap/heap_constants.rs index d3c53417..b95d5868 100644 --- a/nova_vm/src/heap/heap_constants.rs +++ b/nova_vm/src/heap/heap_constants.rs @@ -10,8 +10,8 @@ use super::indexes::{FunctionIndex, ObjectIndex, SymbolIndex}; -#[derive(Debug, Clone, Copy)] #[repr(u32)] +#[derive(Debug, Clone, Copy)] pub enum BuiltinObjectIndexes { // Fundamental objects ObjectPrototypeIndex, @@ -148,6 +148,12 @@ impl Into for BuiltinObjectIndexes { } } +impl Default for BuiltinObjectIndexes { + fn default() -> Self { + Self::ObjectPrototypeIndex + } +} + pub const LAST_BUILTIN_OBJECT_INDEX: u32 = BuiltinObjectIndexes::ProxyConstructorIndex as u32; pub const FIRST_CONSTRUCTOR_INDEX: u32 = BuiltinObjectIndexes::ObjectConstructorIndex as u32; diff --git a/nova_vm/src/heap/math.rs b/nova_vm/src/heap/math.rs index eea06c25..c46fb255 100644 --- a/nova_vm/src/heap/math.rs +++ b/nova_vm/src/heap/math.rs @@ -15,41 +15,41 @@ pub(super) fn initialize_math_object(heap: &mut Heap) { let pi = Value::from_f64(heap, std::f64::consts::PI); let sqrt1_2 = Value::from_f64(heap, std::f64::consts::FRAC_1_SQRT_2); let sqrt2 = Value::from_f64(heap, std::f64::consts::SQRT_2); - let abs = ObjectEntry::new_prototype_function_entry(heap, "abs", 1, false, math_todo); - let acos = ObjectEntry::new_prototype_function_entry(heap, "acos", 1, false, math_todo); - let acosh = ObjectEntry::new_prototype_function_entry(heap, "acosh", 1, false, math_todo); - let asin = ObjectEntry::new_prototype_function_entry(heap, "asin", 1, false, math_todo); - let asinh = ObjectEntry::new_prototype_function_entry(heap, "asinh", 1, false, math_todo); - let atan = ObjectEntry::new_prototype_function_entry(heap, "atan", 1, false, math_todo); - let atanh = ObjectEntry::new_prototype_function_entry(heap, "atanh", 1, false, math_todo); - let atan2 = ObjectEntry::new_prototype_function_entry(heap, "atan2", 2, false, math_todo); - let cbrt = ObjectEntry::new_prototype_function_entry(heap, "cbrt", 1, false, math_todo); - let ceil = ObjectEntry::new_prototype_function_entry(heap, "ceil", 1, false, math_todo); - let clz32 = ObjectEntry::new_prototype_function_entry(heap, "clz32", 1, false, math_todo); - let cos = ObjectEntry::new_prototype_function_entry(heap, "cos", 1, false, math_todo); - let cosh = ObjectEntry::new_prototype_function_entry(heap, "cosh", 1, false, math_todo); - let exp = ObjectEntry::new_prototype_function_entry(heap, "exp", 1, false, math_todo); - let expm1 = ObjectEntry::new_prototype_function_entry(heap, "expm1", 1, false, math_todo); - let floor = ObjectEntry::new_prototype_function_entry(heap, "floor", 1, false, math_todo); - let fround = ObjectEntry::new_prototype_function_entry(heap, "fround", 1, false, math_todo); - let hypot = ObjectEntry::new_prototype_function_entry(heap, "hypot", 2, true, math_todo); - let imul = ObjectEntry::new_prototype_function_entry(heap, "imul", 2, false, math_todo); - let log = ObjectEntry::new_prototype_function_entry(heap, "log", 1, false, math_todo); - let log1p = ObjectEntry::new_prototype_function_entry(heap, "log1p", 1, false, math_todo); - let log10 = ObjectEntry::new_prototype_function_entry(heap, "log10", 1, false, math_todo); - let log2 = ObjectEntry::new_prototype_function_entry(heap, "log2", 1, false, math_todo); - let max = ObjectEntry::new_prototype_function_entry(heap, "max", 2, true, math_todo); - let min = ObjectEntry::new_prototype_function_entry(heap, "min", 2, true, math_todo); - let pow = ObjectEntry::new_prototype_function_entry(heap, "pow", 2, false, math_todo); - let random = ObjectEntry::new_prototype_function_entry(heap, "random", 0, false, math_todo); - let round = ObjectEntry::new_prototype_function_entry(heap, "round", 1, false, math_todo); - let sign = ObjectEntry::new_prototype_function_entry(heap, "sign", 1, false, math_todo); - let sin = ObjectEntry::new_prototype_function_entry(heap, "sin", 1, false, math_todo); - let sinh = ObjectEntry::new_prototype_function_entry(heap, "sinh", 1, false, math_todo); - let sqrt = ObjectEntry::new_prototype_function_entry(heap, "sqrt", 1, false, math_todo); - let tan = ObjectEntry::new_prototype_function_entry(heap, "tan", 1, false, math_todo); - let tanh = ObjectEntry::new_prototype_function_entry(heap, "tanh", 1, false, math_todo); - let trunc = ObjectEntry::new_prototype_function_entry(heap, "trunc", 1, false, math_todo); + let abs = ObjectEntry::new_prototype_function_entry(heap, "abs", 1, false); + let acos = ObjectEntry::new_prototype_function_entry(heap, "acos", 1, false); + let acosh = ObjectEntry::new_prototype_function_entry(heap, "acosh", 1, false); + let asin = ObjectEntry::new_prototype_function_entry(heap, "asin", 1, false); + let asinh = ObjectEntry::new_prototype_function_entry(heap, "asinh", 1, false); + let atan = ObjectEntry::new_prototype_function_entry(heap, "atan", 1, false); + let atanh = ObjectEntry::new_prototype_function_entry(heap, "atanh", 1, false); + let atan2 = ObjectEntry::new_prototype_function_entry(heap, "atan2", 2, false); + let cbrt = ObjectEntry::new_prototype_function_entry(heap, "cbrt", 1, false); + let ceil = ObjectEntry::new_prototype_function_entry(heap, "ceil", 1, false); + let clz32 = ObjectEntry::new_prototype_function_entry(heap, "clz32", 1, false); + let cos = ObjectEntry::new_prototype_function_entry(heap, "cos", 1, false); + let cosh = ObjectEntry::new_prototype_function_entry(heap, "cosh", 1, false); + let exp = ObjectEntry::new_prototype_function_entry(heap, "exp", 1, false); + let expm1 = ObjectEntry::new_prototype_function_entry(heap, "expm1", 1, false); + let floor = ObjectEntry::new_prototype_function_entry(heap, "floor", 1, false); + let fround = ObjectEntry::new_prototype_function_entry(heap, "fround", 1, false); + let hypot = ObjectEntry::new_prototype_function_entry(heap, "hypot", 2, true); + let imul = ObjectEntry::new_prototype_function_entry(heap, "imul", 2, false); + let log = ObjectEntry::new_prototype_function_entry(heap, "log", 1, false); + let log1p = ObjectEntry::new_prototype_function_entry(heap, "log1p", 1, false); + let log10 = ObjectEntry::new_prototype_function_entry(heap, "log10", 1, false); + let log2 = ObjectEntry::new_prototype_function_entry(heap, "log2", 1, false); + let max = ObjectEntry::new_prototype_function_entry(heap, "max", 2, true); + let min = ObjectEntry::new_prototype_function_entry(heap, "min", 2, true); + let pow = ObjectEntry::new_prototype_function_entry(heap, "pow", 2, false); + let random = ObjectEntry::new_prototype_function_entry(heap, "random", 0, false); + let round = ObjectEntry::new_prototype_function_entry(heap, "round", 1, false); + let sign = ObjectEntry::new_prototype_function_entry(heap, "sign", 1, false); + let sin = ObjectEntry::new_prototype_function_entry(heap, "sin", 1, false); + let sinh = ObjectEntry::new_prototype_function_entry(heap, "sinh", 1, false); + let sqrt = ObjectEntry::new_prototype_function_entry(heap, "sqrt", 1, false); + let tan = ObjectEntry::new_prototype_function_entry(heap, "tan", 1, false); + let tanh = ObjectEntry::new_prototype_function_entry(heap, "tanh", 1, false); + let trunc = ObjectEntry::new_prototype_function_entry(heap, "trunc", 1, false); let entries = vec![ ObjectEntry::new_frozen_entry(heap, "E", e), ObjectEntry::new_frozen_entry(heap, "LN10", ln10), diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index ecb69806..2c1280bf 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -32,10 +32,10 @@ pub fn initialize_number_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "isFinite", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isInteger", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isNan", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isSafeInteger", 1, false), ObjectEntry::new( PropertyKey::from_str(heap, "MAX_SAFE_INTEGER"), PropertyDescriptor::roh(Value::from_f64(heap, 9007199254740991.0)), @@ -60,8 +60,8 @@ pub fn initialize_number_heap(heap: &mut Heap) { PropertyKey::from_str(heap, "NEGATIVE_INFINITY"), PropertyDescriptor::roh(Value::from(f32::NEG_INFINITY)), ), - ObjectEntry::new_prototype_function_entry(heap, "parseFloat", 1, false, number_todo), - ObjectEntry::new_prototype_function_entry(heap, "parseInt", 2, false, number_todo), + ObjectEntry::new_prototype_function_entry(heap, "parseFloat", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "parseInt", 2, false), ObjectEntry::new( PropertyKey::from_str(heap, "POSITIVE_INFINITY"), PropertyDescriptor::roh(Value::from(f32::INFINITY)), @@ -85,7 +85,7 @@ pub fn initialize_number_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: number_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -94,12 +94,12 @@ pub fn initialize_number_heap(heap: &mut Heap) { 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), + ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toExponential", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toPrecision", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::NumberPrototypeIndex, diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 66a13224..593d932a 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -1,7 +1,6 @@ use crate::{ execution::JsResult, heap::{ - function::JsBindingFunction, heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, }, @@ -31,7 +30,7 @@ impl ObjectEntry { name: &str, length: u8, uses_arguments: bool, - binding: JsBindingFunction, + // behaviour: Behaviour, ) -> Self { let key = PropertyKey::from_str(heap, name); let name = match key { @@ -40,7 +39,7 @@ impl ObjectEntry { PropertyKey::String(idx) => Value::String(idx), PropertyKey::Symbol(idx) => Value::Symbol(idx), }; - let func_index = heap.create_function(name, length, uses_arguments, binding); + let func_index = heap.create_function(name, length, uses_arguments); let value = PropertyDescriptor::rwxh(Value::Function(func_index)); ObjectEntry { key, value } } @@ -51,11 +50,11 @@ impl ObjectEntry { symbol_index: SymbolIndex, length: u8, uses_arguments: bool, - binding: JsBindingFunction, + // behaviour: Behaviour, ) -> Self { let name = Value::from_str(heap, name); let key = PropertyKey::Symbol(symbol_index); - let func_index = heap.create_function(name, length, uses_arguments, binding); + let func_index = heap.create_function(name, length, uses_arguments); let value = PropertyDescriptor::roxh(Value::Function(func_index)); ObjectEntry { key, value } } @@ -226,7 +225,7 @@ impl PropertyDescriptor { #[derive(Debug, Clone, Copy)] pub(crate) struct ObjectHeapData { - pub(crate) _extensible: bool, + pub(crate) extensible: bool, // TODO: It's probably not necessary to have a whole Value here. // A prototype can only be set to be null or an object, meaning that most of the // possible Value options are impossible. @@ -246,7 +245,7 @@ impl ObjectHeapData { values: ElementsVector, ) -> Self { Self { - _extensible: extensible, + extensible, // TODO: Number, Boolean, etc. objects exist. These can all be // modeled with their own heap vector or alternatively by adding // a [[PrimitiveValue]] field to objects: Normally this field is None @@ -263,56 +262,32 @@ impl ObjectHeapData { pub fn initialize_object_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "assign", 1, true), + ObjectEntry::new_prototype_function_entry(heap, "create", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "defineProperties", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "defineProperty", 3, false), + ObjectEntry::new_prototype_function_entry(heap, "entries", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "freeze", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "fromEntries", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "getOwnPropertyDescriptor", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "getOwnPropertyDescriptors", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "getOwnPropertyNames", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "getOwnPropertySymbols", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "getPrototypeOf", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "hasOwn", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "is", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isExtensible", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isFrozen", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isSealed", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "keys", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "preventExtensions", 1, false), 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), + ObjectEntry::new_prototype_function_entry(heap, "seal", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "setPrototypeOf", 2, false), + ObjectEntry::new_prototype_function_entry(heap, "values", 1, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::ObjectConstructorIndex, @@ -328,7 +303,7 @@ pub fn initialize_object_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: object_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -337,18 +312,12 @@ pub fn initialize_object_heap(heap: &mut Heap) { 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), + ObjectEntry::new_prototype_function_entry(heap, "hasOwnProperty", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "isPrototypeOf", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "propertyIsEnumerable", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toLocaleString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::ObjectConstructorIndex, diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index 245eff4e..af67295a 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -30,7 +30,7 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::Species.into()), PropertyDescriptor::ReadOnly { - get: heap.create_function(species_function_name, 0, false, regexp_species), + get: heap.create_function(species_function_name, 0, false), enumerable: false, configurable: true, }, @@ -50,7 +50,7 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: regexp_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -60,7 +60,7 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { ))), ), // TODO: Write out all the getters - ObjectEntry::new_prototype_function_entry(heap, "exec", 1, false, regexp_todo), + ObjectEntry::new_prototype_function_entry(heap, "exec", 1, false), // TODO: These symbol function properties are actually rwxh, this helper generates roxh instead. ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -68,7 +68,6 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { WellKnownSymbolIndexes::Match.into(), 1, false, - regexp_todo, ), ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -76,7 +75,6 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { WellKnownSymbolIndexes::MatchAll.into(), 1, false, - regexp_todo, ), ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -84,7 +82,6 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { WellKnownSymbolIndexes::Replace.into(), 2, false, - regexp_todo, ), ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -92,7 +89,6 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { WellKnownSymbolIndexes::Search.into(), 1, false, - regexp_todo, ), ObjectEntry::new_prototype_symbol_function_entry( heap, @@ -100,10 +96,9 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { 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), + ObjectEntry::new_prototype_function_entry(heap, "test", 1, false), + ObjectEntry::new_prototype_function_entry(heap, "toString", 0, false), ]; heap.insert_builtin_object( BuiltinObjectIndexes::RegExpPrototypeIndex, diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 128d3e33..2aed37e1 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -37,7 +37,7 @@ pub fn initialize_string_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: string_constructor_binding, + initial_name: Value::Null, }); heap.insert_builtin_object( BuiltinObjectIndexes::StringPrototypeIndex, diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index 10df66fb..cae428d4 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -76,7 +76,7 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "for", 1, false), ObjectEntry::new( PropertyKey::from_str(heap, "hasInstance"), PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::HasInstance.into())), @@ -91,7 +91,7 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { 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_prototype_function_entry(heap, "keyFor", 1, false), ObjectEntry::new( PropertyKey::from_str(heap, "Match"), PropertyDescriptor::roh(Value::Symbol(WellKnownSymbolIndexes::Match.into())), @@ -147,7 +147,7 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { // uses_arguments: false, // bound: None, // visible: None, - binding: symbol_constructor_binding, + initial_name: Value::Null, }); let entries = vec![ ObjectEntry::new( @@ -165,15 +165,14 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { 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_function_entry(heap, "toString", 0, false), + ObjectEntry::new_prototype_function_entry(heap, "valueOf", 0, false), ObjectEntry::new_prototype_symbol_function_entry( heap, "[Symbol.toPrimitive]", WellKnownSymbolIndexes::ToPrimitive.into(), 1, false, - symbol_todo, ), ObjectEntry::new( PropertyKey::Symbol(WellKnownSymbolIndexes::ToStringTag.into()), diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs index ee4945a1..2d392f9e 100644 --- a/nova_vm/src/types.rs +++ b/nova_vm/src/types.rs @@ -1,7 +1,7 @@ mod language; mod spec; -pub use language::{Function, Number, Object, String, Value}; +pub use language::{Function, InternalMethods, Number, Object, PropertyKey, String, Value}; pub use spec::{Base, PropertyDescriptor, Reference, ReferencedName}; impl From for Value { diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs index de08617d..e80f3d79 100644 --- a/nova_vm/src/types/language.rs +++ b/nova_vm/src/types/language.rs @@ -8,6 +8,6 @@ mod value; pub use bigint::BigInt; pub use function::Function; pub use number::Number; -pub use object::{Object, ObjectData}; +pub use object::{InternalMethods, Object, ObjectData, PropertyKey}; pub use string::String; pub use value::Value; diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 74d36e5f..85a7d763 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -2,9 +2,13 @@ mod data; mod internal_methods; mod property_key; -use crate::{execution::Agent, heap::GetHeapData}; - use super::Value; +use crate::{ + builtins::ordinary, + execution::{agent::ExceptionType, Agent, Intrinsics, JsResult}, + heap::{indexes::ObjectIndex, GetHeapData}, + types::PropertyDescriptor, +}; pub use data::ObjectData; pub use internal_methods::InternalMethods; pub use property_key::PropertyKey; @@ -14,6 +18,17 @@ pub use property_key::PropertyKey; #[derive(Debug, Clone, Copy)] pub struct Object(Value); +impl TryFrom for Object { + type Error = (); + fn try_from(value: Value) -> Result { + if let Value::Object(_) | Value::Array(_) | Value::Function(_) = value { + Ok(Self(value)) + } else { + Err(()) + } + } +} + impl Object { pub(crate) fn new(value: Value) -> Self { Self(value) @@ -23,23 +38,91 @@ impl Object { self.0 } + pub fn into_object_handle(self) -> Option { + let object = self.into_value(); + + match object { + Value::Object(handle) => Some(handle), + Value::Function(_) => None, + Value::Array(_) => None, + _ => unreachable!(), + } + } + + pub fn extensible(self, agent: &mut Agent) -> bool { + let object = self.into_value(); + + match object { + Value::Object(object) => agent.current_realm().borrow().heap.get(object).extensible, + Value::Array(_) => true, + Value::Function(_) => true, + _ => unreachable!(), + } + } + pub fn prototype(self, agent: &mut Agent) -> Option { - let realm = agent.current_realm(); let object = self.into_value(); match object { - // Value::Object(object) => { - // let object = realm.heap.get(object); - // } - // Value::ArrayObject(array) => { - // let array = realm.heap.get(array); - // } - Value::Function(function) => { - // let function = realm.heap.get(function); - // function.binding; - todo!() + Value::Object(object) => { + let realm = agent.current_realm(); + let realm = realm.borrow(); + let object = realm.heap.get(object); + object.prototype.try_into().ok() } + Value::Array(_) => Some(Intrinsics::array_prototype()), + Value::Function(_) => Some(Intrinsics::function_prototype()), _ => unreachable!(), } } + + // TODO: Is there a spec variant of this? + pub fn set_prototype(self, agent: &mut Agent, prototype: Option) { + let object = self.into_value(); + + match object { + Value::Object(object) => { + let realm = agent.current_realm(); + let realm = realm.borrow_mut(); + let object = realm.heap.get(object); + // object.prototype = prototype.map(|object| object.into_value()).unwrap(); + } + Value::Array(_) => todo!(), + Value::Function(_) => todo!(), + _ => unreachable!(), + } + } + + pub fn internal_methods<'a>(self, agent: &mut Agent) -> &'a InternalMethods { + // TODO: Logic for fetching methods for objects/anything else. + &ordinary::METHODS + } + + /// /// 7.3.9 DefinePropertyOrThrow ( O, P, desc ) + /// https://tc39.es/ecma262/#sec-definepropertyorthrow + pub fn define_property_or_throw( + self, + agent: &mut Agent, + property_key: PropertyKey, + property_descriptor: PropertyDescriptor, + ) -> JsResult<()> { + // 1. Let success be ? O.[[DefineOwnProperty]](P, desc). + let success = (self.internal_methods(agent).define_own_property)( + agent, + self, + property_key, + property_descriptor, + ); + + // 2. If success is false, throw a TypeError exception. + if !success { + return Err(agent.throw_exception( + ExceptionType::TypeError, + "Cannot assign to property on object.", + )); + } + + // 3. Return unused. + Ok(()) + } } diff --git a/nova_vm/src/types/language/object/internal_methods.rs b/nova_vm/src/types/language/object/internal_methods.rs index bc53cbd9..3c363d93 100644 --- a/nova_vm/src/types/language/object/internal_methods.rs +++ b/nova_vm/src/types/language/object/internal_methods.rs @@ -1,29 +1,49 @@ use super::{Object, PropertyKey}; use crate::{ builtins::ArgumentsList, - execution::JsResult, + execution::{Agent, JsResult}, types::{PropertyDescriptor, Value}, }; -pub type GetPrototypeOf = fn(object: Object) -> JsResult; -pub type SetPrototypeOf = fn(object: Object, prototype: Option) -> JsResult; -pub type IsExtensible = fn(object: Object) -> JsResult; -pub type PreventExtensions = fn(object: Object) -> JsResult; -pub type GetOwnProperty = fn(object: Object, property_key: PropertyKey) -> JsResult<()>; +pub type GetPrototypeOf = fn(agent: &mut Agent, object: Object) -> Option; +pub type SetPrototypeOf = + fn(agent: &mut Agent, object: Object, prototype: Option) -> JsResult; +pub type IsExtensible = fn(agent: &mut Agent, object: Object) -> JsResult; +pub type PreventExtensions = fn(agent: &mut Agent, object: Object) -> JsResult; +pub type GetOwnProperty = + fn(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult<()>; pub type DefineOwnProperty = fn( + agent: &mut Agent, object: Object, property_key: PropertyKey, property_descriptor: PropertyDescriptor, -) -> JsResult; -pub type HasProperty = fn(object: Object, property_key: PropertyKey) -> JsResult; -pub type Get = fn(object: Object, property_key: PropertyKey, receiver: Value) -> JsResult; -pub type Set = - fn(object: Object, property_key: PropertyKey, value: Value, receiver: Value) -> JsResult; -pub type Delete = fn(object: Object, property_key: PropertyKey) -> JsResult; -pub type OwnPropertyKeys = fn(object: Object) -> JsResult>; -pub type Call = - fn(object: Object, this_value: Value, arguments_list: ArgumentsList) -> JsResult; -pub type Construct = fn(object: Object, arguments_list: ArgumentsList) -> JsResult; +) -> bool; +pub type HasProperty = + fn(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult; +pub type Get = fn( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + receiver: Value, +) -> JsResult; +pub type Set = fn( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + value: Value, + receiver: Value, +) -> JsResult; +pub type Delete = + fn(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult; +pub type OwnPropertyKeys = fn(agent: &mut Agent, object: Object) -> JsResult>; +pub type Call = fn( + agent: &mut Agent, + object: Object, + this_value: Value, + arguments_list: ArgumentsList, +) -> JsResult; +pub type Construct = + fn(agent: &mut Agent, object: Object, arguments_list: ArgumentsList) -> JsResult; /// 6.1.7.2 Object Internal Methods and Internal Slots /// https://tc39.es/ecma262/#sec-object-internal-methods-and-internal-slots diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index e8d7e424..d39d9ed1 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -1,4 +1,46 @@ -use crate::types::Value; +use crate::{ + types::{String, Value}, + SmallString, +}; #[derive(Debug, Clone, Copy)] pub struct PropertyKey(Value); + +impl Default for PropertyKey { + fn default() -> Self { + Self(Value::SmallString(SmallString::from_str_unchecked( + "unknown", + ))) + } +} + +impl PropertyKey { + pub(crate) fn new(value: Value) -> Self { + debug_assert!(matches!( + value, + Value::Integer(_) | Value::String(_) | Value::SmallString(_) + )); + Self(value) + } + + pub fn into_value(self) -> Value { + self.0 + } +} + +impl From for PropertyKey { + fn from(value: String) -> Self { + Self(value.into_value()) + } +} + +impl TryFrom for PropertyKey { + type Error = (); + fn try_from(value: Value) -> Result { + if value.is_string() || value.is_symbol() || value.is_number() { + Ok(Self(value)) + } else { + Err(()) + } + } +} diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index 3dd4b6d5..71be6eee 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -123,7 +123,15 @@ impl Value { } pub fn is_object(self) -> bool { - matches!(self, Value::Object(_) | Value::Array(_)) + matches!( + self, + Value::Object(_) + | Value::Array(_) + | Value::Date(_) + | Value::Function(_) + | Value::Error(_) + | Value::RegExp(_) + ) } pub fn is_string(self) -> bool { @@ -170,6 +178,10 @@ impl Value { matches!(self, Value::Symbol(_)) } + pub fn is_number(self) -> bool { + matches!(self, Value::Number(_) | Value::Float(_) | Value::Integer(_)) + } + pub fn is_empty_string(self) -> bool { if let Value::SmallString(s) = self { s.len() == 0 @@ -583,6 +595,73 @@ impl Value { todo!() } + + fn is_same_type(self, y: Self) -> bool { + let x = self; + (x.is_undefined() && y.is_undefined()) + || (x.is_null() && y.is_null()) + || (x.is_boolean() && y.is_boolean()) + || (x.is_string() && y.is_string()) + || (x.is_symbol() && y.is_symbol()) + || (x.is_number() && y.is_number()) + || (x.is_object() && y.is_object()) + } + + /// 7.2.10 SameValue ( x, y ) + /// https://tc39.es/ecma262/#sec-samevalue + pub fn same_value(self, agent: &mut Agent, y: Self) -> bool { + let x = self; + + // 1. If Type(x) is not Type(y), return false. + if !x.is_same_type(y) { + return false; + } + + // 2. If x is a Number, then + if let (Ok(x), Ok(y)) = (Number::try_from(x), Number::try_from(y)) { + // a. Return Number::sameValue(x, y). + return x.same_value(agent, y); + } + + // 3. Return SameValueNonNumber(x, y). + x.same_value_non_number(agent, y) + } + + /// 7.2.12 SameValueNonNumber ( x, y ) + /// https://tc39.es/ecma262/#sec-samevaluenonnumber + pub fn same_value_non_number(self, agent: &mut Agent, y: Self) -> bool { + let x = self; + + // 1. Assert: Type(x) is Type(y). + debug_assert!(x.is_same_type(y)); + + // 2. If x is either null or undefined, return true. + if x.is_null() || x.is_undefined() { + return true; + } + + // 3. If x is a BigInt, then + if x.is_bigint() { + // a. Return BigInt::equal(x, y). + todo!(); + } + + // 4. If x is a String, then + if x.is_string() { + // a. If x and y have the same length and the same code units in the same positions, return true; otherwise, return false. + todo!(); + } + + // 5. If x is a Boolean, then + if x.is_boolean() { + // a. If x and y are both true or both false, return true; otherwise, return false. + return x.is_true() == y.is_true(); + } + + // 6. NOTE: All other ECMAScript language values are compared by identity. + // 7. If x is y, return true; otherwise, return false. + todo!() + } } impl From for Value { @@ -620,6 +699,12 @@ impl TryFrom for Value { } } +impl From for Value { + fn from(value: Number) -> Self { + value.into_value() + } +} + impl From for Value { fn from(value: f32) -> Self { Value::Float(value) diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs index 6df57bf8..9bdbf4f1 100644 --- a/nova_vm/src/types/spec/property_descriptor.rs +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -5,7 +5,7 @@ use crate::{ /// 6.2.6 The Property Descriptor Specification Type /// https://tc39.es/ecma262/#sec-property-descriptor-specification-type -#[derive(Debug)] +#[derive(Debug, Clone, Default)] pub struct PropertyDescriptor { /// [[Value]] pub value: Option, From 7087a03ee1d333e7e26b32e9f89817f786a09eec Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Wed, 26 Jul 2023 14:32:05 -0500 Subject: [PATCH 10/32] pattern-based equality --- nova_vm/src/small_string.rs | 5 +++++ nova_vm/src/types/language/value.rs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index 39a8ee9d..458c88ac 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -36,6 +36,11 @@ impl SmallString { return &self.bytes; } + #[inline] + pub fn is_empty(&self) -> bool { + matches!(self.bytes, [0, 0, 0, 0, 0, 0, 0]) + } + pub(crate) fn from_str_unchecked(string: &str) -> Self { let string_bytes = string.as_bytes(); diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index 71be6eee..e483648e 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -184,7 +184,7 @@ impl Value { pub fn is_empty_string(self) -> bool { if let Value::SmallString(s) = self { - s.len() == 0 + s.is_empty() } else { false } From a02ac1eb215d564b4a92253e73484a0c25df3ce6 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Wed, 26 Jul 2023 15:05:20 -0500 Subject: [PATCH 11/32] fix invalid truncating stuff --- nova_vm/src/types/language/number.rs | 15 ++++++----- nova_vm/src/types/language/value.rs | 40 ++++++++++++++-------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 01f66224..af310a8a 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -181,15 +181,18 @@ impl Number { } /// https://tc39.es/ecma262/#eqn-truncate - /// - /// Guaranteed to be in the valid [`Value::Integer`] range. - pub fn truncate(self, agent: &mut Agent) -> i64 { + pub fn truncate(self, agent: &mut Agent) -> Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n).trunc() as i64, - Value::Integer(n) => n.into_i64(), - Value::Float(n) => n.trunc() as i64, + Value::Number(n) => { + let realm = agent.current_realm(); + let mut realm = realm.borrow_mut(); + let n = realm.heap.get(n).trunc(); + realm.heap.create(n) + } + Value::Integer(_) => self, + Value::Float(n) => n.trunc().into(), _ => unreachable!(), } } diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index e483648e..a1d0db41 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -348,7 +348,7 @@ impl Value { /// https://tc39.es/ecma262/#sec-tointegerorinfinity // TODO: Should we add another [`Value`] newtype for IntegerOrInfinity? pub fn to_integer_or_infinty(self, agent: &mut Agent) -> JsResult { - let argument = self; + let argument: Value = self; // 1. Let number be ? ToNumber(argument). let number = argument.to_number(agent)?; @@ -369,7 +369,7 @@ impl Value { } // 5. Return truncate(ℝ(number)). - Ok(Number::from(number.truncate(agent))) + Ok(number.truncate(agent)) } /// 7.1.6 ToInt32 ( argument ) @@ -386,14 +386,14 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2i64.pow(32); + let int32bit = int % 2f64.powf(32.0); // 5. If int32bit ≥ 2^31, return 𝔽(int32bit - 2^32); otherwise return 𝔽(int32bit). - Ok(if int32bit >= 2i64.pow(32) { - int32bit - 2i64.pow(32) + Ok(if int32bit >= 2f64.powf(32.0) { + int32bit - 2f64.powf(32.0) } else { int32bit } as i32) @@ -413,10 +413,10 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2i64.pow(32); + let int32bit = int % 2f64.powf(32.0); // 5. Return 𝔽(int32bit). Ok(int32bit as u32) @@ -436,14 +436,14 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2i64.pow(16); + let int16bit = int % 2f64.powf(16.0); // 5. If int16bit ≥ 2^15, return 𝔽(int16bit - 2^16); otherwise return 𝔽(int16bit). - Ok(if int16bit >= 2i64.pow(15) { - int16bit - 2i64.pow(16) + Ok(if int16bit >= 2f64.powf(15.0) { + int16bit - 2f64.powf(16.0) } else { int16bit } as i16) @@ -463,10 +463,10 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2i64.pow(16); + let int16bit = int % 2f64.powf(16.0); // Return 𝔽(int16bit). Ok(int16bit as i16) @@ -486,14 +486,14 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2i64.pow(8); + let int8bit = int % 2f64.powf(8.0); // 5. If int8bit ≥ 2^7, return 𝔽(int8bit - 2^8); otherwise return 𝔽(int8bit). - Ok(if int8bit >= 2i64.pow(7) { - int8bit - 2i64.pow(8) + Ok(if int8bit >= 2f64.powf(7.0) { + int8bit - 2f64.powf(8.0) } else { int8bit } as i8) @@ -513,10 +513,10 @@ impl Value { } // 3. Let int be truncate(ℝ(number)). - let int = number.truncate(agent); + let int = number.truncate(agent).into_f64(agent); // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2i64.pow(8); + let int8bit = int % 2f64.powf(8.0); // 5. Return 𝔽(int8bit). Ok(int8bit as u8) From add381959cc9bd30d258b955bbd78a8fb2c69348 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 27 Jul 2023 14:11:59 -0500 Subject: [PATCH 12/32] checkpoint --- nova_vm/src/builtins/builtin_function.rs | 7 ++ nova_vm/src/builtins/number.rs | 39 +++++++- nova_vm/src/builtins/ordinary.rs | 57 ++++++++++- nova_vm/src/heap.rs | 96 ++++++++++--------- nova_vm/src/heap/element_array.rs | 63 +++++++++++- nova_vm/src/heap/object.rs | 2 + nova_vm/src/heap/string.rs | 6 ++ nova_vm/src/types/language.rs | 2 +- nova_vm/src/types/language/number.rs | 30 +++--- nova_vm/src/types/language/object.rs | 35 ++++++- .../types/language/object/internal_methods.rs | 7 +- .../src/types/language/object/property_key.rs | 70 ++++++++++++++ .../types/language/object/property_storage.rs | 55 +++++++++++ nova_vm/src/types/language/value.rs | 30 +++--- 14 files changed, 410 insertions(+), 89 deletions(-) create mode 100644 nova_vm/src/types/language/object/property_storage.rs diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index b48cdc0a..366c3727 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -7,6 +7,13 @@ use crate::{ #[derive(Debug)] pub struct ArgumentsList<'a>(&'a [Value]); +impl ArgumentsList<'_> { + #[inline] + pub fn get(&self, index: usize) -> Value { + *self.0.get(index).unwrap_or(&Value::Undefined) + } +} + pub type RegularFn = fn(&mut Agent, Value, ArgumentsList<'_>) -> JsResult; pub type ConstructorFn = fn(&mut Agent, Value, ArgumentsList<'_>, Option) -> JsResult; diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index fffd8bf7..f937f8de 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -1,6 +1,7 @@ use super::{ builtin_function::{define_builtin_function, define_builtin_property}, - create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, + create_builtin_function, ordinary, todo_builtin, ArgumentsList, Behaviour, Builtin, + BuiltinFunctionArgs, }; use crate::{ execution::{Agent, Intrinsics, JsResult, Realm}, @@ -14,7 +15,7 @@ pub struct NumberConstructor; impl Builtin for NumberConstructor { fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { let object: Object = create_builtin_function( - Behaviour::Regular(NumberConstructor::behaviour), + Behaviour::Constructor(Self::behaviour), BuiltinFunctionArgs { length: 1, name: "Number", @@ -176,11 +177,45 @@ impl Builtin for NumberConstructor { } impl NumberConstructor { + /// 21.1.1.1 Number ( value ) + /// https://tc39.es/ecma262/#sec-number-constructor-number-value fn behaviour( agent: &mut Agent, this_value: Value, arguments: ArgumentsList, + new_target: Option, ) -> JsResult { + let value = arguments.get(0); + + // 1. If value is present, then + let n = if !value.is_undefined() { + // a. Let prim be ? ToNumeric(value). + let prim = value.to_numeric(agent)?; + + // b. If prim is a BigInt, let n be 𝔽(ℝ(prim)). + if prim.is_bigint() { + todo!() + } + // c. Otherwise, let n be prim. + else { + prim + } + } + // 2. Else, + else { + // a. Let n be +0𝔽. + Value::from(0) + }; + + // 3. If NewTarget is undefined, return n. + let Some(new_target) = new_target else { + return Ok(n); + }; + todo!(); + + // 4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%", « [[NumberData]] »). + // 5. Set O.[[NumberData]] to n. + // 6. Return O. } } diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index aaca0be6..e97911c1 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -140,18 +140,65 @@ fn prevent_extensions(agent: &mut Agent, object: Object) -> JsResult { /// https://tc39.es/ecma262/#sec-ordinarypreventextensions pub fn ordinary_prevent_extensions(agent: &mut Agent, object: Object) -> bool { // 1. Set O.[[Extensible]] to false. - todo!(); + object.set_extensible(agent, false); // 2. Return true. - return true; + true } -pub fn get_own_property( +/// 10.1.5 [[GetOwnProperty]] ( P ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-getownproperty-p +fn get_own_property( agent: &mut Agent, object: Object, property_key: PropertyKey, -) -> JsResult<()> { - todo!() +) -> JsResult> { + // 1. Return OrdinaryGetOwnProperty(O, P). + Ok(ordinary_get_own_property(agent, object, property_key)) +} + +/// 10.1.5.1 OrdinaryGetOwnProperty ( O, P ) +/// https://tc39.es/ecma262/#sec-ordinarygetownproperty +pub fn ordinary_get_own_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, +) -> Option { + // 1. If O does not have an own property with key P, return undefined. + // 3. Let X be O's own property whose key is P. + let x = object.property_storage().get(agent, property_key)?; + + // 2. Let D be a newly created Property Descriptor with no fields. + let mut descriptor = PropertyDescriptor::default(); + + // 4. If X is a data property, then + if x.is_data_descriptor() { + // a. Set D.[[Value]] to the value of X's [[Value]] attribute. + descriptor.value = x.value; + + // b. Set D.[[Writable]] to the value of X's [[Writable]] attribute. + descriptor.writable = x.writable; + } + // 5. Else, + else { + // a. Assert: X is an accessor property. + debug_assert!(x.is_accessor_descriptor()); + + // b. Set D.[[Get]] to the value of X's [[Get]] attribute. + descriptor.get = x.get; + + // c. Set D.[[Set]] to the value of X's [[Set]] attribute. + descriptor.set = x.set; + } + + // 6. Set D.[[Enumerable]] to the value of X's [[Enumerable]] attribute. + descriptor.enumerable = x.enumerable; + + // 7. Set D.[[Configurable]] to the value of X's [[Configurable]] attribute. + descriptor.configurable = x.configurable; + + // 8. Return D. + Some(descriptor) } pub fn define_own_property( diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 66e4bc62..2a42ae3d 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -44,7 +44,7 @@ use self::{ symbol::{initialize_symbol_heap, SymbolHeapData}, }; use crate::types::{Function, Number, Object, String, Value}; -use wtf8::Wtf8; +use wtf8::{Wtf8, Wtf8Buf}; #[derive(Debug)] pub struct Heap { @@ -72,7 +72,8 @@ pub trait CreateHeapData { } pub trait GetHeapData<'a, T, F: 'a> { - fn get(&'a self, handle: BaseIndex) -> F; + fn get(&'a self, id: BaseIndex) -> &'a F; + fn get_mut(&'a mut self, id: BaseIndex) -> &'a mut F; } impl CreateHeapData for Heap { @@ -88,18 +89,54 @@ impl CreateHeapData for Heap { } } -impl<'a> GetHeapData<'a, NumberHeapData, f64> for Heap { - fn get(&'a self, id: NumberIndex) -> f64 { - self.numbers - .get(id.into_index()) - .as_ref() - .unwrap() - .as_ref() - .unwrap() - .data - } +macro_rules! impl_heap_data { + ($table: ident, $in: ty, $out: ty) => { + impl<'a> GetHeapData<'a, $in, $out> for Heap { + fn get(&'a self, id: BaseIndex<$in>) -> &'a $out { + self.$table.get(id.into_index()).unwrap().as_ref().unwrap() + } + + fn get_mut(&'a mut self, id: BaseIndex<$in>) -> &'a mut $out { + self.$table + .get_mut(id.into_index()) + .unwrap() + .as_mut() + .unwrap() + } + } + }; + ($table: ident, $in: ty, $out: ty, $accessor: ident) => { + impl<'a> GetHeapData<'a, $in, $out> for Heap { + fn get(&'a self, id: BaseIndex<$in>) -> &'a $out { + &self + .$table + .get(id.into_index()) + .as_ref() + .unwrap() + .as_ref() + .unwrap() + .$accessor + } + + fn get_mut(&'a mut self, id: BaseIndex<$in>) -> &'a mut $out { + &mut self + .$table + .get_mut(id.into_index()) + .unwrap() + .as_mut() + .unwrap() + .$accessor + } + } + }; } +impl_heap_data!(numbers, NumberHeapData, f64, data); +impl_heap_data!(objects, ObjectHeapData, ObjectHeapData); +impl_heap_data!(strings, StringHeapData, Wtf8Buf, data); +impl_heap_data!(functions, FunctionHeapData, FunctionHeapData); +impl_heap_data!(arrays, ArrayHeapData, ArrayHeapData); + impl CreateHeapData<&str, String> for Heap { fn create(&mut self, data: &str) -> String { if let Ok(value) = String::try_from(data) { @@ -111,19 +148,6 @@ impl CreateHeapData<&str, String> for Heap { } } -impl<'a> GetHeapData<'a, StringHeapData, &'a Wtf8> for Heap { - fn get(&'a self, id: StringIndex) -> &'a Wtf8 { - let data = self - .strings - .get(id.into_index()) - .as_ref() - .unwrap() - .as_ref() - .unwrap(); - &data.data.slice(0, data.data.len()) - } -} - impl CreateHeapData for Heap { fn create(&mut self, data: FunctionHeapData) -> Function { self.functions.push(Some(data)); @@ -131,17 +155,6 @@ impl CreateHeapData for Heap { } } -impl<'a> GetHeapData<'a, FunctionHeapData, &'a FunctionHeapData> for Heap { - fn get(&'a self, id: FunctionIndex) -> &'a FunctionHeapData { - self.functions - .get(id.into_index()) - .as_ref() - .unwrap() - .as_ref() - .unwrap() - } -} - impl CreateHeapData for Heap { fn create(&mut self, data: ObjectHeapData) -> Object { self.objects.push(Some(data)); @@ -149,17 +162,6 @@ impl CreateHeapData for Heap { } } -impl<'a> GetHeapData<'a, ObjectHeapData, &'a ObjectHeapData> for Heap { - fn get(&'a self, id: ObjectIndex) -> &'a ObjectHeapData { - self.objects - .get(id.into_index()) - .as_ref() - .unwrap() - .as_ref() - .unwrap() - } -} - impl Heap { pub fn new() -> Heap { let mut heap = Heap { diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs index 6ac743ca..db0a3ad4 100644 --- a/nova_vm/src/heap/element_array.rs +++ b/nova_vm/src/heap/element_array.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::types::Value; use core::panic; -use std::{collections::HashMap, mem::MaybeUninit, num::NonZeroU16}; +use std::{collections::HashMap, mem::MaybeUninit, num::NonZeroU16, vec}; #[derive(Debug, Clone, Copy)] pub(crate) enum ElementArrayKey { @@ -822,4 +822,65 @@ impl ElementArrays { }, ) } + + pub(crate) fn get<'a>(&'a self, vector: ElementsVector) -> () { + // match vector.cap { + // ElementArrayKey::E4 => &self + // .e2pow4 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E6 => &self + // .e2pow6 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E8 => &self + // .e2pow8 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E10 => &self + // .e2pow10 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E12 => &self + // .e2pow12 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E16 => &self + // .e2pow16 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E24 => &self + // .e2pow24 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // ElementArrayKey::E32 => &self + // .e2pow32 + // .values + // .get(vector.elements_index.into_index()) + // .unwrap() + // .unwrap() + // .as_slice()[0..vector.len as usize], + // _ => unreachable!(), + } } diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 593d932a..62c8ddfa 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use crate::{ execution::JsResult, heap::{ diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 2aed37e1..b0e10d33 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -14,6 +14,12 @@ pub(crate) struct StringHeapData { } impl StringHeapData { + pub fn dummy() -> Self { + Self { + data: Wtf8Buf::new(), + } + } + pub fn from_str(str: &str) -> Self { StringHeapData { data: Wtf8Buf::from_str(str), diff --git a/nova_vm/src/types/language.rs b/nova_vm/src/types/language.rs index e80f3d79..fb497550 100644 --- a/nova_vm/src/types/language.rs +++ b/nova_vm/src/types/language.rs @@ -8,6 +8,6 @@ mod value; pub use bigint::BigInt; pub use function::Function; pub use number::Number; -pub use object::{InternalMethods, Object, ObjectData, PropertyKey}; +pub use object::{InternalMethods, Object, ObjectData, PropertyKey, PropertyStorage}; pub use string::String; pub use value::Value; diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index af310a8a..79862965 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -137,7 +137,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n) == f64::INFINITY, + Value::Number(n) => *agent.current_realm().borrow().heap.get(n) == f64::INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::INFINITY, _ => unreachable!(), @@ -148,7 +148,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n) == f64::NEG_INFINITY, + Value::Number(n) => *agent.current_realm().borrow().heap.get(n) == f64::NEG_INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::NEG_INFINITY, _ => unreachable!(), @@ -171,7 +171,7 @@ impl Number { match x { Value::Number(n) => { - let n = agent.current_realm().borrow().heap.get(n); + let n = *agent.current_realm().borrow().heap.get(n); !n.is_sign_negative() && !n.is_sign_positive() } Value::Integer(_) => true, @@ -201,7 +201,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n), + Value::Number(n) => *agent.current_realm().borrow().heap.get(n), Value::Integer(n) => Into::::into(n) as f64, Value::Float(n) => n as f64, _ => unreachable!(), @@ -220,18 +220,18 @@ impl Number { == agent.current_realm().borrow().heap.get(y) } (Value::Number(x), Value::Integer(y)) => { - agent.current_realm().borrow().heap.get(x) == y.into_i64() as f64 + *agent.current_realm().borrow().heap.get(x) == y.into_i64() as f64 } (Value::Number(x), Value::Float(y)) => { - agent.current_realm().borrow().heap.get(x) == y as f64 + *agent.current_realm().borrow().heap.get(x) == y as f64 } (Value::Integer(x), Value::Number(y)) => { - (x.into_i64() as f64) == agent.current_realm().borrow().heap.get(y) + (x.into_i64() as f64) == *agent.current_realm().borrow().heap.get(y) } (Value::Integer(x), Value::Integer(y)) => x.into_i64() == y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) == y as f64, (Value::Float(x), Value::Number(y)) => { - (x as f64) == agent.current_realm().borrow().heap.get(y) + (x as f64) == *agent.current_realm().borrow().heap.get(y) } (Value::Float(x), Value::Integer(y)) => (x as f64) == y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x == y, @@ -244,7 +244,7 @@ impl Number { match x { Value::Number(n) => { - let n = agent.current_realm().borrow().heap.get(n); + let n = *agent.current_realm().borrow().heap.get(n); n % 1.0 == 0.0 && n % 2.0 == 0.0 } Value::Integer(n) => Into::::into(n) % 2 == 0, @@ -258,7 +258,7 @@ impl Number { match x { Value::Number(n) => { - let n = agent.current_realm().borrow().heap.get(n); + let n = *agent.current_realm().borrow().heap.get(n); if n > 0.0 { self } else { @@ -292,7 +292,7 @@ impl Number { Value::Number(n) => { let realm = agent.current_realm(); let mut realm = realm.borrow_mut(); - let value = realm.heap.get(n); + let value = *realm.heap.get(n); realm.heap.create(-value) } Value::Integer(n) => SmallInteger::from_i64_unchecked(-n.into_i64()).into(), @@ -516,18 +516,18 @@ impl Number { < agent.current_realm().borrow().heap.get(y) } (Value::Number(x), Value::Integer(y)) => { - agent.current_realm().borrow().heap.get(x) < y.into_i64() as f64 + *agent.current_realm().borrow().heap.get(x) < y.into_i64() as f64 } (Value::Number(x), Value::Float(y)) => { - agent.current_realm().borrow().heap.get(x) < y as f64 + *agent.current_realm().borrow().heap.get(x) < y as f64 } (Value::Integer(x), Value::Number(y)) => { - (x.into_i64() as f64) < agent.current_realm().borrow().heap.get(y) + (x.into_i64() as f64) < *agent.current_realm().borrow().heap.get(y) } (Value::Integer(x), Value::Integer(y)) => x.into_i64() < y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) < y as f64, (Value::Float(x), Value::Number(y)) => { - (x as f64) < agent.current_realm().borrow().heap.get(y) + (x as f64) < *agent.current_realm().borrow().heap.get(y) } (Value::Float(x), Value::Integer(y)) => (x as f64) < y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x < y, diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 85a7d763..d3cadb1d 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -1,6 +1,7 @@ mod data; mod internal_methods; mod property_key; +mod property_storage; use super::Value; use crate::{ @@ -9,9 +10,11 @@ use crate::{ heap::{indexes::ObjectIndex, GetHeapData}, types::PropertyDescriptor, }; + pub use data::ObjectData; pub use internal_methods::InternalMethods; pub use property_key::PropertyKey; +pub use property_storage::PropertyStorage; /// 6.1.7 The Object Type /// https://tc39.es/ecma262/#sec-object-type @@ -49,6 +52,7 @@ impl Object { } } + /// [[Extensible]] pub fn extensible(self, agent: &mut Agent) -> bool { let object = self.into_value(); @@ -60,6 +64,25 @@ impl Object { } } + /// [[Extensible]] + pub fn set_extensible(self, agent: &mut Agent, value: bool) { + let object = self.into_value(); + + match object { + Value::Object(object) => { + let realm = agent.current_realm(); + let mut realm = realm.borrow_mut(); + let object = realm.heap.get_mut(object); + object.extensible = true; + } + // TODO: Correct object/function impl + Value::Array(_) => {} + Value::Function(_) => {} + _ => unreachable!(), + } + } + + /// [[Prototype]] pub fn prototype(self, agent: &mut Agent) -> Option { let object = self.into_value(); @@ -76,16 +99,16 @@ impl Object { } } - // TODO: Is there a spec variant of this? + /// [[Prototype]] pub fn set_prototype(self, agent: &mut Agent, prototype: Option) { let object = self.into_value(); match object { Value::Object(object) => { let realm = agent.current_realm(); - let realm = realm.borrow_mut(); - let object = realm.heap.get(object); - // object.prototype = prototype.map(|object| object.into_value()).unwrap(); + let mut realm = realm.borrow_mut(); + let object = realm.heap.get_mut(object); + object.prototype = prototype.map(|object| object.into_value()).unwrap(); } Value::Array(_) => todo!(), Value::Function(_) => todo!(), @@ -98,6 +121,10 @@ impl Object { &ordinary::METHODS } + pub fn property_storage(self) -> PropertyStorage { + PropertyStorage::new(self) + } + /// /// 7.3.9 DefinePropertyOrThrow ( O, P, desc ) /// https://tc39.es/ecma262/#sec-definepropertyorthrow pub fn define_property_or_throw( diff --git a/nova_vm/src/types/language/object/internal_methods.rs b/nova_vm/src/types/language/object/internal_methods.rs index 3c363d93..eb6237b2 100644 --- a/nova_vm/src/types/language/object/internal_methods.rs +++ b/nova_vm/src/types/language/object/internal_methods.rs @@ -10,8 +10,11 @@ pub type SetPrototypeOf = fn(agent: &mut Agent, object: Object, prototype: Option) -> JsResult; pub type IsExtensible = fn(agent: &mut Agent, object: Object) -> JsResult; pub type PreventExtensions = fn(agent: &mut Agent, object: Object) -> JsResult; -pub type GetOwnProperty = - fn(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult<()>; +pub type GetOwnProperty = fn( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, +) -> JsResult>; pub type DefineOwnProperty = fn( agent: &mut Agent, object: Object, diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index d39d9ed1..a90a5236 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -1,4 +1,6 @@ use crate::{ + execution::Agent, + heap::GetHeapData, types::{String, Value}, SmallString, }; @@ -26,6 +28,58 @@ impl PropertyKey { pub fn into_value(self) -> Value { self.0 } + + pub(self) fn is_str_eq_num(s: &str, n: i64) -> bool { + let (s, mut n) = if s.starts_with("-") { + if n > 0 { + return false; + } + (&s[1..], -n as usize) + } else { + if n < 0 { + return false; + } + (s, n as usize) + }; + + if Some(s.len()) != n.checked_ilog10().map(|n| n as usize) { + return false; + } + + for c in s.as_bytes().iter().rev() { + let code = (n % 10) as u8 + '0' as u8; + + if *c != code { + return false; + } + + n /= 10; + } + + true + } + + pub fn equals(self, agent: &mut Agent, y: Self) -> bool { + let x = self.into_value(); + let y = y.into_value(); + + match (x, y) { + // Assumes the interner is working correctly. + (Value::String(s1), Value::String(s2)) => s1 == s2, + (Value::String(s), Value::Integer(n)) => { + let realm = agent.current_realm(); + let realm = realm.borrow(); + let s = realm.heap.get(s); + + let Some(s) = s.as_str() else { + return false; + }; + + Self::is_str_eq_num(s, n.into_i64()) + } + _ => unreachable!(), + } + } } impl From for PropertyKey { @@ -44,3 +98,19 @@ impl TryFrom for PropertyKey { } } } + +#[test] +fn compare_num_str() { + assert!(PropertyKey::is_str_eq_num("23", 23)); + assert!(PropertyKey::is_str_eq_num("-23", -23)); + assert!(PropertyKey::is_str_eq_num("-120543809", -120543809)); + assert!(PropertyKey::is_str_eq_num("985493", 985493)); + assert!(PropertyKey::is_str_eq_num("0", 0)); + assert!(PropertyKey::is_str_eq_num("5", 5)); + assert!(PropertyKey::is_str_eq_num("-5", -5)); + assert!(PropertyKey::is_str_eq_num("9302", 9302)); + assert!(PropertyKey::is_str_eq_num("19", 19)); + + assert!(!PropertyKey::is_str_eq_num("19", 91)); + assert!(!PropertyKey::is_str_eq_num("-19", 19)); +} diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs new file mode 100644 index 00000000..f712133b --- /dev/null +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -0,0 +1,55 @@ +use crate::{ + execution::Agent, + heap::GetHeapData, + types::{PropertyDescriptor, Value}, +}; + +use super::{Object, PropertyKey}; + +#[derive(Clone, Copy)] +pub struct PropertyStorage(Object); + +impl PropertyStorage { + pub(crate) fn new(object: Object) -> Self { + Self(object) + } + + fn into_object(self) -> Object { + self.0 + } + + fn into_value(self) -> Value { + self.into_object().into_value() + } + + pub fn has(self, agent: &mut Agent, key: PropertyKey) -> bool { + let object = self.into_value(); + + match object { + Value::Object(object) => { + let realm = agent.current_realm(); + let realm = realm.borrow(); + let keys = &realm.heap.get(object).keys; + // realm.heap.elements.get(keys).iter().any(|k| { + // if let Some(value) = k { + // value.equals(agent, key) + // } + // false + // }); + true + } + Value::Array(array) => { + let realm = agent.current_realm(); + let realm = realm.borrow(); + let array = agent.current_realm().borrow().heap.get(array); + true + } + Value::Function(_) => todo!(), + _ => unreachable!(), + } + } + + pub fn get(self, agent: &mut Agent, key: PropertyKey) -> Option { + todo!(); + } +} diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index a1d0db41..dfa1c700 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -72,6 +72,12 @@ const _VALUE_SIZE_IS_WORD: () = assert!(size_of::() == size_of::() // We may also want to keep Option register sized so that eg. holes in arrays do not start requiring extra bookkeeping. const _OPTIONAL_VALUE_SIZE_IS_WORD: () = assert!(size_of::>() == size_of::()); +impl Default for Value { + fn default() -> Self { + Value::Undefined + } +} + #[derive(Debug, Clone, Copy)] pub enum PreferredType { String, @@ -389,11 +395,11 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2f64.powf(32.0); + let int32bit = int % 2f64.powi(32); // 5. If int32bit ≥ 2^31, return 𝔽(int32bit - 2^32); otherwise return 𝔽(int32bit). - Ok(if int32bit >= 2f64.powf(32.0) { - int32bit - 2f64.powf(32.0) + Ok(if int32bit >= 2f64.powi(32) { + int32bit - 2f64.powi(32) } else { int32bit } as i32) @@ -416,7 +422,7 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int32bit be int modulo 2^32. - let int32bit = int % 2f64.powf(32.0); + let int32bit = int % 2f64.powi(32); // 5. Return 𝔽(int32bit). Ok(int32bit as u32) @@ -439,11 +445,11 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2f64.powf(16.0); + let int16bit = int % 2f64.powi(16); // 5. If int16bit ≥ 2^15, return 𝔽(int16bit - 2^16); otherwise return 𝔽(int16bit). - Ok(if int16bit >= 2f64.powf(15.0) { - int16bit - 2f64.powf(16.0) + Ok(if int16bit >= 2f64.powi(15) { + int16bit - 2f64.powi(16) } else { int16bit } as i16) @@ -466,7 +472,7 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int16bit be int modulo 2^16. - let int16bit = int % 2f64.powf(16.0); + let int16bit = int % 2f64.powi(16); // Return 𝔽(int16bit). Ok(int16bit as i16) @@ -489,11 +495,11 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2f64.powf(8.0); + let int8bit = int % 2f64.powi(8); // 5. If int8bit ≥ 2^7, return 𝔽(int8bit - 2^8); otherwise return 𝔽(int8bit). - Ok(if int8bit >= 2f64.powf(7.0) { - int8bit - 2f64.powf(8.0) + Ok(if int8bit >= 2f64.powi(7) { + int8bit - 2f64.powi(8) } else { int8bit } as i8) @@ -516,7 +522,7 @@ impl Value { let int = number.truncate(agent).into_f64(agent); // 4. Let int8bit be int modulo 2^8. - let int8bit = int % 2f64.powf(8.0); + let int8bit = int % 2f64.powi(8); // 5. Return 𝔽(int8bit). Ok(int8bit as u8) From c63190f65fcb93d91503ae6858487a23921b4ecf Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Thu, 27 Jul 2023 15:02:20 -0500 Subject: [PATCH 13/32] some work Thanks to Phosra for figuring out I messed up descriptor logic. --- nova_vm/src/builtins/ordinary.rs | 12 +++---- nova_vm/src/heap/array.rs | 2 +- nova_vm/src/heap/heap_gc.rs | 4 ++- nova_vm/src/types/language/object.rs | 31 +++++++++---------- nova_vm/src/types/spec/property_descriptor.rs | 7 +++-- 5 files changed, 27 insertions(+), 29 deletions(-) diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index e97911c1..b6fa0e78 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -34,7 +34,8 @@ fn get_prototype_of(agent: &mut Agent, object: Object) -> Option { /// https://tc39.es/ecma262/#sec-ordinarygetprototypeof pub fn ordinary_get_prototype_of(agent: &mut Agent, object: Object) -> Option { // 1. Return O.[[Prototype]]. - object.prototype(agent) + // TODO: This is wrong. + Some(Object::new(object.prototype(agent).unwrap())) } /// 10.1.2 [[SetPrototypeOf]] ( V ) @@ -60,11 +61,7 @@ pub fn ordinary_set_prototype_of( // 2. If SameValue(V, current) is true, return true. match (prototype, current) { - (Some(prototype), Some(current)) - if prototype - .into_value() - .same_value(agent, current.into_value()) => - { + (Some(prototype), Some(current)) if prototype.into_value().same_value(agent, current) => { return Ok(true) } (None, None) => return Ok(true), @@ -105,7 +102,8 @@ pub fn ordinary_set_prototype_of( } // ii. Else, set p to p.[[Prototype]]. - parent_prototype_outer = parent_prototype.prototype(agent); + // TODO: This is wrong + parent_prototype_outer = Some(Object::new(parent_prototype.prototype(agent).unwrap())); } // 8. Set O.[[Prototype]] to V. diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index 05b02700..426a2ac1 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -17,7 +17,7 @@ use super::{ #[derive(Debug, Clone, Copy)] pub(crate) struct ArrayHeapData { - pub(super) object_index: ObjectIndex, + pub object_index: Option, // TODO: Use SmallVec<[Value; 4]> pub(super) elements: ElementsVector, } diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index ac221911..092eb016 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -32,7 +32,9 @@ pub(crate) fn heap_gc(heap: &mut Heap) { } 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)); + if let Some(object_index) = heap_data.object_index { + queues.push_value(Value::Object(object_index)); + } let ElementsVector { elements_index, cap, diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index d3cadb1d..f2e7fe82 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -7,7 +7,7 @@ use super::Value; use crate::{ builtins::ordinary, execution::{agent::ExceptionType, Agent, Intrinsics, JsResult}, - heap::{indexes::ObjectIndex, GetHeapData}, + heap::GetHeapData, types::PropertyDescriptor, }; @@ -41,17 +41,6 @@ impl Object { self.0 } - pub fn into_object_handle(self) -> Option { - let object = self.into_value(); - - match object { - Value::Object(handle) => Some(handle), - Value::Function(_) => None, - Value::Array(_) => None, - _ => unreachable!(), - } - } - /// [[Extensible]] pub fn extensible(self, agent: &mut Agent) -> bool { let object = self.into_value(); @@ -83,18 +72,26 @@ impl Object { } /// [[Prototype]] - pub fn prototype(self, agent: &mut Agent) -> Option { + pub fn prototype(self, agent: &mut Agent) -> Option { let object = self.into_value(); + let realm = agent.current_realm(); + let realm = realm.borrow(); match object { Value::Object(object) => { - let realm = agent.current_realm(); - let realm = realm.borrow(); let object = realm.heap.get(object); object.prototype.try_into().ok() } - Value::Array(_) => Some(Intrinsics::array_prototype()), - Value::Function(_) => Some(Intrinsics::function_prototype()), + Value::Array(array) => { + let array = realm.heap.get(array); + + if let Some(object_index) = array.object_index { + Some(realm.heap.get(object_index).prototype) + } else { + Some(Intrinsics::array_prototype().into_value()) + } + } + Value::Function(_) => Some(Intrinsics::function_prototype().into_value()), _ => unreachable!(), } } diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs index 9bdbf4f1..902fa88c 100644 --- a/nova_vm/src/types/spec/property_descriptor.rs +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -33,9 +33,9 @@ impl PropertyDescriptor { // 1. If Desc is undefined, return false. match (self.get, self.set) { // 2. If Desc has a [[Get]] field, return true. + (Some(_), _) => true, // 3. If Desc has a [[Set]] field, return true. - (Some(_), Some(_)) => true, - + (_, Some(_)) => true, // 4. Return false. _ => false, } @@ -47,8 +47,9 @@ impl PropertyDescriptor { // 1. If Desc is undefined, return false. match (self.value, self.writable) { // 2. If Desc has a [[Value]] field, return true. + (Some(_), _) => true, // 3. If Desc has a [[Writable]] field, return true. - (Some(_), Some(_)) => true, + (_, Some(_)) => true, // 4. Return false. _ => false, } From 6afaed3551498dfbcadc136e6a1e00d630967f37 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 10:58:27 -0500 Subject: [PATCH 14/32] more work towards builtins --- nova_vm/src/builtins/ordinary.rs | 593 +++++++++++++++++- nova_vm/src/heap/array.rs | 2 +- nova_vm/src/heap/function.rs | 2 +- nova_vm/src/types/language/object.rs | 30 +- .../types/language/object/internal_methods.rs | 2 +- .../src/types/language/object/property_key.rs | 5 + .../types/language/object/property_storage.rs | 53 +- nova_vm/src/types/language/string.rs | 74 ++- nova_vm/src/types/spec/property_descriptor.rs | 16 + 9 files changed, 724 insertions(+), 53 deletions(-) diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index b6fa0e78..c72ad537 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -19,8 +19,10 @@ pub static METHODS: InternalMethods = InternalMethods { set, delete, own_property_keys, - call: Some(call), - construct: Some(construct), + // call: todo!(), + // construct: todo!(), + call: None, + construct: None, }; /// 10.1.1 [[GetPrototypeOf]] ( ) @@ -124,7 +126,7 @@ fn is_extensible(agent: &mut Agent, object: Object) -> JsResult { /// https://tc39.es/ecma262/#sec-ordinaryisextensible pub fn ordinary_is_extensible(agent: &mut Agent, object: Object) -> bool { // 1. Return O.[[Extensible]]. - todo!() + object.extensible(agent) } /// 10.1.4 [[PreventExtensions]] ( ) @@ -199,63 +201,606 @@ pub fn ordinary_get_own_property( Some(descriptor) } +/// 10.1.6 [[DefineOwnProperty]] ( P, Desc ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc pub fn define_own_property( agent: &mut Agent, object: Object, property_key: PropertyKey, - property_descriptor: PropertyDescriptor, -) -> bool { - todo!() + descriptor: PropertyDescriptor, +) -> JsResult { + ordinary_define_own_property(agent, object, property_key, descriptor) +} + +/// 10.1.6.1 OrdinaryDefineOwnProperty ( O, P, Desc ) +/// https://tc39.es/ecma262/#sec-ordinarydefineownproperty +pub fn ordinary_define_own_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + descriptor: PropertyDescriptor, +) -> JsResult { + // 1. Let current be ? O.[[GetOwnProperty]](P). + let current = (object.internal_methods(agent).get_own_property)(agent, object, property_key)?; + + // 2. Let extensible be ? IsExtensible(O). + let extensible = object.extensible(agent); + + // 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). + validate_and_apply_property_descriptor( + agent, + Some(object), + property_key, + extensible, + descriptor, + current, + ) +} + +/// 10.1.6.3 ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ) +/// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor +fn validate_and_apply_property_descriptor( + agent: &mut Agent, + object: Option, + property_key: PropertyKey, + extensible: bool, + descriptor: PropertyDescriptor, + current: Option, +) -> JsResult { + // 1. Assert: IsPropertyKey(P) is true. + + // 2. If current is undefined, then + let Some(current) = current else { + // a. If extensible is false, return false. + if !extensible { + return Ok(false); + } + + // b. If O is undefined, return true. + let Some(object) = object else { + return Ok(true); + }; + + // c. If IsAccessorDescriptor(Desc) is true, then + if descriptor.is_accessor_descriptor() { + // i. Create an own accessor property named P of object O whose [[Get]], [[Set]], + // [[Enumerable]], and [[Configurable]] attributes are set to the value of the + // corresponding field in Desc if Desc has that field, or to the attribute's default + // value otherwise. + object.property_storage().set( + agent, + property_key, + PropertyDescriptor { + get: descriptor.get, + set: descriptor.set, + enumerable: Some(descriptor.enumerable.unwrap_or(false)), + configurable: Some(descriptor.configurable.unwrap_or(false)), + ..Default::default() + }, + ) + } + // d. Else, + else { + // i. Create an own data property named P of object O whose [[Value]], [[Writable]], + // [[Enumerable]], and [[Configurable]] attributes are set to the value of the + // corresponding field in Desc if Desc has that field, or to the attribute's default + // value otherwise. + // try object.propertyStorage().set(property_key, PropertyDescriptor{ + // .value = descriptor.value orelse .undefined, + // .writable = descriptor.writable orelse false, + // .enumerable = descriptor.enumerable orelse false, + // .configurable = descriptor.configurable orelse false, + // }); + object.property_storage().set( + agent, + property_key, + PropertyDescriptor { + value: Some(descriptor.value.unwrap_or(Value::Undefined)), + enumerable: Some(descriptor.enumerable.unwrap_or(false)), + configurable: Some(descriptor.configurable.unwrap_or(false)), + ..Default::default() + }, + ) + } + + // e. Return true. + return Ok(true); + }; + + // 3. Assert: current is a fully populated Property Descriptor. + debug_assert!(current.is_fully_populated()); + + // 4. If Desc does not have any fields, return true. + if !descriptor.has_fields() { + return Ok(true); + } + + // 5. If current.[[Configurable]] is false, then + if !current.configurable.unwrap() { + // a. If Desc has a [[Configurable]] field and Desc.[[Configurable]] is true, return false. + if let Some(true) = descriptor.configurable { + return Ok(false); + } + + // b. If Desc has an [[Enumerable]] field and SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) + // is false, return false. + if let Some(true) = descriptor.enumerable { + if descriptor.enumerable != current.enumerable { + return Ok(false); + } + } + + // c. If IsGenericDescriptor(Desc) is false and SameValue(IsAccessorDescriptor(Desc), IsAccessorDescriptor(current)) + // is false, return false. + if !descriptor.is_generic_descriptor() + && descriptor.is_accessor_descriptor() != current.is_accessor_descriptor() + { + return Ok(false); + } + + // d. If IsAccessorDescriptor(current) is true, then + if current.is_accessor_descriptor() { + // i. If Desc has a [[Get]] field and SameValue(Desc.[[Get]], current.[[Get]]) is false, + // return false. + if let Some(desc_get) = descriptor.get { + if let Some(cur_get) = current.get { + if !desc_get + .into_value() + .same_value(agent, cur_get.into_value()) + { + return Ok(false); + } + } else { + return Ok(false); + } + } + + // ii. If Desc has a [[Set]] field and SameValue(Desc.[[Set]], current.[[Set]]) is + // false, return false. + if let Some(desc_set) = descriptor.set { + if let Some(cur_set) = current.set { + if !desc_set + .into_value() + .same_value(agent, cur_set.into_value()) + { + return Ok(false); + } + } else { + return Ok(false); + } + } + } + // e. Else if current.[[Writable]] is false, then + else if let Some(true) = current.writable { + // i. If Desc has a [[Writable]] field and Desc.[[Writable]] is true, return false. + if let Some(true) = descriptor.writable { + return Ok(false); + } + + // ii. If Desc has a [[Value]] field and SameValue(Desc.[[Value]], current.[[Value]]) + // is false, return false. + if let Some(desc_value) = descriptor.value { + if let Some(cur_value) = current.value { + if !desc_value.same_value(agent, cur_value) { + return Ok(false); + } + } else { + return Ok(false); + } + } + } + } + + // 6. If O is not undefined, then + if let Some(object) = object { + // a. If IsDataDescriptor(current) is true and IsAccessorDescriptor(Desc) is true, then + if current.is_data_descriptor() && descriptor.is_accessor_descriptor() { + // i. If Desc has a [[Configurable]] field, let configurable be Desc.[[Configurable]]; + // else let configurable be current.[[Configurable]]. + let configurable = descriptor + .configurable + .unwrap_or_else(|| current.configurable.unwrap()); + + // ii. If Desc has a [[Enumerable]] field, let enumerable be Desc.[[Enumerable]]; else + // let enumerable be current.[[Enumerable]]. + let enumerable = descriptor + .enumerable + .unwrap_or_else(|| current.enumerable.unwrap()); + + // iii. Replace the property named P of object O with an accessor property whose + // [[Configurable]] and [[Enumerable]] attributes are set to configurable and + // enumerable, respectively, and whose [[Get]] and [[Set]] attributes are set to + // the value of the corresponding field in Desc if Desc has that field, or to the + // attribute's default value otherwise. + object.property_storage().set( + agent, + property_key, + PropertyDescriptor { + get: descriptor.get, + set: descriptor.set, + enumerable: Some(enumerable), + configurable: Some(configurable), + ..Default::default() + }, + ); + } + // b. Else if IsAccessorDescriptor(current) is true and IsDataDescriptor(Desc) is true, then + else if current.is_accessor_descriptor() && descriptor.is_data_descriptor() { + // i. If Desc has a [[Configurable]] field, let configurable be Desc.[[Configurable]]; + // else let configurable be current.[[Configurable]]. + let configurable = descriptor + .configurable + .unwrap_or_else(|| current.configurable.unwrap()); + + // ii. If Desc has a [[Enumerable]] field, let enumerable be Desc.[[Enumerable]]; else + // let enumerable be current.[[Enumerable]]. + let enumerable = descriptor + .enumerable + .unwrap_or_else(|| current.enumerable.unwrap()); + + // iii. Replace the property named P of object O with a data property whose + // [[Configurable]] and [[Enumerable]] attributes are set to configurable and + // enumerable, respectively, and whose [[Value]] and [[Writable]] attributes are + // set to the value of the corresponding field in Desc if Desc has that field, or + // to the attribute's default value otherwise. + // try object.propertyStorage().set(property_key, PropertyDescriptor{ + // .value = descriptor.value orelse .undefined, + // .writable = descriptor.writable orelse false, + // .enumerable = enumerable, + // .configurable = configurable, + // }); + object.property_storage().set( + agent, + property_key, + PropertyDescriptor { + value: Some(descriptor.value.unwrap_or(Value::Undefined)), + writable: Some(descriptor.writable.unwrap_or(false)), + enumerable: Some(enumerable), + configurable: Some(configurable), + ..Default::default() + }, + ); + } + // c. Else, + else { + // i. For each field of Desc, set the corresponding attribute of the property named P + // of object O to the value of the field. + object.property_storage().set( + agent, + property_key, + PropertyDescriptor { + value: descriptor.value.or(current.value), + writable: Some(descriptor.writable.unwrap_or(false)), + get: descriptor.get.or(current.get), + set: descriptor.set.or(current.set), + enumerable: descriptor.enumerable.or(current.enumerable), + configurable: descriptor.configurable.or(current.configurable), + ..Default::default() + }, + ); + } + } + + // 7. Return true. + Ok(true) } +/// 10.1.7 [[HasProperty]] ( P ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p pub fn has_property( agent: &mut Agent, object: Object, property_key: PropertyKey, ) -> JsResult { - todo!() + // 1. Return ? OrdinaryHasProperty(O, P). + ordinary_has_property(agent, object, property_key) +} + +/// 10.1.7.1 OrdinaryHasProperty ( O, P ) +/// https://tc39.es/ecma262/#sec-ordinaryhasproperty +pub fn ordinary_has_property( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, +) -> JsResult { + // 1. Let hasOwn be ? O.[[GetOwnProperty]](P). + let has_own = (object.internal_methods(agent).get_own_property)(agent, object, property_key)?; + + // 2. If hasOwn is not undefined, return true. + if let Some(_) = has_own { + return Ok(true); + } + + // 3. Let parent be ? O.[[GetPrototypeOf]](). + let parent = (object.internal_methods(agent).get_prototype_of)(agent, object); + + // 4. If parent is not null, then + if let Some(parent) = parent { + // a. Return ? parent.[[HasProperty]](P). + return (parent.internal_methods(agent).has_property)(agent, parent, property_key); + } + + // 5. Return false. + Ok(false) +} + +/// 10.1.8 [[Get]] ( P, Receiver ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-get-p-receiver +fn get( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + receiver: Value, +) -> JsResult { + // 1. Return ? OrdinaryGet(O, P, Receiver). + ordinary_get(agent, object, property_key, receiver) } -pub fn get( +/// 10.1.8.1 OrdinaryGet ( O, P, Receiver ) +/// https://tc39.es/ecma262/#sec-ordinaryget +pub fn ordinary_get( agent: &mut Agent, object: Object, property_key: PropertyKey, receiver: Value, ) -> JsResult { + // 1. Let desc be ? O.[[GetOwnProperty]](P). + let Some(descriptor) = + (object.internal_methods(agent).get_own_property)(agent, object, property_key)? + else { + // 2. If desc is undefined, then + + // a. Let parent be ? O.[[GetPrototypeOf]](). + let Some(parent) = (object.internal_methods(agent).get_prototype_of)(agent, object) else { + return Ok(Value::Undefined); + }; + + // c. Return ? parent.[[Get]](P, Receiver). + return (parent.internal_methods(agent).get)(agent, parent, property_key, receiver); + }; + + // 3. If IsDataDescriptor(desc) is true, return desc.[[Value]]. + if let Some(value) = descriptor.value { + debug_assert!(descriptor.is_data_descriptor()); + return Ok(value); + } + + // 4. Assert: IsAccessorDescriptor(desc) is true. + debug_assert!(descriptor.is_accessor_descriptor()); + + // 5. Let getter be desc.[[Get]]. + // 6. If getter is undefined, return undefined. + let Some(getter) = descriptor.get else { + return Ok(Value::Undefined); + }; + + // 7. Return ? Call(getter, Receiver). todo!() } -pub fn set( +/// 10.1.9 [[Set]] ( P, V, Receiver ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver +fn set( agent: &mut Agent, object: Object, property_key: PropertyKey, value: Value, receiver: Value, ) -> JsResult { - todo!() + // 1. Return ? OrdinarySet(O, P, V, Receiver). + ordinary_set(agent, object, property_key, value, receiver) } -pub fn delete(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult { - todo!() -} +/// 10.1.9.1 OrdinarySet ( O, P, V, Receiver ) +/// https://tc39.es/ecma262/#sec-ordinaryset +pub fn ordinary_set( + agent: &mut Agent, + object: Object, + property_key: PropertyKey, + value: Value, + receiver: Value, +) -> JsResult { + // 1. Let ownDesc be ? O.[[GetOwnProperty]](P). + let own_descriptor = + (object.internal_methods(agent).get_own_property)(agent, object, property_key)?; -pub fn own_property_keys(agent: &mut Agent, object: Object) -> JsResult> { - todo!() + // 2. Return ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc). + ordinary_set_with_own_descriptor(agent, object, property_key, value, receiver, own_descriptor) } -pub fn call( +/// 10.1.9.2 OrdinarySetWithOwnDescriptor ( O, P, V, Receiver, ownDesc ) +/// https://tc39.es/ecma262/#sec-ordinarysetwithowndescriptor +pub fn ordinary_set_with_own_descriptor( agent: &mut Agent, object: Object, - this_value: Value, - arguments_list: ArgumentsList, -) -> JsResult { - todo!() + property_key: PropertyKey, + value: Value, + receiver: Value, + own_descriptor: Option, +) -> JsResult { + let own_descriptor = if let Some(own_descriptor) = own_descriptor { + own_descriptor + } else { + // 1. If ownDesc is undefined, then + // a. Let parent be ? O.[[GetPrototypeOf]](). + let parent = (object.internal_methods(agent).get_prototype_of)(agent, object); + + // b. If parent is not null, then + if let Some(parent) = parent { + // i. Return ? parent.[[Set]](P, V, Receiver). + return (parent.internal_methods(agent).set)( + agent, + parent, + property_key, + value, + receiver, + ); + } + // c. Else, + else { + // i. Set ownDesc to the PropertyDescriptor { + // [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true + // }. + PropertyDescriptor { + value: Some(Value::Undefined), + writable: Some(true), + enumerable: Some(true), + configurable: Some(true), + ..Default::default() + } + } + }; + + // 2. If IsDataDescriptor(ownDesc) is true, then + if own_descriptor.is_data_descriptor() { + // a. If ownDesc.[[Writable]] is false, return false. + if own_descriptor.writable == Some(false) { + return Ok(false); + } + + // b. If Receiver is not an Object, return false. + if !receiver.is_object() { + return Ok(false); + } + let receiver = Object::new(receiver); + + // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P). + let existing_descriptor = + (receiver.internal_methods(agent).get_own_property)(agent, receiver, property_key)?; + + // d. If existingDescriptor is not undefined, then + if let Some(existing_descriptor) = existing_descriptor { + // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. + if existing_descriptor.is_accessor_descriptor() { + return Ok(false); + } + + // ii. If existingDescriptor.[[Writable]] is false, return false. + if existing_descriptor.writable == Some(false) { + return Ok(false); + } + + // iii. Let valueDesc be the PropertyDescriptor { [[Value]]: V }. + let value_descriptor = PropertyDescriptor { + value: Some(value), + ..Default::default() + }; + + // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). + return Ok((receiver.internal_methods(agent).define_own_property)( + agent, + receiver, + property_key, + value_descriptor, + )); + } + // e. Else, + else { + // i. Assert: Receiver does not currently have a property P. + debug_assert!(!receiver.property_storage().has(agent, property_key)); + + // ii. Return ? CreateDataProperty(Receiver, P, V). + return receiver.create_data_property(agent, property_key, value); + } + } + + // 3. Assert: IsAccessorDescriptor(ownDesc) is true. + debug_assert!(own_descriptor.is_accessor_descriptor()); + + // 4. Let setter be ownDesc.[[Set]]. + // 5. If setter is undefined, return false. + let Some(setter) = own_descriptor.set else { + return Ok(false); + }; + + // 6. Perform ? Call(setter, Receiver, « V »). + todo!(); + // 7. Return true. } -pub fn construct( +/// 10.1.10 [[Delete]] ( P ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-delete-p +fn delete(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult { + // 1. Return ? OrdinaryDelete(O, P). + ordinary_delete(agent, object, property_key) +} + +/// 10.1.10.1 OrdinaryDelete ( O, P ) +/// https://tc39.es/ecma262/#sec-ordinarydelete +pub fn ordinary_delete( agent: &mut Agent, object: Object, - arguments_list: ArgumentsList, -) -> JsResult { - todo!() + property_key: PropertyKey, +) -> JsResult { + // 1. Let desc be ? O.[[GetOwnProperty]](P). + let descriptor = + (object.internal_methods(agent).get_own_property)(agent, object, property_key)?; + + // 2. If desc is undefined, return true. + let Some(descriptor) = descriptor else { + return Ok(true); + }; + + // 3. If desc.[[Configurable]] is true, then + if let Some(true) = descriptor.configurable { + // a. Remove the own property with name P from O. + object.property_storage().remove(agent, property_key); + + // b. Return true. + return Ok(true); + } + + // 4. Return false. + Ok(false) +} + +/// 10.1.11 [[OwnPropertyKeys]] ( ) +/// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys +fn own_property_keys(agent: &mut Agent, object: Object) -> JsResult> { + // 1. Return OrdinaryOwnPropertyKeys(O). + ordinary_own_property_keys(agent, object) +} + +/// 10.1.11.1 OrdinaryOwnPropertyKeys ( O ) +/// https://tc39.es/ecma262/#sec-ordinaryownpropertykeys +pub fn ordinary_own_property_keys(agent: &mut Agent, object: Object) -> JsResult> { + // 1. Let keys be a new empty List. + let mut keys = Vec::new(); + + // 2. For each own property key P of O such that P is an array index, in ascending numeric + // index order, do + // for entry in object.property_storage().entries(agent) { + // if entry.key.is_array_index() { + // // a. Append P to keys. + // keys.push(entry.key); + // } + // } + + // for (object.property_storage().hash_map.keys()) |property_key| { + // if (property_key.is_array_index()) { + // // a. Append P to keys. + // keys.appendAssumeCapacity(property_key); + // } + // } + + // 3. For each own property key P of O such that P is a String and P is not an array index, in + // ascending chronological order of property creation, do + // for (object.propertyStorage().hash_map.keys()) |property_key| { + // if (property_key == .string or (property_key == .integer_index and !property_key.isArrayIndex())) { + // // a. Append P to keys. + // keys.appendAssumeCapacity(property_key); + // } + // } + + // 4. For each own property key P of O such that P is a Symbol, in ascending chronological + // order of property creation, do + // for (object.propertyStorage().hash_map.keys()) |property_key| { + // if (property_key == .symbol) { + // // a. Append P to keys. + // keys.appendAssumeCapacity(property_key); + // } + // } + + // 5. Return keys. + Ok(keys) } diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index 426a2ac1..e57c616b 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -19,7 +19,7 @@ use super::{ pub(crate) struct ArrayHeapData { pub object_index: Option, // TODO: Use SmallVec<[Value; 4]> - pub(super) elements: ElementsVector, + pub elements: ElementsVector, } pub fn initialize_array_heap(heap: &mut Heap) { diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 627de74d..a280ace1 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -16,7 +16,7 @@ use super::{ #[derive(Debug, Clone)] pub(crate) struct FunctionHeapData { - pub(super) object_index: ObjectIndex, + pub(super) object_index: Option, pub(super) length: u8, pub(crate) initial_name: Value, // pub(crate) behaviour: Behaviour, diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index f2e7fe82..4fff0d33 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -136,7 +136,7 @@ impl Object { self, property_key, property_descriptor, - ); + )?; // 2. If success is false, throw a TypeError exception. if !success { @@ -149,4 +149,32 @@ impl Object { // 3. Return unused. Ok(()) } + + /// 7.3.5 CreateDataProperty ( O, P, V ) + /// https://tc39.es/ecma262/#sec-createdataproperty + pub fn create_data_property( + self: Self, + agent: &mut Agent, + property_key: PropertyKey, + value: Value, + ) -> JsResult { + // 1. Let newDesc be the PropertyDescriptor { + // [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true + // }. + let new_descriptor = PropertyDescriptor { + value: Some(value), + writable: Some(true), + enumerable: Some(true), + configurable: Some(true), + ..Default::default() + }; + + // 2. Return ? O.[[DefineOwnProperty]](P, newDesc). + (self.internal_methods(agent).define_own_property)( + agent, + self, + property_key, + new_descriptor, + ) + } } diff --git a/nova_vm/src/types/language/object/internal_methods.rs b/nova_vm/src/types/language/object/internal_methods.rs index eb6237b2..ac8dc6e6 100644 --- a/nova_vm/src/types/language/object/internal_methods.rs +++ b/nova_vm/src/types/language/object/internal_methods.rs @@ -20,7 +20,7 @@ pub type DefineOwnProperty = fn( object: Object, property_key: PropertyKey, property_descriptor: PropertyDescriptor, -) -> bool; +) -> JsResult; pub type HasProperty = fn(agent: &mut Agent, object: Object, property_key: PropertyKey) -> JsResult; pub type Get = fn( diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index a90a5236..18302652 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -29,6 +29,11 @@ impl PropertyKey { self.0 } + pub fn is_array_index(self) -> bool { + // TODO: string check + matches!(self.into_value(), Value::IntegerNumber(_)) + } + pub(self) fn is_str_eq_num(s: &str, n: i64) -> bool { let (s, mut n) = if s.starts_with("-") { if n > 0 { diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs index f712133b..190be2e9 100644 --- a/nova_vm/src/types/language/object/property_storage.rs +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -1,5 +1,10 @@ +use std::{ + cell::{Ref, RefCell}, + rc::Rc, +}; + use crate::{ - execution::Agent, + execution::{Agent, Realm}, heap::GetHeapData, types::{PropertyDescriptor, Value}, }; @@ -39,10 +44,28 @@ impl PropertyStorage { true } Value::Array(array) => { + if key.equals(agent, PropertyKey::new(Value::try_from("length").unwrap())) { + return true; + } + let realm = agent.current_realm(); let realm = realm.borrow(); - let array = agent.current_realm().borrow().heap.get(array); - true + let array = realm.heap.get(array); + + if let Value::Integer(number) = key.into_value() { + if let Some(_) = TryInto::::try_into(number.into_i64()) + .map(|idx| array.elements.get(idx)) + .ok() + { + return true; + } + } + + if let Some(object) = array.object { + return object.property_storage().has(agent, key); + } + + false } Value::Function(_) => todo!(), _ => unreachable!(), @@ -52,4 +75,28 @@ impl PropertyStorage { pub fn get(self, agent: &mut Agent, key: PropertyKey) -> Option { todo!(); } + + pub fn set(self, agent: &mut Agent, property_key: PropertyKey, descriptor: PropertyDescriptor) { + } + + pub fn remove(self, agent: &mut Agent, property_key: PropertyKey) {} + + pub fn entries<'a, 'b>(self, agent: &'a Agent<'b, 'b>) -> Entries<'a, 'b> { + todo!() + } +} + +#[derive(Debug)] +pub struct Entries<'a, 'b> { + pub realm: Ref<'a, Realm<'b, 'b>>, + _rc: std::marker::PhantomData<&'a Rc>>>, +} + +impl<'a, 'b> Entries<'a, 'b> { + fn new(realm: Ref<'a, Realm<'b, 'b>>) -> Self { + Self { + realm, + _rc: Default::default(), + } + } } diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index 3436ae33..30a130c5 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -1,15 +1,54 @@ use super::Value; -use crate::{execution::Agent, heap::GetHeapData, SmallString}; +use crate::{ + execution::Agent, + heap::{GetHeapData, Handle, StringHeapData}, + SmallString, +}; /// 6.1.4 The String Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type -#[derive(Debug)] -pub struct String(Value); +#[derive(Debug, Clone, Copy)] +pub enum String { + String(Handle), + SmallString(SmallString), +} + +impl From> for String { + fn from(value: Handle) -> Self { + String::String(value) + } +} + +impl From for String { + fn from(value: SmallString) -> Self { + String::SmallString(value) + } +} impl TryFrom<&str> for String { type Error = (); fn try_from(value: &str) -> Result { - SmallString::try_from(value).map(|s| String::new(Value::SmallString(s))) + SmallString::try_from(value).map(|s| String::SmallString(s)) + } +} + +impl TryFrom for String { + type Error = (); + fn try_from(value: Value) -> Result { + match value { + Value::String(x) => Ok(String::String(x)), + Value::SmallString(x) => Ok(String::SmallString(x)), + _ => Err(()), + } + } +} + +impl From for Value { + fn from(value: String) -> Self { + match value { + String::String(x) => Value::String(x), + String::SmallString(x) => Value::SmallString(x), + } } } @@ -25,35 +64,26 @@ impl TryFrom for String { } impl String { - pub(crate) fn new(value: Value) -> Self { - matches!(value, Value::String(_) | Value::SmallString(_)); - Self(value) - } - pub fn into_value(self) -> Value { - self.0 + self.into() } /// Byte length of the string. pub fn len(self, agent: &Agent) -> usize { - let s = self.into_value(); - - match s { - Value::String(s) => agent.current_realm().borrow().heap.get(s).len(), - Value::SmallString(s) => s.len(), - _ => unreachable!(), + match self { + String::String(s) => agent.current_realm().borrow().heap.get(s).len(), + String::SmallString(s) => s.len(), } } - pub fn as_str<'a>(&'a self, agent: &'a Agent) -> Option<&'a str> { - match &self.0 { - // SAFETY: The immutable reference to the Agent ensures no mutable + pub fn as_str<'a>(&'a self, agent: &mut Agent) -> Option<&'a str> { + match self { + // SAFETY: The mutable reference to the Agent ensures no mutable // access to the realm. - Value::String(s) => unsafe { + String::String(s) => unsafe { std::mem::transmute(agent.current_realm().borrow().heap.get(*s).as_str()) }, - Value::SmallString(s) => Some(s.as_str()), - _ => unreachable!(), + String::SmallString(s) => Some(s.as_str()), } } diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs index 902fa88c..e20f16f8 100644 --- a/nova_vm/src/types/spec/property_descriptor.rs +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -93,4 +93,20 @@ impl PropertyDescriptor { // 10. Return obj. todo!() } + + pub fn is_fully_populated(&self) -> bool { + ((self.value.is_some() && self.writable.is_some()) + || (self.get.is_some() && self.set.is_some())) + && self.enumerable.is_some() + && self.configurable.is_some() + } + + pub fn has_fields(&self) -> bool { + self.value.is_some() + || self.writable.is_some() + || self.get.is_some() + || self.set.is_some() + || self.enumerable.is_some() + || self.configurable.is_some() + } } From ff01455fe8343b9304279dc4c690b342deb6b6a5 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 11:14:32 -0500 Subject: [PATCH 15/32] make intrinsics const functions --- nova_vm/src/builtins/ordinary.rs | 5 +- nova_vm/src/execution/realm/intrinsics.rs | 74 ++++++++--------- nova_vm/src/heap.rs | 2 +- nova_vm/src/types.rs | 6 -- nova_vm/src/types/language/function.rs | 50 ++++++++++-- nova_vm/src/types/language/object.rs | 96 ++++++++++++++--------- 6 files changed, 144 insertions(+), 89 deletions(-) diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index c72ad537..2a6c611f 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -659,10 +659,9 @@ pub fn ordinary_set_with_own_descriptor( } // b. If Receiver is not an Object, return false. - if !receiver.is_object() { + let Ok(receiver) = Object::try_from(receiver) else { return Ok(false); - } - let receiver = Object::new(receiver); + }; // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P). let existing_descriptor = diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index b48abe33..dbef4f4d 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -11,225 +11,225 @@ pub struct Intrinsics; impl Intrinsics { /// %Array% - pub fn array() -> Object { + pub const fn array() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ArrayConstructorIndex.into(), )) } /// %Array.prototype% - pub fn array_prototype() -> Object { + pub const fn array_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ArrayPrototypeIndex.into(), )) } /// %BigInt% - pub fn big_int() -> Object { + pub const fn big_int() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::BigintConstructorIndex.into(), )) } /// %BigInt.prototype% - pub fn big_int_prototype() -> Object { + pub const fn big_int_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::BigintPrototypeIndex.into(), )) } /// %Boolean% - pub fn boolean() -> Object { + pub const fn boolean() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::BooleanConstructorIndex.into(), )) } /// %Boolean.prototype% - pub fn boolean_prototype() -> Object { + pub const fn boolean_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::BooleanPrototypeIndex.into(), )) } /// %Error% - pub fn error() -> Object { + pub const fn error() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ErrorConstructorIndex.into(), )) } /// %Error.prototype% - pub fn error_prototype() -> Object { + pub const fn error_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ErrorPrototypeIndex.into(), )) } /// %eval% - pub fn eval() -> Object { + pub const fn eval() -> Object { todo!() } /// %EvalError% - pub fn eval_error() -> Object { + pub const fn eval_error() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ArrayConstructorIndex.into(), )) } /// %EvalError.prototype% - pub fn eval_error_prototype() -> Object { + pub const fn eval_error_prototype() -> Object { todo!() } /// %Function% - pub fn function() -> Object { + pub const fn function() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::FunctionConstructorIndex.into(), )) } /// %Function.prototype% - pub fn function_prototype() -> Object { + pub const fn function_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::FunctionPrototypeIndex.into(), )) } /// %isFinite% - pub fn is_finite() -> Object { + pub const fn is_finite() -> Object { todo!() } /// %isNaN% - pub fn is_nan() -> Object { + pub const fn is_nan() -> Object { todo!() } /// %Math% - pub fn math() -> Object { + pub const fn math() -> Object { Object::new(Value::Object(BuiltinObjectIndexes::MathObjectIndex.into())) } /// %Number% - pub fn number() -> Object { + pub const fn number() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::NumberConstructorIndex.into(), )) } /// %Number.prototype% - pub fn number_prototype() -> Object { + pub const fn number_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::NumberPrototypeIndex.into(), )) } /// %Object% - pub fn object() -> Object { + pub const fn object() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ObjectConstructorIndex.into(), )) } /// %Object.prototype% - pub fn object_prototype() -> Object { + pub const fn object_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::ObjectPrototypeIndex.into(), )) } /// %Object.prototype.toString% - pub fn object_prototype_to_string() -> Object { + pub const fn object_prototype_to_string() -> Object { todo!() } /// %RangeError% - pub fn range_error() -> Object { + pub const fn range_error() -> Object { todo!() } /// %RangeError.prototype% - pub fn range_error_prototype() -> Object { + pub const fn range_error_prototype() -> Object { todo!() } /// %ReferenceError% - pub fn reference_error() -> Object { + pub const fn reference_error() -> Object { todo!() } /// %ReferenceError.prototype% - pub fn reference_error_prototype() -> Object { + pub const fn reference_error_prototype() -> Object { todo!() } /// %Reflect% - pub fn reflect() -> Object { + pub const fn reflect() -> Object { todo!() } /// %String% - pub fn string() -> Object { + pub const fn string() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::StringConstructorIndex.into(), )) } /// %String.prototype% - pub fn string_prototype() -> Object { + pub const fn string_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::StringPrototypeIndex.into(), )) } /// %Symbol% - pub fn symbol() -> Object { + pub const fn symbol() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::SymbolConstructorIndex.into(), )) } /// %Symbol.prototype% - pub fn symbol_prototype() -> Object { + pub const fn symbol_prototype() -> Object { Object::new(Value::Object( BuiltinObjectIndexes::SymbolPrototypeIndex.into(), )) } /// %SyntaxError% - pub fn syntax_error() -> Object { + pub const fn syntax_error() -> Object { todo!() } /// %SyntaxError.prototype% - pub fn syntax_error_prototype() -> Object { + pub const fn syntax_error_prototype() -> Object { todo!() } /// %ThrowTypeError% - pub fn throw_type_error() -> Object { + pub const fn throw_type_error() -> Object { todo!() } /// %TypeError% - pub fn type_error() -> Object { + pub const fn type_error() -> Object { todo!() } /// %TypeError.prototype% - pub fn type_error_prototype() -> Object { + pub const fn type_error_prototype() -> Object { todo!() } /// %URIError% - pub fn uri_error() -> Object { + pub const fn uri_error() -> Object { todo!() } /// %URIError.prototype% - pub fn uri_error_prototype() -> Object { + pub const fn uri_error_prototype() -> Object { todo!() } } diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 2a42ae3d..dbdf24c9 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -268,7 +268,7 @@ impl Heap { // behaviour, // bound: None, length, - object_index: ObjectIndex::last(&self.objects), + object_index: Some(ObjectIndex::last(&self.objects)), // uses_arguments, // visible: None, initial_name: Value::Null, diff --git a/nova_vm/src/types.rs b/nova_vm/src/types.rs index 2d392f9e..19b28f2b 100644 --- a/nova_vm/src/types.rs +++ b/nova_vm/src/types.rs @@ -4,11 +4,5 @@ mod spec; pub use language::{Function, InternalMethods, Number, Object, PropertyKey, String, Value}; pub use spec::{Base, PropertyDescriptor, Reference, ReferencedName}; -impl From for Value { - fn from(value: Object) -> Self { - todo!() - } -} - #[derive(Debug)] pub struct Symbol; diff --git a/nova_vm/src/types/language/function.rs b/nova_vm/src/types/language/function.rs index edaa8502..efc2dad0 100644 --- a/nova_vm/src/types/language/function.rs +++ b/nova_vm/src/types/language/function.rs @@ -1,8 +1,10 @@ +use crate::heap::{FunctionHeapData, Handle}; + use super::{Object, Value}; /// https://tc39.es/ecma262/#function-object #[derive(Clone, Copy)] -pub struct Function(Value); +pub struct Function(pub Handle); impl std::fmt::Debug for Function { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -10,16 +12,52 @@ impl std::fmt::Debug for Function { } } -impl Function { - pub(crate) fn new(value: Value) -> Self { - Self(value) +impl From> for Function { + fn from(value: Handle) -> Self { + Function(value) + } +} + +impl TryFrom for Function { + type Error = (); + fn try_from(value: Object) -> Result { + if let Object::Function(value) = value { + Ok(Function(value)) + } else { + Err(()) + } + } +} + +impl TryFrom for Function { + type Error = (); + fn try_from(value: Value) -> Result { + if let Value::Function(value) = value { + Ok(Function(value)) + } else { + Err(()) + } } +} + +impl From for Object { + fn from(value: Function) -> Self { + Object::Function(value.0) + } +} +impl From for Value { + fn from(value: Function) -> Self { + Value::Function(value.0) + } +} + +impl Function { pub fn into_value(self) -> Value { - self.0 + self.into() } pub fn into_object(self) -> Object { - Object::new(self.into_value()) + Object::Function(self.0) } } diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 4fff0d33..78e602dd 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -7,7 +7,10 @@ use super::Value; use crate::{ builtins::ordinary, execution::{agent::ExceptionType, Agent, Intrinsics, JsResult}, - heap::GetHeapData, + heap::{ + indexes::{ArrayIndex, FunctionIndex, ObjectIndex}, + GetHeapData, + }, types::PropertyDescriptor, }; @@ -19,70 +22,93 @@ pub use property_storage::PropertyStorage; /// 6.1.7 The Object Type /// https://tc39.es/ecma262/#sec-object-type #[derive(Debug, Clone, Copy)] -pub struct Object(Value); +pub enum Object { + Object(ObjectIndex), + Array(ArrayIndex), + Function(FunctionIndex), +} + +impl From for Object { + fn from(value: ObjectIndex) -> Self { + Object::Object(value) + } +} + +impl From for Object { + fn from(value: ArrayIndex) -> Self { + Object::Array(value) + } +} + +impl From for Object { + fn from(value: FunctionIndex) -> Self { + Object::Function(value) + } +} + +impl From for Value { + fn from(value: Object) -> Self { + match value { + Object::Object(x) => Value::Object(x), + Object::Array(x) => Value::ArrayObject(x), + Object::Function(x) => Value::Function(x), + } + } +} impl TryFrom for Object { type Error = (); fn try_from(value: Value) -> Result { - if let Value::Object(_) | Value::Array(_) | Value::Function(_) = value { - Ok(Self(value)) - } else { - Err(()) + match value { + Value::Object(x) => Ok(Object::Object(x)), + Value::Array(x) => Ok(Object::Array(x)), + Value::Function(x) => Ok(Object::Function(x)), + _ => Err(()), } } } impl Object { - pub(crate) fn new(value: Value) -> Self { - Self(value) - } - pub fn into_value(self) -> Value { - self.0 + self.into() } /// [[Extensible]] pub fn extensible(self, agent: &mut Agent) -> bool { - let object = self.into_value(); - - match object { - Value::Object(object) => agent.current_realm().borrow().heap.get(object).extensible, - Value::Array(_) => true, - Value::Function(_) => true, - _ => unreachable!(), + match self { + Object::Object(object) => agent.current_realm().borrow().heap.get(object).extensible, + Object::Array(_) => true, + Object::Function(_) => true, } } /// [[Extensible]] pub fn set_extensible(self, agent: &mut Agent, value: bool) { - let object = self.into_value(); - - match object { - Value::Object(object) => { + match self { + Object::Object(object) => { let realm = agent.current_realm(); let mut realm = realm.borrow_mut(); let object = realm.heap.get_mut(object); object.extensible = true; } // TODO: Correct object/function impl - Value::Array(_) => {} - Value::Function(_) => {} + Object::Array(_) => {} + Object::Function(_) => {} _ => unreachable!(), } } /// [[Prototype]] pub fn prototype(self, agent: &mut Agent) -> Option { - let object = self.into_value(); let realm = agent.current_realm(); let realm = realm.borrow(); - match object { - Value::Object(object) => { + match self { + Object::Object(object) => { let object = realm.heap.get(object); object.prototype.try_into().ok() } - Value::Array(array) => { + Object::Array(array) => { let array = realm.heap.get(array); if let Some(object_index) = array.object_index { @@ -91,24 +117,22 @@ impl Object { Some(Intrinsics::array_prototype().into_value()) } } - Value::Function(_) => Some(Intrinsics::function_prototype().into_value()), + Object::Function(_) => Some(Intrinsics::function_prototype().into_value()), _ => unreachable!(), } } /// [[Prototype]] pub fn set_prototype(self, agent: &mut Agent, prototype: Option) { - let object = self.into_value(); - - match object { - Value::Object(object) => { + match self { + Object::Object(object) => { let realm = agent.current_realm(); let mut realm = realm.borrow_mut(); let object = realm.heap.get_mut(object); object.prototype = prototype.map(|object| object.into_value()).unwrap(); } - Value::Array(_) => todo!(), - Value::Function(_) => todo!(), + Object::Array(_) => todo!(), + Object::Function(_) => todo!(), _ => unreachable!(), } } @@ -170,7 +194,7 @@ impl Object { }; // 2. Return ? O.[[DefineOwnProperty]](P, newDesc). - (self.internal_methods(agent).define_own_property)( + Ok(self.internal_methods(agent).define_own_property)( agent, self, property_key, From 5c994b31ef5007bb9a922679b2a9a8d3c29549d8 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 11:15:31 -0500 Subject: [PATCH 16/32] remove double Option --- nova_vm/src/language/script.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 65713309..7cd334cf 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -8,7 +8,7 @@ use oxc_parser::Parser; use oxc_span::SourceType; use std::{any::Any, cell::RefCell, collections::HashMap, rc::Rc}; -pub type HostDefined<'ctx> = Option<&'ctx mut dyn Any>; +pub type HostDefined<'ctx> = &'ctx mut dyn Any; /// 16.1.4 Script Records /// https://tc39.es/ecma262/#sec-script-records From 0b557e7b43f3c7cfea77ae9c3dec3ace7bf4c625 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 11:31:46 -0500 Subject: [PATCH 17/32] adjust for larger small bigint range --- nova_vm/src/builtins/number.rs | 4 +- nova_vm/src/small_integer.rs | 33 +++-- nova_vm/src/types/language/number.rs | 4 +- .../src/types/language/object/property_key.rs | 134 +++++++++--------- .../types/language/object/property_storage.rs | 7 +- 5 files changed, 102 insertions(+), 80 deletions(-) diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index f937f8de..ea40666e 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -46,7 +46,7 @@ impl Builtin for NumberConstructor { object, "MAX_SAFE_INTEGER", PropertyDescriptor { - value: Some(Number::from(SmallInteger::MAX).into()), + value: Some(Number::from(SmallInteger::MAX_NUMBER).into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -74,7 +74,7 @@ impl Builtin for NumberConstructor { object, "MIN_SAFE_INTEGER", PropertyDescriptor { - value: Some(Number::from(SmallInteger::MIN).into()), + value: Some(Number::from(SmallInteger::MIN_NUMBER).into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs index c37a726a..cd2f9063 100644 --- a/nova_vm/src/small_integer.rs +++ b/nova_vm/src/small_integer.rs @@ -11,8 +11,11 @@ impl std::fmt::Debug for SmallInteger { } impl SmallInteger { - pub const MIN: i64 = -(2 as i64).pow(53) / 2 + 1; - pub const MAX: i64 = (2 as i64).pow(53) / 2 - 1; + pub const MIN_BIGINT: i64 = -2i64.pow(56) / 2 + 1; + pub const MAX_BIGINT: i64 = 2i64.pow(56) / 2 + 1; + + pub const MIN_NUMBER: i64 = -(2 as i64).pow(53) / 2 + 1; + pub const MAX_NUMBER: i64 = (2 as i64).pow(53) / 2 - 1; #[inline] pub fn into_i64(self) -> i64 { @@ -20,7 +23,7 @@ impl SmallInteger { } pub(crate) fn from_i64_unchecked(value: i64) -> SmallInteger { - debug_assert!(value >= Self::MIN && value <= Self::MAX); + debug_assert!(value >= Self::MIN_NUMBER && value <= Self::MAX_NUMBER); let bytes = i64::to_ne_bytes(value); let data = if cfg!(target_endian = "little") { @@ -40,7 +43,7 @@ impl SmallInteger { impl TryFrom for SmallInteger { type Error = (); fn try_from(value: i64) -> Result { - if value >= Self::MIN && value <= Self::MAX { + if value >= Self::MIN_NUMBER && value <= Self::MAX_NUMBER { Ok(Self::from_i64_unchecked(value)) } else { Err(()) @@ -74,22 +77,32 @@ fn valid_small_integers() { assert_eq!(5i64, SmallInteger::try_from(5).unwrap().into()); assert_eq!(23i64, SmallInteger::try_from(23).unwrap().into()); assert_eq!( - SmallInteger::MAX, - SmallInteger::try_from(SmallInteger::MAX).unwrap().into() + SmallInteger::MAX_NUMBER, + SmallInteger::try_from(SmallInteger::MAX_NUMBER) + .unwrap() + .into() ); assert_eq!(-5i64, SmallInteger::try_from(-5).unwrap().into()); assert_eq!(-59i64, SmallInteger::try_from(-59).unwrap().into()); assert_eq!( - SmallInteger::MIN, - SmallInteger::try_from(SmallInteger::MIN).unwrap().into() + SmallInteger::MIN_NUMBER, + SmallInteger::try_from(SmallInteger::MIN_NUMBER) + .unwrap() + .into() ); } #[test] fn invalid_small_integers() { - assert_eq!(SmallInteger::try_from(SmallInteger::MAX + 1), Err(())); + assert_eq!( + SmallInteger::try_from(SmallInteger::MAX_NUMBER + 1), + Err(()) + ); assert_eq!(SmallInteger::try_from(i64::MAX), Err(())); - assert_eq!(SmallInteger::try_from(SmallInteger::MIN - 1), Err(())); + assert_eq!( + SmallInteger::try_from(SmallInteger::MIN_NUMBER - 1), + Err(()) + ); assert_eq!(SmallInteger::try_from(i64::MIN), Err(())); } diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 79862965..5f941a82 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -32,7 +32,9 @@ impl From for Number { impl From for Number { fn from(value: i64) -> Self { - let n = value.min(SmallInteger::MAX).max(SmallInteger::MIN); + let n = value + .min(SmallInteger::MAX_NUMBER) + .max(SmallInteger::MIN_NUMBER); Number(Value::Integer(SmallInteger::from_i64_unchecked(n))) } } diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index 18302652..b4575566 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -1,77 +1,91 @@ use crate::{ execution::Agent, - heap::GetHeapData, + heap::{GetHeapData, Handle, StringHeapData}, types::{String, Value}, - SmallString, + SmallInteger, SmallString, }; #[derive(Debug, Clone, Copy)] -pub struct PropertyKey(Value); +pub enum PropertyKey { + String(Handle), + SmallString(SmallString), + SmallInteger(SmallInteger), +} -impl Default for PropertyKey { - fn default() -> Self { - Self(Value::SmallString(SmallString::from_str_unchecked( - "unknown", - ))) +impl From> for PropertyKey { + fn from(value: Handle) -> Self { + PropertyKey::String(value) } } -impl PropertyKey { - pub(crate) fn new(value: Value) -> Self { - debug_assert!(matches!( - value, - Value::Integer(_) | Value::String(_) | Value::SmallString(_) - )); - Self(value) +impl From for PropertyKey { + fn from(value: SmallString) -> Self { + PropertyKey::SmallString(value) } +} - pub fn into_value(self) -> Value { - self.0 +impl From for PropertyKey { + fn from(value: SmallInteger) -> Self { + PropertyKey::SmallInteger(value) } +} - pub fn is_array_index(self) -> bool { - // TODO: string check - matches!(self.into_value(), Value::IntegerNumber(_)) +impl From for PropertyKey { + fn from(value: String) -> Self { + match value { + String::String(x) => PropertyKey::String(x), + String::SmallString(x) => PropertyKey::SmallString(x), + } } +} - pub(self) fn is_str_eq_num(s: &str, n: i64) -> bool { - let (s, mut n) = if s.starts_with("-") { - if n > 0 { - return false; - } - (&s[1..], -n as usize) - } else { - if n < 0 { - return false; - } - (s, n as usize) - }; - - if Some(s.len()) != n.checked_ilog10().map(|n| n as usize) { - return false; +impl TryFrom for PropertyKey { + type Error = (); + fn try_from(value: Value) -> Result { + match value { + Value::String(x) => Ok(PropertyKey::String(x)), + Value::SmallString(x) => Ok(PropertyKey::SmallString(x)), + Value::IntegerNumber(x) => Ok(PropertyKey::SmallInteger(x)), + _ => Err(()), } + } +} - for c in s.as_bytes().iter().rev() { - let code = (n % 10) as u8 + '0' as u8; +impl From for Value { + fn from(value: PropertyKey) -> Self { + match value { + PropertyKey::String(x) => Value::String(x), + PropertyKey::SmallString(x) => Value::SmallString(x), + PropertyKey::SmallInteger(x) => Value::IntegerNumber(x), + } + } +} - if *c != code { - return false; - } +impl PropertyKey { + pub fn into_value(self) -> Value { + self.into() + } - n /= 10; - } + pub fn is_array_index(self) -> bool { + // TODO: string check + matches!(self.into_value(), Value::IntegerNumber(_)) + } - true + pub(self) fn is_str_eq_num(s: &str, n: i64) -> bool { + // TODO: Come up with some advanced algorithm. + s == n.to_string() } pub fn equals(self, agent: &mut Agent, y: Self) -> bool { - let x = self.into_value(); - let y = y.into_value(); + let x = self; match (x, y) { // Assumes the interner is working correctly. - (Value::String(s1), Value::String(s2)) => s1 == s2, - (Value::String(s), Value::Integer(n)) => { + (PropertyKey::String(s1), PropertyKey::String(s2)) => s1 == s2, + (PropertyKey::SmallString(s1), PropertyKey::SmallString(s2)) => { + s1.as_str() == s2.as_str() + } + (PropertyKey::String(s), PropertyKey::SmallInteger(n)) => { let realm = agent.current_realm(); let realm = realm.borrow(); let s = realm.heap.get(s); @@ -82,24 +96,14 @@ impl PropertyKey { Self::is_str_eq_num(s, n.into_i64()) } - _ => unreachable!(), - } - } -} - -impl From for PropertyKey { - fn from(value: String) -> Self { - Self(value.into_value()) - } -} - -impl TryFrom for PropertyKey { - type Error = (); - fn try_from(value: Value) -> Result { - if value.is_string() || value.is_symbol() || value.is_number() { - Ok(Self(value)) - } else { - Err(()) + (PropertyKey::SmallString(s), PropertyKey::SmallInteger(n)) => { + Self::is_str_eq_num(s.as_str(), n.into_i64()) + } + (PropertyKey::SmallInteger(n1), PropertyKey::SmallInteger(n2)) => { + n1.into_i64() == n2.into_i64() + } + (PropertyKey::SmallInteger(_), _) => y.equals(agent, self), + _ => false, } } } diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs index 190be2e9..6e533e08 100644 --- a/nova_vm/src/types/language/object/property_storage.rs +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -6,7 +6,7 @@ use std::{ use crate::{ execution::{Agent, Realm}, heap::GetHeapData, - types::{PropertyDescriptor, Value}, + types::{PropertyDescriptor, String, Value}, }; use super::{Object, PropertyKey}; @@ -44,7 +44,10 @@ impl PropertyStorage { true } Value::Array(array) => { - if key.equals(agent, PropertyKey::new(Value::try_from("length").unwrap())) { + if key.equals( + agent, + PropertyKey::from(String::try_from("length").unwrap()), + ) { return true; } From 01954eef43b36e410546d9f868b8654ac54a7a37 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 11:39:17 -0500 Subject: [PATCH 18/32] update oxc deps --- nova_vm/Cargo.toml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/nova_vm/Cargo.toml b/nova_vm/Cargo.toml index ee98d66c..e66e2a7a 100644 --- a/nova_vm/Cargo.toml +++ b/nova_vm/Cargo.toml @@ -8,10 +8,10 @@ edition = "2021" [dependencies] gc = { version = "0.4", features = ["derive"] } wtf8 = "0.1" -oxc_parser = "0.0.7" -oxc_span = "0.0.7" -oxc_ast = "0.0.7" -oxc_allocator = "0.0.7" -oxc_diagnostics = "0.0.7" +oxc_parser = "0.1.0" +oxc_span = "0.1.0" +oxc_ast = "0.1.0" +oxc_allocator = "0.1.0" +oxc_diagnostics = "0.1.0" num-bigint-dig = "0.8" small_vec = "0.1" From 87a00b6c193dceac667b7c2392e7eb59e0213547 Mon Sep 17 00:00:00 2001 From: Carter Snook Date: Sat, 29 Jul 2023 11:44:37 -0500 Subject: [PATCH 19/32] update to stable and fix cli --- nova_cli/Cargo.toml | 5 +++-- nova_cli/src/main.rs | 3 +-- nova_vm/src/small_string.rs | 2 +- rust-toolchain.toml | 2 -- 4 files changed, 5 insertions(+), 7 deletions(-) delete mode 100644 rust-toolchain.toml diff --git a/nova_cli/Cargo.toml b/nova_cli/Cargo.toml index 121d596e..324870ff 100644 --- a/nova_cli/Cargo.toml +++ b/nova_cli/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] clap = { version = "4.3", features = ["derive"] } -oxc_parser = "0.0.6" -oxc_ast = "0.0.6" +oxc_parser = "0.1.0" +oxc_ast = "0.1.0" +oxc_span = "0.1.0" nova_vm = { path = "../nova_vm" } diff --git a/nova_cli/src/main.rs b/nova_cli/src/main.rs index bb221fa8..63329b8a 100644 --- a/nova_cli/src/main.rs +++ b/nova_cli/src/main.rs @@ -1,7 +1,6 @@ use clap::{Parser as ClapParser, Subcommand}; -use nova_vm::heap::Heap; -use oxc_ast::SourceType; use oxc_parser::Parser; +use oxc_span::SourceType; /// A JavaScript engine #[derive(Debug, ClapParser)] // requires `derive` feature diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index 458c88ac..d82e5991 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -17,7 +17,7 @@ impl SmallString { .iter() .rev() .position(|&x| x != 0) - .unwrap_or(7) + .map_or(0, |i| 7 - i) } #[inline] diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 5d56faf9..00000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly" From cf36a6eb408711caefe42949a394c0ecdedca5d6 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 11 Sep 2023 01:09:11 +0300 Subject: [PATCH 20/32] Fix some post-rebase issues --- nova_vm/src/builtins/builtin_function.rs | 2 +- nova_vm/src/execution/realm/intrinsics.rs | 81 ++++++------------- nova_vm/src/heap.rs | 4 +- nova_vm/src/heap/array.rs | 2 +- nova_vm/src/heap/bigint.rs | 2 +- nova_vm/src/heap/boolean.rs | 2 +- nova_vm/src/heap/date.rs | 2 +- nova_vm/src/heap/error.rs | 2 +- nova_vm/src/heap/function.rs | 2 +- nova_vm/src/heap/heap_gc.rs | 4 +- nova_vm/src/heap/number.rs | 2 +- nova_vm/src/heap/object.rs | 2 +- nova_vm/src/heap/regexp.rs | 2 +- nova_vm/src/heap/string.rs | 2 +- nova_vm/src/heap/symbol.rs | 2 +- nova_vm/src/types/language/function.rs | 9 +-- nova_vm/src/types/language/object.rs | 2 +- .../src/types/language/object/property_key.rs | 14 ++-- .../types/language/object/property_storage.rs | 2 +- nova_vm/src/types/language/string.rs | 19 +---- 20 files changed, 57 insertions(+), 102 deletions(-) diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index 366c3727..95e796dc 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -95,7 +95,7 @@ pub fn create_builtin_function<'a, 'b: 'a>( // a. Perform SetFunctionName(func, name, prefix). // 13. Return func. - Function::new(Value::Function(func)) + Function(func) } pub fn define_builtin_function<'a, 'b>( diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index dbef4f4d..a6856798 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -12,58 +12,42 @@ pub struct Intrinsics; impl Intrinsics { /// %Array% pub const fn array() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ArrayConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::ArrayConstructorIndex.into()) } /// %Array.prototype% pub const fn array_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ArrayPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::ArrayPrototypeIndex.into()) } /// %BigInt% pub const fn big_int() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::BigintConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::BigintConstructorIndex.into()) } /// %BigInt.prototype% pub const fn big_int_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::BigintPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::BigintPrototypeIndex.into()) } /// %Boolean% pub const fn boolean() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::BooleanConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::BooleanConstructorIndex.into()) } /// %Boolean.prototype% pub const fn boolean_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::BooleanPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::BooleanPrototypeIndex.into()) } /// %Error% pub const fn error() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ErrorConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::ErrorConstructorIndex.into()) } /// %Error.prototype% pub const fn error_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ErrorPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::ErrorPrototypeIndex.into()) } /// %eval% @@ -73,9 +57,7 @@ impl Intrinsics { /// %EvalError% pub const fn eval_error() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ArrayConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::ArrayConstructorIndex.into()) } /// %EvalError.prototype% @@ -85,16 +67,15 @@ impl Intrinsics { /// %Function% pub const fn function() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::FunctionConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::FunctionConstructorIndex.into()) } /// %Function.prototype% pub const fn function_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::FunctionPrototypeIndex.into(), - )) + // Note: This is not spec-compliant. Function prototype should + // be a function that always returns undefined no matter how + // it is called. That's stupid so we do not have that. + Object::Object(BuiltinObjectIndexes::FunctionPrototypeIndex.into()) } /// %isFinite% @@ -109,35 +90,27 @@ impl Intrinsics { /// %Math% pub const fn math() -> Object { - Object::new(Value::Object(BuiltinObjectIndexes::MathObjectIndex.into())) + Object::Object(BuiltinObjectIndexes::MathObjectIndex.into()) } /// %Number% pub const fn number() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::NumberConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::NumberConstructorIndex.into()) } /// %Number.prototype% pub const fn number_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::NumberPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::NumberPrototypeIndex.into()) } /// %Object% pub const fn object() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ObjectConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::ObjectConstructorIndex.into()) } /// %Object.prototype% pub const fn object_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::ObjectPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()) } /// %Object.prototype.toString% @@ -172,30 +145,22 @@ impl Intrinsics { /// %String% pub const fn string() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::StringConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::StringConstructorIndex.into()) } /// %String.prototype% pub const fn string_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::StringPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::StringPrototypeIndex.into()) } /// %Symbol% pub const fn symbol() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::SymbolConstructorIndex.into(), - )) + Object::Function(BuiltinObjectIndexes::SymbolConstructorIndex.into()) } /// %Symbol.prototype% pub const fn symbol_prototype() -> Object { - Object::new(Value::Object( - BuiltinObjectIndexes::SymbolPrototypeIndex.into(), - )) + Object::Object(BuiltinObjectIndexes::SymbolPrototypeIndex.into()) } /// %SyntaxError% diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index dbdf24c9..db41e382 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -151,14 +151,14 @@ impl CreateHeapData<&str, String> for Heap { impl CreateHeapData for Heap { fn create(&mut self, data: FunctionHeapData) -> Function { self.functions.push(Some(data)); - Function::new(Value::Function(FunctionIndex::last(&self.functions))) + Function(FunctionIndex::last(&self.functions)) } } impl CreateHeapData for Heap { fn create(&mut self, data: ObjectHeapData) -> Object { self.objects.push(Some(data)); - Object::new(Value::Object(ObjectIndex::last(&self.objects))) + Object::Object(ObjectIndex::last(&self.objects)) } } diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index e57c616b..a23c38af 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -66,7 +66,7 @@ pub fn initialize_array_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::ArrayConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::ArrayConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::ArrayConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index 86679db1..8348f767 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -38,7 +38,7 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::BigintConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: ObjectIndex::last(&heap.objects), + object_index: Some(ObjectIndex::last(&heap.objects)), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index 56703978..34dfaceb 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -26,7 +26,7 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::BooleanConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::BooleanConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::BooleanConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index 2ee8b467..b9143c3d 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -41,7 +41,7 @@ pub fn initialize_date_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::DateConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::DateConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::DateConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index 05c0d3f6..136195a6 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -33,7 +33,7 @@ pub fn initialize_error_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::ErrorConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::ErrorConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::ErrorConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index a280ace1..b4315439 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -42,7 +42,7 @@ pub fn initialize_function_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::FunctionConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::FunctionConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::FunctionConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 092eb016..3502e741 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -77,7 +77,9 @@ pub(crate) fn heap_gc(heap: &mut Heap) { } marked.store(true, Ordering::Relaxed); let data = heap.functions.get(index).unwrap().as_ref().unwrap(); - queues.objects.push(data.object_index); + if let Some(object_index) = data.object_index { + queues.objects.push(object_index); + } // if let Some(bound) = &data.bound { // bound.iter().for_each(|&value| { // queues.push_value(value); diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index 2c1280bf..4b65067f 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -80,7 +80,7 @@ pub fn initialize_number_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::NumberConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::NumberConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::NumberConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 62c8ddfa..9ed7ce95 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -300,7 +300,7 @@ pub fn initialize_object_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::ObjectConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::ObjectConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::ObjectConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index af67295a..0313f1b4 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -45,7 +45,7 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::RegExpConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::RegExpConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::RegExpConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index b0e10d33..e780aee9 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -38,7 +38,7 @@ pub fn initialize_string_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::StringConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::StringConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::StringConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index cae428d4..09a93126 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -142,7 +142,7 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { heap.functions [get_constructor_index(BuiltinObjectIndexes::SymbolConstructorIndex).into_index()] = Some(FunctionHeapData { - object_index: BuiltinObjectIndexes::SymbolConstructorIndex.into(), + object_index: Some(BuiltinObjectIndexes::SymbolConstructorIndex.into()), length: 1, // uses_arguments: false, // bound: None, diff --git a/nova_vm/src/types/language/function.rs b/nova_vm/src/types/language/function.rs index efc2dad0..bd6068fb 100644 --- a/nova_vm/src/types/language/function.rs +++ b/nova_vm/src/types/language/function.rs @@ -1,10 +1,9 @@ -use crate::heap::{FunctionHeapData, Handle}; - use super::{Object, Value}; +use crate::heap::indexes::FunctionIndex; /// https://tc39.es/ecma262/#function-object #[derive(Clone, Copy)] -pub struct Function(pub Handle); +pub struct Function(pub FunctionIndex); impl std::fmt::Debug for Function { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -12,8 +11,8 @@ impl std::fmt::Debug for Function { } } -impl From> for Function { - fn from(value: Handle) -> Self { +impl From for Function { + fn from(value: FunctionIndex) -> Self { Function(value) } } diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 78e602dd..243ac5e7 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -50,7 +50,7 @@ impl From for Value { fn from(value: Object) -> Self { match value { Object::Object(x) => Value::Object(x), - Object::Array(x) => Value::ArrayObject(x), + Object::Array(x) => Value::Array(x), Object::Function(x) => Value::Function(x), } } diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index b4575566..a7c3b97c 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -1,19 +1,19 @@ use crate::{ execution::Agent, - heap::{GetHeapData, Handle, StringHeapData}, + heap::{indexes::StringIndex, GetHeapData}, types::{String, Value}, SmallInteger, SmallString, }; #[derive(Debug, Clone, Copy)] pub enum PropertyKey { - String(Handle), + String(StringIndex), SmallString(SmallString), SmallInteger(SmallInteger), } -impl From> for PropertyKey { - fn from(value: Handle) -> Self { +impl From for PropertyKey { + fn from(value: StringIndex) -> Self { PropertyKey::String(value) } } @@ -45,7 +45,7 @@ impl TryFrom for PropertyKey { match value { Value::String(x) => Ok(PropertyKey::String(x)), Value::SmallString(x) => Ok(PropertyKey::SmallString(x)), - Value::IntegerNumber(x) => Ok(PropertyKey::SmallInteger(x)), + Value::Integer(x) => Ok(PropertyKey::SmallInteger(x)), _ => Err(()), } } @@ -56,7 +56,7 @@ impl From for Value { match value { PropertyKey::String(x) => Value::String(x), PropertyKey::SmallString(x) => Value::SmallString(x), - PropertyKey::SmallInteger(x) => Value::IntegerNumber(x), + PropertyKey::SmallInteger(x) => Value::Integer(x), } } } @@ -68,7 +68,7 @@ impl PropertyKey { pub fn is_array_index(self) -> bool { // TODO: string check - matches!(self.into_value(), Value::IntegerNumber(_)) + matches!(self.into_value(), Value::Integer(_)) } pub(self) fn is_str_eq_num(s: &str, n: i64) -> bool { diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs index 6e533e08..7033dafb 100644 --- a/nova_vm/src/types/language/object/property_storage.rs +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -64,7 +64,7 @@ impl PropertyStorage { } } - if let Some(object) = array.object { + if let Some(object) = array.object_index { return object.property_storage().has(agent, key); } diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index 30a130c5..b1a9741a 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -1,7 +1,7 @@ use super::Value; use crate::{ execution::Agent, - heap::{GetHeapData, Handle, StringHeapData}, + heap::{indexes::StringIndex, GetHeapData}, SmallString, }; @@ -9,12 +9,12 @@ use crate::{ /// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type #[derive(Debug, Clone, Copy)] pub enum String { - String(Handle), + String(StringIndex), SmallString(SmallString), } -impl From> for String { - fn from(value: Handle) -> Self { +impl From for String { + fn from(value: StringIndex) -> Self { String::String(value) } } @@ -52,17 +52,6 @@ impl From for Value { } } -impl TryFrom for String { - type Error = (); - fn try_from(value: Value) -> Result { - if matches!(value, Value::String(_) | Value::SmallString(_)) { - Ok(String(value)) - } else { - Err(()) - } - } -} - impl String { pub fn into_value(self) -> Value { self.into() From 115e75a0f3b4772eab1d1ca6b03ca48c4f93414e Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 17 Sep 2023 22:01:01 +0300 Subject: [PATCH 21/32] Fix build after rebase --- nova_vm/src/builtins/ordinary.rs | 15 +- nova_vm/src/execution/realm/intrinsics.rs | 87 +++++++--- nova_vm/src/heap.rs | 42 ++--- nova_vm/src/heap/array.rs | 2 +- nova_vm/src/heap/bigint.rs | 4 +- nova_vm/src/heap/date.rs | 2 +- nova_vm/src/heap/element_array.rs | 151 +++++++++++++----- nova_vm/src/heap/error.rs | 2 +- nova_vm/src/heap/function.rs | 6 +- nova_vm/src/heap/heap_bits.rs | 12 +- nova_vm/src/heap/heap_gc.rs | 2 +- nova_vm/src/heap/indexes.rs | 35 ++-- nova_vm/src/heap/number.rs | 4 +- nova_vm/src/heap/object.rs | 29 ++-- nova_vm/src/heap/regexp.rs | 2 +- nova_vm/src/heap/string.rs | 4 +- nova_vm/src/heap/symbol.rs | 2 +- nova_vm/src/small_integer.rs | 2 +- nova_vm/src/small_string.rs | 4 +- nova_vm/src/types/language/number.rs | 2 +- nova_vm/src/types/language/object.rs | 8 +- .../types/language/object/property_storage.rs | 20 ++- nova_vm/src/types/language/value.rs | 2 +- wasm/src/decoder/util.rs | 2 +- 24 files changed, 279 insertions(+), 162 deletions(-) diff --git a/nova_vm/src/builtins/ordinary.rs b/nova_vm/src/builtins/ordinary.rs index 2a6c611f..2f88b282 100644 --- a/nova_vm/src/builtins/ordinary.rs +++ b/nova_vm/src/builtins/ordinary.rs @@ -3,8 +3,6 @@ use crate::{ types::{InternalMethods, Object, PropertyDescriptor, PropertyKey, Value}, }; -use super::ArgumentsList; - /// 10.1 Ordinary Object Internal Methods and Internal Slots /// https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots pub static METHODS: InternalMethods = InternalMethods { @@ -37,7 +35,7 @@ fn get_prototype_of(agent: &mut Agent, object: Object) -> Option { pub fn ordinary_get_prototype_of(agent: &mut Agent, object: Object) -> Option { // 1. Return O.[[Prototype]]. // TODO: This is wrong. - Some(Object::new(object.prototype(agent).unwrap())) + Some(Object::try_from(object.prototype(agent).unwrap()).unwrap()) } /// 10.1.2 [[SetPrototypeOf]] ( V ) @@ -105,7 +103,8 @@ pub fn ordinary_set_prototype_of( // ii. Else, set p to p.[[Prototype]]. // TODO: This is wrong - parent_prototype_outer = Some(Object::new(parent_prototype.prototype(agent).unwrap())); + parent_prototype_outer = + Some(Object::try_from(parent_prototype.prototype(agent).unwrap()).unwrap()); } // 8. Set O.[[Prototype]] to V. @@ -686,12 +685,8 @@ pub fn ordinary_set_with_own_descriptor( }; // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). - return Ok((receiver.internal_methods(agent).define_own_property)( - agent, - receiver, - property_key, - value_descriptor, - )); + let define_own_property = receiver.internal_methods(agent).define_own_property; + return define_own_property(agent, receiver, property_key, value_descriptor); } // e. Else, else { diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index a6856798..a259dc24 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -1,6 +1,9 @@ use crate::{ - heap::BuiltinObjectIndexes, - types::{Object, Value}, + heap::{ + indexes::{FunctionIndex, ObjectIndex}, + BuiltinObjectIndexes, + }, + types::Object, }; // TODO: We should probably consider lazily loading intrinsics. This would @@ -12,42 +15,58 @@ pub struct Intrinsics; impl Intrinsics { /// %Array% pub const fn array() -> Object { - Object::Function(BuiltinObjectIndexes::ArrayConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::ArrayConstructorIndex as u32, + )) } /// %Array.prototype% pub const fn array_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::ArrayPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::ArrayPrototypeIndex as u32, + )) } /// %BigInt% pub const fn big_int() -> Object { - Object::Function(BuiltinObjectIndexes::BigintConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::BigintConstructorIndex as u32, + )) } /// %BigInt.prototype% pub const fn big_int_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::BigintPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::BigintPrototypeIndex as u32, + )) } /// %Boolean% pub const fn boolean() -> Object { - Object::Function(BuiltinObjectIndexes::BooleanConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::BooleanConstructorIndex as u32, + )) } /// %Boolean.prototype% pub const fn boolean_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::BooleanPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::BooleanPrototypeIndex as u32, + )) } /// %Error% pub const fn error() -> Object { - Object::Function(BuiltinObjectIndexes::ErrorConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::ErrorConstructorIndex as u32, + )) } /// %Error.prototype% pub const fn error_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::ErrorPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::ErrorPrototypeIndex as u32, + )) } /// %eval% @@ -57,7 +76,9 @@ impl Intrinsics { /// %EvalError% pub const fn eval_error() -> Object { - Object::Function(BuiltinObjectIndexes::ArrayConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::ArrayConstructorIndex as u32, + )) } /// %EvalError.prototype% @@ -67,7 +88,9 @@ impl Intrinsics { /// %Function% pub const fn function() -> Object { - Object::Function(BuiltinObjectIndexes::FunctionConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::FunctionConstructorIndex as u32, + )) } /// %Function.prototype% @@ -75,7 +98,9 @@ impl Intrinsics { // Note: This is not spec-compliant. Function prototype should // be a function that always returns undefined no matter how // it is called. That's stupid so we do not have that. - Object::Object(BuiltinObjectIndexes::FunctionPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::FunctionPrototypeIndex as u32, + )) } /// %isFinite% @@ -90,27 +115,37 @@ impl Intrinsics { /// %Math% pub const fn math() -> Object { - Object::Object(BuiltinObjectIndexes::MathObjectIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::MathObjectIndex as u32, + )) } /// %Number% pub const fn number() -> Object { - Object::Function(BuiltinObjectIndexes::NumberConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::NumberConstructorIndex as u32, + )) } /// %Number.prototype% pub const fn number_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::NumberPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::NumberPrototypeIndex as u32, + )) } /// %Object% pub const fn object() -> Object { - Object::Function(BuiltinObjectIndexes::ObjectConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::ObjectConstructorIndex as u32, + )) } /// %Object.prototype% pub const fn object_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::ObjectPrototypeIndex as u32, + )) } /// %Object.prototype.toString% @@ -145,22 +180,30 @@ impl Intrinsics { /// %String% pub const fn string() -> Object { - Object::Function(BuiltinObjectIndexes::StringConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::StringConstructorIndex as u32, + )) } /// %String.prototype% pub const fn string_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::StringPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::StringPrototypeIndex as u32, + )) } /// %Symbol% pub const fn symbol() -> Object { - Object::Function(BuiltinObjectIndexes::SymbolConstructorIndex.into()) + Object::Function(FunctionIndex::from_u32_index( + BuiltinObjectIndexes::SymbolConstructorIndex as u32, + )) } /// %Symbol.prototype% pub const fn symbol_prototype() -> Object { - Object::Object(BuiltinObjectIndexes::SymbolPrototypeIndex.into()) + Object::Object(ObjectIndex::from_u32_index( + BuiltinObjectIndexes::SymbolPrototypeIndex as u32, + )) } /// %SyntaxError% diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index db41e382..6d050b55 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -8,7 +8,7 @@ mod function; mod heap_bits; mod heap_constants; mod heap_gc; -pub(crate) mod indexes; +pub mod indexes; mod math; mod number; mod object; @@ -16,7 +16,7 @@ mod regexp; mod string; mod symbol; -pub(crate) use self::heap_constants::BuiltinObjectIndexes; +pub use self::heap_constants::BuiltinObjectIndexes; use self::{ array::{initialize_array_heap, ArrayHeapData}, @@ -51,18 +51,18 @@ 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>, - pub(crate) functions: Vec>, - pub(crate) dates: Vec>, - pub(crate) globals: Vec, - pub(crate) numbers: Vec>, - pub(crate) objects: Vec>, - pub(crate) regexps: Vec>, - pub(crate) strings: Vec>, - pub(crate) symbols: Vec>, + pub elements: ElementArrays, + pub arrays: Vec>, + pub bigints: Vec>, + pub errors: Vec>, + pub functions: Vec>, + pub dates: Vec>, + pub globals: Vec, + pub numbers: Vec>, + pub objects: Vec>, + pub regexps: Vec>, + pub strings: Vec>, + pub symbols: Vec>, } pub trait CreateHeapData { @@ -214,7 +214,7 @@ impl Heap { heap } - pub(crate) fn alloc_string(&mut self, message: &str) -> StringIndex { + pub 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)) @@ -233,12 +233,12 @@ impl Heap { } } - pub(crate) fn alloc_number(&mut self, number: f64) -> NumberIndex { + pub fn alloc_number(&mut self, number: f64) -> NumberIndex { self.numbers.push(Some(NumberHeapData::new(number))); NumberIndex::last(&self.numbers) } - pub(crate) fn create_function( + pub fn create_function( &mut self, name: Value, length: u8, @@ -278,7 +278,7 @@ impl Heap { index } - pub(crate) fn create_object(&mut self, entries: Vec) -> ObjectIndex { + pub fn create_object(&mut self, entries: Vec) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(entries); let object_data = ObjectHeapData { extensible: true, @@ -290,7 +290,7 @@ impl Heap { ObjectIndex::last(&self.objects) } - pub(crate) fn create_null_object(&mut self, entries: Vec) -> ObjectIndex { + pub fn create_null_object(&mut self, entries: Vec) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(entries); let object_data = ObjectHeapData { extensible: true, @@ -302,7 +302,7 @@ impl Heap { ObjectIndex::last(&self.objects) } - pub(crate) fn create_object_with_prototype(&mut self, prototype: Value) -> ObjectIndex { + pub fn create_object_with_prototype(&mut self, prototype: Value) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(vec![]); let object_data = ObjectHeapData { extensible: true, @@ -314,7 +314,7 @@ impl Heap { ObjectIndex::last(&self.objects) } - pub(crate) fn insert_builtin_object( + pub fn insert_builtin_object( &mut self, index: BuiltinObjectIndexes, extensible: bool, diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index a23c38af..893690af 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -16,7 +16,7 @@ use super::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct ArrayHeapData { +pub struct ArrayHeapData { pub object_index: Option, // TODO: Use SmallVec<[Value; 4]> pub elements: ElementsVector, diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index 8348f767..c3532ca2 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -10,12 +10,12 @@ use crate::{ use num_bigint_dig::BigInt; #[derive(Debug, Clone)] -pub(crate) struct BigIntHeapData { +pub struct BigIntHeapData { pub(super) data: BigInt, } impl BigIntHeapData { - pub(crate) fn try_into_f64(&self) -> Option { + pub fn try_into_f64(&self) -> Option { None } } diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index b9143c3d..d8e16d4d 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -17,7 +17,7 @@ use super::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct DateHeapData { +pub struct DateHeapData { pub(super) object_index: ObjectIndex, pub(super) _date: SystemTime, } diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs index db0a3ad4..9dea667a 100644 --- a/nova_vm/src/heap/element_array.rs +++ b/nova_vm/src/heap/element_array.rs @@ -2,12 +2,12 @@ use super::{ indexes::{ElementIndex, FunctionIndex}, object::{ObjectEntry, PropertyDescriptor, PropertyKey}, }; -use crate::types::Value; +use crate::{types::Value, Heap}; use core::panic; use std::{collections::HashMap, mem::MaybeUninit, num::NonZeroU16, vec}; #[derive(Debug, Clone, Copy)] -pub(crate) enum ElementArrayKey { +pub enum ElementArrayKey { /// up to 16 elements E4, /// up to 64 elements @@ -51,10 +51,10 @@ impl From for ElementArrayKey { } #[derive(Debug, Clone, Copy)] -pub(crate) struct ElementsVector { - pub(crate) elements_index: ElementIndex, - pub(crate) cap: ElementArrayKey, - pub(crate) len: u32, +pub struct ElementsVector { + pub elements_index: ElementIndex, + pub cap: ElementArrayKey, + pub len: u32, } #[derive(Debug, Clone, Copy)] @@ -386,9 +386,9 @@ impl ElementDescriptor { /// 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>, +pub struct ElementArray2Pow4 { + pub values: Vec; usize::pow(2, 4)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow4 { @@ -411,9 +411,9 @@ impl ElementArray2Pow4 { /// 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>, +pub struct ElementArray2Pow6 { + pub values: Vec; usize::pow(2, 6)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow6 { @@ -436,9 +436,9 @@ impl ElementArray2Pow6 { /// 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>, +pub struct ElementArray2Pow8 { + pub values: Vec; usize::pow(2, 8)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow8 { @@ -461,9 +461,9 @@ impl ElementArray2Pow8 { /// 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>, +pub struct ElementArray2Pow10 { + pub values: Vec; usize::pow(2, 10)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow10 { @@ -486,9 +486,9 @@ impl ElementArray2Pow10 { /// 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>, +pub struct ElementArray2Pow12 { + pub values: Vec; usize::pow(2, 12)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow12 { @@ -511,9 +511,9 @@ impl ElementArray2Pow12 { /// 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>, +pub struct ElementArray2Pow16 { + pub values: Vec; usize::pow(2, 16)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow16 { @@ -536,9 +536,9 @@ impl ElementArray2Pow16 { /// 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>, +pub struct ElementArray2Pow24 { + pub values: Vec; usize::pow(2, 24)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow24 { @@ -561,9 +561,9 @@ impl ElementArray2Pow24 { /// 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>, +pub struct ElementArray2Pow32 { + pub values: Vec; usize::pow(2, 32)]>>, + pub descriptors: HashMap>, } impl Default for ElementArray2Pow32 { @@ -585,23 +585,23 @@ impl ElementArray2Pow32 { } #[derive(Debug)] -pub(crate) struct ElementArrays { +pub struct ElementArrays { /// up to 16 elements - pub(crate) e2pow4: ElementArray2Pow4, + pub e2pow4: ElementArray2Pow4, /// up to 64 elements - pub(crate) e2pow6: ElementArray2Pow6, + pub e2pow6: ElementArray2Pow6, /// up to 256 elements - pub(crate) e2pow8: ElementArray2Pow8, + pub e2pow8: ElementArray2Pow8, /// up to 1024 elements - pub(crate) e2pow10: ElementArray2Pow10, + pub e2pow10: ElementArray2Pow10, /// up to 4096 elements - pub(crate) e2pow12: ElementArray2Pow12, + pub e2pow12: ElementArray2Pow12, /// up to 65536 elements - pub(crate) e2pow16: ElementArray2Pow16, + pub e2pow16: ElementArray2Pow16, /// up to 16777216 elements - pub(crate) e2pow24: ElementArray2Pow24, + pub e2pow24: ElementArray2Pow24, /// up to 4294967296 elements - pub(crate) e2pow32: ElementArray2Pow32, + pub e2pow32: ElementArray2Pow32, } impl ElementArrays { @@ -775,7 +775,7 @@ impl ElementArrays { } } - pub(crate) fn create_object_entries( + pub fn create_object_entries( &mut self, mut entries: Vec, ) -> (ElementsVector, ElementsVector) { @@ -823,7 +823,7 @@ impl ElementArrays { ) } - pub(crate) fn get<'a>(&'a self, vector: ElementsVector) -> () { + pub fn get<'a>(&'a self, vector: ElementsVector) -> () { // match vector.cap { // ElementArrayKey::E4 => &self // .e2pow4 @@ -883,4 +883,73 @@ impl ElementArrays { // .as_slice()[0..vector.len as usize], // _ => unreachable!(), } + + pub fn has(&self, vector: ElementsVector, element: Value) -> bool { + match vector.cap { + ElementArrayKey::E4 => self + .e2pow4 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E6 => self + .e2pow6 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E8 => self + .e2pow8 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E10 => self + .e2pow10 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E12 => self + .e2pow12 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E16 => self + .e2pow16 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E24 => self + .e2pow24 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + ElementArrayKey::E32 => self + .e2pow32 + .values + .get(vector.elements_index.into_index()) + .unwrap() + .unwrap() + .as_slice()[0..vector.len as usize] + .contains(&Some(element)), + } + } } diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index 136195a6..80678dbd 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -14,7 +14,7 @@ use super::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct ErrorHeapData { +pub struct ErrorHeapData { pub(super) object_index: ObjectIndex, // TODO: stack? name? } diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index b4315439..3c1eb39d 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -15,11 +15,11 @@ use super::{ }; #[derive(Debug, Clone)] -pub(crate) struct FunctionHeapData { +pub struct FunctionHeapData { pub(super) object_index: Option, pub(super) length: u8, - pub(crate) initial_name: Value, - // pub(crate) behaviour: Behaviour, + pub initial_name: Value, + // pub behaviour: Behaviour, // TODO: Should we create a `BoundFunctionHeapData` for an exotic object // that allows setting fields and other deoptimizations? // pub(super) uses_arguments: bool, diff --git a/nova_vm/src/heap/heap_bits.rs b/nova_vm/src/heap/heap_bits.rs index 0b0f3403..8cca9ec2 100644 --- a/nova_vm/src/heap/heap_bits.rs +++ b/nova_vm/src/heap/heap_bits.rs @@ -10,7 +10,7 @@ use super::{ Heap, }; -pub(crate) struct HeapBits { +pub struct HeapBits { pub e_2_4: Box<[AtomicBool]>, pub e_2_6: Box<[AtomicBool]>, pub e_2_8: Box<[AtomicBool]>, @@ -31,7 +31,7 @@ pub(crate) struct HeapBits { pub symbols: Box<[AtomicBool]>, } -pub(crate) struct WorkQueues { +pub struct WorkQueues { pub e_2_4: Vec, pub e_2_6: Vec, pub e_2_8: Vec, @@ -53,7 +53,7 @@ pub(crate) struct WorkQueues { } impl HeapBits { - pub(crate) fn new(heap: &Heap) -> Self { + pub 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(), @@ -78,7 +78,7 @@ impl HeapBits { } impl WorkQueues { - pub(crate) fn new(heap: &Heap) -> Self { + pub 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), @@ -101,7 +101,7 @@ impl WorkQueues { } } - pub(crate) fn push_value(&mut self, value: Value) { + pub fn push_value(&mut self, value: Value) { match value { Value::Array(idx) => self.arrays.push(idx), // Value::BigIntObject(_) => todo!(), @@ -128,7 +128,7 @@ impl WorkQueues { } } - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { self.e_2_4.is_empty() && self.e_2_6.is_empty() && self.e_2_8.is_empty() diff --git a/nova_vm/src/heap/heap_gc.rs b/nova_vm/src/heap/heap_gc.rs index 3502e741..d9cbb617 100644 --- a/nova_vm/src/heap/heap_gc.rs +++ b/nova_vm/src/heap/heap_gc.rs @@ -12,7 +12,7 @@ use super::{ ElementsVector, Heap, }; -pub(crate) fn heap_gc(heap: &mut Heap) { +pub fn heap_gc(heap: &mut Heap) { let bits = HeapBits::new(heap); let mut queues = WorkQueues::new(heap); diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index 90ebe47e..7af7a5a3 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -1,4 +1,5 @@ use crate::types::Value; +use crate::Heap; use super::{ array::ArrayHeapData, bigint::BigIntHeapData, date::DateHeapData, error::ErrorHeapData, @@ -15,7 +16,7 @@ use std::{marker::PhantomData, mem::size_of, num::NonZeroU32}; /// /// This index implies a tracing reference count from this /// struct to T at the given index. -pub(crate) struct BaseIndex(NonZeroU32, PhantomData); +pub struct BaseIndex(NonZeroU32, PhantomData); const _INDEX_SIZE_IS_U32: () = assert!(size_of::>() == size_of::()); const _OPTION_INDEX_SIZE_IS_U32: () = @@ -127,14 +128,24 @@ impl BaseIndex { } } -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]>; +pub type ArrayIndex = BaseIndex; +pub type BigIntIndex = BaseIndex; +pub type DateIndex = BaseIndex; +pub type ErrorIndex = BaseIndex; +pub type FunctionIndex = BaseIndex; +pub type NumberIndex = BaseIndex; +pub type ObjectIndex = BaseIndex; +pub type RegExpIndex = BaseIndex; +pub type StringIndex = BaseIndex; +pub type SymbolIndex = BaseIndex; +pub type ElementIndex = BaseIndex<[Option]>; + +impl ObjectIndex { + pub fn get<'a>(self, heap: &'a Heap) -> &'a ObjectHeapData { + heap.objects + .get(self.into_index()) + .unwrap() + .as_ref() + .unwrap() + } +} diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index 4b65067f..24ebd108 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -12,7 +12,7 @@ use crate::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct NumberHeapData { +pub struct NumberHeapData { pub(super) data: f64, } @@ -21,7 +21,7 @@ impl NumberHeapData { NumberHeapData { data } } - pub(crate) fn value(&self) -> f64 { + pub fn value(&self) -> f64 { self.data } } diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 9ed7ce95..5aeb79f6 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -18,16 +18,16 @@ use super::{ #[derive(Debug)] pub struct ObjectEntry { - pub(crate) key: PropertyKey, - pub(crate) value: PropertyDescriptor, + pub key: PropertyKey, + pub value: PropertyDescriptor, } impl ObjectEntry { - pub(crate) fn new(key: PropertyKey, value: PropertyDescriptor) -> Self { + pub fn new(key: PropertyKey, value: PropertyDescriptor) -> Self { ObjectEntry { key, value } } - pub(crate) fn new_prototype_function_entry( + pub fn new_prototype_function_entry( heap: &mut Heap, name: &str, length: u8, @@ -46,7 +46,7 @@ impl ObjectEntry { ObjectEntry { key, value } } - pub(crate) fn new_prototype_symbol_function_entry( + pub fn new_prototype_symbol_function_entry( heap: &mut Heap, name: &str, symbol_index: SymbolIndex, @@ -61,7 +61,7 @@ impl ObjectEntry { ObjectEntry { key, value } } - pub(crate) fn new_constructor_prototype_entry(heap: &mut Heap, idx: ObjectIndex) -> Self { + pub fn new_constructor_prototype_entry(heap: &mut Heap, idx: ObjectIndex) -> Self { ObjectEntry { key: PropertyKey::from_str(heap, "prototype"), value: PropertyDescriptor::Data { @@ -73,7 +73,7 @@ impl ObjectEntry { } } - pub(crate) fn new_frozen_entry(heap: &mut Heap, key: &str, value: Value) -> Self { + pub fn new_frozen_entry(heap: &mut Heap, key: &str, value: Value) -> Self { ObjectEntry { key: PropertyKey::from_str(heap, key), value: PropertyDescriptor::roh(value), @@ -226,17 +226,17 @@ impl PropertyDescriptor { } #[derive(Debug, Clone, Copy)] -pub(crate) struct ObjectHeapData { - pub(crate) extensible: bool, +pub struct ObjectHeapData { + pub extensible: bool, // TODO: It's probably not necessary to have a whole Value here. // A prototype can only be set to be null or an object, meaning that most of the // possible Value options are impossible. // 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: Value, - pub(crate) keys: ElementsVector, - pub(crate) values: ElementsVector, + pub prototype: Value, + pub keys: ElementsVector, + pub values: ElementsVector, } impl ObjectHeapData { @@ -260,6 +260,11 @@ impl ObjectHeapData { values, } } + + pub fn has(&self, heap: &Heap, key: Value) -> bool { + debug_assert!(key.is_string() || key.is_number() || key.is_symbol()); + heap.elements.has(self.keys, key) + } } pub fn initialize_object_heap(heap: &mut Heap) { diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index 0313f1b4..964a0561 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -15,7 +15,7 @@ use super::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct RegExpHeapData { +pub struct RegExpHeapData { pub(super) object_index: ObjectIndex, // pub(super) _regex: RegExp, } diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index e780aee9..2cd4da64 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -9,8 +9,8 @@ use crate::{ use wtf8::Wtf8Buf; #[derive(Debug, Clone)] -pub(crate) struct StringHeapData { - pub(crate) data: Wtf8Buf, +pub struct StringHeapData { + pub data: Wtf8Buf, } impl StringHeapData { diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index 09a93126..1de497b6 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -13,7 +13,7 @@ use super::{ }; #[derive(Debug, Clone, Copy)] -pub(crate) struct SymbolHeapData { +pub struct SymbolHeapData { pub(super) descriptor: Option, } diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs index cd2f9063..51faa1c1 100644 --- a/nova_vm/src/small_integer.rs +++ b/nova_vm/src/small_integer.rs @@ -22,7 +22,7 @@ impl SmallInteger { self.into() } - pub(crate) fn from_i64_unchecked(value: i64) -> SmallInteger { + pub fn from_i64_unchecked(value: i64) -> SmallInteger { debug_assert!(value >= Self::MIN_NUMBER && value <= Self::MAX_NUMBER); let bytes = i64::to_ne_bytes(value); diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index d82e5991..36c4a421 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub struct SmallString { bytes: [u8; 7], } @@ -41,7 +41,7 @@ impl SmallString { matches!(self.bytes, [0, 0, 0, 0, 0, 0, 0]) } - pub(crate) fn from_str_unchecked(string: &str) -> Self { + pub fn from_str_unchecked(string: &str) -> Self { let string_bytes = string.as_bytes(); // We have only 7 bytes to work with, and we cannot tell apart diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 5f941a82..2a2a8011 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -60,7 +60,7 @@ impl TryFrom for Number { } impl Number { - pub(crate) fn new(value: Value) -> Self { + pub fn new(value: Value) -> Self { debug_assert!(matches!( value, Value::Number(_) | Value::Integer(_) | Value::Float(_) diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 243ac5e7..5c153f45 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -194,11 +194,7 @@ impl Object { }; // 2. Return ? O.[[DefineOwnProperty]](P, newDesc). - Ok(self.internal_methods(agent).define_own_property)( - agent, - self, - property_key, - new_descriptor, - ) + let define_own_property = self.internal_methods(agent).define_own_property; + define_own_property(agent, self, property_key, new_descriptor) } } diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs index 7033dafb..d9686cf8 100644 --- a/nova_vm/src/types/language/object/property_storage.rs +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -15,7 +15,7 @@ use super::{Object, PropertyKey}; pub struct PropertyStorage(Object); impl PropertyStorage { - pub(crate) fn new(object: Object) -> Self { + pub fn new(object: Object) -> Self { Self(object) } @@ -55,20 +55,18 @@ impl PropertyStorage { let realm = realm.borrow(); let array = realm.heap.get(array); - if let Value::Integer(number) = key.into_value() { - if let Some(_) = TryInto::::try_into(number.into_i64()) - .map(|idx| array.elements.get(idx)) - .ok() - { - return true; - } + if key.is_array_index() { + return realm.heap.elements.has(array.elements, key.into_value()); } if let Some(object) = array.object_index { - return object.property_storage().has(agent, key); + realm + .heap + .elements + .has(object.get(&realm.heap).keys, key.into_value()) + } else { + false } - - false } Value::Function(_) => todo!(), _ => unreachable!(), diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index dfa1c700..0546eb86 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -13,7 +13,7 @@ use super::{BigInt, Number}; /// 6.1 ECMAScript Language Types /// https://tc39.es/ecma262/#sec-ecmascript-language-types -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Value { /// 6.1.1 The Undefined Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-undefined-type diff --git a/wasm/src/decoder/util.rs b/wasm/src/decoder/util.rs index 97a46718..41246025 100644 --- a/wasm/src/decoder/util.rs +++ b/wasm/src/decoder/util.rs @@ -2,7 +2,7 @@ use super::common; use crate::error::Error; use crate::error::Result; use crate::varint::decode_u32; -pub(crate) fn decode_vec(reader: &mut R, func: F) -> Result> +pub fn decode_vec(reader: &mut R, func: F) -> Result> where R: std::io::Read, F: Fn(&mut R) -> Result, From 08a81ba98288e8d8dc2431f1e33b4f7526cfb377 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 24 Sep 2023 18:48:42 +0300 Subject: [PATCH 22/32] Sub-enums, stuff --- nova_vm/src/builtins/builtin_function.rs | 4 +- .../environments/function_environment.rs | 20 +++++----- nova_vm/src/heap.rs | 16 +++++--- nova_vm/src/heap/array.rs | 10 +++-- nova_vm/src/heap/bigint.rs | 10 +++-- nova_vm/src/heap/boolean.rs | 10 +++-- nova_vm/src/heap/date.rs | 10 +++-- nova_vm/src/heap/error.rs | 10 +++-- nova_vm/src/heap/function.rs | 10 +++-- nova_vm/src/heap/number.rs | 10 +++-- nova_vm/src/heap/object.rs | 22 +++++----- nova_vm/src/heap/regexp.rs | 10 +++-- nova_vm/src/heap/string.rs | 10 +++-- nova_vm/src/heap/symbol.rs | 10 +++-- nova_vm/src/small_integer.rs | 6 +++ nova_vm/src/small_string.rs | 6 +++ nova_vm/src/types/language/bigint.rs | 9 ++++- nova_vm/src/types/language/number.rs | 40 ++++++++++++------- nova_vm/src/types/language/object.rs | 36 +++++++++++------ nova_vm/src/types/language/string.rs | 1 + nova_vm/src/types/language/value.rs | 40 ++++++++++++++++++- nova_vm/src/types/spec/property_descriptor.rs | 6 +-- 22 files changed, 213 insertions(+), 93 deletions(-) diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index 95e796dc..d486b41a 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -76,9 +76,7 @@ pub fn create_builtin_function<'a, 'b: 'a>( // 8. Set func.[[Realm]] to realm. // NOTE: Heap data is implicitly attached to the Realm so I don't think // this matters. - let object = realm - .heap - .create_object_with_prototype(prototype.into_value()); + let object = realm.heap.create_object_with_prototype(prototype); // 9. Set func.[[InitialName]] to null. // TODO: This is non-standard. diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs index 2347ffc3..6adeb323 100644 --- a/nova_vm/src/execution/environments/function_environment.rs +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -1,17 +1,19 @@ use super::{DeclarativeEnvironment, Environment}; -use crate::types::Value; -use std::{cell::RefCell, rc::Rc}; +use crate::{ + heap::indexes::FunctionIndex, + types::{Object, Value}, +}; #[derive(Debug)] pub enum ThisBindingStatus { + /// Function is an ArrowFunction and does not have a local `this` value. Lexical, + /// Function is a normal function and does not have a bound `this` value. Initialized, + /// Function is a normal function and has a bound `this` value. Uninitialized, } -#[derive(Debug)] -struct ECMAScriptFunction; - /// 9.1.1.3 Function Environment Records /// https://tc39.es/ecma262/#sec-function-environment-records #[derive(Debug)] @@ -23,15 +25,13 @@ pub struct FunctionEnvironment { this_binding_status: ThisBindingStatus, /// [[FunctionObject]] - function_object: ECMAScriptFunction, + function_object: FunctionIndex, /// [[NewTarget]] - new_target: Option, + new_target: Option, /// [[OuterEnv]] outer_env: Option, - // NOTE: This is how we implement the spec's inheritance of function - // environments. - declarative_environment: Rc>, + declarative_environment: DeclarativeEnvironment, } diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 6d050b55..6aa4f228 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -261,7 +261,9 @@ impl Heap { extensible: true, keys, values, - prototype: Value::Object(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + prototype: Some(Object::Object( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), }; self.objects.push(Some(func_object_data)); let func_data = FunctionHeapData { @@ -284,7 +286,9 @@ impl Heap { extensible: true, keys, values, - prototype: Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + prototype: Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), }; self.objects.push(Some(object_data)); ObjectIndex::last(&self.objects) @@ -296,19 +300,19 @@ impl Heap { extensible: true, keys, values, - prototype: Value::Null, + prototype: None, }; self.objects.push(Some(object_data)); ObjectIndex::last(&self.objects) } - pub fn create_object_with_prototype(&mut self, prototype: Value) -> ObjectIndex { + pub fn create_object_with_prototype(&mut self, prototype: Object) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(vec![]); let object_data = ObjectHeapData { extensible: true, keys, values, - prototype, + prototype: Some(prototype), }; self.objects.push(Some(object_data)); ObjectIndex::last(&self.objects) @@ -318,7 +322,7 @@ impl Heap { &mut self, index: BuiltinObjectIndexes, extensible: bool, - prototype: Value, + prototype: Option, entries: Vec, ) -> ObjectIndex { let (keys, values) = self.elements.create_object_entries(entries); diff --git a/nova_vm/src/heap/array.rs b/nova_vm/src/heap/array.rs index 893690af..2a789c1e 100644 --- a/nova_vm/src/heap/array.rs +++ b/nova_vm/src/heap/array.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -60,7 +60,9 @@ pub fn initialize_array_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ArrayConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -166,7 +168,9 @@ pub fn initialize_array_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ArrayPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/bigint.rs b/nova_vm/src/heap/bigint.rs index c3532ca2..2f3ca62c 100644 --- a/nova_vm/src/heap/bigint.rs +++ b/nova_vm/src/heap/bigint.rs @@ -5,7 +5,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, ObjectEntry, PropertyDescriptor, PropertyKey, }, - types::Value, + types::{Object, Value}, }; use num_bigint_dig::BigInt; @@ -32,7 +32,9 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::BigintConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -61,7 +63,9 @@ pub fn initialize_bigint_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::BigintPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/boolean.rs b/nova_vm/src/heap/boolean.rs index 34dfaceb..3ad74702 100644 --- a/nova_vm/src/heap/boolean.rs +++ b/nova_vm/src/heap/boolean.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -20,7 +20,9 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::BooleanConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -46,7 +48,9 @@ pub fn initialize_boolean_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::BooleanPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/date.rs b/nova_vm/src/heap/date.rs index d8e16d4d..eaff453b 100644 --- a/nova_vm/src/heap/date.rs +++ b/nova_vm/src/heap/date.rs @@ -6,7 +6,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -35,7 +35,9 @@ pub fn initialize_date_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::DateConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -108,7 +110,9 @@ pub fn initialize_date_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::DatePrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/error.rs b/nova_vm/src/heap/error.rs index 80678dbd..e16dec45 100644 --- a/nova_vm/src/heap/error.rs +++ b/nova_vm/src/heap/error.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -27,7 +27,9 @@ pub fn initialize_error_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ErrorConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -60,7 +62,9 @@ pub fn initialize_error_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ErrorPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 3c1eb39d..66cc220c 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -5,7 +5,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -36,7 +36,9 @@ pub fn initialize_function_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::FunctionConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -74,7 +76,9 @@ pub fn initialize_function_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::FunctionPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/number.rs b/nova_vm/src/heap/number.rs index 24ebd108..7afe6553 100644 --- a/nova_vm/src/heap/number.rs +++ b/nova_vm/src/heap/number.rs @@ -8,7 +8,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; #[derive(Debug, Clone, Copy)] @@ -74,7 +74,9 @@ pub fn initialize_number_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::NumberConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -104,7 +106,9 @@ pub fn initialize_number_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::NumberPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/object.rs b/nova_vm/src/heap/object.rs index 5aeb79f6..95043f27 100644 --- a/nova_vm/src/heap/object.rs +++ b/nova_vm/src/heap/object.rs @@ -6,7 +6,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, }, - types::Value, + types::{Object, Value}, SmallString, }; use std::{fmt::Debug, vec}; @@ -228,13 +228,7 @@ impl PropertyDescriptor { #[derive(Debug, Clone, Copy)] pub struct ObjectHeapData { pub extensible: bool, - // TODO: It's probably not necessary to have a whole Value here. - // A prototype can only be set to be null or an object, meaning that most of the - // possible Value options are impossible. - // 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 prototype: Value, + pub prototype: Option, pub keys: ElementsVector, pub values: ElementsVector, } @@ -246,6 +240,12 @@ impl ObjectHeapData { keys: ElementsVector, values: ElementsVector, ) -> Self { + let prototype = if prototype.is_null() { + None + } else { + // TODO: Throw error. + Some(Object::try_from(prototype).unwrap()) + }; Self { extensible, // TODO: Number, Boolean, etc. objects exist. These can all be @@ -299,7 +299,9 @@ pub fn initialize_object_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ObjectConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -329,7 +331,7 @@ pub fn initialize_object_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::ObjectConstructorIndex, true, - Value::Null, + None, entries, ); } diff --git a/nova_vm/src/heap/regexp.rs b/nova_vm/src/heap/regexp.rs index 964a0561..d8ba79b1 100644 --- a/nova_vm/src/heap/regexp.rs +++ b/nova_vm/src/heap/regexp.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -39,7 +39,9 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::RegExpConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -103,7 +105,9 @@ pub fn initialize_regexp_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::RegExpPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/heap/string.rs b/nova_vm/src/heap/string.rs index 2cd4da64..58889d68 100644 --- a/nova_vm/src/heap/string.rs +++ b/nova_vm/src/heap/string.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes}, FunctionHeapData, Heap, }, - types::Value, + types::{Object, Value}, }; use wtf8::Wtf8Buf; @@ -31,7 +31,9 @@ pub fn initialize_string_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::StringConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), // TODO: Methods and properties Vec::with_capacity(0), ); @@ -48,7 +50,9 @@ pub fn initialize_string_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::StringPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), // TODO: Methods and properties Vec::with_capacity(0), ); diff --git a/nova_vm/src/heap/symbol.rs b/nova_vm/src/heap/symbol.rs index 1de497b6..c6725921 100644 --- a/nova_vm/src/heap/symbol.rs +++ b/nova_vm/src/heap/symbol.rs @@ -4,7 +4,7 @@ use crate::{ heap_constants::{get_constructor_index, BuiltinObjectIndexes, WellKnownSymbolIndexes}, FunctionHeapData, Heap, PropertyDescriptor, }, - types::Value, + types::{Object, Value}, }; use super::{ @@ -136,7 +136,9 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::SymbolConstructorIndex, true, - Value::Function(BuiltinObjectIndexes::FunctionPrototypeIndex.into()), + Some(Object::Function( + BuiltinObjectIndexes::FunctionPrototypeIndex.into(), + )), entries, ); heap.functions @@ -182,7 +184,9 @@ pub fn initialize_symbol_heap(heap: &mut Heap) { heap.insert_builtin_object( BuiltinObjectIndexes::SymbolPrototypeIndex, true, - Value::Object(BuiltinObjectIndexes::ObjectPrototypeIndex.into()), + Some(Object::Object( + BuiltinObjectIndexes::ObjectPrototypeIndex.into(), + )), entries, ); } diff --git a/nova_vm/src/small_integer.rs b/nova_vm/src/small_integer.rs index 51faa1c1..a36c5712 100644 --- a/nova_vm/src/small_integer.rs +++ b/nova_vm/src/small_integer.rs @@ -22,6 +22,12 @@ impl SmallInteger { self.into() } + pub const fn zero() -> SmallInteger { + Self { + data: [0, 0, 0, 0, 0, 0, 0], + } + } + pub fn from_i64_unchecked(value: i64) -> SmallInteger { debug_assert!(value >= Self::MIN_NUMBER && value <= Self::MAX_NUMBER); let bytes = i64::to_ne_bytes(value); diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index 36c4a421..835ab62e 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -41,6 +41,12 @@ impl SmallString { matches!(self.bytes, [0, 0, 0, 0, 0, 0, 0]) } + pub const fn new_empty() -> Self { + Self { + bytes: [0, 0, 0, 0, 0, 0, 0], + } + } + pub fn from_str_unchecked(string: &str) -> Self { let string_bytes = string.as_bytes(); diff --git a/nova_vm/src/types/language/bigint.rs b/nova_vm/src/types/language/bigint.rs index 9e8efb01..3293dc72 100644 --- a/nova_vm/src/types/language/bigint.rs +++ b/nova_vm/src/types/language/bigint.rs @@ -1,4 +1,9 @@ -use super::Value; +use super::value::{BIGINT_DISCRIMINANT, SMALL_BIGINT_DISCRIMINANT}; +use crate::{heap::indexes::BigIntIndex, SmallInteger}; #[derive(Clone, Copy)] -pub struct BigInt(Value); +#[repr(u8)] +pub enum BigInt { + BigInt(BigIntIndex) = BIGINT_DISCRIMINANT, + SmallBigInt(SmallInteger) = SMALL_BIGINT_DISCRIMINANT, +} diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 2a2a8011..8afb61b0 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -1,32 +1,39 @@ -use super::Value; +use super::{ + value::{FLOAT_DISCRIMINANT, INTEGER_DISCRIMINANT, NUMBER_DISCRIMINANT}, + Value, +}; use crate::{ execution::{Agent, JsResult}, - heap::{CreateHeapData, GetHeapData}, + heap::{indexes::NumberIndex, CreateHeapData, GetHeapData}, SmallInteger, }; /// 6.1.6.1 The Number Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-number-type #[derive(Clone, Copy)] -pub struct Number(Value); +#[repr(u8)] +pub enum Number { + Number(NumberIndex) = NUMBER_DISCRIMINANT, + // 56-bit signed integer. + Integer(SmallInteger) = INTEGER_DISCRIMINANT, + Float(f32) = FLOAT_DISCRIMINANT, +} impl std::fmt::Debug for Number { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.0) + write!(f, "{:?}", self) } } impl From for Number { fn from(value: SmallInteger) -> Self { - Number(Value::Integer(value)) + Number::Integer(value) } } impl From for Number { fn from(value: i32) -> Self { - Number(Value::Integer(SmallInteger::from_i64_unchecked( - value as i64, - ))) + Number::Integer(SmallInteger::from_i64_unchecked(value as i64)) } } @@ -35,13 +42,13 @@ impl From for Number { let n = value .min(SmallInteger::MAX_NUMBER) .max(SmallInteger::MIN_NUMBER); - Number(Value::Integer(SmallInteger::from_i64_unchecked(n))) + Number::Integer(SmallInteger::from_i64_unchecked(n)) } } impl From for Number { fn from(value: f32) -> Self { - Number(Value::Float(value)) + Number::Float(value) } } @@ -52,7 +59,8 @@ impl TryFrom for Number { value, Value::Number(_) | Value::Integer(_) | Value::Float(_) ) { - Ok(Number(value)) + // SAFETY: Sub-enum. + Ok(unsafe { std::mem::transmute::(value) }) } else { Err(()) } @@ -65,7 +73,8 @@ impl Number { value, Value::Number(_) | Value::Integer(_) | Value::Float(_) )); - Self(value) + // SAFETY: Sub-enum. + unsafe { std::mem::transmute::(value) } } pub fn nan() -> Self { @@ -89,7 +98,8 @@ impl Number { } pub fn into_value(self) -> Value { - self.0 + // SAFETY: Sub-enum. + unsafe { std::mem::transmute::(self) } } pub fn is_nan(self, agent: &mut Agent) -> bool { @@ -269,9 +279,9 @@ impl Number { } Value::Integer(n) => { let n = n.into_i64(); - Number(Value::Integer(SmallInteger::from_i64_unchecked(n.abs()))) + Number::Integer(SmallInteger::from_i64_unchecked(n.abs())) } - Value::Float(n) => Number(Value::Float(n.abs())), + Value::Float(n) => Number::Float(n.abs()), _ => unreachable!(), } } diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 5c153f45..dd1d16be 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -3,12 +3,18 @@ mod internal_methods; mod property_key; mod property_storage; -use super::Value; +use super::{ + value::{ + ARRAY_DISCRIMINANT, DATE_DISCRIMINANT, ERROR_DISCRIMINANT, FUNCTION_DISCRIMINANT, + OBJECT_DISCRIMINANT, REGEXP_DISCRIMINANT, + }, + Value, +}; use crate::{ builtins::ordinary, execution::{agent::ExceptionType, Agent, Intrinsics, JsResult}, heap::{ - indexes::{ArrayIndex, FunctionIndex, ObjectIndex}, + indexes::{ArrayIndex, DateIndex, ErrorIndex, FunctionIndex, ObjectIndex, RegExpIndex}, GetHeapData, }, types::PropertyDescriptor, @@ -21,11 +27,17 @@ pub use property_storage::PropertyStorage; /// 6.1.7 The Object Type /// https://tc39.es/ecma262/#sec-object-type +/// +/// In Nova #[derive(Debug, Clone, Copy)] +#[repr(u8)] pub enum Object { - Object(ObjectIndex), - Array(ArrayIndex), - Function(FunctionIndex), + Object(ObjectIndex) = OBJECT_DISCRIMINANT, + // Date(DateIndex) = DATE_DISCRIMINANT, + // Error(ErrorIndex) = ERROR_DISCRIMINANT, + Array(ArrayIndex) = ARRAY_DISCRIMINANT, + Function(FunctionIndex) = FUNCTION_DISCRIMINANT, + //RegExp(RegExpIndex) = REGEXP_DISCRIMINANT, } impl From for Object { @@ -48,11 +60,8 @@ impl From for Object { impl From for Value { fn from(value: Object) -> Self { - match value { - Object::Object(x) => Value::Object(x), - Object::Array(x) => Value::Array(x), - Object::Function(x) => Value::Function(x), - } + // SAFETY: Sub-enum. + unsafe { std::mem::transmute::(value) } } } @@ -106,13 +115,14 @@ impl Object { match self { Object::Object(object) => { let object = realm.heap.get(object); - object.prototype.try_into().ok() + object.prototype.map(|v| v.into_value()) } Object::Array(array) => { let array = realm.heap.get(array); if let Some(object_index) = array.object_index { - Some(realm.heap.get(object_index).prototype) + let prototype = realm.heap.get(object_index).prototype; + prototype.map(|v| v.into()) } else { Some(Intrinsics::array_prototype().into_value()) } @@ -129,7 +139,7 @@ impl Object { let realm = agent.current_realm(); let mut realm = realm.borrow_mut(); let object = realm.heap.get_mut(object); - object.prototype = prototype.map(|object| object.into_value()).unwrap(); + object.prototype = prototype; } Object::Array(_) => todo!(), Object::Function(_) => todo!(), diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index b1a9741a..2d54fcc0 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -8,6 +8,7 @@ use crate::{ /// 6.1.4 The String Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type #[derive(Debug, Clone, Copy)] +#[repr(u8)] pub enum String { String(StringIndex), SmallString(SmallString), diff --git a/nova_vm/src/types/language/value.rs b/nova_vm/src/types/language/value.rs index 0546eb86..24c40a45 100644 --- a/nova_vm/src/types/language/value.rs +++ b/nova_vm/src/types/language/value.rs @@ -14,10 +14,11 @@ use super::{BigInt, Number}; /// 6.1 ECMAScript Language Types /// https://tc39.es/ecma262/#sec-ecmascript-language-types #[derive(Debug, Clone, Copy, PartialEq)] +#[repr(u8)] pub enum Value { /// 6.1.1 The Undefined Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-undefined-type - Undefined, + Undefined = 1, /// 6.1.2 The Null Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-null-type @@ -83,6 +84,43 @@ pub enum PreferredType { String, Number, } +const fn value_discriminant(value: Value) -> u8 { + // SAFETY: Because `Self` is marked `repr(u8)`, its layout is a `repr(C)` `union` + // between `repr(C)` structs, each of which has the `u8` discriminant as its first + // field, so we can read the discriminant without offsetting the pointer. + unsafe { *(&value as *const Value).cast::() } +} + +pub(crate) const UNDEFINED_DISCRIMINANT: u8 = value_discriminant(Value::Undefined); +pub(crate) const NULL_DISCRIMINANT: u8 = value_discriminant(Value::Null); +pub(crate) const BOOLEAN_DISCRIMINANT: u8 = value_discriminant(Value::Boolean(true)); +pub(crate) const STRING_DISCRIMINANT: u8 = + value_discriminant(Value::String(StringIndex::from_u32_index(0))); +pub(crate) const SMALL_STRING_DISCRIMINANT: u8 = + value_discriminant(Value::SmallString(SmallString::new_empty())); +pub(crate) const SYMBOL_DISCRIMINANT: u8 = + value_discriminant(Value::Symbol(SymbolIndex::from_u32_index(0))); +pub(crate) const NUMBER_DISCRIMINANT: u8 = + value_discriminant(Value::Number(NumberIndex::from_u32_index(0))); +pub(crate) const INTEGER_DISCRIMINANT: u8 = + value_discriminant(Value::Integer(SmallInteger::zero())); +pub(crate) const FLOAT_DISCRIMINANT: u8 = value_discriminant(Value::Float(0f32)); +pub(crate) const BIGINT_DISCRIMINANT: u8 = + value_discriminant(Value::BigInt(BigIntIndex::from_u32_index(0))); +pub(crate) const SMALL_BIGINT_DISCRIMINANT: u8 = + value_discriminant(Value::SmallBigInt(SmallInteger::zero())); +pub(crate) const OBJECT_DISCRIMINANT: u8 = + value_discriminant(Value::Object(ObjectIndex::from_u32_index(0))); +pub(crate) const ARRAY_DISCRIMINANT: u8 = + value_discriminant(Value::Array(ArrayIndex::from_u32_index(0))); +pub(crate) const DATE_DISCRIMINANT: u8 = + value_discriminant(Value::Date(DateIndex::from_u32_index(0))); +pub(crate) const ERROR_DISCRIMINANT: u8 = + value_discriminant(Value::Error(ErrorIndex::from_u32_index(0))); +pub(crate) const FUNCTION_DISCRIMINANT: u8 = + value_discriminant(Value::Function(FunctionIndex::from_u32_index(0))); +pub(crate) const REGEXP_DISCRIMINANT: u8 = + value_discriminant(Value::RegExp(RegExpIndex::from_u32_index(0))); impl Value { pub fn from_str(heap: &mut Heap, message: &str) -> Value { diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs index e20f16f8..f97dff1d 100644 --- a/nova_vm/src/types/spec/property_descriptor.rs +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -1,6 +1,6 @@ use crate::{ execution::{Agent, JsResult}, - types::{Object, Value}, + types::{Function, Object, Value}, }; /// 6.2.6 The Property Descriptor Specification Type @@ -14,10 +14,10 @@ pub struct PropertyDescriptor { pub writable: Option, /// [[Get]] - pub get: Option, + pub get: Option, /// [[Set]] - pub set: Option, + pub set: Option, /// [[Enumerable]] pub enumerable: Option, From ce879b25fe5bf92bf950de90ab735883b0e937f7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Mon, 2 Oct 2023 11:52:29 +0300 Subject: [PATCH 23/32] Work on Environments --- .../environments/declarative_environment.rs | 11 ++++++---- .../environments/function_environment.rs | 16 +++++++++++--- .../environments/global_environment.rs | 22 +++++++++++++------ .../environments/private_environment.rs | 11 +++++++++- nova_vm/src/execution/execution_context.rs | 6 ++++- nova_vm/src/heap.rs | 2 +- nova_vm/src/heap/element_array.rs | 1 + nova_vm/src/small_string.rs | 2 +- nova_vm/src/types/language/string.rs | 2 +- 9 files changed, 54 insertions(+), 19 deletions(-) diff --git a/nova_vm/src/execution/environments/declarative_environment.rs b/nova_vm/src/execution/environments/declarative_environment.rs index c1e75df0..1a33bea9 100644 --- a/nova_vm/src/execution/environments/declarative_environment.rs +++ b/nova_vm/src/execution/environments/declarative_environment.rs @@ -1,5 +1,5 @@ use super::Environment; -use crate::types::Value; +use crate::types::{String, Value}; use std::collections::HashMap; /// 9.1.1.1 Declarative Environment Records @@ -7,9 +7,12 @@ use std::collections::HashMap; #[derive(Debug)] pub struct DeclarativeEnvironment { pub outer_env: Option, - pub bindings: HashMap<&'static str, Binding>, + pub bindings: HashMap, } +// TODO: Consider splitting binding this into ValueBinding +// and BindingOptions. The options can fit a u8 and are fairly +// often not needed by algorithms. #[derive(Debug)] pub struct Binding { pub value: Option, @@ -21,9 +24,9 @@ pub struct Binding { impl DeclarativeEnvironment { /// 9.1.1.1.1 HasBinding ( N ) /// https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n - pub fn has_binding(self, name: &str) -> bool { + pub fn has_binding(self, name: String) -> bool { // 1. If envRec has a binding for N, return true. // 2. Return false. - return self.bindings.contains_key(name); + return self.bindings.contains_key(&name); } } diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs index 6adeb323..e02d2255 100644 --- a/nova_vm/src/execution/environments/function_environment.rs +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -1,7 +1,9 @@ -use super::{DeclarativeEnvironment, Environment}; +use std::collections::HashMap; + +use super::{declarative_environment::Binding, Environment}; use crate::{ heap::indexes::FunctionIndex, - types::{Object, Value}, + types::{Object, String, Value}, }; #[derive(Debug)] @@ -28,10 +30,18 @@ pub struct FunctionEnvironment { function_object: FunctionIndex, /// [[NewTarget]] + /// + /// If this FunctionEnvironment was created with a [[Construct]] internal method, + /// this is the value of the _newTarget_ parameter. Otherwise, its value is **undefined** + /// (implementation wise here None). new_target: Option, /// [[OuterEnv]] outer_env: Option, - declarative_environment: DeclarativeEnvironment, + /// Per https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy: + /// > A _Function Environment Record_ is a _Declarative Environment Record_ [...] + /// + /// The Declaration Environment Record is inlined here. + declarative_environment: HashMap, } diff --git a/nova_vm/src/execution/environments/global_environment.rs b/nova_vm/src/execution/environments/global_environment.rs index 4f1d07da..7a321010 100644 --- a/nova_vm/src/execution/environments/global_environment.rs +++ b/nova_vm/src/execution/environments/global_environment.rs @@ -1,23 +1,31 @@ +use std::collections::HashMap; + +use super::declarative_environment::Binding; use super::{DeclarativeEnvironment, Environment, ObjectEnvironment}; -use crate::types::Object; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use crate::heap::element_array::ElementsVector; +use crate::types::{Object, String}; /// 9.1.1.4 Global Environment Records /// https://tc39.es/ecma262/#sec-global-environment-records #[derive(Debug)] pub struct GlobalEnvironment { - // [[ObjectRecord]] - object_record: Rc>, + /// [[ObjectRecord]] + /// The Object Environment Record is inlined here. + object_record: Object, /// [[GlobalThisValue]] global_this_value: Object, /// [[DeclarativeRecord]] - declarative_record: Rc>, + /// The Declaration Environment Record is inlined here. + declarative_record: HashMap, /// [[VarNames]] - var_names: HashMap<&'static str, ()>, + var_names: ElementsVector, /// [[OuterEnv]] - outer_env: Option, + /// + /// Per https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy: + /// > A _Global Environment Record_ is used for Script global declarations. It does not have an outer environment; its \[\[OuterEnv\]\] is null. + outer_env: (), } diff --git a/nova_vm/src/execution/environments/private_environment.rs b/nova_vm/src/execution/environments/private_environment.rs index 9fd9b48e..b5379452 100644 --- a/nova_vm/src/execution/environments/private_environment.rs +++ b/nova_vm/src/execution/environments/private_environment.rs @@ -1,5 +1,14 @@ +use crate::types::{Function, String, Value}; use std::{cell::RefCell, collections::HashMap, rc::Rc}; +#[derive(Debug)] +pub enum PrivateElement { + Field(Option), + Method(Option), + /// Accssor(get, set) + Accessor(Option, Option), +} + /// 9.2 PrivateEnvironment Records /// https://tc39.es/ecma262/#sec-privateenvironment-records #[derive(Debug)] @@ -8,5 +17,5 @@ pub struct PrivateEnvironment { outer_private_environment: Option>>, /// [[Names]] - names: HashMap<&'static str, ()>, // TODO: Implement private names + names: HashMap, // TODO: Implement private names } diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index 9ce50452..b08ad337 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -28,7 +28,11 @@ pub struct ECMAScriptCode { #[derive(Debug)] pub struct ExecutionContext<'ctx, 'host> { /// Function - pub function: Option, + /// + /// > If this execution context is evaluating the code of a function object, then the value + /// > of this component is that function object. If the context is evaluating the code of + /// > a *Script* or *Module*, the value is **null** (here represented by None). + pub function: Option, /// Realm pub realm: Rc>>, diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 6aa4f228..25abe163 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -2,7 +2,7 @@ mod array; mod bigint; mod boolean; mod date; -mod element_array; +pub mod element_array; mod error; mod function; mod heap_bits; diff --git a/nova_vm/src/heap/element_array.rs b/nova_vm/src/heap/element_array.rs index 9dea667a..ddecad42 100644 --- a/nova_vm/src/heap/element_array.rs +++ b/nova_vm/src/heap/element_array.rs @@ -58,6 +58,7 @@ pub struct ElementsVector { } #[derive(Debug, Clone, Copy)] +#[repr(u8)] pub enum ElementDescriptor { /// ```js /// { value, writable: true, enumerable: true, configurable: true } diff --git a/nova_vm/src/small_string.rs b/nova_vm/src/small_string.rs index 835ab62e..49b02750 100644 --- a/nova_vm/src/small_string.rs +++ b/nova_vm/src/small_string.rs @@ -1,4 +1,4 @@ -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq, Hash)] pub struct SmallString { bytes: [u8; 7], } diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index 2d54fcc0..9d0e89ad 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -7,7 +7,7 @@ use crate::{ /// 6.1.4 The String Type /// https://tc39.es/ecma262/#sec-ecmascript-language-types-string-type -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u8)] pub enum String { String(StringIndex), From 843be8e19c107c8cbc6beff28216e413151b7cfa Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Oct 2023 00:56:02 +0300 Subject: [PATCH 24/32] Prepare for multi-realm heap by implementing Intrinsics struct --- nova_vm/src/builtins/builtin_function.rs | 2 +- nova_vm/src/builtins/number.rs | 7 +- nova_vm/src/execution/default_host_hooks.rs | 4 +- nova_vm/src/execution/realm.rs | 6 +- nova_vm/src/execution/realm/intrinsics.rs | 342 ++++++++++++++------ nova_vm/src/types/language/object.rs | 4 +- 6 files changed, 251 insertions(+), 114 deletions(-) diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index d486b41a..2c73ecda 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -60,7 +60,7 @@ pub fn create_builtin_function<'a, 'b: 'a>( // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. let prototype = args .prototype - .unwrap_or_else(Intrinsics::function_prototype); + .unwrap_or_else(|| realm.intrinsics.function_prototype().into()); // TODO: Steps 3-4 // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index ea40666e..4c333ed1 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -14,13 +14,14 @@ pub struct NumberConstructor; impl Builtin for NumberConstructor { fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { + let prototype = Some(realm.intrinsics.function_prototype()); let object: Object = create_builtin_function( Behaviour::Constructor(Self::behaviour), BuiltinFunctionArgs { length: 1, name: "Number", realm: Some(realm), - prototype: Some(Intrinsics::function_prototype()), + prototype, ..Default::default() }, ) @@ -150,7 +151,7 @@ impl Builtin for NumberConstructor { object, "prototype", PropertyDescriptor { - value: Some(Intrinsics::number_prototype().into()), + value: Some(realm.intrinsics.number_prototype().into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -161,7 +162,7 @@ impl Builtin for NumberConstructor { // 21.1.3.1 Number.prototype.constructor // https://tc39.es/ecma262/#sec-number.prototype.constructor define_builtin_property( - Intrinsics::number_prototype(), + realm.intrinsics.number_prototype(), "constructor", PropertyDescriptor { value: Some(object.into_value()), diff --git a/nova_vm/src/execution/default_host_hooks.rs b/nova_vm/src/execution/default_host_hooks.rs index 33916ab4..1679d217 100644 --- a/nova_vm/src/execution/default_host_hooks.rs +++ b/nova_vm/src/execution/default_host_hooks.rs @@ -1,5 +1,5 @@ use super::{JsResult, Realm}; -use crate::types::Object; +use crate::types::Function; /// 19.2.1.2 HostEnsureCanCompileStrings ( calleeRealm ) /// https://tc39.es/ecma262/#sec-hostensurecancompilestrings @@ -9,7 +9,7 @@ pub fn host_ensure_can_compile_strings(_: &mut Realm) -> JsResult<()> { /// 20.2.5 HostHasSourceTextAvailable ( func ) /// https://tc39.es/ecma262/#sec-hosthassourcetextavailable -pub fn host_has_source_text_available(_: Object) -> bool { +pub fn host_has_source_text_available(_: Function) -> bool { // The default implementation of HostHasSourceTextAvailable is to return true. return true; } diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 1f1333d5..396e47e3 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -5,6 +5,8 @@ use crate::{types::Object, Heap}; pub use intrinsics::Intrinsics; use std::{any::Any, cell::RefCell, rc::Rc}; +pub struct RealmIdentifier(u32); + /// 9.3 Realms /// https://tc39.es/ecma262/#sec-code-realms #[derive(Debug)] @@ -15,7 +17,9 @@ pub struct Realm<'ctx, 'host> { // NOTE: We will need an rng here at some point. - // NOTE: [[Intrinsics]] are statically known via the [`Intrinsics`] struct. + // [[Intrinsics]] + pub intrinsics: Intrinsics, + /// [[GlobalObject]] pub global_object: Object, diff --git a/nova_vm/src/execution/realm/intrinsics.rs b/nova_vm/src/execution/realm/intrinsics.rs index a259dc24..7b91d12a 100644 --- a/nova_vm/src/execution/realm/intrinsics.rs +++ b/nova_vm/src/execution/realm/intrinsics.rs @@ -3,241 +3,373 @@ use crate::{ indexes::{FunctionIndex, ObjectIndex}, BuiltinObjectIndexes, }, - types::Object, + types::{Function, Object}, }; -// TODO: We should probably consider lazily loading intrinsics. This would -// contain a mutable reference to [`Realm`] and be created via a -// `Realm::intrinsic()` method to guarantee safety. +#[derive(Debug, Clone, Copy)] +pub struct Intrinsics { + /// %Array% + array: FunctionIndex, + /// %Array.prototype% + array_prototype: ObjectIndex, + /// %BigInt% + big_int: FunctionIndex, + /// %BigInt.prototype% + big_int_prototype: ObjectIndex, + /// %Boolean% + boolean: FunctionIndex, + /// %Boolean.prototype% + boolean_prototype: ObjectIndex, + /// %Error% + error: FunctionIndex, + /// %Error.prototype% + error_prototype: ObjectIndex, + /// %eval% + eval: FunctionIndex, + /// %EvalError% + eval_error: FunctionIndex, + /// %EvalError.prototype% + eval_error_prototype: ObjectIndex, + /// %Function% + function: FunctionIndex, + /// %Function.prototype% + /// + /// NOTE: This is not spec-compliant. Function prototype should + /// be a function that always returns undefined no matter how + /// it is called. That's stupid so we do not have that. + function_prototype: ObjectIndex, + /// %isFinite% + is_finite: FunctionIndex, + /// %isNaN% + is_nan: FunctionIndex, + /// %Math% + math: ObjectIndex, + /// %Number% + number: FunctionIndex, + /// %Number.prototype% + number_prototype: ObjectIndex, + /// %Object% + object: FunctionIndex, + /// %Object.prototype% + object_prototype: ObjectIndex, + /// %Object.prototype.toString% + object_prototype_to_string: FunctionIndex, + /// %RangeError% + range_error: FunctionIndex, + /// %RangeError.prototype% + range_error_prototype: ObjectIndex, + /// %ReferenceError% + reference_error: FunctionIndex, + /// %ReferenceError.prototype% + reference_error_prototype: ObjectIndex, + /// %Reflect% + reflect: FunctionIndex, + /// %String% + string: FunctionIndex, + /// %String.prototype% + string_prototype: ObjectIndex, + /// %Symbol% + symbol: FunctionIndex, + /// %Symbol.prototype% + symbol_prototype: ObjectIndex, + /// %SyntaxError% + syntax_error: FunctionIndex, + /// %SyntaxError.prototype% + syntax_error_prototype: ObjectIndex, + /// %ThrowTypeError% + throw_type_error: FunctionIndex, + /// %TypeError% + type_error: FunctionIndex, + /// %TypeError.prototype% + type_error_prototype: ObjectIndex, + /// %URIError% + uri_error: FunctionIndex, + /// %URIError.prototype% + uri_error_prototype: ObjectIndex, +} -pub struct Intrinsics; +impl Default for Intrinsics { + fn default() -> Self { + let array = BuiltinObjectIndexes::ArrayConstructorIndex.into(); + let array_prototype = BuiltinObjectIndexes::ArrayPrototypeIndex.into(); + let big_int = BuiltinObjectIndexes::BigintConstructorIndex.into(); + let big_int_prototype = BuiltinObjectIndexes::BigintPrototypeIndex.into(); + let boolean = BuiltinObjectIndexes::BooleanConstructorIndex.into(); + let boolean_prototype = BuiltinObjectIndexes::BooleanPrototypeIndex.into(); + let error = BuiltinObjectIndexes::ErrorConstructorIndex.into(); + let error_prototype = BuiltinObjectIndexes::ErrorPrototypeIndex.into(); + // TODO: Placeholder. + let eval = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let eval_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let eval_error_prototype = ObjectIndex::from_u32_index(0); + let function = BuiltinObjectIndexes::FunctionConstructorIndex.into(); + let function_prototype = BuiltinObjectIndexes::FunctionPrototypeIndex.into(); + // TODO: Placeholder. + let is_finite = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let is_nan = FunctionIndex::from_u32_index(0); + let math = BuiltinObjectIndexes::MathObjectIndex.into(); + let number = BuiltinObjectIndexes::NumberConstructorIndex.into(); + let number_prototype = BuiltinObjectIndexes::NumberPrototypeIndex.into(); + let object = BuiltinObjectIndexes::ObjectConstructorIndex.into(); + let object_prototype = BuiltinObjectIndexes::ObjectPrototypeIndex.into(); + // TODO: Placeholder. + let object_prototype_to_string = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let range_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let range_error_prototype = ObjectIndex::from_u32_index(0); + // TODO: Placeholder. + let reference_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let reference_error_prototype = ObjectIndex::from_u32_index(0); + let reflect = BuiltinObjectIndexes::ReflectObjectIndex.into(); + let string = BuiltinObjectIndexes::StringConstructorIndex.into(); + let string_prototype = BuiltinObjectIndexes::StringPrototypeIndex.into(); + let symbol = BuiltinObjectIndexes::SymbolConstructorIndex.into(); + let symbol_prototype = BuiltinObjectIndexes::SymbolPrototypeIndex.into(); + // TODO: Placeholder. + let syntax_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let syntax_error_prototype = ObjectIndex::from_u32_index(0); + // TODO: Placeholder. + let throw_type_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let type_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let type_error_prototype = ObjectIndex::from_u32_index(0); + // TODO: Placeholder. + let uri_error = FunctionIndex::from_u32_index(0); + // TODO: Placeholder. + let uri_error_prototype = ObjectIndex::from_u32_index(0); + + Self { + array, + array_prototype, + big_int, + big_int_prototype, + boolean, + boolean_prototype, + error, + error_prototype, + eval, + eval_error, + eval_error_prototype, + function, + function_prototype, + is_finite, + is_nan, + math, + number, + number_prototype, + object, + object_prototype, + object_prototype_to_string, + range_error, + range_error_prototype, + reference_error, + reference_error_prototype, + reflect, + string, + string_prototype, + symbol, + symbol_prototype, + syntax_error, + syntax_error_prototype, + throw_type_error, + type_error, + type_error_prototype, + uri_error, + uri_error_prototype, + } + } +} impl Intrinsics { /// %Array% - pub const fn array() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::ArrayConstructorIndex as u32, - )) + pub const fn array(&self) -> Function { + Function(self.array) } /// %Array.prototype% - pub const fn array_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::ArrayPrototypeIndex as u32, - )) + pub const fn array_prototype(&self) -> Object { + Object::Object(self.array_prototype) } /// %BigInt% - pub const fn big_int() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::BigintConstructorIndex as u32, - )) + pub const fn big_int(&self) -> Function { + Function(self.big_int) } /// %BigInt.prototype% - pub const fn big_int_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::BigintPrototypeIndex as u32, - )) + pub const fn big_int_prototype(&self) -> Object { + Object::Object(self.big_int_prototype) } /// %Boolean% - pub const fn boolean() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::BooleanConstructorIndex as u32, - )) + pub const fn boolean(&self) -> Function { + Function(self.boolean) } /// %Boolean.prototype% - pub const fn boolean_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::BooleanPrototypeIndex as u32, - )) + pub const fn boolean_prototype(&self) -> Object { + Object::Object(self.boolean_prototype) } /// %Error% - pub const fn error() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::ErrorConstructorIndex as u32, - )) + pub const fn error(&self) -> Function { + Function(self.error) } /// %Error.prototype% - pub const fn error_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::ErrorPrototypeIndex as u32, - )) + pub const fn error_prototype(&self) -> Object { + Object::Object(self.error_prototype) } /// %eval% - pub const fn eval() -> Object { + pub const fn eval(&self) -> Function { todo!() } /// %EvalError% - pub const fn eval_error() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::ArrayConstructorIndex as u32, - )) + pub const fn eval_error(&self) -> Function { + Function(self.eval_error) } /// %EvalError.prototype% - pub const fn eval_error_prototype() -> Object { + pub const fn eval_error_prototype(&self) -> Object { todo!() } /// %Function% - pub const fn function() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::FunctionConstructorIndex as u32, - )) + pub const fn function(&self) -> Function { + Function(self.function) } /// %Function.prototype% - pub const fn function_prototype() -> Object { - // Note: This is not spec-compliant. Function prototype should - // be a function that always returns undefined no matter how - // it is called. That's stupid so we do not have that. - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::FunctionPrototypeIndex as u32, - )) + pub const fn function_prototype(&self) -> Object { + Object::Object(self.function_prototype) } /// %isFinite% - pub const fn is_finite() -> Object { + pub const fn is_finite(&self) -> Function { todo!() } /// %isNaN% - pub const fn is_nan() -> Object { + pub const fn is_nan(&self) -> Function { todo!() } /// %Math% - pub const fn math() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::MathObjectIndex as u32, - )) + pub const fn math(&self) -> Object { + Object::Object(self.math) } /// %Number% - pub const fn number() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::NumberConstructorIndex as u32, - )) + pub const fn number(&self) -> Function { + Function(self.number) } /// %Number.prototype% - pub const fn number_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::NumberPrototypeIndex as u32, - )) + pub const fn number_prototype(&self) -> Object { + Object::Object(self.number_prototype) } /// %Object% - pub const fn object() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::ObjectConstructorIndex as u32, - )) + pub const fn object(&self) -> Function { + Function(self.object) } /// %Object.prototype% - pub const fn object_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::ObjectPrototypeIndex as u32, - )) + pub const fn object_prototype(&self) -> Object { + Object::Object(self.object_prototype) } /// %Object.prototype.toString% - pub const fn object_prototype_to_string() -> Object { + pub const fn object_prototype_to_string(&self) -> Object { todo!() } /// %RangeError% - pub const fn range_error() -> Object { + pub const fn range_error(&self) -> Object { todo!() } /// %RangeError.prototype% - pub const fn range_error_prototype() -> Object { + pub const fn range_error_prototype(&self) -> Object { todo!() } /// %ReferenceError% - pub const fn reference_error() -> Object { + pub const fn reference_error(&self) -> Object { todo!() } /// %ReferenceError.prototype% - pub const fn reference_error_prototype() -> Object { + pub const fn reference_error_prototype(&self) -> Object { todo!() } /// %Reflect% - pub const fn reflect() -> Object { + pub const fn reflect(&self) -> Object { todo!() } /// %String% - pub const fn string() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::StringConstructorIndex as u32, - )) + pub const fn string(&self) -> Function { + Function(self.string) } /// %String.prototype% - pub const fn string_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::StringPrototypeIndex as u32, - )) + pub const fn string_prototype(&self) -> Object { + Object::Object(self.string_prototype) } /// %Symbol% - pub const fn symbol() -> Object { - Object::Function(FunctionIndex::from_u32_index( - BuiltinObjectIndexes::SymbolConstructorIndex as u32, - )) + pub const fn symbol(&self) -> Function { + Function(self.symbol) } /// %Symbol.prototype% - pub const fn symbol_prototype() -> Object { - Object::Object(ObjectIndex::from_u32_index( - BuiltinObjectIndexes::SymbolPrototypeIndex as u32, - )) + pub const fn symbol_prototype(&self) -> Object { + Object::Object(self.symbol_prototype) } /// %SyntaxError% - pub const fn syntax_error() -> Object { + pub const fn syntax_error(&self) -> Object { todo!() } /// %SyntaxError.prototype% - pub const fn syntax_error_prototype() -> Object { + pub const fn syntax_error_prototype(&self) -> Object { todo!() } /// %ThrowTypeError% - pub const fn throw_type_error() -> Object { + pub const fn throw_type_error(&self) -> Object { todo!() } /// %TypeError% - pub const fn type_error() -> Object { + pub const fn type_error(&self) -> Object { todo!() } /// %TypeError.prototype% - pub const fn type_error_prototype() -> Object { + pub const fn type_error_prototype(&self) -> Object { todo!() } /// %URIError% - pub const fn uri_error() -> Object { + pub const fn uri_error(&self) -> Object { todo!() } /// %URIError.prototype% - pub const fn uri_error_prototype() -> Object { + pub const fn uri_error_prototype(&self) -> Object { todo!() } } diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index dd1d16be..d7b874f2 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -124,10 +124,10 @@ impl Object { let prototype = realm.heap.get(object_index).prototype; prototype.map(|v| v.into()) } else { - Some(Intrinsics::array_prototype().into_value()) + Some(realm.intrinsics.array_prototype().into_value()) } } - Object::Function(_) => Some(Intrinsics::function_prototype().into_value()), + Object::Function(_) => Some(realm.intrinsics.function_prototype().into_value()), _ => unreachable!(), } } From 173c9b78970b5b4fe01f058b5c5257c97b507d4b Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Oct 2023 01:01:10 +0300 Subject: [PATCH 25/32] Remove vestigal src/value.rs --- nova_vm/src/value.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 nova_vm/src/value.rs diff --git a/nova_vm/src/value.rs b/nova_vm/src/value.rs deleted file mode 100644 index e69de29b..00000000 From de84f0c8c2fed789c492225f8d9cc64069fbfcad Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Oct 2023 01:20:56 +0300 Subject: [PATCH 26/32] get_object_index helper for Object sub-enum --- nova_vm/src/heap/function.rs | 4 +- nova_vm/src/types/language/object.rs | 66 ++++++++++++++++------------ 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/nova_vm/src/heap/function.rs b/nova_vm/src/heap/function.rs index 66cc220c..bc176e01 100644 --- a/nova_vm/src/heap/function.rs +++ b/nova_vm/src/heap/function.rs @@ -16,8 +16,8 @@ use super::{ #[derive(Debug, Clone)] pub struct FunctionHeapData { - pub(super) object_index: Option, - pub(super) length: u8, + pub(crate) object_index: Option, + pub(crate) length: u8, pub initial_name: Value, // pub behaviour: Behaviour, // TODO: Should we create a `BoundFunctionHeapData` for an exotic object diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index d7b874f2..1b58f830 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -3,6 +3,8 @@ mod internal_methods; mod property_key; mod property_storage; +use std::array; + use super::{ value::{ ARRAY_DISCRIMINANT, DATE_DISCRIMINANT, ERROR_DISCRIMINANT, FUNCTION_DISCRIMINANT, @@ -18,6 +20,7 @@ use crate::{ GetHeapData, }, types::PropertyDescriptor, + Heap, }; pub use data::ObjectData; @@ -82,29 +85,42 @@ impl Object { self.into() } - /// [[Extensible]] - pub fn extensible(self, agent: &mut Agent) -> bool { + fn get_object_index(self, heap: &Heap) -> ObjectIndex { match self { - Object::Object(object) => agent.current_realm().borrow().heap.get(object).extensible, - Object::Array(_) => true, - Object::Function(_) => true, + Object::Object(index) => index, + Object::Array(array_index) => heap + .arrays + .get(array_index.into_index()) + .unwrap() + .unwrap() + .object_index + .unwrap(), + Object::Function(function_index) => heap + .functions + .get(function_index.into_index()) + .unwrap() + .as_ref() + .unwrap() + .object_index + .unwrap(), } } + /// [[Extensible]] + pub fn extensible(self, agent: &mut Agent) -> bool { + let realm = agent.current_realm(); + let realm = realm.borrow(); + let object_index = self.get_object_index(&realm.heap); + agent.current_realm().borrow().heap.get(object_index).extensible + } + /// [[Extensible]] pub fn set_extensible(self, agent: &mut Agent, value: bool) { - match self { - Object::Object(object) => { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let object = realm.heap.get_mut(object); - object.extensible = true; - } - // TODO: Correct object/function impl - Object::Array(_) => {} - Object::Function(_) => {} - _ => unreachable!(), - } + let realm = agent.current_realm(); + let mut realm = realm.borrow_mut(); + let object_index = self.get_object_index(&realm.heap); + let object = realm.heap.get_mut(object_index); + object.extensible = true; } /// [[Prototype]] @@ -134,17 +150,11 @@ impl Object { /// [[Prototype]] pub fn set_prototype(self, agent: &mut Agent, prototype: Option) { - match self { - Object::Object(object) => { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let object = realm.heap.get_mut(object); - object.prototype = prototype; - } - Object::Array(_) => todo!(), - Object::Function(_) => todo!(), - _ => unreachable!(), - } + let realm = agent.current_realm(); + let mut realm = realm.borrow_mut(); + let object_index = self.get_object_index(&realm.heap); + let object = realm.heap.get_mut(object_index); + object.prototype = prototype; } pub fn internal_methods<'a>(self, agent: &mut Agent) -> &'a InternalMethods { From c1d263cec4399645a6fe4f9ff8c5e61ae39e1c93 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Thu, 12 Oct 2023 08:09:43 +0300 Subject: [PATCH 27/32] Move Heap to Agent --- nova_vm/src/builtins/array.rs | 6 +- nova_vm/src/builtins/builtin_function.rs | 13 ++- nova_vm/src/builtins/number.rs | 26 +++--- nova_vm/src/execution/agent.rs | 1 + nova_vm/src/execution/realm.rs | 2 - nova_vm/src/types/language/number.rs | 88 ++++++------------- nova_vm/src/types/language/object.rs | 30 +++---- .../src/types/language/object/property_key.rs | 4 +- .../types/language/object/property_storage.rs | 14 ++- nova_vm/src/types/language/string.rs | 6 +- 10 files changed, 77 insertions(+), 113 deletions(-) diff --git a/nova_vm/src/builtins/array.rs b/nova_vm/src/builtins/array.rs index 46cba49d..01a05a5f 100644 --- a/nova_vm/src/builtins/array.rs +++ b/nova_vm/src/builtins/array.rs @@ -10,10 +10,12 @@ use crate::{ pub struct ArrayConstructor; impl Builtin for ArrayConstructor { - fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { + fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult { + let realm = agent.current_realm(); let object = create_builtin_function( + agent, Behaviour::Regular(Self::behaviour), - BuiltinFunctionArgs::new(1, "Array", realm), + BuiltinFunctionArgs::new(1, "Array", &mut realm.borrow_mut()), ); Ok(object.into_object()) diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index 2c73ecda..e18fc60d 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -2,6 +2,7 @@ use crate::{ execution::{Agent, Intrinsics, JsResult, Realm}, heap::CreateHeapData, types::{Function, Object, PropertyDescriptor, Value}, + Heap, }; #[derive(Debug)] @@ -25,7 +26,7 @@ pub enum Behaviour { } pub trait Builtin { - fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult; + fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult; } #[derive(Debug, Default)] @@ -51,9 +52,11 @@ impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { /// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ) /// https://tc39.es/ecma262/#sec-createbuiltinfunction pub fn create_builtin_function<'a, 'b: 'a>( + agent: &mut Agent, behaviour: Behaviour, args: BuiltinFunctionArgs<'a, 'b, 'b>, ) -> Function { + let heap = &mut agent.heap; // 1. If realm is not present, set realm to the current Realm Record. let realm = args.realm.unwrap(); // TODO: load record @@ -76,15 +79,15 @@ pub fn create_builtin_function<'a, 'b: 'a>( // 8. Set func.[[Realm]] to realm. // NOTE: Heap data is implicitly attached to the Realm so I don't think // this matters. - let object = realm.heap.create_object_with_prototype(prototype); + let object = heap.create_object_with_prototype(prototype); // 9. Set func.[[InitialName]] to null. // TODO: This is non-standard. - let initial_name = realm.heap.create(args.name).into_value(); + let initial_name = heap.create(args.name).into_value(); // 10. Perform SetFunctionLength(func, length). let length = args.length as u8; // TODO: Actually set behaviour somewhere - let func = realm.heap.create_function(initial_name, length, false); + let func = heap.create_function(initial_name, length, false); // TODO: Steps 11-12 // 11. If prefix is not present, then @@ -97,6 +100,7 @@ pub fn create_builtin_function<'a, 'b: 'a>( } pub fn define_builtin_function<'a, 'b>( + agent: &mut Agent, object: Object, name: &'a str, behaviour: RegularFn, @@ -104,6 +108,7 @@ pub fn define_builtin_function<'a, 'b>( realm: &'a mut Realm<'b, 'b>, ) -> JsResult<()> { let function = create_builtin_function( + agent, Behaviour::Regular(behaviour), BuiltinFunctionArgs::new(length, name, realm), ); diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index 4c333ed1..4ef41b66 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -1,3 +1,5 @@ +use std::{borrow::BorrowMut, cell::RefCell, rc::Rc}; + use super::{ builtin_function::{define_builtin_function, define_builtin_property}, create_builtin_function, ordinary, todo_builtin, ArgumentsList, Behaviour, Builtin, @@ -13,14 +15,18 @@ use crate::{ pub struct NumberConstructor; impl Builtin for NumberConstructor { - fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult { + fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult { + let mut realm = agent.current_realm(); + let mut realm = RefCell::borrow_mut(&mut realm); + let prototype = Some(realm.intrinsics.function_prototype()); let object: Object = create_builtin_function( + agent, Behaviour::Constructor(Self::behaviour), BuiltinFunctionArgs { length: 1, name: "Number", - realm: Some(realm), + realm: Some(&mut realm), prototype, ..Default::default() }, @@ -33,7 +39,7 @@ impl Builtin for NumberConstructor { object, "EPSILON", PropertyDescriptor { - value: Some(realm.heap.create(f64::EPSILON).into()), + value: Some(agent.heap.create(f64::EPSILON).into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -61,7 +67,7 @@ impl Builtin for NumberConstructor { object, "MAX_VALUE", PropertyDescriptor { - value: Some(realm.heap.create(f64::MAX).into()), + value: Some(agent.heap.create(f64::MAX).into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -89,7 +95,7 @@ impl Builtin for NumberConstructor { object, "MIN_VALUE", PropertyDescriptor { - value: Some(realm.heap.create(f64::MIN).into()), + value: Some(agent.heap.create(f64::MIN).into()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -139,11 +145,11 @@ impl Builtin for NumberConstructor { }, )?; - define_builtin_function(object, "isFinite", todo_builtin, 1, realm)?; - define_builtin_function(object, "isNaN", todo_builtin, 1, realm)?; - define_builtin_function(object, "isSafeInteger", todo_builtin, 1, realm)?; - define_builtin_function(object, "parseFloat", todo_builtin, 1, realm)?; - define_builtin_function(object, "parseInt", todo_builtin, 2, realm)?; + define_builtin_function(agent, object, "isFinite", todo_builtin, 1, &mut realm)?; + define_builtin_function(agent, object, "isNaN", todo_builtin, 1, &mut realm)?; + define_builtin_function(agent, object, "isSafeInteger", todo_builtin, 1, &mut realm)?; + define_builtin_function(agent, object, "parseFloat", todo_builtin, 1, &mut realm)?; + define_builtin_function(agent, object, "parseInt", todo_builtin, 2, &mut realm)?; // 21.1.2.15 Number.prototype // https://tc39.es/ecma262/#sec-number.prototype diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs index f3550716..3376909a 100644 --- a/nova_vm/src/execution/agent.rs +++ b/nova_vm/src/execution/agent.rs @@ -27,6 +27,7 @@ pub struct HostHooks { /// https://tc39.es/ecma262/#sec-agents #[derive(Debug)] pub struct Agent<'ctx, 'host> { + pub heap: Heap, pub options: Options, // pre_allocated: PreAllocated, pub exception: Option, diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 396e47e3..60d98c54 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -11,8 +11,6 @@ pub struct RealmIdentifier(u32); /// https://tc39.es/ecma262/#sec-code-realms #[derive(Debug)] pub struct Realm<'ctx, 'host> { - pub heap: Heap, - pub agent: Rc>>, // NOTE: We will need an rng here at some point. diff --git a/nova_vm/src/types/language/number.rs b/nova_vm/src/types/language/number.rs index 8afb61b0..035df5dc 100644 --- a/nova_vm/src/types/language/number.rs +++ b/nova_vm/src/types/language/number.rs @@ -106,7 +106,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n).is_nan(), + Value::Number(n) => agent.heap.get(n).is_nan(), Value::Integer(_) => false, Value::Float(n) => n.is_nan(), _ => unreachable!(), @@ -117,12 +117,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent - .current_realm() - .borrow() - .heap - .get(n) - .is_sign_positive(), + Value::Number(n) => agent.heap.get(n).is_sign_positive(), Value::Integer(n) => 0i64 == n.into(), Value::Float(n) => n.is_sign_positive(), _ => unreachable!(), @@ -133,12 +128,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent - .current_realm() - .borrow() - .heap - .get(n) - .is_sign_negative(), + Value::Number(n) => agent.heap.get(n).is_sign_negative(), Value::Integer(_) => false, Value::Float(n) => n.is_sign_negative(), _ => unreachable!(), @@ -149,7 +139,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => *agent.current_realm().borrow().heap.get(n) == f64::INFINITY, + Value::Number(n) => *agent.heap.get(n) == f64::INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::INFINITY, _ => unreachable!(), @@ -160,7 +150,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => *agent.current_realm().borrow().heap.get(n) == f64::NEG_INFINITY, + Value::Number(n) => *agent.heap.get(n) == f64::NEG_INFINITY, Value::Integer(_) => false, Value::Float(n) => n == f32::NEG_INFINITY, _ => unreachable!(), @@ -171,7 +161,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => agent.current_realm().borrow().heap.get(n).is_finite(), + Value::Number(n) => agent.heap.get(n).is_finite(), Value::Integer(_) => true, Value::Float(n) => n.is_finite(), _ => unreachable!(), @@ -183,7 +173,7 @@ impl Number { match x { Value::Number(n) => { - let n = *agent.current_realm().borrow().heap.get(n); + let n = *agent.heap.get(n); !n.is_sign_negative() && !n.is_sign_positive() } Value::Integer(_) => true, @@ -198,10 +188,8 @@ impl Number { match x { Value::Number(n) => { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let n = realm.heap.get(n).trunc(); - realm.heap.create(n) + let n = agent.heap.get(n).trunc(); + agent.heap.create(n) } Value::Integer(_) => self, Value::Float(n) => n.trunc().into(), @@ -213,7 +201,7 @@ impl Number { let x = self.into_value(); match x { - Value::Number(n) => *agent.current_realm().borrow().heap.get(n), + Value::Number(n) => *agent.heap.get(n), Value::Integer(n) => Into::::into(n) as f64, Value::Float(n) => n as f64, _ => unreachable!(), @@ -227,24 +215,13 @@ impl Number { let y = y.into_value(); match (x, y) { - (Value::Number(x), Value::Number(y)) => { - agent.current_realm().borrow().heap.get(x) - == agent.current_realm().borrow().heap.get(y) - } - (Value::Number(x), Value::Integer(y)) => { - *agent.current_realm().borrow().heap.get(x) == y.into_i64() as f64 - } - (Value::Number(x), Value::Float(y)) => { - *agent.current_realm().borrow().heap.get(x) == y as f64 - } - (Value::Integer(x), Value::Number(y)) => { - (x.into_i64() as f64) == *agent.current_realm().borrow().heap.get(y) - } + (Value::Number(x), Value::Number(y)) => agent.heap.get(x) == agent.heap.get(y), + (Value::Number(x), Value::Integer(y)) => *agent.heap.get(x) == y.into_i64() as f64, + (Value::Number(x), Value::Float(y)) => *agent.heap.get(x) == y as f64, + (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) == *agent.heap.get(y), (Value::Integer(x), Value::Integer(y)) => x.into_i64() == y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) == y as f64, - (Value::Float(x), Value::Number(y)) => { - (x as f64) == *agent.current_realm().borrow().heap.get(y) - } + (Value::Float(x), Value::Number(y)) => (x as f64) == *agent.heap.get(y), (Value::Float(x), Value::Integer(y)) => (x as f64) == y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x == y, _ => unreachable!(), @@ -256,7 +233,7 @@ impl Number { match x { Value::Number(n) => { - let n = *agent.current_realm().borrow().heap.get(n); + let n = *agent.heap.get(n); n % 1.0 == 0.0 && n % 2.0 == 0.0 } Value::Integer(n) => Into::::into(n) % 2 == 0, @@ -270,11 +247,11 @@ impl Number { match x { Value::Number(n) => { - let n = *agent.current_realm().borrow().heap.get(n); + let n = *agent.heap.get(n); if n > 0.0 { self } else { - agent.current_realm().borrow_mut().heap.create(-n) + agent.heap.create(-n) } } Value::Integer(n) => { @@ -302,10 +279,8 @@ impl Number { // 2. Return the result of negating x; that is, compute a Number with the same magnitude but opposite sign. match x { Value::Number(n) => { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let value = *realm.heap.get(n); - realm.heap.create(-value) + let value = *agent.heap.get(n); + agent.heap.create(-value) } Value::Integer(n) => SmallInteger::from_i64_unchecked(-n.into_i64()).into(), Value::Float(n) => (-n).into(), @@ -458,8 +433,6 @@ impl Number { // 13. Return an implementation-approximated Number value representing the result of raising ℝ(base) to the ℝ(exponent) power. agent - .current_realm() - .borrow_mut() .heap .create(base.into_f64(agent).powf(exponent.into_f64(agent))) } @@ -523,24 +496,13 @@ impl Number { // 11. If ℝ(x) < ℝ(y), return true. Otherwise, return false. Value::Boolean(match (x.into_value(), y.into_value()) { - (Value::Number(x), Value::Number(y)) => { - agent.current_realm().borrow().heap.get(x) - < agent.current_realm().borrow().heap.get(y) - } - (Value::Number(x), Value::Integer(y)) => { - *agent.current_realm().borrow().heap.get(x) < y.into_i64() as f64 - } - (Value::Number(x), Value::Float(y)) => { - *agent.current_realm().borrow().heap.get(x) < y as f64 - } - (Value::Integer(x), Value::Number(y)) => { - (x.into_i64() as f64) < *agent.current_realm().borrow().heap.get(y) - } + (Value::Number(x), Value::Number(y)) => agent.heap.get(x) < agent.heap.get(y), + (Value::Number(x), Value::Integer(y)) => *agent.heap.get(x) < y.into_i64() as f64, + (Value::Number(x), Value::Float(y)) => *agent.heap.get(x) < y as f64, + (Value::Integer(x), Value::Number(y)) => (x.into_i64() as f64) < *agent.heap.get(y), (Value::Integer(x), Value::Integer(y)) => x.into_i64() < y.into_i64(), (Value::Integer(x), Value::Float(y)) => (x.into_i64() as f64) < y as f64, - (Value::Float(x), Value::Number(y)) => { - (x as f64) < *agent.current_realm().borrow().heap.get(y) - } + (Value::Float(x), Value::Number(y)) => (x as f64) < *agent.heap.get(y), (Value::Float(x), Value::Integer(y)) => (x as f64) < y.into_i64() as f64, (Value::Float(x), Value::Float(y)) => x < y, _ => unreachable!(), diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 1b58f830..30a54969 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -108,36 +108,35 @@ impl Object { /// [[Extensible]] pub fn extensible(self, agent: &mut Agent) -> bool { - let realm = agent.current_realm(); - let realm = realm.borrow(); - let object_index = self.get_object_index(&realm.heap); - agent.current_realm().borrow().heap.get(object_index).extensible + let heap = &agent.heap; + let object_index = self.get_object_index(heap); + heap.get(object_index).extensible } /// [[Extensible]] pub fn set_extensible(self, agent: &mut Agent, value: bool) { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let object_index = self.get_object_index(&realm.heap); - let object = realm.heap.get_mut(object_index); - object.extensible = true; + let heap = &mut agent.heap; + let object_index = self.get_object_index(heap); + let object = heap.get_mut(object_index); + object.extensible = value; } /// [[Prototype]] pub fn prototype(self, agent: &mut Agent) -> Option { + let heap = &agent.heap; let realm = agent.current_realm(); let realm = realm.borrow(); match self { Object::Object(object) => { - let object = realm.heap.get(object); + let object = heap.get(object); object.prototype.map(|v| v.into_value()) } Object::Array(array) => { - let array = realm.heap.get(array); + let array = heap.get(array); if let Some(object_index) = array.object_index { - let prototype = realm.heap.get(object_index).prototype; + let prototype = heap.get(object_index).prototype; prototype.map(|v| v.into()) } else { Some(realm.intrinsics.array_prototype().into_value()) @@ -150,10 +149,9 @@ impl Object { /// [[Prototype]] pub fn set_prototype(self, agent: &mut Agent, prototype: Option) { - let realm = agent.current_realm(); - let mut realm = realm.borrow_mut(); - let object_index = self.get_object_index(&realm.heap); - let object = realm.heap.get_mut(object_index); + let heap = &mut agent.heap; + let object_index = self.get_object_index(&heap); + let object = heap.get_mut(object_index); object.prototype = prototype; } diff --git a/nova_vm/src/types/language/object/property_key.rs b/nova_vm/src/types/language/object/property_key.rs index a7c3b97c..0748c6b1 100644 --- a/nova_vm/src/types/language/object/property_key.rs +++ b/nova_vm/src/types/language/object/property_key.rs @@ -86,9 +86,7 @@ impl PropertyKey { s1.as_str() == s2.as_str() } (PropertyKey::String(s), PropertyKey::SmallInteger(n)) => { - let realm = agent.current_realm(); - let realm = realm.borrow(); - let s = realm.heap.get(s); + let s = agent.heap.get(s); let Some(s) = s.as_str() else { return false; diff --git a/nova_vm/src/types/language/object/property_storage.rs b/nova_vm/src/types/language/object/property_storage.rs index d9686cf8..d1766441 100644 --- a/nova_vm/src/types/language/object/property_storage.rs +++ b/nova_vm/src/types/language/object/property_storage.rs @@ -32,9 +32,7 @@ impl PropertyStorage { match object { Value::Object(object) => { - let realm = agent.current_realm(); - let realm = realm.borrow(); - let keys = &realm.heap.get(object).keys; + let keys = &agent.heap.get(object).keys; // realm.heap.elements.get(keys).iter().any(|k| { // if let Some(value) = k { // value.equals(agent, key) @@ -51,19 +49,17 @@ impl PropertyStorage { return true; } - let realm = agent.current_realm(); - let realm = realm.borrow(); - let array = realm.heap.get(array); + let array = agent.heap.get(array); if key.is_array_index() { - return realm.heap.elements.has(array.elements, key.into_value()); + return agent.heap.elements.has(array.elements, key.into_value()); } if let Some(object) = array.object_index { - realm + agent .heap .elements - .has(object.get(&realm.heap).keys, key.into_value()) + .has(object.get(&agent.heap).keys, key.into_value()) } else { false } diff --git a/nova_vm/src/types/language/string.rs b/nova_vm/src/types/language/string.rs index 9d0e89ad..1319393a 100644 --- a/nova_vm/src/types/language/string.rs +++ b/nova_vm/src/types/language/string.rs @@ -61,7 +61,7 @@ impl String { /// Byte length of the string. pub fn len(self, agent: &Agent) -> usize { match self { - String::String(s) => agent.current_realm().borrow().heap.get(s).len(), + String::String(s) => agent.heap.get(s).len(), String::SmallString(s) => s.len(), } } @@ -70,9 +70,7 @@ impl String { match self { // SAFETY: The mutable reference to the Agent ensures no mutable // access to the realm. - String::String(s) => unsafe { - std::mem::transmute(agent.current_realm().borrow().heap.get(*s).as_str()) - }, + String::String(s) => unsafe { std::mem::transmute(agent.heap.get(*s).as_str()) }, String::SmallString(s) => Some(s.as_str()), } } From c99a42df054d6ff94b723cfbc2c8b032056ecdd7 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sat, 14 Oct 2023 17:59:26 +0300 Subject: [PATCH 28/32] Begin vector-based environments work --- nova_vm/src/builtins/ecmascript_function.rs | 6 +++--- nova_vm/src/execution.rs | 7 ++++--- nova_vm/src/execution/agent.rs | 7 ++++--- nova_vm/src/execution/environments.rs | 21 ++++++++++--------- .../environments/declarative_environment.rs | 18 +++++++++++++++- .../environments/function_environment.rs | 18 +++++++++++++++- .../environments/global_environment.rs | 19 ++++++++++++++++- .../environments/object_environment.rs | 18 ++++++++++++++++ .../environments/private_environment.rs | 20 ++++++++++++++++-- nova_vm/src/execution/execution_context.rs | 6 +++--- nova_vm/src/execution/realm.rs | 11 +++++----- nova_vm/src/heap/indexes.rs | 2 -- nova_vm/src/language/script.rs | 6 +++--- 13 files changed, 122 insertions(+), 37 deletions(-) diff --git a/nova_vm/src/builtins/ecmascript_function.rs b/nova_vm/src/builtins/ecmascript_function.rs index 433edbe8..3336c3f9 100644 --- a/nova_vm/src/builtins/ecmascript_function.rs +++ b/nova_vm/src/builtins/ecmascript_function.rs @@ -3,7 +3,7 @@ use std::{cell::RefCell, rc::Rc}; use oxc_ast::ast::{FormalParameters, FunctionBody}; use crate::{ - execution::{Agent, Environment, JsResult, PrivateEnvironment, Realm, ScriptOrModule}, + execution::{Agent, Environment, JsResult, PrivateEnvironmentIndex, RealmIdentifier, ScriptOrModule}, types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, }; @@ -28,7 +28,7 @@ pub struct ECMAScriptFunction<'ctx, 'host> { pub environment: Environment, /// [[PrivateEnvironment]] - pub private_environment: Option>>, + pub private_environment: Option, /// [[FormalParameters]] pub formal_parameters: &'host FormalParameters<'host>, @@ -40,7 +40,7 @@ pub struct ECMAScriptFunction<'ctx, 'host> { pub constructor_kind: ConstructorKind, /// [[Realm]] - pub realm: Rc>>, + pub realm: RealmIdentifier<'ctx, 'host>, /// [[ScriptOrModule]] pub script_or_module: ScriptOrModule<'ctx, 'host>, diff --git a/nova_vm/src/execution.rs b/nova_vm/src/execution.rs index 6f33692c..b3181b62 100644 --- a/nova_vm/src/execution.rs +++ b/nova_vm/src/execution.rs @@ -6,8 +6,9 @@ mod realm; pub use agent::{Agent, JsResult}; pub use environments::{ - DeclarativeEnvironment, Environment, FunctionEnvironment, GlobalEnvironment, ObjectEnvironment, - PrivateEnvironment, + DeclarativeEnvironment, DeclarativeEnvironmentIndex, Environment, FunctionEnvironment, + FunctionEnvironmentIndex, GlobalEnvironment, GlobalEnvironmentIndex, ObjectEnvironment, + ObjectEnvironmentIndex, PrivateEnvironment, PrivateEnvironmentIndex, }; pub use execution_context::{ECMAScriptCode, ExecutionContext, ScriptOrModule}; -pub use realm::{Intrinsics, Realm}; +pub use realm::{Intrinsics, Realm, RealmIdentifier}; diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs index 3376909a..3219bf87 100644 --- a/nova_vm/src/execution/agent.rs +++ b/nova_vm/src/execution/agent.rs @@ -1,4 +1,4 @@ -use super::{ExecutionContext, Realm}; +use super::{ExecutionContext, Realm, RealmIdentifier}; use crate::{ types::{Object, Symbol, Value}, Heap, @@ -34,12 +34,13 @@ pub struct Agent<'ctx, 'host> { pub symbol_id: usize, pub global_symbol_registry: HashMap<&'static str, Symbol>, pub host_hooks: HostHooks, + pub realms: Vec>, pub execution_context_stack: Vec>, } impl<'ctx, 'host> Agent<'ctx, 'host> { - pub fn current_realm(&self) -> Rc>> { - self.execution_context_stack.last().unwrap().realm.clone() + pub fn current_realm(&self) -> RealmIdentifier<'ctx, 'host> { + self.execution_context_stack.last().unwrap().realm } /// 5.2.3.2 Throw an Exception diff --git a/nova_vm/src/execution/environments.rs b/nova_vm/src/execution/environments.rs index dfffc310..6c5a2e97 100644 --- a/nova_vm/src/execution/environments.rs +++ b/nova_vm/src/execution/environments.rs @@ -7,19 +7,20 @@ pub mod global_environment; pub mod object_environment; pub mod private_environment; -pub use declarative_environment::DeclarativeEnvironment; -pub use function_environment::FunctionEnvironment; -pub use global_environment::GlobalEnvironment; -pub use object_environment::ObjectEnvironment; -pub use private_environment::PrivateEnvironment; -use std::{cell::RefCell, rc::Rc}; +pub use declarative_environment::{DeclarativeEnvironment, DeclarativeEnvironmentIndex}; +pub use function_environment::{FunctionEnvironment, FunctionEnvironmentIndex}; +pub use global_environment::{GlobalEnvironment, GlobalEnvironmentIndex}; +pub use object_environment::{ObjectEnvironment, ObjectEnvironmentIndex}; +pub use private_environment::{PrivateEnvironment, PrivateEnvironmentIndex}; /// 9.1.1 The Environment Record Type Hierarchy /// https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy #[derive(Debug, Clone)] +#[repr(u8)] pub enum Environment { - DeclarativeEnvironment(Rc>), - ObjectEnvironment(Rc>), - FunctionEnvironment(Rc>), - GlobalEnvironment(Rc>), + // Leave 0 for None option + DeclarativeEnvironment(DeclarativeEnvironmentIndex) = 1, + FunctionEnvironment(FunctionEnvironmentIndex), + GlobalEnvironment(GlobalEnvironmentIndex), + ObjectEnvironment(ObjectEnvironmentIndex), } diff --git a/nova_vm/src/execution/environments/declarative_environment.rs b/nova_vm/src/execution/environments/declarative_environment.rs index 1a33bea9..12ba668c 100644 --- a/nova_vm/src/execution/environments/declarative_environment.rs +++ b/nova_vm/src/execution/environments/declarative_environment.rs @@ -1,6 +1,22 @@ use super::Environment; use crate::types::{String, Value}; -use std::collections::HashMap; +use std::{collections::HashMap, num::NonZeroU32, marker::PhantomData}; + +#[derive(Debug, Clone, Copy)] +pub struct DeclarativeEnvironmentIndex(NonZeroU32, PhantomData); + +impl DeclarativeEnvironmentIndex { + pub const fn from_u32_index(value: u32) -> Self { + 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 into_index(self) -> usize { + self.0.get() as usize - 1 + } +} /// 9.1.1.1 Declarative Environment Records /// https://tc39.es/ecma262/#sec-declarative-environment-records diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs index e02d2255..57cea34c 100644 --- a/nova_vm/src/execution/environments/function_environment.rs +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{collections::HashMap, num::NonZeroU32, marker::PhantomData}; use super::{declarative_environment::Binding, Environment}; use crate::{ @@ -6,6 +6,22 @@ use crate::{ types::{Object, String, Value}, }; +#[derive(Debug, Clone, Copy)] +pub struct FunctionEnvironmentIndex(NonZeroU32, PhantomData); + +impl FunctionEnvironmentIndex { + pub const fn from_u32_index(value: u32) -> Self { + 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 into_index(self) -> usize { + self.0.get() as usize - 1 + } +} + #[derive(Debug)] pub enum ThisBindingStatus { /// Function is an ArrowFunction and does not have a local `this` value. diff --git a/nova_vm/src/execution/environments/global_environment.rs b/nova_vm/src/execution/environments/global_environment.rs index 7a321010..db3e359f 100644 --- a/nova_vm/src/execution/environments/global_environment.rs +++ b/nova_vm/src/execution/environments/global_environment.rs @@ -1,10 +1,27 @@ use std::collections::HashMap; +use std::marker::PhantomData; +use std::num::NonZeroU32; use super::declarative_environment::Binding; -use super::{DeclarativeEnvironment, Environment, ObjectEnvironment}; use crate::heap::element_array::ElementsVector; use crate::types::{Object, String}; +#[derive(Debug, Clone, Copy)] +pub struct GlobalEnvironmentIndex(NonZeroU32, PhantomData); + +impl GlobalEnvironmentIndex { + pub const fn from_u32_index(value: u32) -> Self { + 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 into_index(self) -> usize { + self.0.get() as usize - 1 + } +} + /// 9.1.1.4 Global Environment Records /// https://tc39.es/ecma262/#sec-global-environment-records #[derive(Debug)] diff --git a/nova_vm/src/execution/environments/object_environment.rs b/nova_vm/src/execution/environments/object_environment.rs index 2200038f..5261f484 100644 --- a/nova_vm/src/execution/environments/object_environment.rs +++ b/nova_vm/src/execution/environments/object_environment.rs @@ -1,6 +1,24 @@ +use std::{marker::PhantomData, num::NonZeroU32}; + use super::Environment; use crate::types::Object; +#[derive(Debug, Clone, Copy)] +pub struct ObjectEnvironmentIndex(NonZeroU32, PhantomData); + +impl ObjectEnvironmentIndex { + pub const fn from_u32_index(value: u32) -> Self { + 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 into_index(self) -> usize { + self.0.get() as usize - 1 + } +} + /// 9.1.1.2 Object Environment Records /// https://tc39.es/ecma262/#sec-object-environment-records #[derive(Debug)] diff --git a/nova_vm/src/execution/environments/private_environment.rs b/nova_vm/src/execution/environments/private_environment.rs index b5379452..0cf13586 100644 --- a/nova_vm/src/execution/environments/private_environment.rs +++ b/nova_vm/src/execution/environments/private_environment.rs @@ -1,5 +1,21 @@ use crate::types::{Function, String, Value}; -use std::{cell::RefCell, collections::HashMap, rc::Rc}; +use std::{collections::HashMap, marker::PhantomData, num::NonZeroU32}; + +#[derive(Debug, Clone, Copy)] +pub struct PrivateEnvironmentIndex(NonZeroU32, PhantomData); + +impl PrivateEnvironmentIndex { + pub const fn from_u32_index(value: u32) -> Self { + 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 into_index(self) -> usize { + self.0.get() as usize - 1 + } +} #[derive(Debug)] pub enum PrivateElement { @@ -14,7 +30,7 @@ pub enum PrivateElement { #[derive(Debug)] pub struct PrivateEnvironment { /// [[OuterPrivateEnvironment]] - outer_private_environment: Option>>, + outer_private_environment: Option, /// [[Names]] names: HashMap, // TODO: Implement private names diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index b08ad337..4c2a241c 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -1,4 +1,4 @@ -use super::{Environment, PrivateEnvironment}; +use super::{Environment, PrivateEnvironment, RealmIdentifier, PrivateEnvironmentIndex}; use crate::{execution::Realm, language::Script, types::*}; use std::{cell::RefCell, rc::Rc}; @@ -20,7 +20,7 @@ pub struct ECMAScriptCode { pub variable_environment: Environment, /// PrivateEnvironment - pub private_environment: Option>>, + pub private_environment: Option, } /// 9.4 Execution Contexts @@ -35,7 +35,7 @@ pub struct ExecutionContext<'ctx, 'host> { pub function: Option, /// Realm - pub realm: Rc>>, + pub realm: RealmIdentifier<'ctx, 'host>, /// ScriptOrModule pub script_or_module: Option>, diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 60d98c54..2e2f6c29 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -1,11 +1,12 @@ mod intrinsics; -use super::{Agent, GlobalEnvironment}; -use crate::{types::Object, Heap}; +use super::{environments::global_environment::GlobalEnvironmentIndex, Agent}; +use crate::types::Object; pub use intrinsics::Intrinsics; -use std::{any::Any, cell::RefCell, rc::Rc}; +use std::{any::Any, cell::RefCell, rc::Rc, marker::PhantomData}; -pub struct RealmIdentifier(u32); +#[derive(Debug, Clone, Copy)] +pub struct RealmIdentifier<'ctx, 'host>(u32, PhantomData>); /// 9.3 Realms /// https://tc39.es/ecma262/#sec-code-realms @@ -22,7 +23,7 @@ pub struct Realm<'ctx, 'host> { pub global_object: Object, /// [[GlobalEnv]] - pub global_env: Rc>, + pub global_env: GlobalEnvironmentIndex, /// [[HostDefined]] pub host_defined: Option>>, diff --git a/nova_vm/src/heap/indexes.rs b/nova_vm/src/heap/indexes.rs index 7af7a5a3..8ccf236f 100644 --- a/nova_vm/src/heap/indexes.rs +++ b/nova_vm/src/heap/indexes.rs @@ -96,7 +96,6 @@ impl BaseIndex { } 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. @@ -115,7 +114,6 @@ impl BaseIndex { } 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. diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 7cd334cf..27726bf1 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -1,5 +1,5 @@ use crate::{ - execution::{ECMAScriptCode, Environment, ExecutionContext, Realm, ScriptOrModule}, + execution::{ECMAScriptCode, Environment, ExecutionContext, Realm, ScriptOrModule, RealmIdentifier}, types::Value, }; use oxc_allocator::Allocator; @@ -15,7 +15,7 @@ pub type HostDefined<'ctx> = &'ctx mut dyn Any; #[derive(Debug)] pub struct Script<'ctx, 'host> { /// [[Realm]] - pub realm: Rc>>, + pub realm: RealmIdentifier<'ctx, 'host>, /// [[ECMAScriptCode]] pub ecmascript_code: Rc>, @@ -37,7 +37,7 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { pub fn parse( allocator: &'host Allocator, source_text: &'host str, - realm: Rc>>, + realm: RealmIdentifier<'ctx, 'host>, host_defined: Option>, ) -> ScriptOrErrors<'ctx, 'host> { // 1. Let script be ParseText(sourceText, Script). From 24204e2f185ce6adde5660777cb5b90bc080be08 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 15 Oct 2023 10:43:28 +0300 Subject: [PATCH 29/32] Environments in Heap --- nova_vm/src/builtins/array.rs | 11 ++-- nova_vm/src/builtins/builtin_function.rs | 26 ++++---- nova_vm/src/builtins/ecmascript_function.rs | 4 +- nova_vm/src/builtins/number.rs | 35 +++++----- nova_vm/src/execution.rs | 6 +- nova_vm/src/execution/agent.rs | 22 ++++++- nova_vm/src/execution/environments.rs | 65 +++++++++++++++++++ .../environments/declarative_environment.rs | 2 +- .../environments/function_environment.rs | 2 +- nova_vm/src/execution/execution_context.rs | 2 +- nova_vm/src/execution/realm.rs | 12 +++- nova_vm/src/heap.rs | 7 +- nova_vm/src/language/script.rs | 30 ++++----- nova_vm/src/types/language/object.rs | 13 +--- nova_vm/src/types/spec/property_descriptor.rs | 3 +- 15 files changed, 163 insertions(+), 77 deletions(-) diff --git a/nova_vm/src/builtins/array.rs b/nova_vm/src/builtins/array.rs index 01a05a5f..b20cca7a 100644 --- a/nova_vm/src/builtins/array.rs +++ b/nova_vm/src/builtins/array.rs @@ -1,9 +1,6 @@ -use super::{ - builtin_function::define_builtin_function, create_builtin_function, ArgumentsList, Behaviour, - Builtin, BuiltinFunctionArgs, -}; +use super::{create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs}; use crate::{ - execution::{Agent, JsResult, Realm}, + execution::{Agent, JsResult}, types::{Object, Value}, }; @@ -11,11 +8,11 @@ pub struct ArrayConstructor; impl Builtin for ArrayConstructor { fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult { - let realm = agent.current_realm(); + let realm = agent.current_realm_id(); let object = create_builtin_function( agent, Behaviour::Regular(Self::behaviour), - BuiltinFunctionArgs::new(1, "Array", &mut realm.borrow_mut()), + BuiltinFunctionArgs::new(1, "Array", realm), ); Ok(object.into_object()) diff --git a/nova_vm/src/builtins/builtin_function.rs b/nova_vm/src/builtins/builtin_function.rs index e18fc60d..5ee4f2f5 100644 --- a/nova_vm/src/builtins/builtin_function.rs +++ b/nova_vm/src/builtins/builtin_function.rs @@ -1,5 +1,5 @@ use crate::{ - execution::{Agent, Intrinsics, JsResult, Realm}, + execution::{Agent, Intrinsics, JsResult, Realm, RealmIdentifier}, heap::CreateHeapData, types::{Function, Object, PropertyDescriptor, Value}, Heap, @@ -33,13 +33,13 @@ pub trait Builtin { pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> { pub length: u32, pub name: &'a str, - pub realm: Option<&'a mut Realm<'ctx, 'host>>, + pub realm: Option>, pub prototype: Option, pub prefix: Option, } impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { - pub fn new(length: u32, name: &'a str, realm: &'a mut Realm<'ctx, 'host>) -> Self { + pub fn new(length: u32, name: &'a str, realm: RealmIdentifier<'ctx, 'host>) -> Self { Self { length, name, @@ -52,18 +52,22 @@ impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> { /// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ) /// https://tc39.es/ecma262/#sec-createbuiltinfunction pub fn create_builtin_function<'a, 'b: 'a>( - agent: &mut Agent, + agent: &mut Agent<'b, 'b>, behaviour: Behaviour, args: BuiltinFunctionArgs<'a, 'b, 'b>, ) -> Function { - let heap = &mut agent.heap; // 1. If realm is not present, set realm to the current Realm Record. - let realm = args.realm.unwrap(); // TODO: load record + let realm_id = args.realm.unwrap_or(agent.current_realm_id()); // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]]. - let prototype = args - .prototype - .unwrap_or_else(|| realm.intrinsics.function_prototype().into()); + let prototype = args.prototype.unwrap_or_else(|| { + agent + .get_realm(realm_id) + .intrinsics + .function_prototype() + .into() + }); + let heap = &mut agent.heap; // TODO: Steps 3-4 // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 @@ -100,12 +104,12 @@ pub fn create_builtin_function<'a, 'b: 'a>( } pub fn define_builtin_function<'a, 'b>( - agent: &mut Agent, + agent: &mut Agent<'b, 'b>, object: Object, name: &'a str, behaviour: RegularFn, length: u32, - realm: &'a mut Realm<'b, 'b>, + realm: RealmIdentifier<'b, 'b>, ) -> JsResult<()> { let function = create_builtin_function( agent, diff --git a/nova_vm/src/builtins/ecmascript_function.rs b/nova_vm/src/builtins/ecmascript_function.rs index 3336c3f9..e83eba36 100644 --- a/nova_vm/src/builtins/ecmascript_function.rs +++ b/nova_vm/src/builtins/ecmascript_function.rs @@ -3,7 +3,9 @@ use std::{cell::RefCell, rc::Rc}; use oxc_ast::ast::{FormalParameters, FunctionBody}; use crate::{ - execution::{Agent, Environment, JsResult, PrivateEnvironmentIndex, RealmIdentifier, ScriptOrModule}, + execution::{ + Agent, Environment, JsResult, PrivateEnvironmentIndex, RealmIdentifier, ScriptOrModule, + }, types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, }; diff --git a/nova_vm/src/builtins/number.rs b/nova_vm/src/builtins/number.rs index 4ef41b66..b79d083a 100644 --- a/nova_vm/src/builtins/number.rs +++ b/nova_vm/src/builtins/number.rs @@ -1,14 +1,11 @@ -use std::{borrow::BorrowMut, cell::RefCell, rc::Rc}; - use super::{ builtin_function::{define_builtin_function, define_builtin_property}, - create_builtin_function, ordinary, todo_builtin, ArgumentsList, Behaviour, Builtin, - BuiltinFunctionArgs, + create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs, }; use crate::{ - execution::{Agent, Intrinsics, JsResult, Realm}, - heap::{BuiltinObjectIndexes, CreateHeapData}, - types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, + execution::{Agent, JsResult}, + heap::CreateHeapData, + types::{Number, Object, PropertyDescriptor, Value}, SmallInteger, }; @@ -16,18 +13,18 @@ pub struct NumberConstructor; impl Builtin for NumberConstructor { fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult { - let mut realm = agent.current_realm(); - let mut realm = RefCell::borrow_mut(&mut realm); + let realm_id = agent.current_realm_id(); - let prototype = Some(realm.intrinsics.function_prototype()); + let function_prototype = agent.current_realm().intrinsics.function_prototype(); + let number_prototype = agent.current_realm().intrinsics.number_prototype(); let object: Object = create_builtin_function( agent, Behaviour::Constructor(Self::behaviour), BuiltinFunctionArgs { length: 1, name: "Number", - realm: Some(&mut realm), - prototype, + realm: Some(realm_id), + prototype: Some(function_prototype), ..Default::default() }, ) @@ -145,11 +142,11 @@ impl Builtin for NumberConstructor { }, )?; - define_builtin_function(agent, object, "isFinite", todo_builtin, 1, &mut realm)?; - define_builtin_function(agent, object, "isNaN", todo_builtin, 1, &mut realm)?; - define_builtin_function(agent, object, "isSafeInteger", todo_builtin, 1, &mut realm)?; - define_builtin_function(agent, object, "parseFloat", todo_builtin, 1, &mut realm)?; - define_builtin_function(agent, object, "parseInt", todo_builtin, 2, &mut realm)?; + define_builtin_function(agent, object, "isFinite", todo_builtin, 1, realm_id)?; + define_builtin_function(agent, object, "isNaN", todo_builtin, 1, realm_id)?; + define_builtin_function(agent, object, "isSafeInteger", todo_builtin, 1, realm_id)?; + define_builtin_function(agent, object, "parseFloat", todo_builtin, 1, realm_id)?; + define_builtin_function(agent, object, "parseInt", todo_builtin, 2, realm_id)?; // 21.1.2.15 Number.prototype // https://tc39.es/ecma262/#sec-number.prototype @@ -157,7 +154,7 @@ impl Builtin for NumberConstructor { object, "prototype", PropertyDescriptor { - value: Some(realm.intrinsics.number_prototype().into()), + value: Some(number_prototype.into_value()), writable: Some(false), enumerable: Some(false), configurable: Some(false), @@ -168,7 +165,7 @@ impl Builtin for NumberConstructor { // 21.1.3.1 Number.prototype.constructor // https://tc39.es/ecma262/#sec-number.prototype.constructor define_builtin_property( - realm.intrinsics.number_prototype(), + number_prototype, "constructor", PropertyDescriptor { value: Some(object.into_value()), diff --git a/nova_vm/src/execution.rs b/nova_vm/src/execution.rs index b3181b62..ea4a08c8 100644 --- a/nova_vm/src/execution.rs +++ b/nova_vm/src/execution.rs @@ -6,9 +6,9 @@ mod realm; pub use agent::{Agent, JsResult}; pub use environments::{ - DeclarativeEnvironment, DeclarativeEnvironmentIndex, Environment, FunctionEnvironment, - FunctionEnvironmentIndex, GlobalEnvironment, GlobalEnvironmentIndex, ObjectEnvironment, - ObjectEnvironmentIndex, PrivateEnvironment, PrivateEnvironmentIndex, + DeclarativeEnvironment, DeclarativeEnvironmentIndex, Environment, Environments, + FunctionEnvironment, FunctionEnvironmentIndex, GlobalEnvironment, GlobalEnvironmentIndex, + ObjectEnvironment, ObjectEnvironmentIndex, PrivateEnvironment, PrivateEnvironmentIndex, }; pub use execution_context::{ECMAScriptCode, ExecutionContext, ScriptOrModule}; pub use realm::{Intrinsics, Realm, RealmIdentifier}; diff --git a/nova_vm/src/execution/agent.rs b/nova_vm/src/execution/agent.rs index 3219bf87..27d839f0 100644 --- a/nova_vm/src/execution/agent.rs +++ b/nova_vm/src/execution/agent.rs @@ -39,10 +39,30 @@ pub struct Agent<'ctx, 'host> { } impl<'ctx, 'host> Agent<'ctx, 'host> { - pub fn current_realm(&self) -> RealmIdentifier<'ctx, 'host> { + pub fn current_realm_id(&self) -> RealmIdentifier<'ctx, 'host> { self.execution_context_stack.last().unwrap().realm } + pub fn current_realm(&self) -> &Realm<'ctx, 'host> { + self.get_realm(self.current_realm_id()) + } + + pub fn current_realm_mut(&mut self) -> &mut Realm<'ctx, 'host> { + self.get_realm_mut(self.current_realm_id()) + } + + pub fn get_realm(&self, id: RealmIdentifier<'ctx, 'host>) -> &Realm<'ctx, 'host> { + self.realms + .get(id.into_index()) + .expect("RealmIdentifier did not match a Realm") + } + + pub fn get_realm_mut(&mut self, id: RealmIdentifier<'ctx, 'host>) -> &mut Realm<'ctx, 'host> { + self.realms + .get_mut(id.into_index()) + .expect("RealmIdentifier did not match a Realm") + } + /// 5.2.3.2 Throw an Exception /// https://tc39.es/ecma262/#sec-throw-an-exception pub fn throw_exception(&mut self, kind: ExceptionType, message: &'static str) -> () { diff --git a/nova_vm/src/execution/environments.rs b/nova_vm/src/execution/environments.rs index 6c5a2e97..ae94cf6e 100644 --- a/nova_vm/src/execution/environments.rs +++ b/nova_vm/src/execution/environments.rs @@ -24,3 +24,68 @@ pub enum Environment { GlobalEnvironment(GlobalEnvironmentIndex), ObjectEnvironment(ObjectEnvironmentIndex), } + +#[derive(Debug)] +pub struct Environments { + declarative: Vec>, + function: Vec>, + global: Vec>, + object: Vec>, +} + +impl Default for Environments { + fn default() -> Self { + Self { + declarative: Vec::with_capacity(256), + function: Vec::with_capacity(1024), + global: Vec::with_capacity(1), + object: Vec::with_capacity(1024), + } + } +} + +impl Environments { + pub fn get_declarative_environment<'a>( + &'a self, + index: DeclarativeEnvironmentIndex, + ) -> &'a DeclarativeEnvironment { + self.declarative + .get(index.into_index()) + .expect("DeclarativeEnvironmentIndex did not match to any vector index") + .as_ref() + .expect("DeclarativeEnvironmentIndex pointed to a None") + } + + pub fn get_function_environment<'a>( + &'a self, + index: FunctionEnvironmentIndex, + ) -> &'a FunctionEnvironment { + self.function + .get(index.into_index()) + .expect("FunctionEnvironmentIndex did not match to any vector index") + .as_ref() + .expect("FunctionEnvironmentIndex pointed to a None") + } + + pub fn get_global_environment<'a>( + &'a self, + index: GlobalEnvironmentIndex, + ) -> &'a GlobalEnvironment { + self.global + .get(index.into_index()) + .expect("GlobalEnvironmentIndex did not match to any vector index") + .as_ref() + .expect("GlobalEnvironmentIndex pointed to a None") + } + + pub fn get_object_environment<'a>( + &'a self, + index: ObjectEnvironmentIndex, + ) -> &'a ObjectEnvironment { + self.object + .get(index.into_index()) + .expect("ObjectEnvironmentIndex did not match to any vector index") + .as_ref() + .expect("ObjectEnvironmentIndex pointed to a None") + } +} diff --git a/nova_vm/src/execution/environments/declarative_environment.rs b/nova_vm/src/execution/environments/declarative_environment.rs index 12ba668c..e0122a6d 100644 --- a/nova_vm/src/execution/environments/declarative_environment.rs +++ b/nova_vm/src/execution/environments/declarative_environment.rs @@ -1,6 +1,6 @@ use super::Environment; use crate::types::{String, Value}; -use std::{collections::HashMap, num::NonZeroU32, marker::PhantomData}; +use std::{collections::HashMap, marker::PhantomData, num::NonZeroU32}; #[derive(Debug, Clone, Copy)] pub struct DeclarativeEnvironmentIndex(NonZeroU32, PhantomData); diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs index 57cea34c..1060de07 100644 --- a/nova_vm/src/execution/environments/function_environment.rs +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, num::NonZeroU32, marker::PhantomData}; +use std::{collections::HashMap, marker::PhantomData, num::NonZeroU32}; use super::{declarative_environment::Binding, Environment}; use crate::{ diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index 4c2a241c..bfde5304 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -1,4 +1,4 @@ -use super::{Environment, PrivateEnvironment, RealmIdentifier, PrivateEnvironmentIndex}; +use super::{Environment, PrivateEnvironment, PrivateEnvironmentIndex, RealmIdentifier}; use crate::{execution::Realm, language::Script, types::*}; use std::{cell::RefCell, rc::Rc}; diff --git a/nova_vm/src/execution/realm.rs b/nova_vm/src/execution/realm.rs index 2e2f6c29..08a60048 100644 --- a/nova_vm/src/execution/realm.rs +++ b/nova_vm/src/execution/realm.rs @@ -3,11 +3,21 @@ mod intrinsics; use super::{environments::global_environment::GlobalEnvironmentIndex, Agent}; use crate::types::Object; pub use intrinsics::Intrinsics; -use std::{any::Any, cell::RefCell, rc::Rc, marker::PhantomData}; +use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc}; #[derive(Debug, Clone, Copy)] pub struct RealmIdentifier<'ctx, 'host>(u32, PhantomData>); +impl<'ctx, 'host> RealmIdentifier<'ctx, 'host> { + pub const fn from_u32_index(value: u32) -> Self { + Self(value, PhantomData) + } + + pub const fn into_index(self) -> usize { + self.0 as usize + } +} + /// 9.3 Realms /// https://tc39.es/ecma262/#sec-code-realms #[derive(Debug)] diff --git a/nova_vm/src/heap.rs b/nova_vm/src/heap.rs index 25abe163..4b05ea81 100644 --- a/nova_vm/src/heap.rs +++ b/nova_vm/src/heap.rs @@ -43,11 +43,15 @@ use self::{ string::{initialize_string_heap, StringHeapData}, symbol::{initialize_symbol_heap, SymbolHeapData}, }; -use crate::types::{Function, Number, Object, String, Value}; +use crate::{ + execution::Environments, + types::{Function, Number, Object, String, Value}, +}; use wtf8::{Wtf8, Wtf8Buf}; #[derive(Debug)] pub struct Heap { + pub environments: Environments, /// ElementsArrays is where all element arrays live; /// Element arrays are static arrays of Values plus /// a HashMap of possible property descriptors. @@ -165,6 +169,7 @@ impl CreateHeapData for Heap { impl Heap { pub fn new() -> Heap { let mut heap = Heap { + environments: Default::default(), elements: ElementArrays { e2pow4: ElementArray2Pow4::with_capacity(1024), e2pow6: ElementArray2Pow6::with_capacity(1024), diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 27726bf1..9ef45a8a 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -1,5 +1,8 @@ use crate::{ - execution::{ECMAScriptCode, Environment, ExecutionContext, Realm, ScriptOrModule, RealmIdentifier}, + execution::{ + Agent, ECMAScriptCode, Environment, ExecutionContext, Realm, RealmIdentifier, + ScriptOrModule, + }, types::Value, }; use oxc_allocator::Allocator; @@ -61,19 +64,13 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { /// 16.1.6 ScriptEvaluation ( scriptRecord ) /// https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation - pub fn evaluate(self) -> Value { + pub fn evaluate(self, agent: &mut Agent<'ctx, 'host>) -> Value { let ecmascript_code = self.ecmascript_code.clone(); - let realm = self.realm.clone(); - let agent = { - let realm = self.realm.borrow(); - realm.agent.clone() - }; + let realm_id = self.realm; + let realm = agent.get_realm_mut(realm_id); // 1. Let globalEnv be scriptRecord.[[Realm]].[[GlobalEnv]]. - let global_env = { - let realm = self.realm.borrow(); - realm.global_env.clone() - }; + let global_env = realm.global_env; // 2. Let scriptContext be a new ECMAScript code execution context. let script_context = ExecutionContext { @@ -81,7 +78,7 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { function: None, // 4. Set the Realm of scriptContext to scriptRecord.[[Realm]]. - realm, + realm: realm_id, // 5. Set the ScriptOrModule of scriptContext to scriptRecord. script_or_module: Some(ScriptOrModule::Script(Rc::new(RefCell::new(self)))), @@ -101,10 +98,7 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { // TODO: 9. Suspend the running execution context. // 10. Push scriptContext onto the execution context stack; scriptContext is now the running execution context. - agent - .borrow_mut() - .execution_context_stack - .push(script_context); + agent.execution_context_stack.push(script_context); // 11. Let script be scriptRecord.[[ECMAScriptCode]]. let script = ecmascript_code.as_ref(); @@ -144,10 +138,10 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { // i. Set result to NormalCompletion(undefined). // 14. Suspend scriptContext and remove it from the execution context stack. - _ = agent.borrow_mut().execution_context_stack.pop(); + _ = agent.execution_context_stack.pop(); // 15. Assert: The execution context stack is not empty. - debug_assert!(agent.borrow().execution_context_stack.len() > 0); + debug_assert!(agent.execution_context_stack.len() > 0); // TODO: 16. Resume the context that is now on the top of the execution context stack as the // running execution context. diff --git a/nova_vm/src/types/language/object.rs b/nova_vm/src/types/language/object.rs index 30a54969..92b985f9 100644 --- a/nova_vm/src/types/language/object.rs +++ b/nova_vm/src/types/language/object.rs @@ -2,21 +2,15 @@ mod data; mod internal_methods; mod property_key; mod property_storage; - -use std::array; - use super::{ - value::{ - ARRAY_DISCRIMINANT, DATE_DISCRIMINANT, ERROR_DISCRIMINANT, FUNCTION_DISCRIMINANT, - OBJECT_DISCRIMINANT, REGEXP_DISCRIMINANT, - }, + value::{ARRAY_DISCRIMINANT, FUNCTION_DISCRIMINANT, OBJECT_DISCRIMINANT}, Value, }; use crate::{ builtins::ordinary, - execution::{agent::ExceptionType, Agent, Intrinsics, JsResult}, + execution::{agent::ExceptionType, Agent, JsResult}, heap::{ - indexes::{ArrayIndex, DateIndex, ErrorIndex, FunctionIndex, ObjectIndex, RegExpIndex}, + indexes::{ArrayIndex, FunctionIndex, ObjectIndex}, GetHeapData, }, types::PropertyDescriptor, @@ -125,7 +119,6 @@ impl Object { pub fn prototype(self, agent: &mut Agent) -> Option { let heap = &agent.heap; let realm = agent.current_realm(); - let realm = realm.borrow(); match self { Object::Object(object) => { diff --git a/nova_vm/src/types/spec/property_descriptor.rs b/nova_vm/src/types/spec/property_descriptor.rs index f97dff1d..8382258b 100644 --- a/nova_vm/src/types/spec/property_descriptor.rs +++ b/nova_vm/src/types/spec/property_descriptor.rs @@ -68,8 +68,7 @@ impl PropertyDescriptor { /// 6.2.6.4 FromPropertyDescriptor ( Desc ) /// https://tc39.es/ecma262/#sec-frompropertydescriptor pub fn from_property_descriptor(&self, agent: &mut Agent) -> JsResult { - let realm = agent.current_realm(); - let realm = realm.borrow_mut(); + let _realm = agent.current_realm(); // 1. If Desc is undefined, return undefined. From d57cc474a66824c582a400a8794540145a23bb63 Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 15 Oct 2023 11:26:02 +0300 Subject: [PATCH 30/32] Remove some unsafe from bytecode --- nova_vm/src/execution/execution_context.rs | 4 ++-- nova_vm/src/language/bytecode/executable.rs | 7 ++++--- nova_vm/src/language/bytecode/instructions.rs | 1 + nova_vm/src/language/bytecode/vm.rs | 10 +++++----- nova_vm/src/language/script.rs | 3 +-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index bfde5304..085b24c7 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -1,5 +1,5 @@ -use super::{Environment, PrivateEnvironment, PrivateEnvironmentIndex, RealmIdentifier}; -use crate::{execution::Realm, language::Script, types::*}; +use super::{Environment, PrivateEnvironmentIndex, RealmIdentifier}; +use crate::{language::Script, types::*}; use std::{cell::RefCell, rc::Rc}; #[derive(Debug)] diff --git a/nova_vm/src/language/bytecode/executable.rs b/nova_vm/src/language/bytecode/executable.rs index ea9e3438..4f41bd91 100644 --- a/nova_vm/src/language/bytecode/executable.rs +++ b/nova_vm/src/language/bytecode/executable.rs @@ -1,12 +1,13 @@ use super::Instruction; use crate::{types::Value, Heap}; use oxc_span::Atom; +use std::marker::PhantomData; pub type IndexType = u16; #[derive(Debug)] pub struct Executable<'ctx> { - pub heap: &'ctx mut Heap, + pub heap: PhantomData<&'ctx mut Heap>, pub instructions: Vec, pub constants: Vec, pub identifiers: Vec, @@ -40,7 +41,7 @@ impl<'ctx> Executable<'ctx> { pub fn add_index(&mut self, index: usize) { assert!(index < IndexType::MAX as usize); - let bytes: [u8; 2] = unsafe { std::mem::transmute(index as IndexType) }; + let bytes: [u8; 2] = (index as IndexType).to_ne_bytes(); self.instructions[index] = unsafe { std::mem::transmute(bytes[0]) }; self.instructions[index + 1] = unsafe { std::mem::transmute(bytes[0]) }; } @@ -54,7 +55,7 @@ impl<'ctx> Executable<'ctx> { pub fn set_jump_target(&mut self, jump: JumpIndex, index: usize) { assert!(index < IndexType::MAX as usize); - let bytes: [u8; 2] = unsafe { std::mem::transmute(index as IndexType) }; + let bytes: [u8; 2] = (index as IndexType).to_ne_bytes(); self.instructions[jump.index] = unsafe { std::mem::transmute(bytes[0]) }; self.instructions[jump.index + 1] = unsafe { std::mem::transmute(bytes[0]) }; } diff --git a/nova_vm/src/language/bytecode/instructions.rs b/nova_vm/src/language/bytecode/instructions.rs index d81f8168..8680cd92 100644 --- a/nova_vm/src/language/bytecode/instructions.rs +++ b/nova_vm/src/language/bytecode/instructions.rs @@ -1,6 +1,7 @@ use super::IndexType; #[derive(Debug, Clone, Copy)] +#[repr(u8)] #[non_exhaustive] pub enum Instruction { /// Store ApplyStringOrNumericBinaryOperator() as the result value. diff --git a/nova_vm/src/language/bytecode/vm.rs b/nova_vm/src/language/bytecode/vm.rs index 27694b40..53b727f4 100644 --- a/nova_vm/src/language/bytecode/vm.rs +++ b/nova_vm/src/language/bytecode/vm.rs @@ -51,10 +51,10 @@ impl<'ctx, 'host> Vm<'ctx, 'host> { } fn fetch_index(&mut self, executable: &Executable) -> IndexType { - let bytes: [u8; std::mem::size_of::()] = [ - unsafe { std::mem::transmute(self.fetch_instruction(executable).unwrap()) }, - unsafe { std::mem::transmute(self.fetch_instruction(executable).unwrap()) }, - ]; - unsafe { std::mem::transmute(bytes) } + let bytes = IndexType::from_ne_bytes([ + self.fetch_instruction(executable).unwrap() as u8, + self.fetch_instruction(executable).unwrap() as u8, + ]); + bytes as IndexType } } diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 9ef45a8a..805f939d 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -1,7 +1,6 @@ use crate::{ execution::{ - Agent, ECMAScriptCode, Environment, ExecutionContext, Realm, RealmIdentifier, - ScriptOrModule, + Agent, ECMAScriptCode, Environment, ExecutionContext, RealmIdentifier, ScriptOrModule, }, types::Value, }; From 9b8f3f6bb382f73c0c69e57508256d39a4e58c4f Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 15 Oct 2023 11:29:19 +0300 Subject: [PATCH 31/32] Rename Environment enum to EnvironmentIndex --- nova_vm/src/builtins/ecmascript_function.rs | 4 ++-- nova_vm/src/execution.rs | 2 +- nova_vm/src/execution/environments.rs | 2 +- .../src/execution/environments/declarative_environment.rs | 4 ++-- nova_vm/src/execution/environments/function_environment.rs | 4 ++-- nova_vm/src/execution/environments/object_environment.rs | 4 ++-- nova_vm/src/execution/execution_context.rs | 6 +++--- nova_vm/src/language/script.rs | 6 +++--- nova_vm/src/types/spec/reference.rs | 4 ++-- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/nova_vm/src/builtins/ecmascript_function.rs b/nova_vm/src/builtins/ecmascript_function.rs index e83eba36..869d231a 100644 --- a/nova_vm/src/builtins/ecmascript_function.rs +++ b/nova_vm/src/builtins/ecmascript_function.rs @@ -4,7 +4,7 @@ use oxc_ast::ast::{FormalParameters, FunctionBody}; use crate::{ execution::{ - Agent, Environment, JsResult, PrivateEnvironmentIndex, RealmIdentifier, ScriptOrModule, + Agent, EnvironmentIndex, JsResult, PrivateEnvironmentIndex, RealmIdentifier, ScriptOrModule, }, types::{Number, Object, PropertyDescriptor, PropertyKey, Value}, }; @@ -27,7 +27,7 @@ pub enum ThisMode { #[derive(Debug, Clone)] pub struct ECMAScriptFunction<'ctx, 'host> { /// [[Environment]] - pub environment: Environment, + pub environment: EnvironmentIndex, /// [[PrivateEnvironment]] pub private_environment: Option, diff --git a/nova_vm/src/execution.rs b/nova_vm/src/execution.rs index ea4a08c8..e9087dd4 100644 --- a/nova_vm/src/execution.rs +++ b/nova_vm/src/execution.rs @@ -6,7 +6,7 @@ mod realm; pub use agent::{Agent, JsResult}; pub use environments::{ - DeclarativeEnvironment, DeclarativeEnvironmentIndex, Environment, Environments, + DeclarativeEnvironment, DeclarativeEnvironmentIndex, EnvironmentIndex, Environments, FunctionEnvironment, FunctionEnvironmentIndex, GlobalEnvironment, GlobalEnvironmentIndex, ObjectEnvironment, ObjectEnvironmentIndex, PrivateEnvironment, PrivateEnvironmentIndex, }; diff --git a/nova_vm/src/execution/environments.rs b/nova_vm/src/execution/environments.rs index ae94cf6e..06f769e7 100644 --- a/nova_vm/src/execution/environments.rs +++ b/nova_vm/src/execution/environments.rs @@ -17,7 +17,7 @@ pub use private_environment::{PrivateEnvironment, PrivateEnvironmentIndex}; /// https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy #[derive(Debug, Clone)] #[repr(u8)] -pub enum Environment { +pub enum EnvironmentIndex { // Leave 0 for None option DeclarativeEnvironment(DeclarativeEnvironmentIndex) = 1, FunctionEnvironment(FunctionEnvironmentIndex), diff --git a/nova_vm/src/execution/environments/declarative_environment.rs b/nova_vm/src/execution/environments/declarative_environment.rs index e0122a6d..f5ab8c9a 100644 --- a/nova_vm/src/execution/environments/declarative_environment.rs +++ b/nova_vm/src/execution/environments/declarative_environment.rs @@ -1,4 +1,4 @@ -use super::Environment; +use super::EnvironmentIndex; use crate::types::{String, Value}; use std::{collections::HashMap, marker::PhantomData, num::NonZeroU32}; @@ -22,7 +22,7 @@ impl DeclarativeEnvironmentIndex { /// https://tc39.es/ecma262/#sec-declarative-environment-records #[derive(Debug)] pub struct DeclarativeEnvironment { - pub outer_env: Option, + pub outer_env: Option, pub bindings: HashMap, } diff --git a/nova_vm/src/execution/environments/function_environment.rs b/nova_vm/src/execution/environments/function_environment.rs index 1060de07..378090c7 100644 --- a/nova_vm/src/execution/environments/function_environment.rs +++ b/nova_vm/src/execution/environments/function_environment.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, marker::PhantomData, num::NonZeroU32}; -use super::{declarative_environment::Binding, Environment}; +use super::{declarative_environment::Binding, EnvironmentIndex}; use crate::{ heap::indexes::FunctionIndex, types::{Object, String, Value}, @@ -53,7 +53,7 @@ pub struct FunctionEnvironment { new_target: Option, /// [[OuterEnv]] - outer_env: Option, + outer_env: Option, /// Per https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy: /// > A _Function Environment Record_ is a _Declarative Environment Record_ [...] diff --git a/nova_vm/src/execution/environments/object_environment.rs b/nova_vm/src/execution/environments/object_environment.rs index 5261f484..be64eabd 100644 --- a/nova_vm/src/execution/environments/object_environment.rs +++ b/nova_vm/src/execution/environments/object_environment.rs @@ -1,6 +1,6 @@ use std::{marker::PhantomData, num::NonZeroU32}; -use super::Environment; +use super::EnvironmentIndex; use crate::types::Object; #[derive(Debug, Clone, Copy)] @@ -30,5 +30,5 @@ pub struct ObjectEnvironment { is_with_environment: bool, /// [[OuterEnv]] - outer_env: Option, + outer_env: Option, } diff --git a/nova_vm/src/execution/execution_context.rs b/nova_vm/src/execution/execution_context.rs index 085b24c7..8f7a74de 100644 --- a/nova_vm/src/execution/execution_context.rs +++ b/nova_vm/src/execution/execution_context.rs @@ -1,4 +1,4 @@ -use super::{Environment, PrivateEnvironmentIndex, RealmIdentifier}; +use super::{EnvironmentIndex, PrivateEnvironmentIndex, RealmIdentifier}; use crate::{language::Script, types::*}; use std::{cell::RefCell, rc::Rc}; @@ -14,10 +14,10 @@ pub enum ScriptOrModule<'ctx, 'host> { #[derive(Debug)] pub struct ECMAScriptCode { /// LexicalEnvironment - pub lexical_environment: Environment, + pub lexical_environment: EnvironmentIndex, /// VariableEnvironment - pub variable_environment: Environment, + pub variable_environment: EnvironmentIndex, /// PrivateEnvironment pub private_environment: Option, diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index 805f939d..f2c32bfd 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -1,6 +1,6 @@ use crate::{ execution::{ - Agent, ECMAScriptCode, Environment, ExecutionContext, RealmIdentifier, ScriptOrModule, + Agent, ECMAScriptCode, EnvironmentIndex, ExecutionContext, RealmIdentifier, ScriptOrModule, }, types::Value, }; @@ -84,10 +84,10 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { ecmascript_code: Some(ECMAScriptCode { // 6. Set the VariableEnvironment of scriptContext to globalEnv. - variable_environment: Environment::GlobalEnvironment(global_env.clone()), + variable_environment: EnvironmentIndex::GlobalEnvironment(global_env.clone()), // 7. Set the LexicalEnvironment of scriptContext to globalEnv. - lexical_environment: Environment::GlobalEnvironment(global_env.clone()), + lexical_environment: EnvironmentIndex::GlobalEnvironment(global_env.clone()), // 8. Set the PrivateEnvironment of scriptContext to null. private_environment: None, diff --git a/nova_vm/src/types/spec/reference.rs b/nova_vm/src/types/spec/reference.rs index 0027e6fb..ebaf8453 100644 --- a/nova_vm/src/types/spec/reference.rs +++ b/nova_vm/src/types/spec/reference.rs @@ -1,5 +1,5 @@ use crate::{ - execution::Environment, + execution::EnvironmentIndex, types::{Symbol, Value}, }; use oxc_span::Atom; @@ -53,7 +53,7 @@ impl Reference { #[derive(Debug)] pub enum Base { Value(Value), - Environment(Environment), + Environment(EnvironmentIndex), Unresolvable, } From 225e4fbe7e514ee7cf5f870970525a5876dffa2a Mon Sep 17 00:00:00 2001 From: Aapo Alasuutari Date: Sun, 15 Oct 2023 11:34:08 +0300 Subject: [PATCH 32/32] Remove meaningless clone --- nova_vm/src/language/script.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova_vm/src/language/script.rs b/nova_vm/src/language/script.rs index f2c32bfd..690a0106 100644 --- a/nova_vm/src/language/script.rs +++ b/nova_vm/src/language/script.rs @@ -84,10 +84,10 @@ impl<'ctx, 'host: 'ctx> Script<'ctx, 'host> { ecmascript_code: Some(ECMAScriptCode { // 6. Set the VariableEnvironment of scriptContext to globalEnv. - variable_environment: EnvironmentIndex::GlobalEnvironment(global_env.clone()), + variable_environment: EnvironmentIndex::GlobalEnvironment(global_env), // 7. Set the LexicalEnvironment of scriptContext to globalEnv. - lexical_environment: EnvironmentIndex::GlobalEnvironment(global_env.clone()), + lexical_environment: EnvironmentIndex::GlobalEnvironment(global_env), // 8. Set the PrivateEnvironment of scriptContext to null. private_environment: None,