From 97e9feca81269d8d115e430d761c8ca80d296382 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sat, 25 Apr 2026 21:04:19 +0900 Subject: [PATCH 01/10] refactor(jsruntime): add Builtins --- .../src/backend/clir/compiler/mod.rs | 16 +-- .../jsruntime/src/builtins/builtin_mod.rs.hbs | 14 +-- libs/jsruntime/src/builtins/error/imp.rs | 2 +- libs/jsruntime/src/builtins/function/mod.rs | 4 +- libs/jsruntime/src/builtins/mod.rs | 103 ++++++++++++++---- .../src/builtins/native_error_imp.rs.hbs | 4 +- libs/jsruntime/src/builtins/object/mod.rs | 4 +- libs/jsruntime/src/builtins/promise/imp.rs | 2 +- libs/jsruntime/src/builtins/string/imp.rs | 2 +- libs/jsruntime/src/jobs.rs | 4 +- libs/jsruntime/src/lib.rs | 73 ++----------- libs/jsruntime/src/semantics/mod.rs | 3 +- 12 files changed, 118 insertions(+), 113 deletions(-) diff --git a/libs/jsruntime/src/backend/clir/compiler/mod.rs b/libs/jsruntime/src/backend/clir/compiler/mod.rs index be442337b..6a21e48e5 100644 --- a/libs/jsruntime/src/backend/clir/compiler/mod.rs +++ b/libs/jsruntime/src/backend/clir/compiler/mod.rs @@ -170,7 +170,7 @@ pub fn compile( let program = &runtime.programs[program_id.index()]; for func in program.functions.iter() { let mut session = { - let global_object = runtime.global_object.as_handle(); + let global_object = runtime.builtins.global_object; Session { pref: &runtime.pref, program, @@ -178,9 +178,9 @@ pub fn compile( lambda_registry: &mut runtime.lambda_registry, code_registry: &mut runtime.code_registry, global_object, - object_prototype: runtime.object_prototype.unwrap(), - function_prototype: runtime.function_prototype.unwrap(), - promise_prototype: runtime.promise_prototype.unwrap(), + object_prototype: runtime.builtins.object_prototype.unwrap(), + function_prototype: runtime.builtins.function_prototype.unwrap(), + promise_prototype: runtime.builtins.promise_prototype.unwrap(), } }; context.compile_function(func, &mut session, &program.scope_tree); @@ -215,7 +215,7 @@ pub fn compile_function( let func = &program.functions[function_index]; let mut session = { - let global_object = runtime.global_object.as_handle(); + let global_object = runtime.builtins.global_object; Session { pref: &runtime.pref, program, @@ -223,9 +223,9 @@ pub fn compile_function( lambda_registry: &mut runtime.lambda_registry, code_registry: &mut runtime.code_registry, global_object, - object_prototype: runtime.object_prototype.unwrap(), - function_prototype: runtime.function_prototype.unwrap(), - promise_prototype: runtime.promise_prototype.unwrap(), + object_prototype: runtime.builtins.object_prototype.unwrap(), + function_prototype: runtime.builtins.function_prototype.unwrap(), + promise_prototype: runtime.builtins.promise_prototype.unwrap(), } }; diff --git a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs index ab16c0313..bbad91f24 100644 --- a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs +++ b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs @@ -22,7 +22,7 @@ impl Runtime { /// Returns `true` if `object` is a {{metadata.class}} object. #[allow(unused)] pub(crate) fn is_{{metadata.id}}_object(&self, object: HandleMut) -> bool { - object.is_instance_of(self.{{metadata.id}}_prototype) + object.is_instance_of(self.builtins.{{metadata.id}}_prototype) } /// Returns `true` if `value` holds a {{metadata.class}} object. @@ -34,7 +34,7 @@ impl Runtime { /// Creates the {{metadata.class}} constructor. pub(super) fn create_{{metadata.id}}_constructor(&mut self) -> HandleMut { logger::debug!(event = "create_{{metadata.id}}_constructor"); - debug_assert!(self.{{metadata.id}}_prototype.is_some()); + debug_assert!(self.builtins.{{metadata.id}}_prototype.is_some()); #[allow(unused_mut)] let mut constructor = self.create_builtin_function(&BuiltinFunctionParams { @@ -42,7 +42,7 @@ impl Runtime { name: const_string!("{{metadata.class}}"), length: 1, slots: &[], - prototype: self.{{metadata.id}}_prototype, + prototype: self.builtins.{{metadata.id}}_prototype, }); {{#each constructorProperties}} @@ -59,7 +59,7 @@ impl Runtime { {{/if}} {{/each}} // TODO(refactor): bind outside this function - if let Some(mut prototype) = self.{{metadata.id}}_prototype { + if let Some(mut prototype) = self.builtins.{{metadata.id}}_prototype { let _ = prototype.define_own_property(Symbol::CONSTRUCTOR.into(), Property::data_wxc(Value::Object(constructor))); } @@ -69,11 +69,11 @@ impl Runtime { /// Creates the {{metadata.class}} prototype object. pub(super) fn create_{{metadata.id}}_prototype(&mut self) -> HandleMut { logger::debug!(event = "creater_{{metadata.id}}_prototype"); - debug_assert!(self.{{metadata.inherits}}_prototype.is_some()); - debug_assert!(self.function_prototype.is_some()); + debug_assert!(self.builtins.{{metadata.inherits}}_prototype.is_some()); + debug_assert!(self.builtins.function_prototype.is_some()); #[allow(unused_mut)] - let mut prototype = self.create_object(self.{{metadata.inherits}}_prototype); + let mut prototype = self.create_object(self.builtins.{{metadata.inherits}}_prototype); {{#each prototypeProperties}} {{#if (eq kind "prototype.property")}} diff --git a/libs/jsruntime/src/builtins/error/imp.rs b/libs/jsruntime/src/builtins/error/imp.rs index 858c2eab3..c0e062703 100644 --- a/libs/jsruntime/src/builtins/error/imp.rs +++ b/libs/jsruntime/src/builtins/error/imp.rs @@ -27,7 +27,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.error_prototype) + runtime.create_object(runtime.builtins.error_prototype) }; object.set_error(); diff --git a/libs/jsruntime/src/builtins/function/mod.rs b/libs/jsruntime/src/builtins/function/mod.rs index 9e77eaba2..f084c58ad 100644 --- a/libs/jsruntime/src/builtins/function/mod.rs +++ b/libs/jsruntime/src/builtins/function/mod.rs @@ -20,7 +20,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::FUNCTION), length: 1, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, }) } @@ -28,7 +28,7 @@ impl Runtime { logger::debug!(event = "creater_function_prototype"); // TODO(fix): Function.prototype is a built-in function object. - let mut prototype = self.create_object(self.object_prototype); + let mut prototype = self.create_object(self.builtins.object_prototype); let _ = prototype.define_own_property( Symbol::LENGTH.into(), Property::data_xxx(Value::Number(0.0)), diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index ea4e59171..adee7be76 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -14,6 +14,7 @@ mod uri_error; use jsgc::Handle; use jsgc::HandleMut; +use jsgc::Heap; use jsparser::Symbol; use crate::Error; @@ -28,6 +29,62 @@ use crate::types::Status; use crate::types::String; use crate::types::Value; +#[derive(jsgc_derive::Trace)] +pub(crate) struct Builtins { + // [[GlobalObject]] + pub(crate) global_object: HandleMut, + // %Object.prototype% + pub(crate) object_prototype: Option>, + // %Function.prototype% + pub(crate) function_prototype: Option>, + // %String.prototype% + pub(crate) string_prototype: Option>, + // %Promise.prototype% + pub(crate) promise_prototype: Option>, + // %Error.prototype% + pub(crate) error_prototype: Option>, + // %AggregateError.prototype% + pub(crate) aggregate_error_prototype: Option>, + // %EvalError.prototype% + pub(crate) eval_error_prototype: Option>, + // %InternalError.prototype% + pub(crate) internal_error_prototype: Option>, + // %RangeError.prototype% + pub(crate) range_error_prototype: Option>, + // %ReferenceError.prototype% + pub(crate) reference_error_prototype: Option>, + // %SyntaxError.prototype% + pub(crate) syntax_error_prototype: Option>, + // %TypeError.prototype% + pub(crate) type_error_prototype: Option>, + // URIError.prototype% + pub(crate) uri_error_prototype: Option>, +} + +impl Builtins { + pub(crate) fn new(heap: &mut Heap) -> Self { + // TODO: pass [[Prototype]] of the global object. + let global_object = heap.alloc_mut(Object::new(None)); + + Self { + global_object, + object_prototype: None, + function_prototype: None, + string_prototype: None, + promise_prototype: None, + error_prototype: None, + aggregate_error_prototype: None, + eval_error_prototype: None, + internal_error_prototype: None, + reference_error_prototype: None, + range_error_prototype: None, + syntax_error_prototype: None, + type_error_prototype: None, + uri_error_prototype: None, + } + } +} + impl Runtime { // 19 The Global Object pub(crate) fn define_builtin_global_properties(&mut self) { @@ -41,26 +98,26 @@ impl Runtime { }; (kv: $key:expr, $value:expr) => { let prop = Property::data_xxx($value); - let result = self.global_object.define_own_property($key.into(), prop); + let result = self.builtins.global_object.define_own_property($key.into(), prop); debug_assert!(matches!(result, Ok(true))); }; } - self.object_prototype = Some(self.create_object(None)); - self.function_prototype = Some(self.create_function_prototype()); - self.string_prototype = Some(self.create_string_prototype()); - self.promise_prototype = Some(self.create_promise_prototype()); - self.error_prototype = Some(self.create_error_prototype()); - self.aggregate_error_prototype = Some(self.create_aggregate_error_prototype()); - self.eval_error_prototype = Some(self.create_eval_error_prototype()); - self.internal_error_prototype = Some(self.create_internal_error_prototype()); - self.range_error_prototype = Some(self.create_range_error_prototype()); - self.reference_error_prototype = Some(self.create_reference_error_prototype()); - self.syntax_error_prototype = Some(self.create_syntax_error_prototype()); - self.type_error_prototype = Some(self.create_type_error_prototype()); - self.uri_error_prototype = Some(self.create_uri_error_prototype()); - - let this = self.global_object.as_handle(); + self.builtins.object_prototype = Some(self.create_object(None)); + self.builtins.function_prototype = Some(self.create_function_prototype()); + self.builtins.string_prototype = Some(self.create_string_prototype()); + self.builtins.promise_prototype = Some(self.create_promise_prototype()); + self.builtins.error_prototype = Some(self.create_error_prototype()); + self.builtins.aggregate_error_prototype = Some(self.create_aggregate_error_prototype()); + self.builtins.eval_error_prototype = Some(self.create_eval_error_prototype()); + self.builtins.internal_error_prototype = Some(self.create_internal_error_prototype()); + self.builtins.range_error_prototype = Some(self.create_range_error_prototype()); + self.builtins.reference_error_prototype = Some(self.create_reference_error_prototype()); + self.builtins.syntax_error_prototype = Some(self.create_syntax_error_prototype()); + self.builtins.type_error_prototype = Some(self.create_type_error_prototype()); + self.builtins.uri_error_prototype = Some(self.create_uri_error_prototype()); + + let this = self.builtins.global_object.as_handle(); define! { // TODO: 19.1.1 globalThis @@ -77,7 +134,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::EVAL), length: 1, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, })), // 19.2.2 isFinite ( number ) Symbol::IS_FINITE => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -85,7 +142,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::IS_FINITE), length: 1, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, })), // 19.2.3 isNaN ( number ) Symbol::IS_NAN => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -93,7 +150,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::IS_NAN), length: 1, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, })), // 19.2.4 parseFloat ( string ) Symbol::PARSE_FLOAT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -101,7 +158,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::PARSE_FLOAT), length: 1, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, })), // 19.2.5 parseInt ( string, radix ) Symbol::PARSE_INT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -109,7 +166,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::PARSE_INT), length: 2, slots: &[], - prototype: self.function_prototype, + prototype: self.builtins.function_prototype, })), // 19.3.1 AggregateError ( . . . ) Symbol::AGGREGATE_ERROR => Value::Object(self.create_aggregate_error_constructor()), @@ -149,9 +206,9 @@ impl Runtime { ?params.slots, ?params.prototype ); - debug_assert!(self.function_prototype.is_some()); + debug_assert!(self.builtins.function_prototype.is_some()); let closure = self.create_closure(params.lambda, LambdaId::HOST, 0); - let mut func = self.create_object(self.function_prototype); + let mut func = self.create_object(self.builtins.function_prototype); func.slots_mut().extend_from_slice(params.slots); func.set_closure(closure); if let Some(prototype) = params.prototype { diff --git a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs index 7c6c40087..7ba25d887 100644 --- a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs +++ b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs @@ -33,7 +33,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.{{id}}_prototype) + runtime.create_object(runtime.builtins.{{id}}_prototype) }; object.set_error(); @@ -79,7 +79,7 @@ pub fn {{id}}_prototype_name(_runtime: &mut Runtime, mut prototype: Handle impl Runtime { #[allow(unused)] pub(crate) fn create_{{id}}(&mut self, msg: Option>) -> HandleMut { - let mut object = self.create_object(self.{{id}}_prototype); + let mut object = self.create_object(self.builtins.{{id}}_prototype); if let Some(msg) = msg { let _ = object.define_own_property( Symbol::MESSAGE.into(), diff --git a/libs/jsruntime/src/builtins/object/mod.rs b/libs/jsruntime/src/builtins/object/mod.rs index 4478c4c0c..6304aedc6 100644 --- a/libs/jsruntime/src/builtins/object/mod.rs +++ b/libs/jsruntime/src/builtins/object/mod.rs @@ -17,14 +17,14 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::OBJECT), length: 0, slots: &[], - prototype: self.object_prototype, + prototype: self.builtins.object_prototype, }) } fn create_object_object(&mut self, context: &mut CallContext) -> Result { match context.args().first() { None | Some(Value::Undefined) | Some(Value::Null) => { - let object = self.create_object(self.object_prototype); + let object = self.create_object(self.builtins.object_prototype); // TODO(feat): NewTarget Ok(Value::Object(object)) } diff --git a/libs/jsruntime/src/builtins/promise/imp.rs b/libs/jsruntime/src/builtins/promise/imp.rs index 64254b8d0..0a91cd003 100644 --- a/libs/jsruntime/src/builtins/promise/imp.rs +++ b/libs/jsruntime/src/builtins/promise/imp.rs @@ -37,7 +37,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.promise_prototype) + runtime.create_object(runtime.builtins.promise_prototype) }; object.set_promise(promise); diff --git a/libs/jsruntime/src/builtins/string/imp.rs b/libs/jsruntime/src/builtins/string/imp.rs index 9b712389c..220d714ee 100644 --- a/libs/jsruntime/src/builtins/string/imp.rs +++ b/libs/jsruntime/src/builtins/string/imp.rs @@ -603,7 +603,7 @@ impl Runtime { this } else { // 10.4.3.4 StringCreate ( value, prototype ) - self.create_object(self.string_prototype) + self.create_object(self.builtins.string_prototype) }; let length = string.len(); object.set_string(string); diff --git a/libs/jsruntime/src/jobs.rs b/libs/jsruntime/src/jobs.rs index 1f6161223..5c1fec0ab 100644 --- a/libs/jsruntime/src/jobs.rs +++ b/libs/jsruntime/src/jobs.rs @@ -183,7 +183,7 @@ mod tests { let closure = runtime.create_closure(dummy, LambdaId::HOST, 0); let coroutine = runtime.create_coroutine(closure, 0, 0, 0); let promise = runtime.create_promise(coroutine); - let mut object = runtime.create_object(runtime.promise_prototype); + let mut object = runtime.create_object(runtime.builtins.promise_prototype); object.set_promise(promise); runtime.emit_promise_resolved(object, $value); }; @@ -194,7 +194,7 @@ mod tests { let closure = runtime.create_closure(dummy, LambdaId::HOST, 0); let coroutine = runtime.create_coroutine(closure, 0, 0, 0); let promise = runtime.create_promise(coroutine); - let mut object = runtime.create_object(runtime.promise_prototype); + let mut object = runtime.create_object(runtime.builtins.promise_prototype); object.set_promise(promise); runtime.emit_promise_rejected(object, $value); }; diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 76af6e384..08d1c10b2 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -20,6 +20,7 @@ use jsparser::Symbol; use jsparser::SymbolRegistry; use backend::CodeRegistry; +use builtins::Builtins; use jobs::JobRunner; use lambda::LambdaKind; use lambda::LambdaRegistry; @@ -102,37 +103,8 @@ pub struct Runtime { code_registry: CodeRegistry, programs: Vec, heap: Heap, + builtins: Builtins, job_runner: JobRunner, - - // [[GlobalObject]] - global_object: HandleMut, - // %Object.prototype% - object_prototype: Option>, - // %Function.prototype% - function_prototype: Option>, - // %String.prototype% - string_prototype: Option>, - // %Promise.prototype% - promise_prototype: Option>, - // %Error.prototype% - error_prototype: Option>, - // %AggregateError.prototype% - aggregate_error_prototype: Option>, - // %EvalError.prototype% - eval_error_prototype: Option>, - // %InternalError.prototype% - internal_error_prototype: Option>, - // %RangeError.prototype% - range_error_prototype: Option>, - // %ReferenceError.prototype% - reference_error_prototype: Option>, - // %SyntaxError.prototype% - syntax_error_prototype: Option>, - // %TypeError.prototype% - type_error_prototype: Option>, - // URIError.prototype% - uri_error_prototype: Option>, - monitor: Option>, extension: X, } @@ -140,9 +112,7 @@ pub struct Runtime { impl Runtime { pub fn with_extension(extension: X) -> Self { let mut heap = Heap::new(); - - // TODO: pass [[Prototype]] of the global object. - let global_object = heap.alloc_mut(Object::new(Default::default())); + let builtins = Builtins::new(&mut heap); let mut runtime = Self { pref: Default::default(), @@ -151,21 +121,8 @@ impl Runtime { code_registry: CodeRegistry::new(), programs: vec![], heap, + builtins, job_runner: Default::default(), - global_object, - object_prototype: None, - function_prototype: None, - string_prototype: None, - promise_prototype: None, - error_prototype: None, - aggregate_error_prototype: None, - eval_error_prototype: None, - internal_error_prototype: None, - reference_error_prototype: None, - range_error_prototype: None, - syntax_error_prototype: None, - type_error_prototype: None, - uri_error_prototype: None, monitor: None, extension, }; @@ -204,12 +161,15 @@ impl Runtime { logger::debug!(event = "register_host_function", name, ?symbol); let lambda = types::into_lambda(host_fn); let closure = self.create_closure(lambda, LambdaId::HOST, 0); - let mut object = self.create_object(self.function_prototype); + let mut object = self.create_object(self.builtins.function_prototype); object.set_closure(closure); let value = Value::Object(object); // TODO: add `flags` to the arguments. let prop = Property::data_xxx(value); - let result = self.global_object.define_own_property(symbol.into(), prop); + let result = self + .builtins + .global_object + .define_own_property(symbol.into(), prop); debug_assert!(matches!(result, Ok(true))); } @@ -528,20 +488,7 @@ impl Drop for Runtime { // TODO(feat): derive(Trace) impl Trace for Runtime { fn trace(&self, visits: &mut jsgc::VisitList) { - self.global_object.trace(visits); - self.object_prototype.trace(visits); - self.function_prototype.trace(visits); - self.string_prototype.trace(visits); - self.promise_prototype.trace(visits); - self.error_prototype.trace(visits); - self.aggregate_error_prototype.trace(visits); - self.eval_error_prototype.trace(visits); - self.internal_error_prototype.trace(visits); - self.range_error_prototype.trace(visits); - self.reference_error_prototype.trace(visits); - self.syntax_error_prototype.trace(visits); - self.type_error_prototype.trace(visits); - self.uri_error_prototype.trace(visits); + self.builtins.trace(visits); // TODO: tracing X if X implements Trace. } } diff --git a/libs/jsruntime/src/semantics/mod.rs b/libs/jsruntime/src/semantics/mod.rs index c03e9cd06..9ea610ab6 100644 --- a/libs/jsruntime/src/semantics/mod.rs +++ b/libs/jsruntime/src/semantics/mod.rs @@ -289,7 +289,8 @@ impl AnalyzerSupport for Runtime { name: Symbol, property: Property, ) -> Result { - self.global_object + self.builtins + .global_object .define_own_property(name.into(), property) } } From 7f7091c34b1645397f9bd03f8834d2ae3d059e7f Mon Sep 17 00:00:00 2001 From: masnagam Date: Sun, 26 Apr 2026 14:30:35 +0900 Subject: [PATCH 02/10] refactor(jsruntime): two-phase construction --- libs/jsruntime/src/backend/bridge.rs | 6 +- .../src/backend/clir/compiler/mod.rs | 12 +- .../jsruntime/src/builtins/builtin_mod.rs.hbs | 17 +-- libs/jsruntime/src/builtins/error/imp.rs | 4 +- libs/jsruntime/src/builtins/function/mod.rs | 12 +- libs/jsruntime/src/builtins/mod.rs | 117 ++++++++++-------- .../src/builtins/native_error_imp.rs.hbs | 7 +- libs/jsruntime/src/builtins/object/mod.rs | 5 +- libs/jsruntime/src/builtins/promise/imp.rs | 4 +- libs/jsruntime/src/builtins/string/imp.rs | 4 +- libs/jsruntime/src/jobs.rs | 8 +- libs/jsruntime/src/lib.rs | 9 +- libs/jsruntime/src/types/object.rs | 13 +- 13 files changed, 119 insertions(+), 99 deletions(-) diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index 843ae3df1..100d8f028 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -463,8 +463,10 @@ pub(crate) extern "C" fn runtime_create_object( runtime: &mut Runtime, prototype: *mut Object, ) -> *mut Object { - let prototype = HandleMut::from_ptr(prototype); - runtime.create_object(prototype).as_ptr() + let prototype = HandleMut::from_ptr(prototype).unwrap(); + let mut object = runtime.create_object(); + object.set_prototype(prototype); + object.as_ptr() } pub(crate) extern "C" fn runtime_create_reference_error( diff --git a/libs/jsruntime/src/backend/clir/compiler/mod.rs b/libs/jsruntime/src/backend/clir/compiler/mod.rs index 6a21e48e5..6549d02da 100644 --- a/libs/jsruntime/src/backend/clir/compiler/mod.rs +++ b/libs/jsruntime/src/backend/clir/compiler/mod.rs @@ -178,9 +178,9 @@ pub fn compile( lambda_registry: &mut runtime.lambda_registry, code_registry: &mut runtime.code_registry, global_object, - object_prototype: runtime.builtins.object_prototype.unwrap(), - function_prototype: runtime.builtins.function_prototype.unwrap(), - promise_prototype: runtime.builtins.promise_prototype.unwrap(), + object_prototype: runtime.builtins.object_prototype, + function_prototype: runtime.builtins.function_prototype, + promise_prototype: runtime.builtins.promise_prototype, } }; context.compile_function(func, &mut session, &program.scope_tree); @@ -223,9 +223,9 @@ pub fn compile_function( lambda_registry: &mut runtime.lambda_registry, code_registry: &mut runtime.code_registry, global_object, - object_prototype: runtime.builtins.object_prototype.unwrap(), - function_prototype: runtime.builtins.function_prototype.unwrap(), - promise_prototype: runtime.builtins.promise_prototype.unwrap(), + object_prototype: runtime.builtins.object_prototype, + function_prototype: runtime.builtins.function_prototype, + promise_prototype: runtime.builtins.promise_prototype, } }; diff --git a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs index bbad91f24..e7b429122 100644 --- a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs +++ b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs @@ -34,7 +34,6 @@ impl Runtime { /// Creates the {{metadata.class}} constructor. pub(super) fn create_{{metadata.id}}_constructor(&mut self) -> HandleMut { logger::debug!(event = "create_{{metadata.id}}_constructor"); - debug_assert!(self.builtins.{{metadata.id}}_prototype.is_some()); #[allow(unused_mut)] let mut constructor = self.create_builtin_function(&BuiltinFunctionParams { @@ -42,7 +41,7 @@ impl Runtime { name: const_string!("{{metadata.class}}"), length: 1, slots: &[], - prototype: self.builtins.{{metadata.id}}_prototype, + prototype: Some(self.builtins.{{metadata.id}}_prototype), }); {{#each constructorProperties}} @@ -59,21 +58,18 @@ impl Runtime { {{/if}} {{/each}} // TODO(refactor): bind outside this function - if let Some(mut prototype) = self.builtins.{{metadata.id}}_prototype { - let _ = prototype.define_own_property(Symbol::CONSTRUCTOR.into(), Property::data_wxc(Value::Object(constructor))); - } + let _ = self.builtins.{{metadata.id}}_prototype.define_own_property(Symbol::CONSTRUCTOR.into(), Property::data_wxc(Value::Object(constructor))); constructor } /// Creates the {{metadata.class}} prototype object. - pub(super) fn create_{{metadata.id}}_prototype(&mut self) -> HandleMut { + pub(super) fn init_{{metadata.id}}_prototype(&mut self) { logger::debug!(event = "creater_{{metadata.id}}_prototype"); - debug_assert!(self.builtins.{{metadata.inherits}}_prototype.is_some()); - debug_assert!(self.builtins.function_prototype.is_some()); - #[allow(unused_mut)] - let mut prototype = self.create_object(self.builtins.{{metadata.inherits}}_prototype); + let mut prototype = self.builtins.{{metadata.id}}_prototype; + + prototype.set_prototype(self.builtins.{{metadata.inherits}}_prototype); {{#each prototypeProperties}} {{#if (eq kind "prototype.property")}} @@ -91,7 +87,6 @@ impl Runtime { {{/if}} {{/each}} - prototype } } diff --git a/libs/jsruntime/src/builtins/error/imp.rs b/libs/jsruntime/src/builtins/error/imp.rs index c0e062703..3c5e462c4 100644 --- a/libs/jsruntime/src/builtins/error/imp.rs +++ b/libs/jsruntime/src/builtins/error/imp.rs @@ -27,7 +27,9 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.builtins.error_prototype) + let mut object = runtime.create_object(); + object.set_prototype(runtime.builtins.error_prototype); + object }; object.set_error(); diff --git a/libs/jsruntime/src/builtins/function/mod.rs b/libs/jsruntime/src/builtins/function/mod.rs index f084c58ad..473381a35 100644 --- a/libs/jsruntime/src/builtins/function/mod.rs +++ b/libs/jsruntime/src/builtins/function/mod.rs @@ -20,15 +20,17 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::FUNCTION), length: 1, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), }) } - pub(super) fn create_function_prototype(&mut self) -> HandleMut { - logger::debug!(event = "creater_function_prototype"); + pub(super) fn init_function_prototype(&mut self) { + logger::debug!(event = "init_function_prototype"); // TODO(fix): Function.prototype is a built-in function object. - let mut prototype = self.create_object(self.builtins.object_prototype); + let mut prototype = self.builtins.function_prototype; + prototype.set_prototype(self.builtins.object_prototype); + let _ = prototype.define_own_property( Symbol::LENGTH.into(), Property::data_xxx(Value::Number(0.0)), @@ -39,8 +41,6 @@ impl Runtime { ); // TODO: Function.prototype.constructor - - prototype } } diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index adee7be76..7a77268ce 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -29,65 +29,87 @@ use crate::types::Status; use crate::types::String; use crate::types::Value; +// The built-in objects are created in two-phase construction in order to avoid circular +// references. +// +// 1. Create an empty object for each built-in objects. +// 2. Initialize built-in objects. +// #[derive(jsgc_derive::Trace)] pub(crate) struct Builtins { // [[GlobalObject]] pub(crate) global_object: HandleMut, // %Object.prototype% - pub(crate) object_prototype: Option>, + pub(crate) object_prototype: HandleMut, // %Function.prototype% - pub(crate) function_prototype: Option>, + pub(crate) function_prototype: HandleMut, // %String.prototype% - pub(crate) string_prototype: Option>, + pub(crate) string_prototype: HandleMut, // %Promise.prototype% - pub(crate) promise_prototype: Option>, + pub(crate) promise_prototype: HandleMut, // %Error.prototype% - pub(crate) error_prototype: Option>, + pub(crate) error_prototype: HandleMut, // %AggregateError.prototype% - pub(crate) aggregate_error_prototype: Option>, + pub(crate) aggregate_error_prototype: HandleMut, // %EvalError.prototype% - pub(crate) eval_error_prototype: Option>, + pub(crate) eval_error_prototype: HandleMut, // %InternalError.prototype% - pub(crate) internal_error_prototype: Option>, + pub(crate) internal_error_prototype: HandleMut, // %RangeError.prototype% - pub(crate) range_error_prototype: Option>, + pub(crate) range_error_prototype: HandleMut, // %ReferenceError.prototype% - pub(crate) reference_error_prototype: Option>, + pub(crate) reference_error_prototype: HandleMut, // %SyntaxError.prototype% - pub(crate) syntax_error_prototype: Option>, + pub(crate) syntax_error_prototype: HandleMut, // %TypeError.prototype% - pub(crate) type_error_prototype: Option>, + pub(crate) type_error_prototype: HandleMut, // URIError.prototype% - pub(crate) uri_error_prototype: Option>, + pub(crate) uri_error_prototype: HandleMut, } impl Builtins { + // The first phase of the two-phase construction. pub(crate) fn new(heap: &mut Heap) -> Self { - // TODO: pass [[Prototype]] of the global object. - let global_object = heap.alloc_mut(Object::new(None)); - Self { - global_object, - object_prototype: None, - function_prototype: None, - string_prototype: None, - promise_prototype: None, - error_prototype: None, - aggregate_error_prototype: None, - eval_error_prototype: None, - internal_error_prototype: None, - reference_error_prototype: None, - range_error_prototype: None, - syntax_error_prototype: None, - type_error_prototype: None, - uri_error_prototype: None, + global_object: heap.alloc_mut(Object::new()), + object_prototype: heap.alloc_mut(Object::new()), + function_prototype: heap.alloc_mut(Object::new()), + string_prototype: heap.alloc_mut(Object::new()), + promise_prototype: heap.alloc_mut(Object::new()), + error_prototype: heap.alloc_mut(Object::new()), + aggregate_error_prototype: heap.alloc_mut(Object::new()), + eval_error_prototype: heap.alloc_mut(Object::new()), + internal_error_prototype: heap.alloc_mut(Object::new()), + reference_error_prototype: heap.alloc_mut(Object::new()), + range_error_prototype: heap.alloc_mut(Object::new()), + syntax_error_prototype: heap.alloc_mut(Object::new()), + type_error_prototype: heap.alloc_mut(Object::new()), + uri_error_prototype: heap.alloc_mut(Object::new()), } } } impl Runtime { - // 19 The Global Object - pub(crate) fn define_builtin_global_properties(&mut self) { + // The second phase of the two-phase construction. + pub(crate) fn init_builtin_objects(&mut self) { + // Initialize intrinsic objects. + self.init_function_prototype(); + self.init_string_prototype(); + self.init_promise_prototype(); + self.init_error_prototype(); + self.init_aggregate_error_prototype(); + self.init_eval_error_prototype(); + self.init_internal_error_prototype(); + self.init_range_error_prototype(); + self.init_reference_error_prototype(); + self.init_syntax_error_prototype(); + self.init_type_error_prototype(); + self.init_uri_error_prototype(); + + // TODO: pass [[Prototype]] of the global object. + + let this = self.builtins.global_object.as_handle(); + macro_rules! define { ($key:expr => $value:expr,) => { define!(kv: $key, $value); @@ -103,22 +125,7 @@ impl Runtime { }; } - self.builtins.object_prototype = Some(self.create_object(None)); - self.builtins.function_prototype = Some(self.create_function_prototype()); - self.builtins.string_prototype = Some(self.create_string_prototype()); - self.builtins.promise_prototype = Some(self.create_promise_prototype()); - self.builtins.error_prototype = Some(self.create_error_prototype()); - self.builtins.aggregate_error_prototype = Some(self.create_aggregate_error_prototype()); - self.builtins.eval_error_prototype = Some(self.create_eval_error_prototype()); - self.builtins.internal_error_prototype = Some(self.create_internal_error_prototype()); - self.builtins.range_error_prototype = Some(self.create_range_error_prototype()); - self.builtins.reference_error_prototype = Some(self.create_reference_error_prototype()); - self.builtins.syntax_error_prototype = Some(self.create_syntax_error_prototype()); - self.builtins.type_error_prototype = Some(self.create_type_error_prototype()); - self.builtins.uri_error_prototype = Some(self.create_uri_error_prototype()); - - let this = self.builtins.global_object.as_handle(); - + // 19 The Global Object define! { // TODO: 19.1.1 globalThis Symbol::GLOBAL_THIS => Value::Object(this), @@ -134,7 +141,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::EVAL), length: 1, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), })), // 19.2.2 isFinite ( number ) Symbol::IS_FINITE => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -142,7 +149,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::IS_FINITE), length: 1, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), })), // 19.2.3 isNaN ( number ) Symbol::IS_NAN => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -150,7 +157,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::IS_NAN), length: 1, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), })), // 19.2.4 parseFloat ( string ) Symbol::PARSE_FLOAT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -158,7 +165,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::PARSE_FLOAT), length: 1, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), })), // 19.2.5 parseInt ( string, radix ) Symbol::PARSE_INT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { @@ -166,7 +173,7 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::PARSE_INT), length: 2, slots: &[], - prototype: self.builtins.function_prototype, + prototype: Some(self.builtins.function_prototype), })), // 19.3.1 AggregateError ( . . . ) Symbol::AGGREGATE_ERROR => Value::Object(self.create_aggregate_error_constructor()), @@ -206,9 +213,9 @@ impl Runtime { ?params.slots, ?params.prototype ); - debug_assert!(self.builtins.function_prototype.is_some()); let closure = self.create_closure(params.lambda, LambdaId::HOST, 0); - let mut func = self.create_object(self.builtins.function_prototype); + let mut func = self.create_object(); + func.set_prototype(self.builtins.function_prototype); func.slots_mut().extend_from_slice(params.slots); func.set_closure(closure); if let Some(prototype) = params.prototype { diff --git a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs index 7ba25d887..adaa1a92a 100644 --- a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs +++ b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs @@ -33,7 +33,9 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.builtins.{{id}}_prototype) + let mut object = runtime.create_object(); + object.set_prototype(runtime.builtins.{{id}}_prototype); + object }; object.set_error(); @@ -79,7 +81,8 @@ pub fn {{id}}_prototype_name(_runtime: &mut Runtime, mut prototype: Handle impl Runtime { #[allow(unused)] pub(crate) fn create_{{id}}(&mut self, msg: Option>) -> HandleMut { - let mut object = self.create_object(self.builtins.{{id}}_prototype); + let mut object = self.create_object(); + object.set_prototype(self.builtins.{{id}}_prototype); if let Some(msg) = msg { let _ = object.define_own_property( Symbol::MESSAGE.into(), diff --git a/libs/jsruntime/src/builtins/object/mod.rs b/libs/jsruntime/src/builtins/object/mod.rs index 6304aedc6..c3e286885 100644 --- a/libs/jsruntime/src/builtins/object/mod.rs +++ b/libs/jsruntime/src/builtins/object/mod.rs @@ -17,14 +17,15 @@ impl Runtime { name: const_string!(jsparser::symbol::builtin::names::OBJECT), length: 0, slots: &[], - prototype: self.builtins.object_prototype, + prototype: Some(self.builtins.object_prototype), }) } fn create_object_object(&mut self, context: &mut CallContext) -> Result { match context.args().first() { None | Some(Value::Undefined) | Some(Value::Null) => { - let object = self.create_object(self.builtins.object_prototype); + let mut object = self.create_object(); + object.set_prototype(self.builtins.object_prototype); // TODO(feat): NewTarget Ok(Value::Object(object)) } diff --git a/libs/jsruntime/src/builtins/promise/imp.rs b/libs/jsruntime/src/builtins/promise/imp.rs index 0a91cd003..93835fbf0 100644 --- a/libs/jsruntime/src/builtins/promise/imp.rs +++ b/libs/jsruntime/src/builtins/promise/imp.rs @@ -37,7 +37,9 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re let mut object = if let Value::Object(this) = context.this() { *this } else { - runtime.create_object(runtime.builtins.promise_prototype) + let mut object = runtime.create_object(); + object.set_prototype(runtime.builtins.promise_prototype); + object }; object.set_promise(promise); diff --git a/libs/jsruntime/src/builtins/string/imp.rs b/libs/jsruntime/src/builtins/string/imp.rs index 220d714ee..73518e1c9 100644 --- a/libs/jsruntime/src/builtins/string/imp.rs +++ b/libs/jsruntime/src/builtins/string/imp.rs @@ -603,7 +603,9 @@ impl Runtime { this } else { // 10.4.3.4 StringCreate ( value, prototype ) - self.create_object(self.builtins.string_prototype) + let mut object = self.create_object(); + object.set_prototype(self.builtins.string_prototype); + object }; let length = string.len(); object.set_string(string); diff --git a/libs/jsruntime/src/jobs.rs b/libs/jsruntime/src/jobs.rs index 5c1fec0ab..3dcc9fb59 100644 --- a/libs/jsruntime/src/jobs.rs +++ b/libs/jsruntime/src/jobs.rs @@ -183,7 +183,8 @@ mod tests { let closure = runtime.create_closure(dummy, LambdaId::HOST, 0); let coroutine = runtime.create_coroutine(closure, 0, 0, 0); let promise = runtime.create_promise(coroutine); - let mut object = runtime.create_object(runtime.builtins.promise_prototype); + let mut object = runtime.create_object(); + object.set_prototype(runtime.builtins.promise_prototype); object.set_promise(promise); runtime.emit_promise_resolved(object, $value); }; @@ -194,13 +195,14 @@ mod tests { let closure = runtime.create_closure(dummy, LambdaId::HOST, 0); let coroutine = runtime.create_coroutine(closure, 0, 0, 0); let promise = runtime.create_promise(coroutine); - let mut object = runtime.create_object(runtime.builtins.promise_prototype); + let mut object = runtime.create_object(); + object.set_prototype(runtime.builtins.promise_prototype); object.set_promise(promise); runtime.emit_promise_rejected(object, $value); }; } - let object = runtime.create_object(None); + let object = runtime.create_object(); resolved!(Value::Undefined); resolved!(Value::String(const_string!("resolved"))); diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 08d1c10b2..19ae1e567 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -127,7 +127,7 @@ impl Runtime { extension, }; - runtime.define_builtin_global_properties(); + runtime.init_builtin_objects(); runtime } @@ -161,7 +161,8 @@ impl Runtime { logger::debug!(event = "register_host_function", name, ?symbol); let lambda = types::into_lambda(host_fn); let closure = self.create_closure(lambda, LambdaId::HOST, 0); - let mut object = self.create_object(self.builtins.function_prototype); + let mut object = self.create_object(); + object.set_prototype(self.builtins.function_prototype); object.set_closure(closure); let value = Value::Object(object); // TODO: add `flags` to the arguments. @@ -398,8 +399,8 @@ impl Runtime { self.heap.alloc_mut(Promise::new(coroutine)) } - fn create_object(&mut self, prototype: Option>) -> HandleMut { - self.heap.alloc_mut(Object::new(prototype)) + fn create_object(&mut self) -> HandleMut { + self.heap.alloc_mut(Object::new()) } fn make_property_key(&mut self, value: &Value) -> Result { diff --git a/libs/jsruntime/src/types/object.rs b/libs/jsruntime/src/types/object.rs index 3e9b310ff..8837b4356 100644 --- a/libs/jsruntime/src/types/object.rs +++ b/libs/jsruntime/src/types/object.rs @@ -239,16 +239,20 @@ impl Object { std::mem::offset_of!(Self, kernel) + Kernel::TRACING_OFFSET; pub(crate) const FLAGS_OFFSET: usize = std::mem::offset_of!(Self, flags); - pub fn new(prototype: Option>) -> Self { + pub fn new() -> Self { Self { kernel: Default::default(), flags: ObjectFlags::empty(), - prototype, + prototype: None, properties: Default::default(), slots: Default::default(), } } + pub fn set_prototype(&mut self, prototype: HandleMut) { + self.prototype = Some(prototype); + } + // TODO(perf): Which one is better? `Option::None` or `&Value::None`. // In JIT-compiled code, we need a `nullptr` check if we choose `Option::None`. // If we choose `&Value::None`, we always need a memory access for the discriminant check of @@ -333,10 +337,9 @@ impl Object { HandleMut::from_mut(self) } - pub fn is_instance_of(&self, prototype: Option>) -> bool { - debug_assert!(prototype.is_some()); + pub fn is_instance_of(&self, prototype: HandleMut) -> bool { // TODO: prototype chain - self.prototype == prototype + matches!(self.prototype, Some(p) if p == prototype) } pub(crate) fn set_constructor(&mut self) { From 8a4968a7c86b4cda32fc3854deea05cd4231cd60 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sun, 26 Apr 2026 14:39:46 +0900 Subject: [PATCH 03/10] refactor(jsruntime): refactoring --- libs/jsruntime/src/backend/bridge.rs | 48 ++++++++++++---------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index 100d8f028..14724ed06 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -368,7 +368,7 @@ pub(crate) extern "C" fn runtime_create_string( pub(crate) extern "C" fn runtime_create_capture( runtime: &mut Runtime, target: *mut Value, -) -> *mut Capture { +) -> HandleMut { logger::debug!(event = "runtime_create_capture", ?target); debug_assert!( @@ -386,16 +386,12 @@ pub(crate) extern "C" fn runtime_create_capture( ) }; - let handle = runtime - .heap - .alloc_layout_mut::(LAYOUT, move |ptr| { - // SAFETY: `ptr` is a non-null pointer to a `Capture`. - let capture = unsafe { ptr.cast::().as_mut() }; - capture.target = target; - // `capture.escaped` will be filled with an actual value. - }); - - handle.as_ptr() + runtime.heap.alloc_layout_mut(LAYOUT, move |ptr| { + // SAFETY: `ptr` is a non-null pointer to a `Capture`. + let capture = unsafe { ptr.cast::().as_mut() }; + capture.target = target; + // `capture.escaped` will be filled with an actual value. + }) } pub(crate) extern "C" fn runtime_create_closure( @@ -403,16 +399,14 @@ pub(crate) extern "C" fn runtime_create_closure( lambda: Lambda, lambda_id: u32, num_captures: u16, -) -> *mut Closure { +) -> HandleMut { logger::debug!( event = "runtime_create_closure", ?lambda, lambda_id, num_captures ); - runtime - .create_closure(lambda, lambda_id.into(), num_captures) - .as_ptr() + runtime.create_closure(lambda, lambda_id.into(), num_captures) } pub(crate) extern "C" fn runtime_create_coroutine( @@ -421,7 +415,7 @@ pub(crate) extern "C" fn runtime_create_coroutine( num_locals: u16, scratch_buffer_len: u16, capture_buffer_len: u16, -) -> *mut Coroutine { +) -> HandleMut { logger::debug!( event = "runtime_create_coroutine", ?closure, @@ -430,9 +424,7 @@ pub(crate) extern "C" fn runtime_create_coroutine( capture_buffer_len, ); let closure = HandleMut::from_ptr(closure).expect("closure must be a non-null pointer"); - runtime - .create_coroutine(closure, num_locals, scratch_buffer_len, capture_buffer_len) - .as_ptr() + runtime.create_coroutine(closure, num_locals, scratch_buffer_len, capture_buffer_len) } pub(crate) extern "C" fn runtime_create_promise( @@ -462,28 +454,30 @@ pub(crate) extern "C" fn runtime_emit_promise_resolved( pub(crate) extern "C" fn runtime_create_object( runtime: &mut Runtime, prototype: *mut Object, -) -> *mut Object { +) -> HandleMut { let prototype = HandleMut::from_ptr(prototype).unwrap(); let mut object = runtime.create_object(); object.set_prototype(prototype); - object.as_ptr() + object } pub(crate) extern "C" fn runtime_create_reference_error( runtime: &mut Runtime, -) -> *mut Object { - runtime.create_reference_error(None).as_ptr() +) -> HandleMut { + runtime.create_reference_error(None) } -pub(crate) extern "C" fn runtime_create_type_error(runtime: &mut Runtime) -> *mut Object { - runtime.create_type_error(None).as_ptr() +pub(crate) extern "C" fn runtime_create_type_error( + runtime: &mut Runtime, +) -> HandleMut { + runtime.create_type_error(None) } pub(crate) extern "C" fn runtime_create_internal_error( runtime: &mut Runtime, message: Handle, -) -> *mut Object { - runtime.create_internal_error(Some(message)).as_ptr() +) -> HandleMut { + runtime.create_internal_error(Some(message)) } pub(crate) extern "C" fn runtime_get_value_by_symbol( From b8e0ae80507c2506458639d15ac21266658697c8 Mon Sep 17 00:00:00 2001 From: masnagam Date: Sun, 26 Apr 2026 21:12:41 +0900 Subject: [PATCH 04/10] refactor(jsruntime): refactoring --- libs/jsruntime/src/backend/bridge.rs | 50 ++++++--------- libs/jsruntime/src/builtins/.gitignore | 2 + libs/jsruntime/src/builtins/Makefile | 2 +- .../jsruntime/src/builtins/builtin_mod.rs.hbs | 4 +- libs/jsruntime/src/builtins/error/imp.rs | 3 +- libs/jsruntime/src/builtins/function/imp.rs | 18 ++++++ libs/jsruntime/src/builtins/function/mod.rs | 55 ---------------- libs/jsruntime/src/builtins/imp.js | 2 +- libs/jsruntime/src/builtins/mod.rs | 7 +- .../src/builtins/native_error_imp.rs.hbs | 2 +- libs/jsruntime/src/builtins/object/imp.rs | 51 +++++++++++++++ libs/jsruntime/src/builtins/object/mod.rs | 64 ------------------- libs/jsruntime/src/builtins/promise/imp.rs | 7 +- libs/jsruntime/src/builtins/string/imp.rs | 7 +- libs/jsruntime/src/lib.rs | 7 +- libs/jsruntime/src/macros.rs | 4 +- 16 files changed, 114 insertions(+), 171 deletions(-) create mode 100644 libs/jsruntime/src/builtins/function/imp.rs delete mode 100644 libs/jsruntime/src/builtins/function/mod.rs create mode 100644 libs/jsruntime/src/builtins/object/imp.rs delete mode 100644 libs/jsruntime/src/builtins/object/mod.rs diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index 14724ed06..d373f6717 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -1,6 +1,7 @@ use jsgc::Handle; use jsgc::HandleMut; +use crate::Error; use crate::Runtime; use crate::lambda::LambdaKind; use crate::logger; @@ -192,46 +193,35 @@ pub(crate) extern "C" fn runtime_to_object( retv: &mut Value, ) -> Status { logger::debug!(event = "runtime_to_object", ?value); - runtime.value_to_object(value, retv) + match runtime.value_to_object(value) { + Ok(value) => { + *retv = value; + Status::Normal + } + Err(err) => { + *retv = runtime.create_exception(err); + Status::Exception + } + } } impl Runtime { - pub(crate) fn value_to_object(&mut self, value: &Value, retv: &mut Value) -> Status { + pub(crate) fn value_to_object(&mut self, value: &Value) -> Result { logger::debug!(event = "to_object", ?value); match value { Value::None => unreachable!("Value::None"), - Value::Undefined | Value::Null => { - *retv = Value::Object(self.create_type_error(None)); - Status::Exception + Value::Undefined | Value::Null => Err(Error::TypeError), + Value::Boolean(_value) => { + runtime_todo!("ToObject: not yet implemented for Boolean values") + } + Value::Number(_value) => { + runtime_todo!("ToObject: not yet implemented for Number values") } - Value::Boolean(_value) => runtime_todo!( - self, - "ToObject: not yet implemented for Boolean values", - retv - ), - Value::Number(_value) => runtime_todo!( - self, - "ToObject: not yet implemented for Number values", - retv - ), Value::String(value) => { // TODO(refactor): rewrite using `new String(value)` - match self.create_string_object(None, &[Value::String(*value)], true) { - Ok(Value::Object(object)) => { - *retv = Value::Object(object); - Status::Normal - } - Ok(_) => unreachable!(), - Err(err) => { - *retv = self.create_exception(err); - Status::Exception - } - } - } - Value::Object(_) => { - *retv = value.clone(); - Status::Normal + self.create_string_object(None, &[Value::String(*value)], true) } + Value::Object(_) => Ok(value.clone()), } } } diff --git a/libs/jsruntime/src/builtins/.gitignore b/libs/jsruntime/src/builtins/.gitignore index 743c6cbc6..3ad1b2638 100644 --- a/libs/jsruntime/src/builtins/.gitignore +++ b/libs/jsruntime/src/builtins/.gitignore @@ -3,8 +3,10 @@ /error/mod.rs /eval_error/imp.rs /eval_error/mod.rs +/function/mod.rs /internal_error/imp.rs /internal_error/mod.rs +/object/mod.rs /promise/mod.rs /range_error/imp.rs /range_error/mod.rs diff --git a/libs/jsruntime/src/builtins/Makefile b/libs/jsruntime/src/builtins/Makefile index 435ed3e08..d20380f77 100644 --- a/libs/jsruntime/src/builtins/Makefile +++ b/libs/jsruntime/src/builtins/Makefile @@ -15,7 +15,7 @@ NATIVE_ERROR_NAMES := \ uri_error NATIVE_ERROR_IMP_RS_FILES := $(addsuffix /imp.rs,$(NATIVE_ERROR_NAMES)) -BUILTINS := error promise string $(NATIVE_ERROR_NAMES) +BUILTINS := error function object promise string $(NATIVE_ERROR_NAMES) BUILTINS_README_MD_FILES := $(addsuffix /README.md,$(BUILTINS)) BUILTINS_MOD_RS_FILES := $(addsuffix /mod.rs,$(BUILTINS)) BUILTINS_IMP_JSON_FILES := $(addsuffix /imp.json,$(BUILTINS)) diff --git a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs index e7b429122..ae2a08779 100644 --- a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs +++ b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs @@ -67,9 +67,11 @@ impl Runtime { pub(super) fn init_{{metadata.id}}_prototype(&mut self) { logger::debug!(event = "creater_{{metadata.id}}_prototype"); + #[allow(unused)] let mut prototype = self.builtins.{{metadata.id}}_prototype; - + {{#if metadata.inherits}} prototype.set_prototype(self.builtins.{{metadata.inherits}}_prototype); + {{/if}} {{#each prototypeProperties}} {{#if (eq kind "prototype.property")}} diff --git a/libs/jsruntime/src/builtins/error/imp.rs b/libs/jsruntime/src/builtins/error/imp.rs index 3c5e462c4..f1005be0b 100644 --- a/libs/jsruntime/src/builtins/error/imp.rs +++ b/libs/jsruntime/src/builtins/error/imp.rs @@ -1,5 +1,6 @@ //$id error //$class Error +//$inherits object use jsgc::Handle; use jsgc::HandleMut; @@ -21,7 +22,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError); + return Err(Error::InternalError(None)); } let mut object = if let Value::Object(this) = context.this() { diff --git a/libs/jsruntime/src/builtins/function/imp.rs b/libs/jsruntime/src/builtins/function/imp.rs new file mode 100644 index 000000000..07af10a31 --- /dev/null +++ b/libs/jsruntime/src/builtins/function/imp.rs @@ -0,0 +1,18 @@ +//$id function +//$class Function +//$inherits object + +use crate::Error; +use crate::Runtime; +use crate::logger; +use crate::types::CallContext; +use crate::types::Value; + +//#sec-function-p1-p2-pn-body constructor +pub fn constructor( + _runtime: &mut Runtime, + _context: &mut CallContext, +) -> Result { + logger::debug!(event = "function"); + runtime_todo!("TODO: Function constructor") +} diff --git a/libs/jsruntime/src/builtins/function/mod.rs b/libs/jsruntime/src/builtins/function/mod.rs deleted file mode 100644 index 473381a35..000000000 --- a/libs/jsruntime/src/builtins/function/mod.rs +++ /dev/null @@ -1,55 +0,0 @@ -use jsgc::HandleMut; -use jsparser::Symbol; - -use crate::logger; - -use crate::Runtime; -use crate::types::CallContext; -use crate::types::Object; -use crate::types::Property; -use crate::types::Status; -use crate::types::Value; - -use super::BuiltinFunctionParams; - -impl Runtime { - pub(super) fn create_function_constructor(&mut self) -> HandleMut { - logger::debug!(event = "creater_function_constructor"); - self.create_builtin_function(&BuiltinFunctionParams { - lambda: constructor::, - name: const_string!(jsparser::symbol::builtin::names::FUNCTION), - length: 1, - slots: &[], - prototype: Some(self.builtins.function_prototype), - }) - } - - pub(super) fn init_function_prototype(&mut self) { - logger::debug!(event = "init_function_prototype"); - - // TODO(fix): Function.prototype is a built-in function object. - let mut prototype = self.builtins.function_prototype; - prototype.set_prototype(self.builtins.object_prototype); - - let _ = prototype.define_own_property( - Symbol::LENGTH.into(), - Property::data_xxx(Value::Number(0.0)), - ); - let _ = prototype.define_own_property( - Symbol::NAME.into(), - Property::data_xxx(Value::String(crate::types::string::EMPTY)), - ); - - // TODO: Function.prototype.constructor - } -} - -// lambda functions - -extern "C" fn constructor( - runtime: &mut Runtime, - _context: &mut CallContext, - retv: &mut Value, -) -> Status { - runtime_todo!(runtime, "TODO: Function constructor", retv) -} diff --git a/libs/jsruntime/src/builtins/imp.js b/libs/jsruntime/src/builtins/imp.js index 55012e436..1228c24fc 100644 --- a/libs/jsruntime/src/builtins/imp.js +++ b/libs/jsruntime/src/builtins/imp.js @@ -42,7 +42,7 @@ async function main(args, options) { const json = { metadata: { - inherits: 'object', + inherits: null, }, constructor: null, constructorProperties: [], diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index 7a77268ce..542c22ac2 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -93,6 +93,7 @@ impl Runtime { // The second phase of the two-phase construction. pub(crate) fn init_builtin_objects(&mut self) { // Initialize intrinsic objects. + self.init_object_prototype(); self.init_function_prototype(); self.init_string_prototype(); self.init_promise_prototype(); @@ -256,7 +257,7 @@ impl Runtime { Value::Boolean(true) => Ok(1.0), Value::Boolean(false) => Ok(0.0), Value::Number(value) => Ok(*value), - Value::String(_value) => Err(Error::InternalError), // TODO + Value::String(_value) => Err(Error::InternalError(None)), // TODO // TODO(feat): 7.1.1 ToPrimitive() Value::Object(_) => Ok(f64::NAN), } @@ -322,7 +323,7 @@ impl Runtime { Error::SyntaxError => self.create_syntax_error(None), Error::TypeError => self.create_type_error(None), Error::RangeError => self.create_range_error(None), - Error::InternalError => self.create_internal_error(None), + Error::InternalError(msg) => self.create_internal_error(msg), }) } @@ -464,7 +465,7 @@ mod imp { pub fn eval(_runtime: &mut Runtime, _context: &mut CallContext) -> Result { // TODO: impl - Err(Error::InternalError) + Err(Error::InternalError(None)) } pub fn is_finite( diff --git a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs index adaa1a92a..1312c2903 100644 --- a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs +++ b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs @@ -27,7 +27,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError); + return Err(Error::InternalError(None)); } let mut object = if let Value::Object(this) = context.this() { diff --git a/libs/jsruntime/src/builtins/object/imp.rs b/libs/jsruntime/src/builtins/object/imp.rs new file mode 100644 index 000000000..f30c7c815 --- /dev/null +++ b/libs/jsruntime/src/builtins/object/imp.rs @@ -0,0 +1,51 @@ +//$id object +//$class Object + +use crate::Error; +use crate::Runtime; +use crate::logger; +use crate::types::CallContext; +use crate::types::Value; + +//#sec-object-value constructor +pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Result { + logger::debug!(event = "object"); + let this = context.this(); + let args = context.args(); + let new = context.is_new(); + runtime.create_object_object(Some(this), args, new) +} + +// helpers + +impl Runtime { + pub(crate) fn create_object_object( + &mut self, + this: Option<&Value>, + args: &[Value], + new: bool, + ) -> Result { + // TODO(feat): NewTarget + if new { + let object = if let Some(&Value::Object(this)) = this { + this + } else { + // 10.4.3.4 StringCreate ( value, prototype ) + let mut object = self.create_object(); + object.set_prototype(self.builtins.string_prototype); + object + }; + Ok(Value::Object(object)) + } else { + match args.first() { + None | Some(Value::Undefined) | Some(Value::Null) => { + let mut object = self.create_object(); + object.set_prototype(self.builtins.object_prototype); + // TODO(feat): NewTarget + Ok(Value::Object(object)) + } + Some(value) => self.value_to_object(value), + } + } + } +} diff --git a/libs/jsruntime/src/builtins/object/mod.rs b/libs/jsruntime/src/builtins/object/mod.rs deleted file mode 100644 index c3e286885..000000000 --- a/libs/jsruntime/src/builtins/object/mod.rs +++ /dev/null @@ -1,64 +0,0 @@ -use jsgc::HandleMut; - -use crate::Runtime; -use crate::logger; -use crate::types::CallContext; -use crate::types::Object; -use crate::types::Status; -use crate::types::Value; - -use super::BuiltinFunctionParams; - -impl Runtime { - pub(super) fn create_object_constructor(&mut self) -> HandleMut { - logger::debug!(event = "creater_object_constructor"); - self.create_builtin_function(&BuiltinFunctionParams { - lambda: constructor::, - name: const_string!(jsparser::symbol::builtin::names::OBJECT), - length: 0, - slots: &[], - prototype: Some(self.builtins.object_prototype), - }) - } - - fn create_object_object(&mut self, context: &mut CallContext) -> Result { - match context.args().first() { - None | Some(Value::Undefined) | Some(Value::Null) => { - let mut object = self.create_object(); - object.set_prototype(self.builtins.object_prototype); - // TODO(feat): NewTarget - Ok(Value::Object(object)) - } - Some(value) => { - let mut retv = Value::None; - match self.value_to_object(value, &mut retv) { - Status::Normal => { - debug_assert!(matches!(retv, Value::Object(_))); - Ok(retv) - } - Status::Exception => Err(retv), - Status::Suspend => unreachable!(), - } - } - } - } -} - -// lambda functions - -extern "C" fn constructor( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match runtime.create_object_object(context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(value) => { - *retv = value; - Status::Exception - } - } -} diff --git a/libs/jsruntime/src/builtins/promise/imp.rs b/libs/jsruntime/src/builtins/promise/imp.rs index 93835fbf0..42c04892b 100644 --- a/libs/jsruntime/src/builtins/promise/imp.rs +++ b/libs/jsruntime/src/builtins/promise/imp.rs @@ -1,5 +1,6 @@ //$id promise //$class Promise +//$inherits object use jsgc::HandleMut; @@ -20,7 +21,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError); + return Err(Error::InternalError(None)); } let args = context.args(); @@ -124,11 +125,11 @@ fn promise_resolve_sync( runtime: &mut Runtime, context: &mut CallContext, ) -> Result { - let func = context.func().ok_or(Error::InternalError)?; + let func = context.func().ok_or(Error::InternalError(None))?; let promise = match func.slots().first() { Some(Value::Object(promise)) => *promise, - _ => return Err(Error::InternalError), + _ => return Err(Error::InternalError(None)), }; debug_assert!(runtime.is_promise_object(promise)); diff --git a/libs/jsruntime/src/builtins/string/imp.rs b/libs/jsruntime/src/builtins/string/imp.rs index 73518e1c9..38ce6decd 100644 --- a/libs/jsruntime/src/builtins/string/imp.rs +++ b/libs/jsruntime/src/builtins/string/imp.rs @@ -1,5 +1,6 @@ //$id string //$class String +//$inherits object use jsgc::Handle; use jsparser::Symbol; @@ -17,7 +18,7 @@ use crate::types::string::SPACE; //#sec-string-constructor-string-value constructor pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Result { - logger::debug!(event = "promise"); + logger::debug!(event = "string"); let this = context.this(); let args = context.args(); let new = context.is_new(); @@ -377,7 +378,7 @@ fn string_padding_builtins_impl( } if int_max_length > u32::MAX as u64 { - return Err(Error::InternalError); + return Err(Error::InternalError(None)); } let fill_string = args.get(1).unwrap_or(&Value::Undefined); @@ -443,7 +444,7 @@ pub fn string_prototype_repeat( } if n > u32::MAX as f64 { - return Err(Error::InternalError); + return Err(Error::InternalError(None)); } let result = runtime.repeat_string(s, n as u32); diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 19ae1e567..9e52580b9 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -462,11 +462,6 @@ impl Runtime { target.set_value(&LENGTH, &Value::from(length + 1.0)); Ok(()) } - - fn throw_internal_error(&mut self, message: Handle, retv: &mut Value) -> Status { - *retv = Value::Object(self.create_internal_error(Some(message))); - Status::Exception - } } impl Default for Runtime @@ -504,7 +499,7 @@ pub enum Error { SyntaxError, TypeError, RangeError, - InternalError, + InternalError(Option>), } #[cfg(test)] diff --git a/libs/jsruntime/src/macros.rs b/libs/jsruntime/src/macros.rs index a12a4f612..dc7bd8875 100644 --- a/libs/jsruntime/src/macros.rs +++ b/libs/jsruntime/src/macros.rs @@ -11,7 +11,7 @@ macro_rules! const_string { } macro_rules! runtime_todo { - ($runtime:expr, $message:literal, $retv:expr) => { - $runtime.throw_internal_error(const_string!($message), $retv) + ($message:literal) => { + Err($crate::Error::InternalError(Some(const_string!($message)))) }; } From 34576e02a9030c595dbc0ee745273908d16680f0 Mon Sep 17 00:00:00 2001 From: masnagam Date: Mon, 27 Apr 2026 14:14:54 +0900 Subject: [PATCH 05/10] refactor(jsruntime): global object --- libs/jsruntime/src/builtins/.gitignore | 1 + libs/jsruntime/src/builtins/Makefile | 7 +- .../jsruntime/src/builtins/builtin_mod.rs.hbs | 2 +- libs/jsruntime/src/builtins/global/README.md | 57 ++++ libs/jsruntime/src/builtins/global/imp.rs | 167 ++++++++++++ libs/jsruntime/src/builtins/global/mod.rs.hbs | 78 ++++++ libs/jsruntime/src/builtins/imp.js | 25 +- libs/jsruntime/src/builtins/mod.rs | 255 +----------------- 8 files changed, 339 insertions(+), 253 deletions(-) create mode 100644 libs/jsruntime/src/builtins/global/README.md create mode 100644 libs/jsruntime/src/builtins/global/imp.rs create mode 100644 libs/jsruntime/src/builtins/global/mod.rs.hbs diff --git a/libs/jsruntime/src/builtins/.gitignore b/libs/jsruntime/src/builtins/.gitignore index 3ad1b2638..9f22f4b46 100644 --- a/libs/jsruntime/src/builtins/.gitignore +++ b/libs/jsruntime/src/builtins/.gitignore @@ -4,6 +4,7 @@ /eval_error/imp.rs /eval_error/mod.rs /function/mod.rs +/global/mod.rs /internal_error/imp.rs /internal_error/mod.rs /object/mod.rs diff --git a/libs/jsruntime/src/builtins/Makefile b/libs/jsruntime/src/builtins/Makefile index d20380f77..e19d9bc94 100644 --- a/libs/jsruntime/src/builtins/Makefile +++ b/libs/jsruntime/src/builtins/Makefile @@ -15,7 +15,7 @@ NATIVE_ERROR_NAMES := \ uri_error NATIVE_ERROR_IMP_RS_FILES := $(addsuffix /imp.rs,$(NATIVE_ERROR_NAMES)) -BUILTINS := error function object promise string $(NATIVE_ERROR_NAMES) +BUILTINS := error function global object promise string $(NATIVE_ERROR_NAMES) BUILTINS_README_MD_FILES := $(addsuffix /README.md,$(BUILTINS)) BUILTINS_MOD_RS_FILES := $(addsuffix /mod.rs,$(BUILTINS)) BUILTINS_IMP_JSON_FILES := $(addsuffix /imp.json,$(BUILTINS)) @@ -34,6 +34,11 @@ update: $(UPDATE_TARGETS) clean: @rm -f $(CLEAN_TARGETS) +global/mod.rs: global/imp.json global/mod.rs.hbs $(TOOLS_BIN)/handlebars.js + @echo 'Generating $(abspath $@)...' + @cat $< | deno run -q --allow-read=. $(TOOLS_BIN)/handlebars.js --no-escape --input-stdin global/mod.rs.hbs | \ + rustfmt --emit=stdout >$@ + %/mod.rs: %/imp.json builtin_mod.rs.hbs $(TOOLS_BIN)/handlebars.js @echo 'Generating $(abspath $@)...' @cat $< | deno run -q --allow-read=. $(TOOLS_BIN)/handlebars.js --no-escape --input-stdin builtin_mod.rs.hbs | \ diff --git a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs index ae2a08779..6fa666235 100644 --- a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs +++ b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs @@ -65,7 +65,7 @@ impl Runtime { /// Creates the {{metadata.class}} prototype object. pub(super) fn init_{{metadata.id}}_prototype(&mut self) { - logger::debug!(event = "creater_{{metadata.id}}_prototype"); + logger::debug!(event = "init_{{metadata.id}}_prototype"); #[allow(unused)] let mut prototype = self.builtins.{{metadata.id}}_prototype; diff --git a/libs/jsruntime/src/builtins/global/README.md b/libs/jsruntime/src/builtins/global/README.md new file mode 100644 index 000000000..f5bac842c --- /dev/null +++ b/libs/jsruntime/src/builtins/global/README.md @@ -0,0 +1,57 @@ +# Global Object + +* [x] [globalThis](https://tc39.es/ecma262/#sec-globalthis) +* [x] [Infinity](https://tc39.es/ecma262/#sec-value-properties-of-the-global-object-infinity) +* [x] [NaN](https://tc39.es/ecma262/#sec-value-properties-of-the-global-object-nan) +* [x] [undefined](https://tc39.es/ecma262/#sec-undefined) +* [ ] [eval](https://tc39.es/ecma262/#sec-eval-x) +* [x] [isFinite](https://tc39.es/ecma262/#sec-isfinite-number) +* [x] [isNaN](https://tc39.es/ecma262/#sec-isnan-number) +* [x] [parseFloat](https://tc39.es/ecma262/#sec-parsefloat-string) +* [x] [parseInt](https://tc39.es/ecma262/#sec-parseint-string-radix) +* [x] [AggregateError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-aggregate-error) +* [ ] [Array](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-array) +* [ ] [ArrayBuffer](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-arraybuffer) +* [ ] [BigInt](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-bigint) +* [ ] [BigInt64Array](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-bigint64array) +* [ ] [BigUint64Array](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-biguint64array) +* [ ] [Boolean](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-boolean) +* [ ] [DataView](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-dataview) +* [ ] [Date](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-date) +* [x] [Error](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-error) +* [x] [EvalError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-evalerror) +* [ ] [FinalizationRegistry](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-finalization-registry) +* [ ] [Float16Array](https://tc39.es/ecma262/#sec-float16array) +* [ ] [Float32Array](https://tc39.es/ecma262/#sec-float32array) +* [ ] [Float64Array](https://tc39.es/ecma262/#sec-float64array) +* [x] [Function](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-function) +* [ ] [Int8Array](https://tc39.es/ecma262/#sec-int8array) +* [ ] [Int16Array](https://tc39.es/ecma262/#sec-int16array) +* [ ] [Int32Array](https://tc39.es/ecma262/#sec-int32array) +* [ ] [Iterator](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-iterator) +* [ ] [Map](https://tc39.es/ecma262/#sec-map) +* [ ] [Number](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-number) +* [x] [Object](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-object) +* [x] [Promise](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-promise) +* [ ] [Proxy](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-proxy) +* [x] [RangeError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-rangeerror) +* [x] [ReferenceError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-referenceerror) +* [ ] [RegExp](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-regexp) +* [ ] [Set](https://tc39.es/ecma262/#sec-set) +* [ ] [SharedArrayBuffer](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-sharedarraybuffer) +* [x] [String](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-string) +* [ ] [Symbol](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-symbol) +* [x] [SyntaxError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-syntaxerror) +* [x] [TypeError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-typeerror) +* [ ] [Uint8Array](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-uint8array) +* [ ] [Uint8ClampedArray](https://tc39.es/ecma262/#sec-uint8clampedarray) +* [ ] [Uint16Array](https://tc39.es/ecma262/#sec-uint16array) +* [ ] [Uint32Array](https://tc39.es/ecma262/#sec-uint32array) +* [x] [URIError](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-urierror) +* [ ] [WeakMap](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-weakmap) +* [ ] [WeakRef](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-weakref) +* [ ] [WeakSet](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-weakset) +* [ ] [Atomics](https://tc39.es/ecma262/#sec-atomics) +* [ ] [JSON](https://tc39.es/ecma262/#sec-json) +* [ ] [Math](https://tc39.es/ecma262/#sec-math) +* [ ] [Reflect](https://tc39.es/ecma262/#sec-reflect) diff --git a/libs/jsruntime/src/builtins/global/imp.rs b/libs/jsruntime/src/builtins/global/imp.rs new file mode 100644 index 000000000..64012a057 --- /dev/null +++ b/libs/jsruntime/src/builtins/global/imp.rs @@ -0,0 +1,167 @@ +//$id global + +use jsparser::Symbol; + +use crate::Error; +use crate::Runtime; +use crate::types::CallContext; +use crate::types::Value; +use crate::types::object::Property; + +//#sec-globalthis global.property +pub fn global_global_this(runtime: &mut Runtime) { + let prop = Property::data_wxc(Value::Object(runtime.builtins.global_object)); + runtime.define_global_property(Symbol::GLOBAL_THIS, prop); +} + +//#sec-value-properties-of-the-global-object-infinity global.property +pub fn global_infinity(runtime: &mut Runtime) { + let prop = Property::data_xxx(Value::Number(f64::INFINITY)); + runtime.define_global_property(Symbol::INFINITY, prop); +} + +//#sec-value-properties-of-the-global-object-nan global.property +pub fn global_nan(runtime: &mut Runtime) { + let prop = Property::data_xxx(Value::Number(f64::NAN)); + runtime.define_global_property(Symbol::NAN, prop); +} + +//#sec-undefined global.property +pub fn global_undefined(runtime: &mut Runtime) { + let prop = Property::data_xxx(Value::Undefined); + runtime.define_global_property(Symbol::KEYWORD_UNDEFINED, prop); +} + +//#sec-eval-x global.function +pub fn eval(_runtime: &mut Runtime, _context: &mut CallContext) -> Result { + // TODO: impl + Err(Error::InternalError(None)) +} + +//#sec-isfinite-number global.function +pub fn is_finite(runtime: &mut Runtime, context: &mut CallContext) -> Result { + let number = context.args().first().unwrap_or(&Value::Undefined); + let num = runtime.value_to_number(number)?; + Ok(Value::Boolean(num.is_finite())) +} + +//#sec-isnan-number global.function +pub fn is_nan(runtime: &mut Runtime, context: &mut CallContext) -> Result { + let number = context.args().first().unwrap_or(&Value::Undefined); + let num = runtime.value_to_number(number)?; + Ok(Value::Boolean(num.is_nan())) +} + +//#sec-parsefloat-string global.function +pub fn parse_float(runtime: &mut Runtime, context: &mut CallContext) -> Result { + let string = context.args().first().unwrap_or(&Value::Undefined); + let input_string = runtime.value_to_string(string)?; + let trimmed_string = runtime.trim_string(input_string, true, false)?; + // TODO: 11.1.6 Static Semantics: ParseText ( sourceText, goalSymbol ) + let utf8 = char::decode_utf16(trimmed_string.code_units()) + .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) + .collect::(); + match utf8.parse::() { + Ok(n) => Ok(Value::Number(n)), + Err(_) => Err(Error::SyntaxError), + } +} + +//#sec-parseint-string-radix global.function +pub fn parse_int(runtime: &mut Runtime, context: &mut CallContext) -> Result { + // TODO: impl + let string = context.args().first().unwrap_or(&Value::Undefined); + let input_string = runtime.value_to_string(string)?; + let s = runtime.trim_string(input_string, true, false)?; + let radix = context.args().get(1).unwrap_or(&Value::Undefined); + let radix = runtime.value_to_number(radix)?; + let radix = if radix.is_finite() { radix as i32 } else { 10 }; + if !(2..=36).contains(&radix) { + return Ok(Value::Number(f64::NAN)); + } + let utf8 = char::decode_utf16(s.code_units()) + .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) + .collect::(); + match i64::from_str_radix(&utf8, radix as u32) { + Ok(n) => Ok(Value::Number(n as f64)), + Err(_) => Ok(Value::Number(f64::NAN)), + } +} + +//#sec-constructor-properties-of-the-global-object-aggregate-error global.property +pub fn define_aggregate_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_aggregate_error_constructor(); + runtime.define_constructor(Symbol::AGGREGATE_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-error global.property +pub fn define_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_error_constructor(); + runtime.define_constructor(Symbol::ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-evalerror global.property +pub fn define_eval_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_eval_error_constructor(); + runtime.define_constructor(Symbol::EVAL_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-function global.property +pub fn define_function_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_function_constructor(); + runtime.define_constructor(Symbol::FUNCTION, constructor); +} + +//#_internalerror global.property { "property": "InternalError" } +pub fn define_internal_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_internal_error_constructor(); + runtime.define_constructor(Symbol::FUNCTION, constructor); +} + +//#sec-constructor-properties-of-the-global-object-object global.property +pub fn define_object_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_object_constructor(); + runtime.define_constructor(Symbol::OBJECT, constructor); +} + +//#sec-constructor-properties-of-the-global-object-promise global.property +pub fn define_promise_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_promise_constructor(); + runtime.define_constructor(Symbol::PROMISE, constructor); +} + +//#sec-constructor-properties-of-the-global-object-rangeerror global.property +pub fn define_range_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_range_error_constructor(); + runtime.define_constructor(Symbol::RANGE_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-referenceerror global.property +pub fn define_reference_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_reference_error_constructor(); + runtime.define_constructor(Symbol::REFERENCE_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-string global.property +pub fn define_string_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_string_constructor(); + runtime.define_constructor(Symbol::STRING, constructor); +} + +//#sec-constructor-properties-of-the-global-object-syntaxerror global.property +pub fn define_syntax_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_syntax_error_constructor(); + runtime.define_constructor(Symbol::SYNTAX_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-typeerror global.property +pub fn define_type_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_type_error_constructor(); + runtime.define_constructor(Symbol::TYPE_ERROR, constructor); +} + +//#sec-constructor-properties-of-the-global-object-urierror global.property +pub fn define_uri_error_constructor(runtime: &mut Runtime) { + let constructor = runtime.create_uri_error_constructor(); + runtime.define_constructor(Symbol::URI_ERROR, constructor); +} diff --git a/libs/jsruntime/src/builtins/global/mod.rs.hbs b/libs/jsruntime/src/builtins/global/mod.rs.hbs new file mode 100644 index 000000000..96d1190ec --- /dev/null +++ b/libs/jsruntime/src/builtins/global/mod.rs.hbs @@ -0,0 +1,78 @@ +// DO NOT EDIT THIS FILE BY HAND. +// +// This file was automagically generated with: +// template: {{@template}} + +mod imp; + +use jsgc::HandleMut; +use jsparser::Symbol; + +use crate::Runtime; +use crate::logger; +use crate::types::CallContext; +use crate::types::Object; +use crate::types::Property; +use crate::types::Status; +use crate::types::Value; + +use super::BuiltinFunctionParams; + +impl Runtime { + pub(super) fn init_global_object(&mut self) { + logger::debug!(event = "init_global_object"); + + // TODO: pass [[Prototype]] of the global object. + + {{#each globalProperties}} + {{#if (eq kind "global.property")}} + imp::{{imp}}(self); + + {{else if (eq kind "global.function")}} + let func = self.create_builtin_function(&BuiltinFunctionParams { + lambda: {{imp}}, + name: const_string!("{{name}}"), + length: {{this.length}}, + slots: &[], + prototype: None, + }); + self.define_global_property(Symbol::{{symbol}}, Property::data_wxc(Value::Object(func))); + + {{/if}} + {{/each}} + } + + fn define_global_property(&mut self, symbol: Symbol, prop: Property) { + let result = self.builtins.global_object.define_own_property(symbol.into(), prop); + debug_assert!(matches!(result, Ok(true))); + } + + fn define_constructor(&mut self, symbol: Symbol, constructor: HandleMut) { + self.define_global_property(symbol, Property::data_xxx(Value::Object(constructor))); + } +} + +// lambda functions +// TODO: use proc-macro + +{{#each globalProperties}} +{{#if (eq kind "global.function")}} + +extern "C" fn {{imp}}( + runtime: &mut Runtime, + context: &mut CallContext, + retv: &mut Value, +) -> Status { + match imp::{{imp}}(runtime, context) { + Ok(value) => { + *retv = value; + Status::Normal + } + Err(err) => { + *retv = runtime.create_exception(err); + Status::Exception + } + } +} +{{/if}} +{{/each}} diff --git a/libs/jsruntime/src/builtins/imp.js b/libs/jsruntime/src/builtins/imp.js index 1228c24fc..66c246968 100644 --- a/libs/jsruntime/src/builtins/imp.js +++ b/libs/jsruntime/src/builtins/imp.js @@ -47,6 +47,7 @@ async function main(args, options) { constructor: null, constructorProperties: [], prototypeProperties: [], + globalProperties: [], }; for await (const data of dataStream(args.impRs)) { @@ -70,6 +71,14 @@ async function main(args, options) { collectFunctionDataFromSpec(spec, data, json.metadata); json.prototypeProperties.push(data); break; + case 'global.property': + collectPropertyDataFromSpec(spec, data, json.metadata); + json.globalProperties.push(data); + break; + case 'global.function': + collectFunctionDataFromSpec(spec, data, json.metadata); + json.globalProperties.push(data); + break; default: unreachable(); break; @@ -167,8 +176,12 @@ function dataStream(impRs) { } function collectPropertyDataFromSpec(spec, data, metadata) { - let clause = spec.window.document.getElementById(data.id); - data.property = clause.firstElementChild.textContent.trim(); + if (data.options?.property) { + data.property = data.options.property; + } else { + let clause = spec.window.document.getElementById(data.id); + data.property = clause.firstElementChild.textContent.trim(); + } if (data.property.startsWith('_NativeError_.')) { data.property = data.property.replace('_NativeError_', metadata['class']); } @@ -208,6 +221,14 @@ function collectFunctionDataFromSpec(spec, data, metadata) { data.name = data.signature.name.split('.')[2]; data.symbol = constantCase(data.name); break; + case 'global.function': + data.name = data.signature.name; + if (data.name === 'isNaN') { + data.symbol = 'IS_NAN'; + } else { + data.symbol = constantCase(data.name); + } + break; default: unreachable(); } diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index 542c22ac2..dd9a1c634 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -2,6 +2,7 @@ mod aggregate_error; mod error; mod eval_error; mod function; +mod global; mod internal_error; mod object; mod promise; @@ -21,11 +22,9 @@ use crate::Error; use crate::Runtime; use crate::lambda::LambdaId; use crate::logger; -use crate::types::CallContext; use crate::types::Lambda; use crate::types::Object; use crate::types::Property; -use crate::types::Status; use crate::types::String; use crate::types::Value; @@ -92,7 +91,11 @@ impl Builtins { impl Runtime { // The second phase of the two-phase construction. pub(crate) fn init_builtin_objects(&mut self) { - // Initialize intrinsic objects. + self.init_intrinsic_objects(); + self.init_global_object(); + } + + fn init_intrinsic_objects(&mut self) { self.init_object_prototype(); self.init_function_prototype(); self.init_string_prototype(); @@ -106,103 +109,6 @@ impl Runtime { self.init_syntax_error_prototype(); self.init_type_error_prototype(); self.init_uri_error_prototype(); - - // TODO: pass [[Prototype]] of the global object. - - let this = self.builtins.global_object.as_handle(); - - macro_rules! define { - ($key:expr => $value:expr,) => { - define!(kv: $key, $value); - }; - ($key:expr => $value:expr, $($keys:expr => $values:expr,)+) => { - define!(kv: $key, $value); - define!($($keys => $values,)+); - }; - (kv: $key:expr, $value:expr) => { - let prop = Property::data_xxx($value); - let result = self.builtins.global_object.define_own_property($key.into(), prop); - debug_assert!(matches!(result, Ok(true))); - }; - } - - // 19 The Global Object - define! { - // TODO: 19.1.1 globalThis - Symbol::GLOBAL_THIS => Value::Object(this), - // 19.1.2 Infinity - Symbol::INFINITY => Value::Number(f64::INFINITY), - // 19.1.3 NaN - Symbol::NAN => Value::Number(f64::NAN), - // 19.1.4 undefined - Symbol::KEYWORD_UNDEFINED => Value::Undefined, - // 19.2.1 eval ( x ) - Symbol::EVAL => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { - lambda: eval, - name: const_string!(jsparser::symbol::builtin::names::EVAL), - length: 1, - slots: &[], - prototype: Some(self.builtins.function_prototype), - })), - // 19.2.2 isFinite ( number ) - Symbol::IS_FINITE => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { - lambda: is_finite, - name: const_string!(jsparser::symbol::builtin::names::IS_FINITE), - length: 1, - slots: &[], - prototype: Some(self.builtins.function_prototype), - })), - // 19.2.3 isNaN ( number ) - Symbol::IS_NAN => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { - lambda: is_nan, - name: const_string!(jsparser::symbol::builtin::names::IS_NAN), - length: 1, - slots: &[], - prototype: Some(self.builtins.function_prototype), - })), - // 19.2.4 parseFloat ( string ) - Symbol::PARSE_FLOAT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { - lambda: parse_float, - name: const_string!(jsparser::symbol::builtin::names::PARSE_FLOAT), - length: 1, - slots: &[], - prototype: Some(self.builtins.function_prototype), - })), - // 19.2.5 parseInt ( string, radix ) - Symbol::PARSE_INT => Value::Object(self.create_builtin_function(&BuiltinFunctionParams { - lambda: parse_int, - name: const_string!(jsparser::symbol::builtin::names::PARSE_INT), - length: 2, - slots: &[], - prototype: Some(self.builtins.function_prototype), - })), - // 19.3.1 AggregateError ( . . . ) - Symbol::AGGREGATE_ERROR => Value::Object(self.create_aggregate_error_constructor()), - // 19.3.10 Error ( . . . ) - Symbol::ERROR => Value::Object(self.create_error_constructor()), - // 19.3.11 EvalError ( . . . ) - Symbol::EVAL_ERROR => Value::Object(self.create_eval_error_constructor()), - // 19.3.16 Function ( . . . ) - Symbol::FUNCTION => Value::Object(self.create_function_constructor()), - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/InternalError - Symbol::INTERNAL_ERROR => Value::Object(self.create_internal_error_constructor()), - // 19.3.23 Object ( . . . ) - Symbol::OBJECT => Value::Object(self.create_object_constructor()), - // 19.3.24 Promise ( . . . ) - Symbol::PROMISE => Value::Object(self.create_promise_constructor()), - // 19.3.26 RangeError ( . . . ) - Symbol::RANGE_ERROR => Value::Object(self.create_range_error_constructor()), - // 19.3.27 ReferenceError ( . . . ) - Symbol::REFERENCE_ERROR => Value::Object(self.create_reference_error_constructor()), - // 19.3.31 String() - Symbol::STRING => Value::Object(self.create_string_constructor()), - // 19.3.33 SyntaxError ( . . . ) - Symbol::SYNTAX_ERROR => Value::Object(self.create_syntax_error_constructor()), - // 19.3.34 TypeError ( . . . ) - Symbol::TYPE_ERROR => Value::Object(self.create_type_error_constructor()), - // 19.3.39 URIError ( . . . ) - Symbol::URI_ERROR => Value::Object(self.create_uri_error_constructor()), - } } fn create_builtin_function(&mut self, params: &BuiltinFunctionParams) -> HandleMut { @@ -357,91 +263,6 @@ impl Runtime { } } -extern "C" fn eval( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match imp::eval(runtime, context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(err) => { - *retv = runtime.create_exception(err); - Status::Exception - } - } -} - -extern "C" fn is_finite( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match imp::is_finite(runtime, context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(err) => { - *retv = runtime.create_exception(err); - Status::Exception - } - } -} - -extern "C" fn is_nan( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match imp::is_nan(runtime, context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(err) => { - *retv = runtime.create_exception(err); - Status::Exception - } - } -} - -extern "C" fn parse_float( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match imp::parse_float(runtime, context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(err) => { - *retv = runtime.create_exception(err); - Status::Exception - } - } -} - -extern "C" fn parse_int( - runtime: &mut Runtime, - context: &mut CallContext, - retv: &mut Value, -) -> Status { - match imp::parse_int(runtime, context) { - Ok(value) => { - *retv = value; - Status::Normal - } - Err(err) => { - *retv = runtime.create_exception(err); - Status::Exception - } - } -} - // 7.2.1 RequireObjectCoercible ( argument ) fn require_object_coercible(value: &Value) -> Result<(), Error> { match value { @@ -459,67 +280,3 @@ struct BuiltinFunctionParams<'a, X> { slots: &'a [Value], prototype: Option>, } - -mod imp { - use super::*; - - pub fn eval(_runtime: &mut Runtime, _context: &mut CallContext) -> Result { - // TODO: impl - Err(Error::InternalError(None)) - } - - pub fn is_finite( - runtime: &mut Runtime, - context: &mut CallContext, - ) -> Result { - let number = context.args().first().unwrap_or(&Value::Undefined); - let num = runtime.value_to_number(number)?; - Ok(Value::Boolean(num.is_finite())) - } - - pub fn is_nan(runtime: &mut Runtime, context: &mut CallContext) -> Result { - let number = context.args().first().unwrap_or(&Value::Undefined); - let num = runtime.value_to_number(number)?; - Ok(Value::Boolean(num.is_nan())) - } - - pub fn parse_float( - runtime: &mut Runtime, - context: &mut CallContext, - ) -> Result { - let string = context.args().first().unwrap_or(&Value::Undefined); - let input_string = runtime.value_to_string(string)?; - let trimmed_string = runtime.trim_string(input_string, true, false)?; - // TODO: 11.1.6 Static Semantics: ParseText ( sourceText, goalSymbol ) - let utf8 = char::decode_utf16(trimmed_string.code_units()) - .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) - .collect::(); - match utf8.parse::() { - Ok(n) => Ok(Value::Number(n)), - Err(_) => Err(Error::SyntaxError), - } - } - - pub fn parse_int( - runtime: &mut Runtime, - context: &mut CallContext, - ) -> Result { - // TODO: impl - let string = context.args().first().unwrap_or(&Value::Undefined); - let input_string = runtime.value_to_string(string)?; - let s = runtime.trim_string(input_string, true, false)?; - let radix = context.args().get(1).unwrap_or(&Value::Undefined); - let radix = runtime.value_to_number(radix)?; - let radix = if radix.is_finite() { radix as i32 } else { 10 }; - if !(2..=36).contains(&radix) { - return Ok(Value::Number(f64::NAN)); - } - let utf8 = char::decode_utf16(s.code_units()) - .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER)) - .collect::(); - match i64::from_str_radix(&utf8, radix as u32) { - Ok(n) => Ok(Value::Number(n as f64)), - Err(_) => Ok(Value::Number(f64::NAN)), - } - } -} From e5520a87943bacb59becef27c7418667265299d8 Mon Sep 17 00:00:00 2001 From: masnagam Date: Mon, 27 Apr 2026 14:56:19 +0900 Subject: [PATCH 06/10] docs(jsruntime): update README.md files --- libs/jsruntime/src/builtins/global/README.md | 3 ++- libs/jsruntime/src/builtins/global/imp.rs | 26 +++++++++---------- libs/jsruntime/src/builtins/global/mod.rs.hbs | 5 +++- libs/jsruntime/src/builtins/imp.js | 16 ++++++++++++ libs/jsruntime/src/builtins/object/README.md | 2 +- .../src/builtins/update_readme_md.js | 17 ++++++++++++ 6 files changed, 53 insertions(+), 16 deletions(-) diff --git a/libs/jsruntime/src/builtins/global/README.md b/libs/jsruntime/src/builtins/global/README.md index f5bac842c..9c6149f13 100644 --- a/libs/jsruntime/src/builtins/global/README.md +++ b/libs/jsruntime/src/builtins/global/README.md @@ -4,7 +4,7 @@ * [x] [Infinity](https://tc39.es/ecma262/#sec-value-properties-of-the-global-object-infinity) * [x] [NaN](https://tc39.es/ecma262/#sec-value-properties-of-the-global-object-nan) * [x] [undefined](https://tc39.es/ecma262/#sec-undefined) -* [ ] [eval](https://tc39.es/ecma262/#sec-eval-x) +* [x] [eval](https://tc39.es/ecma262/#sec-eval-x) * [x] [isFinite](https://tc39.es/ecma262/#sec-isfinite-number) * [x] [isNaN](https://tc39.es/ecma262/#sec-isnan-number) * [x] [parseFloat](https://tc39.es/ecma262/#sec-parsefloat-string) @@ -28,6 +28,7 @@ * [ ] [Int8Array](https://tc39.es/ecma262/#sec-int8array) * [ ] [Int16Array](https://tc39.es/ecma262/#sec-int16array) * [ ] [Int32Array](https://tc39.es/ecma262/#sec-int32array) +* [x] [InternalError](https://tc39.es/ecma262/#_internalerror) * [ ] [Iterator](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-iterator) * [ ] [Map](https://tc39.es/ecma262/#sec-map) * [ ] [Number](https://tc39.es/ecma262/#sec-constructor-properties-of-the-global-object-number) diff --git a/libs/jsruntime/src/builtins/global/imp.rs b/libs/jsruntime/src/builtins/global/imp.rs index 64012a057..b0fa677d8 100644 --- a/libs/jsruntime/src/builtins/global/imp.rs +++ b/libs/jsruntime/src/builtins/global/imp.rs @@ -88,79 +88,79 @@ pub fn parse_int(runtime: &mut Runtime, context: &mut CallContext) -> Resu } } -//#sec-constructor-properties-of-the-global-object-aggregate-error global.property +//#sec-constructor-properties-of-the-global-object-aggregate-error global.constructor pub fn define_aggregate_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_aggregate_error_constructor(); runtime.define_constructor(Symbol::AGGREGATE_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-error global.property +//#sec-constructor-properties-of-the-global-object-error global.constructor pub fn define_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_error_constructor(); runtime.define_constructor(Symbol::ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-evalerror global.property +//#sec-constructor-properties-of-the-global-object-evalerror global.constructor pub fn define_eval_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_eval_error_constructor(); runtime.define_constructor(Symbol::EVAL_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-function global.property +//#sec-constructor-properties-of-the-global-object-function global.constructor pub fn define_function_constructor(runtime: &mut Runtime) { let constructor = runtime.create_function_constructor(); runtime.define_constructor(Symbol::FUNCTION, constructor); } -//#_internalerror global.property { "property": "InternalError" } +//#_internalerror global.constructor { "name": "InternalError" } pub fn define_internal_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_internal_error_constructor(); runtime.define_constructor(Symbol::FUNCTION, constructor); } -//#sec-constructor-properties-of-the-global-object-object global.property +//#sec-constructor-properties-of-the-global-object-object global.constructor pub fn define_object_constructor(runtime: &mut Runtime) { let constructor = runtime.create_object_constructor(); runtime.define_constructor(Symbol::OBJECT, constructor); } -//#sec-constructor-properties-of-the-global-object-promise global.property +//#sec-constructor-properties-of-the-global-object-promise global.constructor pub fn define_promise_constructor(runtime: &mut Runtime) { let constructor = runtime.create_promise_constructor(); runtime.define_constructor(Symbol::PROMISE, constructor); } -//#sec-constructor-properties-of-the-global-object-rangeerror global.property +//#sec-constructor-properties-of-the-global-object-rangeerror global.constructor pub fn define_range_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_range_error_constructor(); runtime.define_constructor(Symbol::RANGE_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-referenceerror global.property +//#sec-constructor-properties-of-the-global-object-referenceerror global.constructor pub fn define_reference_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_reference_error_constructor(); runtime.define_constructor(Symbol::REFERENCE_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-string global.property +//#sec-constructor-properties-of-the-global-object-string global.constructor pub fn define_string_constructor(runtime: &mut Runtime) { let constructor = runtime.create_string_constructor(); runtime.define_constructor(Symbol::STRING, constructor); } -//#sec-constructor-properties-of-the-global-object-syntaxerror global.property +//#sec-constructor-properties-of-the-global-object-syntaxerror global.constructor pub fn define_syntax_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_syntax_error_constructor(); runtime.define_constructor(Symbol::SYNTAX_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-typeerror global.property +//#sec-constructor-properties-of-the-global-object-typeerror global.constructor pub fn define_type_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_type_error_constructor(); runtime.define_constructor(Symbol::TYPE_ERROR, constructor); } -//#sec-constructor-properties-of-the-global-object-urierror global.property +//#sec-constructor-properties-of-the-global-object-urierror global.constructor pub fn define_uri_error_constructor(runtime: &mut Runtime) { let constructor = runtime.create_uri_error_constructor(); runtime.define_constructor(Symbol::URI_ERROR, constructor); diff --git a/libs/jsruntime/src/builtins/global/mod.rs.hbs b/libs/jsruntime/src/builtins/global/mod.rs.hbs index 96d1190ec..01bf97b17 100644 --- a/libs/jsruntime/src/builtins/global/mod.rs.hbs +++ b/libs/jsruntime/src/builtins/global/mod.rs.hbs @@ -25,7 +25,10 @@ impl Runtime { // TODO: pass [[Prototype]] of the global object. {{#each globalProperties}} - {{#if (eq kind "global.property")}} + {{#if (eq kind "global.constructor")}} + imp::{{imp}}(self); + + {{else if (eq kind "global.property")}} imp::{{imp}}(self); {{else if (eq kind "global.function")}} diff --git a/libs/jsruntime/src/builtins/imp.js b/libs/jsruntime/src/builtins/imp.js index 66c246968..dc2dd3fc7 100644 --- a/libs/jsruntime/src/builtins/imp.js +++ b/libs/jsruntime/src/builtins/imp.js @@ -71,6 +71,10 @@ async function main(args, options) { collectFunctionDataFromSpec(spec, data, json.metadata); json.prototypeProperties.push(data); break; + case 'global.constructor': + collectConstructorDataFromSpec(spec, data, json.metadata); + json.globalProperties.push(data); + break; case 'global.property': collectPropertyDataFromSpec(spec, data, json.metadata); json.globalProperties.push(data); @@ -175,6 +179,18 @@ function dataStream(impRs) { })); } +function collectConstructorDataFromSpec(spec, data, metadata) { + if (data.options?.name) { + data.name = data.options.name; + } else { + const clause = spec.window.document.getElementById(data.id); + const signature = parseSignature(clause.firstElementChild.textContent.trim()); + data.name = signature.name; + } + data.symbol = constantCase(data.name); + return data; +} + function collectPropertyDataFromSpec(spec, data, metadata) { if (data.options?.property) { data.property = data.options.property; diff --git a/libs/jsruntime/src/builtins/object/README.md b/libs/jsruntime/src/builtins/object/README.md index fbde5df4f..a496d7212 100644 --- a/libs/jsruntime/src/builtins/object/README.md +++ b/libs/jsruntime/src/builtins/object/README.md @@ -1,6 +1,6 @@ # Object -* [ ] [Object](https://tc39.es/ecma262/#sec-object-value) +* [x] [Object](https://tc39.es/ecma262/#sec-object-value) * [ ] [Object.assign](https://tc39.es/ecma262/#sec-object.assign) * [ ] [Object.create](https://tc39.es/ecma262/#sec-object.create) * [ ] [Object.defineProperties](https://tc39.es/ecma262/#sec-object.defineproperties) diff --git a/libs/jsruntime/src/builtins/update_readme_md.js b/libs/jsruntime/src/builtins/update_readme_md.js index 003b2ab2a..3dc663944 100644 --- a/libs/jsruntime/src/builtins/update_readme_md.js +++ b/libs/jsruntime/src/builtins/update_readme_md.js @@ -76,6 +76,23 @@ async function main(args, options) { } continue; } + prop = data.globalProperties.find((prop) => prop.id === id); + if (prop) { + switch (prop.kind) { + case 'global.constructor': + lines.push(`* [x] [${prop.name}](${ECMA262_SPEC_URL_BASE}${prop.id})`); + break; + case 'global.property': + lines.push(`* [x] [${prop.property}](${ECMA262_SPEC_URL_BASE}${prop.id})`); + break; + case 'global.function': + lines.push(`* [x] [${prop.signature.name}](${ECMA262_SPEC_URL_BASE}${prop.id})`); + break; + default: + unreachable(); + } + continue; + } lines.push(line.replace('[x]', '[ ]')); } From 08f5eb4c3e5be6f349fa1d8356e1779008296f68 Mon Sep 17 00:00:00 2001 From: masnagam Date: Mon, 27 Apr 2026 19:53:29 +0900 Subject: [PATCH 07/10] refactor(jsruntime): const_string! const_string_handle! --- .../src/backend/clir/compiler/mod.rs | 26 +++++++++---------- .../jsruntime/src/builtins/builtin_mod.rs.hbs | 6 ++--- libs/jsruntime/src/builtins/error/imp.rs | 4 +-- libs/jsruntime/src/builtins/global/mod.rs.hbs | 2 +- libs/jsruntime/src/builtins/mod.rs | 10 +++---- .../src/builtins/native_error_imp.rs.hbs | 2 +- libs/jsruntime/src/jobs.rs | 4 +-- libs/jsruntime/src/lib.rs | 7 +++-- libs/jsruntime/src/macros.rs | 17 +++++++++--- libs/jsruntime/src/types/value.rs | 12 ++++----- 10 files changed, 50 insertions(+), 40 deletions(-) diff --git a/libs/jsruntime/src/backend/clir/compiler/mod.rs b/libs/jsruntime/src/backend/clir/compiler/mod.rs index 6549d02da..5047fb3ea 100644 --- a/libs/jsruntime/src/backend/clir/compiler/mod.rs +++ b/libs/jsruntime/src/backend/clir/compiler/mod.rs @@ -440,7 +440,7 @@ where self.editor .put_branch(too_deep, then_block, &[], merge_block, &[]); self.editor.switch_to_block(then_block); - self.emit_throw_internal_error(const_string!("Call stack too deep")); + self.emit_throw_internal_error(const_string_handle!("Call stack too deep")); self.editor.put_jump(merge_block, &[]); self.editor.switch_to_block(merge_block); } @@ -1168,7 +1168,7 @@ where fn process_call(&mut self, argc: u16) { // TODO: dynamic allocation if argc > 8 { - self.emit_throw_internal_error(const_string!("TODO: too many arguments")); + self.emit_throw_internal_error(const_string_handle!("TODO: too many arguments")); let len = self.operand_stack.len() - (argc as usize) - 1; self.operand_stack.truncate(len); self.operand_stack.push(Operand::Undefined); // TODO: dummy @@ -1226,7 +1226,7 @@ where fn process_new(&mut self, argc: u16) { // TODO: dynamic allocation if argc > 8 { - self.emit_throw_internal_error(const_string!("TODO: too many arguments")); + self.emit_throw_internal_error(const_string_handle!("TODO: too many arguments")); let len = self.operand_stack.len() - (argc as usize) - 1; self.operand_stack.truncate(len); self.operand_stack.push(Operand::Undefined); // TODO: dummy @@ -1424,7 +1424,7 @@ where .put_runtime_number_to_string(self.support, *value), Operand::String(value, _) => *value, Operand::Object(_) => { - self.emit_throw_internal_error(const_string!("TODO: ToString(object)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToString(object)")); self.editor.put_create_string(self.support, &[]) } Operand::Any(value, _) => self.editor.put_runtime_to_string(self.support, *value), @@ -1612,7 +1612,7 @@ where // 13.5.1.2 Runtime Semantics: Evaluation fn process_delete(&mut self) { let (_operand, ..) = self.dereference(); - self.emit_throw_internal_error(const_string!("TODO: delete operator")); + self.emit_throw_internal_error(const_string_handle!("TODO: delete operator")); self.process_boolean(true); } @@ -2057,7 +2057,7 @@ where fn process_instanceof(&mut self) { let (_lhs, ..) = self.dereference(); let (_rhs, ..) = self.dereference(); - self.emit_throw_internal_error(const_string!("TODO: instanceof operator")); + self.emit_throw_internal_error(const_string_handle!("TODO: instanceof operator")); self.process_boolean(false); } @@ -2065,7 +2065,7 @@ where fn process_in(&mut self) { let (_lhs, ..) = self.dereference(); let (_rhs, ..) = self.dereference(); - self.emit_throw_internal_error(const_string!("TODO: in operator")); + self.emit_throw_internal_error(const_string_handle!("TODO: in operator")); self.process_boolean(false); } @@ -2286,15 +2286,15 @@ where None } PropertyOwner::Boolean(_) => { - self.emit_throw_internal_error(const_string!("TODO: ToObject(boolean)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToObject(boolean)")); None } PropertyOwner::Number(_) => { - self.emit_throw_internal_error(const_string!("TODO: ToObject(number)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToObject(number)")); None } PropertyOwner::String(_) => { - self.emit_throw_internal_error(const_string!("TODO: ToObject(string)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToObject(string)")); None } PropertyOwner::Object(value) => Some(*value), @@ -3192,7 +3192,7 @@ where Operand::Boolean(value, ..) => *value, Operand::Number(value, ..) => self.editor.put_number_to_boolean(*value), Operand::String(..) => { - self.emit_throw_internal_error(const_string!("TODO: ToBoolean(string)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToBoolean(string)")); self.editor.put_boolean(false) } Operand::Object(_) => self.editor.put_boolean(true), @@ -3216,11 +3216,11 @@ where Operand::Boolean(value, ..) => self.editor.put_boolean_to_number(*value), Operand::Number(value, ..) => *value, Operand::String(..) => { - self.emit_throw_internal_error(const_string!("TODO: ToNumber(string)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToNumber(string)")); self.editor.put_number(f64::NAN) } Operand::Object(_) => { - self.emit_throw_internal_error(const_string!("TODO: ToNumber(object)")); + self.emit_throw_internal_error(const_string_handle!("TODO: ToNumber(object)")); self.editor.put_number(f64::NAN) } Operand::Any(value, ..) => self.editor.put_runtime_to_numeric(self.support, *value), diff --git a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs index 6fa666235..3501acedf 100644 --- a/libs/jsruntime/src/builtins/builtin_mod.rs.hbs +++ b/libs/jsruntime/src/builtins/builtin_mod.rs.hbs @@ -38,7 +38,7 @@ impl Runtime { #[allow(unused_mut)] let mut constructor = self.create_builtin_function(&BuiltinFunctionParams { lambda: constructor::, - name: const_string!("{{metadata.class}}"), + name: const_string_handle!("{{metadata.class}}"), length: 1, slots: &[], prototype: Some(self.builtins.{{metadata.id}}_prototype), @@ -48,7 +48,7 @@ impl Runtime { {{#if (eq kind "constructor.function")}} let func = self.create_builtin_function(&BuiltinFunctionParams { lambda: {{imp}}, - name: const_string!("{{name}}"), + name: const_string_handle!("{{name}}"), length: {{this.length}}, slots: &[], prototype: None, @@ -80,7 +80,7 @@ impl Runtime { {{else if (eq kind "prototype.function")}} let func = self.create_builtin_function(&BuiltinFunctionParams { lambda: {{imp}}, - name: const_string!("{{name}}"), + name: const_string_handle!("{{name}}"), length: {{this.length}}, slots: &[], prototype: None, diff --git a/libs/jsruntime/src/builtins/error/imp.rs b/libs/jsruntime/src/builtins/error/imp.rs index f1005be0b..d2a44678d 100644 --- a/libs/jsruntime/src/builtins/error/imp.rs +++ b/libs/jsruntime/src/builtins/error/imp.rs @@ -111,11 +111,11 @@ pub fn error_prototype_to_string( } else if message.is_empty() { name } else { - let result = runtime.concat_strings(const_string!(&[0x003A, 0x0020]), message); + let result = runtime.concat_strings(const_string_handle!(&[0x003A, 0x0020]), message); runtime.concat_strings(name, result) }; Ok(Value::String(result)) } -const NAME: Handle = const_string!(jsparser::symbol::builtin::names::ERROR); +const NAME: Handle = const_string_handle!(jsparser::symbol::builtin::names::ERROR); diff --git a/libs/jsruntime/src/builtins/global/mod.rs.hbs b/libs/jsruntime/src/builtins/global/mod.rs.hbs index 01bf97b17..cace26260 100644 --- a/libs/jsruntime/src/builtins/global/mod.rs.hbs +++ b/libs/jsruntime/src/builtins/global/mod.rs.hbs @@ -34,7 +34,7 @@ impl Runtime { {{else if (eq kind "global.function")}} let func = self.create_builtin_function(&BuiltinFunctionParams { lambda: {{imp}}, - name: const_string!("{{name}}"), + name: const_string_handle!("{{name}}"), length: {{this.length}}, slots: &[], prototype: None, diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index dd9a1c634..0a395c4c5 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -204,10 +204,10 @@ impl Runtime { logger::debug!(event = "runtime.value_to_string", ?value); match value { Value::None => unreachable!("Value::None"), - Value::Undefined => Ok(const_string!("undefined")), - Value::Null => Ok(const_string!("null")), - Value::Boolean(true) => Ok(const_string!("true")), - Value::Boolean(false) => Ok(const_string!("false")), + Value::Undefined => Ok(const_string_handle!("undefined")), + Value::Null => Ok(const_string_handle!("null")), + Value::Boolean(true) => Ok(const_string_handle!("true")), + Value::Boolean(false) => Ok(const_string_handle!("false")), Value::Number(value) => Ok(self.number_to_string(*value)), Value::String(value) => Ok(*value), // TODO(feat): Value::Symbol(_) => Err(Error::TypeError), @@ -220,7 +220,7 @@ impl Runtime { if self.is_string_object(object) { Ok(object.string()) } else { - Ok(const_string!("[object Object]")) + Ok(const_string_handle!("[object Object]")) } } diff --git a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs index 1312c2903..d2f3eef63 100644 --- a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs +++ b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs @@ -74,7 +74,7 @@ pub fn {{id}}_prototype_message(_runtime: &mut Runtime, mut prototype: Han pub fn {{id}}_prototype_name(_runtime: &mut Runtime, mut prototype: HandleMut) { let _ = prototype.define_own_property( Symbol::NAME.into(), - Property::data_xxx(Value::String(const_string!("{{class}}"))), + Property::data_xxx(Value::String(const_string_handle!("{{class}}"))), ); } diff --git a/libs/jsruntime/src/jobs.rs b/libs/jsruntime/src/jobs.rs index 3dcc9fb59..ad76434ff 100644 --- a/libs/jsruntime/src/jobs.rs +++ b/libs/jsruntime/src/jobs.rs @@ -205,11 +205,11 @@ mod tests { let object = runtime.create_object(); resolved!(Value::Undefined); - resolved!(Value::String(const_string!("resolved"))); + resolved!(Value::String(const_string_handle!("resolved"))); resolved!(Value::Object(object)); rejected!(Value::Undefined); - rejected!(Value::String(const_string!("rejected"))); + rejected!(Value::String(const_string_handle!("rejected"))); rejected!(Value::Object(object)); let mut roots = vec![]; diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index 9e52580b9..eb6e6bcd7 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -414,10 +414,9 @@ impl Runtime { Value::String(value) => { Ok(self.symbol_registry.intern_utf16(value.make_utf16()).into()) } - Value::Object(_) => { - const MESSAGE: Handle = const_string!("TODO: make_property_key"); - Err(Value::Object(self.create_internal_error(Some(MESSAGE)))) - } + Value::Object(_) => Err(Value::Object( + self.create_internal_error(Some(const_string_handle!("TODO: make_property_key"))), + )), } } diff --git a/libs/jsruntime/src/macros.rs b/libs/jsruntime/src/macros.rs index dc7bd8875..a57e05fee 100644 --- a/libs/jsruntime/src/macros.rs +++ b/libs/jsruntime/src/macros.rs @@ -2,16 +2,27 @@ macro_rules! const_string { ($utf8:literal) => {{ const STRING: $crate::types::String = $crate::types::String::new_const(base::utf16!(&$utf8)); - jsgc::Handle::from_ref(&STRING) + &STRING }}; ($slice:expr) => {{ const STRING: $crate::types::String = $crate::types::String::new_const($slice); - jsgc::Handle::from_ref(&STRING) + &STRING }}; } +macro_rules! const_string_handle { + ($utf8:literal) => { + jsgc::Handle::from_ref(const_string!($utf8)) + }; + ($slice:expr) => { + jsgc::Handle::from_ref(const_string!($slice)) + }; +} + macro_rules! runtime_todo { ($message:literal) => { - Err($crate::Error::InternalError(Some(const_string!($message)))) + Err($crate::Error::InternalError(Some(const_string_handle!( + $message + )))) }; } diff --git a/libs/jsruntime/src/types/value.rs b/libs/jsruntime/src/types/value.rs index 50bfe1bef..ae6f5d5df 100644 --- a/libs/jsruntime/src/types/value.rs +++ b/libs/jsruntime/src/types/value.rs @@ -83,12 +83,12 @@ impl Value { pub fn get_typeof(&self) -> Handle { match self { Self::None => unreachable!(), - Self::Undefined => const_string!("undefined"), - Self::Boolean(_) => const_string!("boolean"), - Self::Number(_) => const_string!("number"), - Self::String(_) => const_string!("string"), - Self::Object(object) if object.is_callable() => const_string!("function"), - Self::Null | Self::Object(_) => const_string!("object"), + Self::Undefined => const_string_handle!("undefined"), + Self::Boolean(_) => const_string_handle!("boolean"), + Self::Number(_) => const_string_handle!("number"), + Self::String(_) => const_string_handle!("string"), + Self::Object(object) if object.is_callable() => const_string_handle!("function"), + Self::Null | Self::Object(_) => const_string_handle!("object"), } } From 533b131d4fa717f08b2a14e9d1c3e4018e62db58 Mon Sep 17 00:00:00 2001 From: masnagam Date: Mon, 27 Apr 2026 20:34:33 +0900 Subject: [PATCH 08/10] feat(jsruntime): Error --- libs/jsruntime/src/backend/bridge.rs | 2 +- libs/jsruntime/src/builtins/error/imp.rs | 4 +- libs/jsruntime/src/builtins/global/imp.rs | 5 +-- libs/jsruntime/src/builtins/mod.rs | 18 +++++---- libs/jsruntime/src/builtins/promise/imp.rs | 13 +++--- libs/jsruntime/src/builtins/string/imp.rs | 10 ++--- libs/jsruntime/src/lib.rs | 29 ++++++++++++-- libs/jsruntime/src/macros.rs | 46 ++++++++++++++++++++-- libs/jsruntime/src/types/value.rs | 2 +- 9 files changed, 97 insertions(+), 32 deletions(-) diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index d373f6717..a208244e7 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -210,7 +210,7 @@ impl Runtime { logger::debug!(event = "to_object", ?value); match value { Value::None => unreachable!("Value::None"), - Value::Undefined | Value::Null => Err(Error::TypeError), + Value::Undefined | Value::Null => type_error!(), Value::Boolean(_value) => { runtime_todo!("ToObject: not yet implemented for Boolean values") } diff --git a/libs/jsruntime/src/builtins/error/imp.rs b/libs/jsruntime/src/builtins/error/imp.rs index d2a44678d..49de1989d 100644 --- a/libs/jsruntime/src/builtins/error/imp.rs +++ b/libs/jsruntime/src/builtins/error/imp.rs @@ -22,7 +22,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError(None)); + return runtime_todo!(); } let mut object = if let Value::Object(this) = context.this() { @@ -93,7 +93,7 @@ pub fn error_prototype_to_string( logger::debug!(event = "error_prototype_to_string"); let object = match context.this() { Value::Object(object) => object, - _ => return Err(Error::TypeError), + _ => return type_error!(), }; let name = match object.get_value(&Symbol::NAME.into()) { diff --git a/libs/jsruntime/src/builtins/global/imp.rs b/libs/jsruntime/src/builtins/global/imp.rs index b0fa677d8..3f21f92eb 100644 --- a/libs/jsruntime/src/builtins/global/imp.rs +++ b/libs/jsruntime/src/builtins/global/imp.rs @@ -34,8 +34,7 @@ pub fn global_undefined(runtime: &mut Runtime) { //#sec-eval-x global.function pub fn eval(_runtime: &mut Runtime, _context: &mut CallContext) -> Result { - // TODO: impl - Err(Error::InternalError(None)) + runtime_todo!("eval: not implemented") } //#sec-isfinite-number global.function @@ -63,7 +62,7 @@ pub fn parse_float(runtime: &mut Runtime, context: &mut CallContext) -> Re .collect::(); match utf8.parse::() { Ok(n) => Ok(Value::Number(n)), - Err(_) => Err(Error::SyntaxError), + Err(_) => syntax_error!(), } } diff --git a/libs/jsruntime/src/builtins/mod.rs b/libs/jsruntime/src/builtins/mod.rs index 0a395c4c5..1e25900a7 100644 --- a/libs/jsruntime/src/builtins/mod.rs +++ b/libs/jsruntime/src/builtins/mod.rs @@ -19,6 +19,7 @@ use jsgc::Heap; use jsparser::Symbol; use crate::Error; +use crate::ErrorKind; use crate::Runtime; use crate::lambda::LambdaId; use crate::logger; @@ -163,7 +164,7 @@ impl Runtime { Value::Boolean(true) => Ok(1.0), Value::Boolean(false) => Ok(0.0), Value::Number(value) => Ok(*value), - Value::String(_value) => Err(Error::InternalError(None)), // TODO + Value::String(_value) => runtime_todo!(), // TODO(feat): 7.1.1 ToPrimitive() Value::Object(_) => Ok(f64::NAN), } @@ -210,7 +211,7 @@ impl Runtime { Value::Boolean(false) => Ok(const_string_handle!("false")), Value::Number(value) => Ok(self.number_to_string(*value)), Value::String(value) => Ok(*value), - // TODO(feat): Value::Symbol(_) => Err(Error::TypeError), + // TODO(feat): Value::Symbol(_) => type_error!(), Value::Object(value) => self.object_to_string(*value), } } @@ -225,11 +226,12 @@ impl Runtime { } pub(crate) fn create_exception(&mut self, err: Error) -> Value { - Value::Object(match err { - Error::SyntaxError => self.create_syntax_error(None), - Error::TypeError => self.create_type_error(None), - Error::RangeError => self.create_range_error(None), - Error::InternalError(msg) => self.create_internal_error(msg), + let msg = err.message.map(Handle::from_ref); + Value::Object(match err.kind { + ErrorKind::SyntaxError => self.create_syntax_error(msg), + ErrorKind::TypeError => self.create_type_error(msg), + ErrorKind::RangeError => self.create_range_error(msg), + ErrorKind::InternalError => self.create_internal_error(msg), }) } @@ -267,7 +269,7 @@ impl Runtime { fn require_object_coercible(value: &Value) -> Result<(), Error> { match value { Value::None => unreachable!(), - Value::Undefined | Value::Null => Err(Error::TypeError), + Value::Undefined | Value::Null => type_error!(), _ => Ok(()), } } diff --git a/libs/jsruntime/src/builtins/promise/imp.rs b/libs/jsruntime/src/builtins/promise/imp.rs index 42c04892b..df1e1d135 100644 --- a/libs/jsruntime/src/builtins/promise/imp.rs +++ b/libs/jsruntime/src/builtins/promise/imp.rs @@ -21,14 +21,14 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError(None)); + return runtime_todo!(); } let args = context.args(); let executor = match args.first() { Some(Value::Object(executor)) if executor.is_callable() => *executor, - _ => return Err(Error::TypeError), + _ => return type_error!(), }; let closure = runtime.create_closure(promise_coroutine, LambdaId::HOST, 0); @@ -125,17 +125,20 @@ fn promise_resolve_sync( runtime: &mut Runtime, context: &mut CallContext, ) -> Result { - let func = context.func().ok_or(Error::InternalError(None))?; + let func = match context.func() { + Some(func) => func, + _ => return runtime_todo!(), + }; let promise = match func.slots().first() { Some(Value::Object(promise)) => *promise, - _ => return Err(Error::InternalError(None)), + _ => return runtime_todo!(), }; debug_assert!(runtime.is_promise_object(promise)); let resolution = context.args().first().unwrap_or(&Value::Undefined); match resolution { - Value::Object(object) if *object == promise => Err(Error::TypeError), + Value::Object(object) if *object == promise => type_error!(), // TODO: the 'then' property _ => { // TODO: fullfill_promise diff --git a/libs/jsruntime/src/builtins/string/imp.rs b/libs/jsruntime/src/builtins/string/imp.rs index 38ce6decd..f1b110a35 100644 --- a/libs/jsruntime/src/builtins/string/imp.rs +++ b/libs/jsruntime/src/builtins/string/imp.rs @@ -51,11 +51,11 @@ pub fn string_from_code_point( for arg in context.args().iter() { let num = crate::types::number::to_number(arg)?; if num.is_infinite() || num.is_nan() || num.fract() != 0.0 { - return Err(Error::RangeError); + return range_error!(); } let cp = num as i64; if !(0..=0x10FFFF).contains(&cp) { - return Err(Error::RangeError); + return range_error!(); } utf16.extend_from_slice(encode_code_point(cp, &mut buf)); } @@ -378,7 +378,7 @@ fn string_padding_builtins_impl( } if int_max_length > u32::MAX as u64 { - return Err(Error::InternalError(None)); + return runtime_todo!(); } let fill_string = args.get(1).unwrap_or(&Value::Undefined); @@ -436,7 +436,7 @@ pub fn string_prototype_repeat( let n = runtime.value_to_integer_or_infinity(count)?; if n < 0.0 || n.is_infinite() { - return Err(Error::RangeError); + return range_error!(); } if n == 0.0 { @@ -444,7 +444,7 @@ pub fn string_prototype_repeat( } if n > u32::MAX as f64 { - return Err(Error::InternalError(None)); + return runtime_todo!(); } let result = runtime.repeat_string(s, n as u32); diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index eb6e6bcd7..e80f5c046 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -492,13 +492,34 @@ pub trait Monitor { fn print_function_ir(&mut self, id: LambdaId, ir: &dyn std::fmt::Display); } -#[allow(clippy::enum_variant_names)] -#[derive(Debug)] -pub enum Error { +#[derive(Clone, Debug)] +pub struct Error { + kind: ErrorKind, + message: Option<&'static String>, +} + +impl Error { + /// Returns the corresponding `ErrorKind` of this error. + pub fn kind(&self) -> ErrorKind { + self.kind + } + + /// Returns the optional message of this error. + pub fn message(&self) -> Option<&'static String> { + self.message + } + + fn new(kind: ErrorKind, message: Option<&'static String>) -> Self { + Self { kind, message } + } +} + +#[derive(Clone, Copy, Debug)] +pub enum ErrorKind { SyntaxError, TypeError, RangeError, - InternalError(Option>), + InternalError, } #[cfg(test)] diff --git a/libs/jsruntime/src/macros.rs b/libs/jsruntime/src/macros.rs index a57e05fee..316c63999 100644 --- a/libs/jsruntime/src/macros.rs +++ b/libs/jsruntime/src/macros.rs @@ -19,10 +19,50 @@ macro_rules! const_string_handle { }; } +macro_rules! syntax_error { + () => { + Err($crate::Error::new($crate::ErrorKind::SyntaxError, None)) + }; + ($message:literal) => { + Err($crate::Error::new( + $crate::ErrorKind::SyntaxError, + Some(const_string!($message)), + )) + }; +} + +macro_rules! error { + ($kind:expr) => { + Err($crate::Error::new($kind, None)) + }; + ($kind:expr, $message:literal) => { + Err($crate::Error::new($kind, Some(const_string!($message)))) + }; +} + +macro_rules! type_error { + () => { + error!($crate::ErrorKind::TypeError) + }; + ($message:literal) => { + error!($crate::ErrorKind::TypeError, $message) + }; +} + +macro_rules! range_error { + () => { + error!($crate::ErrorKind::RangeError) + }; + ($message:literal) => { + error!($crate::ErrorKind::RangeError, $message) + }; +} + macro_rules! runtime_todo { + () => { + error!($crate::ErrorKind::InternalError) + }; ($message:literal) => { - Err($crate::Error::InternalError(Some(const_string_handle!( - $message - )))) + error!($crate::ErrorKind::InternalError, $message) }; } diff --git a/libs/jsruntime/src/types/value.rs b/libs/jsruntime/src/types/value.rs index ae6f5d5df..dc9886c8b 100644 --- a/libs/jsruntime/src/types/value.rs +++ b/libs/jsruntime/src/types/value.rs @@ -61,7 +61,7 @@ impl Value { // 7.1.18 ToObject ( argument ) pub fn to_object(&self) -> Result, Error> { match self { - Self::Undefined | Self::Null => Err(Error::TypeError), + Self::Undefined | Self::Null => type_error!(), Self::Boolean(_value) => unimplemented!("new Boolean(value)"), Self::Number(_value) => unimplemented!("new Number(value)"), Self::String(_value) => unimplemented!("new String(value)"), From c36bc20863788e366eea975e906f51ac2a046122 Mon Sep 17 00:00:00 2001 From: masnagam Date: Tue, 28 Apr 2026 13:24:23 +0900 Subject: [PATCH 09/10] fix(jsruntime): build errors --- libs/jsruntime/src/builtins/native_error_imp.rs.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs index d2f3eef63..e0809d150 100644 --- a/libs/jsruntime/src/builtins/native_error_imp.rs.hbs +++ b/libs/jsruntime/src/builtins/native_error_imp.rs.hbs @@ -27,7 +27,7 @@ pub fn constructor(runtime: &mut Runtime, context: &mut CallContext) -> Re // TODO(feat): NewTarget if !context.is_new() { - return Err(Error::InternalError(None)); + return runtime_todo!(); } let mut object = if let Value::Object(this) = context.this() { From 1c2161db933f3019634310c3dffd8e1c9dea06e1 Mon Sep 17 00:00:00 2001 From: masnagam Date: Tue, 28 Apr 2026 14:20:22 +0900 Subject: [PATCH 10/10] refactor(jsruntime): refactoring --- libs/jsruntime/src/backend/bridge.rs | 10 +++++----- libs/jsruntime/src/lib.rs | 8 +++----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libs/jsruntime/src/backend/bridge.rs b/libs/jsruntime/src/backend/bridge.rs index a208244e7..6d0b71bcb 100644 --- a/libs/jsruntime/src/backend/bridge.rs +++ b/libs/jsruntime/src/backend/bridge.rs @@ -537,7 +537,7 @@ pub(crate) extern "C" fn runtime_get_value_by_value( let key = match runtime.make_property_key(key) { Ok(key) => key, Err(err) => { - *retv = err; + *retv = runtime.create_exception(err); return Status::Exception; } }; @@ -597,7 +597,7 @@ pub(crate) extern "C" fn runtime_set_value_by_value( let key = match runtime.make_property_key(key) { Ok(key) => key, Err(err) => { - *retv = err; + *retv = runtime.create_exception(err); return Status::Exception; } }; @@ -680,7 +680,7 @@ pub(crate) extern "C" fn runtime_create_data_property_by_value( let key = match runtime.make_property_key(key) { Ok(key) => key, Err(err) => { - *retv = err; + *retv = runtime.create_exception(err); return Status::Exception; } }; @@ -734,8 +734,8 @@ pub(crate) extern "C" fn runtime_push_value( *retv = Value::None; Status::Normal } - Err(exception) => { - *retv = exception; + Err(err) => { + *retv = runtime.create_exception(err); Status::Exception } } diff --git a/libs/jsruntime/src/lib.rs b/libs/jsruntime/src/lib.rs index e80f5c046..a9c14102b 100644 --- a/libs/jsruntime/src/lib.rs +++ b/libs/jsruntime/src/lib.rs @@ -403,7 +403,7 @@ impl Runtime { self.heap.alloc_mut(Object::new()) } - fn make_property_key(&mut self, value: &Value) -> Result { + fn make_property_key(&mut self, value: &Value) -> Result { match value { Value::None => unreachable!(), Value::Undefined => Ok(Symbol::KEYWORD_UNDEFINED.into()), @@ -414,9 +414,7 @@ impl Runtime { Value::String(value) => { Ok(self.symbol_registry.intern_utf16(value.make_utf16()).into()) } - Value::Object(_) => Err(Value::Object( - self.create_internal_error(Some(const_string_handle!("TODO: make_property_key"))), - )), + Value::Object(_) => runtime_todo!("TODO: make_property_key"), } } @@ -443,7 +441,7 @@ impl Runtime { Ok(()) } - fn push_value(&mut self, target: &mut Object, value: &Value) -> Result<(), Value> { + fn push_value(&mut self, target: &mut Object, value: &Value) -> Result<(), Error> { const LENGTH: PropertyKey = PropertyKey::Symbol(Symbol::LENGTH); let length = match target.get_value(&LENGTH) {