Skip to content

Commit

Permalink
Make Error and %NativeError% spec compliant (#1879)
Browse files Browse the repository at this point in the history
This PR makes `Error` and `%NativeError%` spec compliant.

It changes the following:
- Adds cause argument object.
- Makes `message` non-enumerable.
  • Loading branch information
HalidOdat committed Mar 1, 2022
1 parent d7ed2fe commit 3fe7d09
Show file tree
Hide file tree
Showing 8 changed files with 232 additions and 61 deletions.
35 changes: 26 additions & 9 deletions boa_engine/src/builtins/error/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/EvalError

use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
Expand All @@ -22,6 +22,8 @@ use crate::{
};
use boa_profiler::Profiler;

use super::Error;

/// JavaScript `EvalError` impleentation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct EvalError;
Expand Down Expand Up @@ -64,14 +66,29 @@ impl EvalError {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardObjects::eval_error_object,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}
}
49 changes: 42 additions & 7 deletions boa_engine/src/builtins/error/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ pub(crate) use self::reference::ReferenceError;
pub(crate) use self::syntax::SyntaxError;
pub(crate) use self::uri::UriError;

use super::JsArgs;

/// Built-in `Error` object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Error;
Expand Down Expand Up @@ -73,23 +75,56 @@ impl Error {
/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;

/// `Error( message )`
pub(crate) fn install_error_cause(
o: &JsObject,
options: &JsValue,
context: &mut Context,
) -> JsResult<()> {
// 1. If Type(options) is Object and ? HasProperty(options, "cause") is true, then
if let Some(options) = options.as_object() {
if options.has_property("cause", context)? {
// a. Let cause be ? Get(options, "cause").
let cause = options.get("cause", context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
o.create_non_enumerable_data_property_or_throw("cause", cause, context);
}
}

// 2. Return unused.
Ok(())
}

/// `Error( message [ , options ] )`
///
/// Create a new error object.
pub(crate) fn constructor(
new_target: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.

// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Self::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}

/// `Error.prototype.toString()`
Expand Down
35 changes: 26 additions & 9 deletions boa_engine/src/builtins/error/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError

use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
Expand All @@ -20,6 +20,8 @@ use crate::{
};
use boa_profiler::Profiler;

use super::Error;

/// JavaScript `RangeError` implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct RangeError;
Expand Down Expand Up @@ -62,14 +64,29 @@ impl RangeError {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardObjects::range_error_object,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}
}
35 changes: 26 additions & 9 deletions boa_engine/src/builtins/error/reference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError

use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
Expand All @@ -20,6 +20,8 @@ use crate::{
};
use boa_profiler::Profiler;

use super::Error;

#[derive(Debug, Clone, Copy)]
pub(crate) struct ReferenceError;

Expand Down Expand Up @@ -61,14 +63,29 @@ impl ReferenceError {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardObjects::reference_error_object,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}
}
35 changes: 26 additions & 9 deletions boa_engine/src/builtins/error/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError

use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
Expand All @@ -22,6 +22,8 @@ use crate::{
};
use boa_profiler::Profiler;

use super::Error;

/// JavaScript `SyntaxError` impleentation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SyntaxError;
Expand Down Expand Up @@ -64,14 +66,29 @@ impl SyntaxError {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardObjects::syntax_error_object,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}
}
35 changes: 26 additions & 9 deletions boa_engine/src/builtins/error/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError

use crate::{
builtins::BuiltIn,
builtins::{BuiltIn, JsArgs},
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
Expand All @@ -26,6 +26,8 @@ use crate::{
};
use boa_profiler::Profiler;

use super::Error;

/// JavaScript `TypeError` implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct TypeError;
Expand Down Expand Up @@ -68,14 +70,29 @@ impl TypeError {
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
obj.set("message", message.to_string(context)?, false, context)?;
}
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
let prototype = get_prototype_from_constructor(
new_target,
StandardObjects::type_error_object,
context,
)?;
let o = JsObject::from_proto_and_data(prototype, ObjectData::error());

// 3. If message is not undefined, then
let message = args.get_or_undefined(0);
if !message.is_undefined() {
// a. Let msg be ? ToString(message).
let msg = message.to_string(context)?;

// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
o.create_non_enumerable_data_property_or_throw("message", msg, context);
}
Ok(obj.into())

// 4. Perform ? InstallErrorCause(O, options).
Error::install_error_cause(&o, args.get_or_undefined(1), context)?;

// 5. Return O.
Ok(o.into())
}
}

0 comments on commit 3fe7d09

Please sign in to comment.