diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs index 7996bf021..55d47dfc0 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs @@ -5,18 +5,15 @@ use crate::{ ecmascript::{ Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, - BuiltinIntrinsicConstructor, Error, ExceptionType, Function, JsResult, Object, - PropertyDescriptor, PropertyKey, ProtoIntrinsics, Realm, String, Value, - builders::BuiltinFunctionBuilder, create_array_from_scoped_list, define_property_or_throw, - get_iterator, iterator_to_list, ordinary_create_from_constructor, throw_not_callable, - to_string, + BuiltinIntrinsicConstructor, ErrorConstructor, ExceptionType, InternalMethods, JsResult, + Object, PropertyDescriptor, Realm, String, Value, builders::BuiltinFunctionBuilder, + create_array_from_scoped_list, get_iterator, iterator_to_list, throw_not_callable, + unwrap_try, }, engine::{Bindable, GcScope, Scopable}, - heap::{ArenaAccessMut, IntrinsicConstructorIndexes}, + heap::IntrinsicConstructorIndexes, }; -use super::error_constructor::get_error_cause; - pub(crate) struct AggregateErrorConstructor; impl Builtin for AggregateErrorConstructor { const NAME: String<'static> = BUILTIN_STRING_MEMORY.AggregateError; @@ -38,45 +35,17 @@ impl AggregateErrorConstructor { mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { let errors = arguments.get(0).scope(agent, gc.nogc()); - let message = arguments.get(1).scope(agent, gc.nogc()); - let options = arguments.get(2); - // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. - let new_target = new_target.map_or_else( - || agent.running_execution_context().function.unwrap(), - |new_target| Function::try_from(new_target).unwrap(), - ); - // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »). - let o = ordinary_create_from_constructor( + let message = arguments.get(1).bind(gc.nogc()); + let options = arguments.get(2).bind(gc.nogc()); + let o = ErrorConstructor::base_constructor( agent, - new_target.unbind(), - ProtoIntrinsics::AggregateError, + ExceptionType::AggregateError, + ArgumentsList::from_mut_slice(&mut [message.unbind(), options.unbind()]), + new_target, gc.reborrow(), ) .unbind()? - .bind(gc.nogc()); - let o = Error::try_from(o.unbind()).unwrap(); - // 3. If message is not undefined, then - let message = message.get(agent).bind(gc.nogc()); - let message = if !message.is_undefined() { - // a. Let msg be ? ToString(message). - Some( - to_string(agent, message.unbind(), gc.reborrow()) - .unbind()? - .scope(agent, gc.nogc()), - ) - } else { - None - }; - // 4. Perform ? InstallErrorCause(O, options). - let cause = get_error_cause(agent, options, gc.reborrow()) - .unbind()? - .bind(gc.nogc()); - // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - let message = message.map(|message| message.get(agent).bind(gc.nogc())); - let heap_data = o.get_mut(agent); - heap_data.kind = ExceptionType::AggregateError; - heap_data.message = message.unbind(); - heap_data.cause = cause.unbind(); + .scope(agent, gc.nogc()); // 5. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)). let Some(iterator_record) = get_iterator(agent, errors.get(agent), false, gc.reborrow()) .unbind()? @@ -85,33 +54,25 @@ impl AggregateErrorConstructor { else { return Err(throw_not_callable(agent, gc.into_nogc())); }; - let errors_list = iterator_to_list(agent, iterator_record.unbind(), gc.reborrow()) - .unbind()? - .bind(gc.nogc()); + let errors_list = + iterator_to_list(agent, iterator_record.unbind(), gc.reborrow()).unbind()?; + let gc = gc.into_nogc(); + let o = unsafe { o.take(agent) }.bind(gc); // 6. Perform ! DefinePropertyOrThrow(O, "errors", PropertyDescriptor { - let property_descriptor = PropertyDescriptor { - // [[Configurable]]: true, - configurable: Some(true), - // [[Enumerable]]: false, - enumerable: Some(false), - // [[Writable]]: true, - writable: Some(true), - // [[Value]]: CreateArrayFromList(errorsList) - value: Some( - create_array_from_scoped_list(agent, errors_list, gc.nogc()) - .unbind() - .into(), - ), - ..Default::default() - }; - define_property_or_throw( + // [[Configurable]]: true, + // [[Enumerable]]: false, + // [[Writable]]: true, + // [[Value]]: CreateArrayFromList(errorsList) + let property_descriptor = PropertyDescriptor::non_enumerable_data_descriptor( + create_array_from_scoped_list(agent, errors_list, gc), + ); + unwrap_try(o.try_define_own_property( agent, - o.unbind(), - PropertyKey::from(BUILTIN_STRING_MEMORY.errors), + BUILTIN_STRING_MEMORY.errors.into(), property_descriptor, - gc.reborrow(), - ) - .unbind()?; + None, + gc, + )); // }). // 7. Return O. Ok(o.into()) diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs index ff1bfd780..9ddb50876 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs @@ -7,12 +7,13 @@ use crate::engine::NoGcScope; use crate::{ ecmascript::{ Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, - BuiltinIntrinsicConstructor, Error, ExceptionType, Function, JsResult, Object, PropertyKey, - ProtoIntrinsics, Realm, String, Value, builders::BuiltinFunctionBuilder, get, has_property, - ordinary_create_from_constructor, to_string, + BuiltinIntrinsicConstructor, Error, ErrorHeapData, ExceptionType, Function, + InternalMethods, JsResult, Object, PropertyDescriptor, PropertyKey, ProtoIntrinsics, Realm, + String, Value, builders::BuiltinFunctionBuilder, get, has_property, + ordinary_populate_from_constructor, to_string, unwrap_try, }, engine::{Bindable, GcScope, Scopable}, - heap::{ArenaAccessMut, IntrinsicConstructorIndexes}, + heap::{CreateHeapData, IntrinsicConstructorIndexes}, }; pub(crate) struct ErrorConstructor; @@ -39,74 +40,126 @@ impl Builtin for ErrorIsError { } impl ErrorConstructor { - /// ### [20.5.1.1 Error ( message \[ , options \] )](https://tc39.es/ecma262/#sec-error-message) - fn constructor<'gc>( + /// ### [20.5.6.1.1 NativeError ( message \[ , options \] )](https://tc39.es/ecma262/#sec-nativeerror) + pub(crate) fn base_constructor<'gc>( agent: &mut Agent, - _this_value: Value, + error_kind: ExceptionType, arguments: ArgumentsList, new_target: Option, mut gc: GcScope<'gc, '_>, - ) -> JsResult<'gc, Value<'gc>> { - let message = arguments.get(0).bind(gc.nogc()); - let mut options = arguments.get(1).bind(gc.nogc()); - let mut new_target = new_target.bind(gc.nogc()); + ) -> JsResult<'gc, Error<'gc>> { + let nogc = gc.nogc(); + let scoped_message = arguments.get(0).scope(agent, nogc); + let options = arguments.get(1).scope(agent, nogc); + let new_target = new_target.bind(nogc); + + let intrinsic = match error_kind { + ExceptionType::Error => ProtoIntrinsics::Error, + ExceptionType::AggregateError => ProtoIntrinsics::AggregateError, + ExceptionType::EvalError => ProtoIntrinsics::EvalError, + ExceptionType::RangeError => ProtoIntrinsics::RangeError, + ExceptionType::ReferenceError => ProtoIntrinsics::ReferenceError, + ExceptionType::SyntaxError => ProtoIntrinsics::SyntaxError, + ExceptionType::TypeError => ProtoIntrinsics::TypeError, + ExceptionType::UriError => ProtoIntrinsics::URIError, + }; + + // 1. If NewTarget is undefined, let newTarget be the active function + // object; else let newTarget be NewTarget. + let new_target = new_target.map_or_else( + || agent.running_execution_context().function.unwrap(), + |new_target| Function::try_from(new_target).unwrap(), + ); + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). + let o = agent + .heap + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + .create(ErrorHeapData::new(error_kind, None, None)) + .bind(gc.nogc()); + let o = ordinary_populate_from_constructor( + agent, + o.unbind().into(), + new_target.unbind(), + intrinsic, + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + let mut o = Error::try_from(o).unwrap(); + + // SAFETY: not shared. + let message = unsafe { scoped_message.take(agent) }.bind(gc.nogc()); // 3. If message is not undefined, then - let message = if let Ok(message) = String::try_from(message) { - Some(message.scope(agent, gc.nogc())) + let msg = if let Ok(msg) = String::try_from(message) { + Some(msg) } else if !message.is_undefined() { + let scoped_o = o.scope(agent, gc.nogc()); // a. Let msg be ? ToString(message). - let scoped_options = options.scope(agent, gc.nogc()); - let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc())); - let message = to_string(agent, message.unbind(), gc.reborrow()) + let ms = to_string(agent, message.unbind(), gc.reborrow()) .unbind()? - .scope(agent, gc.nogc()); - // SAFETY: Never shared. - unsafe { - new_target = scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc()); - options = scoped_options.take(agent).bind(gc.nogc()); - } - Some(message) + .bind(gc.nogc()); + // SAFETY: not shared. + o = unsafe { scoped_o.take(agent) }.bind(gc.nogc()); + Some(ms) } else { None }; + + // 3. If message is not undefined, then + if let Some(msg) = msg { + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + unwrap_try(o.try_define_own_property( + agent, + BUILTIN_STRING_MEMORY.message.into(), + PropertyDescriptor::non_enumerable_data_descriptor(msg), + None, + gc.nogc(), + )); + } + + // SAFETY: not shared. + let options = unsafe { options.take(agent) }.bind(gc.nogc()); + // 4. Perform ? InstallErrorCause(O, options). let cause = if !options.is_object() { None } else { - let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc())); + let scoped_o = o.scope(agent, gc.nogc()); let cause = get_error_cause(agent, options.unbind(), gc.reborrow()) .unbind()? .bind(gc.nogc()); - // SAFETY: Never shared. - new_target = unsafe { scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc()) }; - cause.map(|c| c.scope(agent, gc.nogc())) + // SAFETY: not shared. + o = unsafe { scoped_o.take(agent) }.bind(gc.nogc()); + cause }; + // 1. If options is an Object and ? HasProperty(options, "cause") is + // true, then + if let Some(cause) = cause { + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause). + unwrap_try(o.try_define_own_property( + agent, + BUILTIN_STRING_MEMORY.cause.into(), + PropertyDescriptor::non_enumerable_data_descriptor(cause), + None, + gc.nogc(), + )); + } - // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. - let new_target = new_target.map_or_else( - || agent.running_execution_context().function.unwrap(), - |new_target| Function::try_from(new_target).unwrap(), - ); - // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »). - let o = ordinary_create_from_constructor( - agent, - new_target.unbind(), - ProtoIntrinsics::Error, - gc.reborrow(), - ) - .unbind()? - .bind(gc.into_nogc()); - let o = Error::try_from(o).unwrap(); - // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - let message = message.map(|message| message.get(agent)); - let cause = cause.map(|c| c.get(agent)); - let heap_data = o.get_mut(agent); - heap_data.kind = ExceptionType::Error; - heap_data.message = message; - heap_data.cause = cause; // 5. Return O. - Ok(o.into()) + Ok(o.unbind()) + } + + /// ### [20.5.1.1 Error ( message \[ , options \] )](https://tc39.es/ecma262/#sec-error-message) + fn constructor<'gc>( + agent: &mut Agent, + _this_value: Value, + arguments: ArgumentsList, + new_target: Option, + gc: GcScope<'gc, '_>, + ) -> JsResult<'gc, Value<'gc>> { + Self::base_constructor(agent, ExceptionType::Error, arguments, new_target, gc) + .map(Value::from) } #[cfg(feature = "proposal-is-error")] diff --git a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs index fccf3bb7c..9b3366b65 100644 --- a/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs +++ b/nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/native_error_constructors.rs @@ -5,16 +5,13 @@ use crate::{ ecmascript::{ Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin, - BuiltinIntrinsicConstructor, Error, ExceptionType, Function, JsResult, Object, - ProtoIntrinsics, Realm, String, Value, builders::BuiltinFunctionBuilder, - ordinary_create_from_constructor, to_string, + BuiltinIntrinsicConstructor, ErrorConstructor, ExceptionType, JsResult, Object, Realm, + String, Value, builders::BuiltinFunctionBuilder, }, - engine::{Bindable, GcScope, Scopable}, - heap::{ArenaAccessMut, IntrinsicConstructorIndexes}, + engine::GcScope, + heap::IntrinsicConstructorIndexes, }; -use super::error_constructor::get_error_cause; - struct EvalErrorConstructor; impl Builtin for EvalErrorConstructor { const NAME: String<'static> = BUILTIN_STRING_MEMORY.EvalError; @@ -88,74 +85,9 @@ impl BuiltinIntrinsicConstructor for URIErrorConstructor { const INDEX: IntrinsicConstructorIndexes = IntrinsicConstructorIndexes::URIError; } +/// ### [20.5.6.1.1 NativeError ( message \[ , options \] )](https://tc39.es/ecma262/#sec-nativeerror) pub(crate) struct NativeErrorConstructors; impl NativeErrorConstructors { - /// ### [20.5.6.1.1 NativeError ( message \[ , options \] )](https://tc39.es/ecma262/#sec-nativeerror) - #[inline(always)] - fn constructor<'gc>( - agent: &mut Agent, - error_kind: ExceptionType, - arguments: ArgumentsList, - new_target: Option, - mut gc: GcScope<'gc, '_>, - ) -> JsResult<'gc, Value<'gc>> { - let nogc = gc.nogc(); - let scoped_message = arguments.get(0).scope(agent, nogc); - let options = arguments.get(1).scope(agent, nogc); - let new_target = new_target.bind(nogc); - - let intrinsic = match error_kind { - ExceptionType::Error => ProtoIntrinsics::Error, - ExceptionType::AggregateError => ProtoIntrinsics::AggregateError, - ExceptionType::EvalError => ProtoIntrinsics::EvalError, - ExceptionType::RangeError => ProtoIntrinsics::RangeError, - ExceptionType::ReferenceError => ProtoIntrinsics::ReferenceError, - ExceptionType::SyntaxError => ProtoIntrinsics::SyntaxError, - ExceptionType::TypeError => ProtoIntrinsics::TypeError, - ExceptionType::UriError => ProtoIntrinsics::URIError, - }; - - // 1. If NewTarget is undefined, let newTarget be the active function - // object; else let newTarget be NewTarget. - let new_target = new_target - .unwrap_or_else(|| agent.running_execution_context().function.unwrap().into()); - // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »). - let o = ordinary_create_from_constructor( - agent, - Function::try_from(new_target.unbind()).unwrap(), - intrinsic, - gc.reborrow(), - ) - .unbind()? - .scope(agent, gc.nogc()); - let message = scoped_message.get(agent).bind(gc.nogc()); - // 3. If message is not undefined, then - let msg = if !message.is_undefined() { - // a. Let msg be ? ToString(message). - let msg = to_string(agent, message.unbind(), gc.reborrow()) - .unbind()? - .bind(gc.nogc()); - // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - // Safety: scoped_message is never shared. - Some(unsafe { scoped_message.replace_self(agent, msg.unbind()) }) - } else { - None - }; - // 4. Perform ? InstallErrorCause(O, options). - // 5. Return O. - let cause = get_error_cause(agent, options.get(agent), gc.reborrow()).unbind()?; - let gc = gc.into_nogc(); - let cause = cause.bind(gc); - let o = Error::try_from(o.get(agent).bind(gc)).unwrap(); - // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). - let msg = msg.map(|msg| msg.get(agent).bind(gc)); - let heap_data = o.get_mut(agent); - heap_data.kind = error_kind; - heap_data.message = msg.unbind(); - heap_data.cause = cause.unbind(); - Ok(o.into()) - } - fn eval_error_constructor<'gc>( agent: &mut Agent, _this_value: Value, @@ -163,7 +95,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor(agent, ExceptionType::EvalError, arguments, new_target, gc) + ErrorConstructor::base_constructor( + agent, + ExceptionType::EvalError, + arguments, + new_target, + gc, + ) + .map(Value::from) } fn range_error_constructor<'gc>( @@ -173,7 +112,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor(agent, ExceptionType::RangeError, arguments, new_target, gc) + ErrorConstructor::base_constructor( + agent, + ExceptionType::RangeError, + arguments, + new_target, + gc, + ) + .map(Value::from) } fn reference_error_constructor<'gc>( @@ -183,13 +129,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor( + ErrorConstructor::base_constructor( agent, ExceptionType::ReferenceError, arguments, new_target, gc, ) + .map(Value::from) } fn syntax_error_constructor<'gc>( @@ -199,7 +146,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor(agent, ExceptionType::SyntaxError, arguments, new_target, gc) + ErrorConstructor::base_constructor( + agent, + ExceptionType::SyntaxError, + arguments, + new_target, + gc, + ) + .map(Value::from) } fn type_error_constructor<'gc>( @@ -209,7 +163,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor(agent, ExceptionType::TypeError, arguments, new_target, gc) + ErrorConstructor::base_constructor( + agent, + ExceptionType::TypeError, + arguments, + new_target, + gc, + ) + .map(Value::from) } fn uri_error_constructor<'gc>( @@ -219,7 +180,14 @@ impl NativeErrorConstructors { new_target: Option, gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Self::constructor(agent, ExceptionType::UriError, arguments, new_target, gc) + ErrorConstructor::base_constructor( + agent, + ExceptionType::UriError, + arguments, + new_target, + gc, + ) + .map(Value::from) } pub(crate) fn create_intrinsic(agent: &mut Agent, realm: Realm<'static>) { diff --git a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs index f0af6f582..9a945c880 100644 --- a/nova_vm/src/ecmascript/types/spec/property_descriptor.rs +++ b/nova_vm/src/ecmascript/types/spec/property_descriptor.rs @@ -136,6 +136,17 @@ impl<'a> PropertyDescriptor<'a> { } } + pub fn non_enumerable_data_descriptor(value: impl Into>) -> Self { + Self { + value: Some(value.into()), + writable: Some(true), + get: None, + set: None, + enumerable: Some(false), + configurable: Some(true), + } + } + pub fn new_prototype_method_descriptor(function: impl Into>) -> Self { Self { value: Some(function.into().unbind().into()), diff --git a/tests/expectations.json b/tests/expectations.json index 81a85c87f..acaec4789 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -6808,18 +6808,10 @@ "language/statements/class/static-init-await-binding-valid.js": "FAIL", "language/statements/class/static-init-super-property.js": "CRASH", "language/statements/class/subclass-builtins/subclass-Promise.js": "FAIL", - "language/statements/class/subclass/builtin-objects/Error/message-property-assignment.js": "FAIL", - "language/statements/class/subclass/builtin-objects/Error/regular-subclassing.js": "FAIL", "language/statements/class/subclass/builtin-objects/Function/instance-length.js": "FAIL", "language/statements/class/subclass/builtin-objects/Function/instance-name.js": "FAIL", "language/statements/class/subclass/builtin-objects/GeneratorFunction/instance-length.js": "FAIL", "language/statements/class/subclass/builtin-objects/GeneratorFunction/instance-name.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/EvalError-message.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/RangeError-message.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/ReferenceError-message.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/SyntaxError-message.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/TypeError-message.js": "FAIL", - "language/statements/class/subclass/builtin-objects/NativeError/URIError-message.js": "FAIL", "language/statements/class/subclass/builtin-objects/Promise/regular-subclassing.js": "FAIL", "language/statements/class/subclass/builtin-objects/Promise/super-must-be-called.js": "FAIL", "language/statements/class/super/in-constructor-superproperty-evaluation.js": "FAIL", diff --git a/tests/metrics.json b/tests/metrics.json index 37bc39f64..f3434b531 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { "crash": 40, - "fail": 7419, - "pass": 39893, + "fail": 7411, + "pass": 39901, "skip": 3326, "timeout": 18, "unresolved": 37