Skip to content

Commit 66fca44

Browse files
authored
fix(ecmascript): Error constructor message and cause properties (#945)
1 parent fcd215b commit 66fca44

6 files changed

Lines changed: 191 additions & 206 deletions

File tree

nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/aggregate_error_constructors.rs

Lines changed: 28 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,15 @@
55
use crate::{
66
ecmascript::{
77
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin,
8-
BuiltinIntrinsicConstructor, Error, ExceptionType, Function, JsResult, Object,
9-
PropertyDescriptor, PropertyKey, ProtoIntrinsics, Realm, String, Value,
10-
builders::BuiltinFunctionBuilder, create_array_from_scoped_list, define_property_or_throw,
11-
get_iterator, iterator_to_list, ordinary_create_from_constructor, throw_not_callable,
12-
to_string,
8+
BuiltinIntrinsicConstructor, ErrorConstructor, ExceptionType, InternalMethods, JsResult,
9+
Object, PropertyDescriptor, Realm, String, Value, builders::BuiltinFunctionBuilder,
10+
create_array_from_scoped_list, get_iterator, iterator_to_list, throw_not_callable,
11+
unwrap_try,
1312
},
1413
engine::{Bindable, GcScope, Scopable},
15-
heap::{ArenaAccessMut, IntrinsicConstructorIndexes},
14+
heap::IntrinsicConstructorIndexes,
1615
};
1716

18-
use super::error_constructor::get_error_cause;
19-
2017
pub(crate) struct AggregateErrorConstructor;
2118
impl Builtin for AggregateErrorConstructor {
2219
const NAME: String<'static> = BUILTIN_STRING_MEMORY.AggregateError;
@@ -38,45 +35,17 @@ impl AggregateErrorConstructor {
3835
mut gc: GcScope<'gc, '_>,
3936
) -> JsResult<'gc, Value<'gc>> {
4037
let errors = arguments.get(0).scope(agent, gc.nogc());
41-
let message = arguments.get(1).scope(agent, gc.nogc());
42-
let options = arguments.get(2);
43-
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
44-
let new_target = new_target.map_or_else(
45-
|| agent.running_execution_context().function.unwrap(),
46-
|new_target| Function::try_from(new_target).unwrap(),
47-
);
48-
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
49-
let o = ordinary_create_from_constructor(
38+
let message = arguments.get(1).bind(gc.nogc());
39+
let options = arguments.get(2).bind(gc.nogc());
40+
let o = ErrorConstructor::base_constructor(
5041
agent,
51-
new_target.unbind(),
52-
ProtoIntrinsics::AggregateError,
42+
ExceptionType::AggregateError,
43+
ArgumentsList::from_mut_slice(&mut [message.unbind(), options.unbind()]),
44+
new_target,
5345
gc.reborrow(),
5446
)
5547
.unbind()?
56-
.bind(gc.nogc());
57-
let o = Error::try_from(o.unbind()).unwrap();
58-
// 3. If message is not undefined, then
59-
let message = message.get(agent).bind(gc.nogc());
60-
let message = if !message.is_undefined() {
61-
// a. Let msg be ? ToString(message).
62-
Some(
63-
to_string(agent, message.unbind(), gc.reborrow())
64-
.unbind()?
65-
.scope(agent, gc.nogc()),
66-
)
67-
} else {
68-
None
69-
};
70-
// 4. Perform ? InstallErrorCause(O, options).
71-
let cause = get_error_cause(agent, options, gc.reborrow())
72-
.unbind()?
73-
.bind(gc.nogc());
74-
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
75-
let message = message.map(|message| message.get(agent).bind(gc.nogc()));
76-
let heap_data = o.get_mut(agent);
77-
heap_data.kind = ExceptionType::AggregateError;
78-
heap_data.message = message.unbind();
79-
heap_data.cause = cause.unbind();
48+
.scope(agent, gc.nogc());
8049
// 5. Let errorsList be ? IteratorToList(? GetIterator(errors, sync)).
8150
let Some(iterator_record) = get_iterator(agent, errors.get(agent), false, gc.reborrow())
8251
.unbind()?
@@ -85,33 +54,25 @@ impl AggregateErrorConstructor {
8554
else {
8655
return Err(throw_not_callable(agent, gc.into_nogc()));
8756
};
88-
let errors_list = iterator_to_list(agent, iterator_record.unbind(), gc.reborrow())
89-
.unbind()?
90-
.bind(gc.nogc());
57+
let errors_list =
58+
iterator_to_list(agent, iterator_record.unbind(), gc.reborrow()).unbind()?;
59+
let gc = gc.into_nogc();
60+
let o = unsafe { o.take(agent) }.bind(gc);
9161
// 6. Perform ! DefinePropertyOrThrow(O, "errors", PropertyDescriptor {
92-
let property_descriptor = PropertyDescriptor {
93-
// [[Configurable]]: true,
94-
configurable: Some(true),
95-
// [[Enumerable]]: false,
96-
enumerable: Some(false),
97-
// [[Writable]]: true,
98-
writable: Some(true),
99-
// [[Value]]: CreateArrayFromList(errorsList)
100-
value: Some(
101-
create_array_from_scoped_list(agent, errors_list, gc.nogc())
102-
.unbind()
103-
.into(),
104-
),
105-
..Default::default()
106-
};
107-
define_property_or_throw(
62+
// [[Configurable]]: true,
63+
// [[Enumerable]]: false,
64+
// [[Writable]]: true,
65+
// [[Value]]: CreateArrayFromList(errorsList)
66+
let property_descriptor = PropertyDescriptor::non_enumerable_data_descriptor(
67+
create_array_from_scoped_list(agent, errors_list, gc),
68+
);
69+
unwrap_try(o.try_define_own_property(
10870
agent,
109-
o.unbind(),
110-
PropertyKey::from(BUILTIN_STRING_MEMORY.errors),
71+
BUILTIN_STRING_MEMORY.errors.into(),
11172
property_descriptor,
112-
gc.reborrow(),
113-
)
114-
.unbind()?;
73+
None,
74+
gc,
75+
));
11576
// }).
11677
// 7. Return O.
11778
Ok(o.into())

nova_vm/src/ecmascript/builtins/fundamental_objects/error_objects/error_constructor.rs

Lines changed: 103 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ use crate::engine::NoGcScope;
77
use crate::{
88
ecmascript::{
99
Agent, ArgumentsList, BUILTIN_STRING_MEMORY, Behaviour, Builtin,
10-
BuiltinIntrinsicConstructor, Error, ExceptionType, Function, JsResult, Object, PropertyKey,
11-
ProtoIntrinsics, Realm, String, Value, builders::BuiltinFunctionBuilder, get, has_property,
12-
ordinary_create_from_constructor, to_string,
10+
BuiltinIntrinsicConstructor, Error, ErrorHeapData, ExceptionType, Function,
11+
InternalMethods, JsResult, Object, PropertyDescriptor, PropertyKey, ProtoIntrinsics, Realm,
12+
String, Value, builders::BuiltinFunctionBuilder, get, has_property,
13+
ordinary_populate_from_constructor, to_string, unwrap_try,
1314
},
1415
engine::{Bindable, GcScope, Scopable},
15-
heap::{ArenaAccessMut, IntrinsicConstructorIndexes},
16+
heap::{CreateHeapData, IntrinsicConstructorIndexes},
1617
};
1718

1819
pub(crate) struct ErrorConstructor;
@@ -39,74 +40,126 @@ impl Builtin for ErrorIsError {
3940
}
4041

4142
impl ErrorConstructor {
42-
/// ### [20.5.1.1 Error ( message \[ , options \] )](https://tc39.es/ecma262/#sec-error-message)
43-
fn constructor<'gc>(
43+
/// ### [20.5.6.1.1 NativeError ( message \[ , options \] )](https://tc39.es/ecma262/#sec-nativeerror)
44+
pub(crate) fn base_constructor<'gc>(
4445
agent: &mut Agent,
45-
_this_value: Value,
46+
error_kind: ExceptionType,
4647
arguments: ArgumentsList,
4748
new_target: Option<Object>,
4849
mut gc: GcScope<'gc, '_>,
49-
) -> JsResult<'gc, Value<'gc>> {
50-
let message = arguments.get(0).bind(gc.nogc());
51-
let mut options = arguments.get(1).bind(gc.nogc());
52-
let mut new_target = new_target.bind(gc.nogc());
50+
) -> JsResult<'gc, Error<'gc>> {
51+
let nogc = gc.nogc();
52+
let scoped_message = arguments.get(0).scope(agent, nogc);
53+
let options = arguments.get(1).scope(agent, nogc);
54+
let new_target = new_target.bind(nogc);
55+
56+
let intrinsic = match error_kind {
57+
ExceptionType::Error => ProtoIntrinsics::Error,
58+
ExceptionType::AggregateError => ProtoIntrinsics::AggregateError,
59+
ExceptionType::EvalError => ProtoIntrinsics::EvalError,
60+
ExceptionType::RangeError => ProtoIntrinsics::RangeError,
61+
ExceptionType::ReferenceError => ProtoIntrinsics::ReferenceError,
62+
ExceptionType::SyntaxError => ProtoIntrinsics::SyntaxError,
63+
ExceptionType::TypeError => ProtoIntrinsics::TypeError,
64+
ExceptionType::UriError => ProtoIntrinsics::URIError,
65+
};
66+
67+
// 1. If NewTarget is undefined, let newTarget be the active function
68+
// object; else let newTarget be NewTarget.
69+
let new_target = new_target.map_or_else(
70+
|| agent.running_execution_context().function.unwrap(),
71+
|new_target| Function::try_from(new_target).unwrap(),
72+
);
73+
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%NativeError.prototype%", « [[ErrorData]] »).
74+
let o = agent
75+
.heap
76+
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
77+
.create(ErrorHeapData::new(error_kind, None, None))
78+
.bind(gc.nogc());
79+
let o = ordinary_populate_from_constructor(
80+
agent,
81+
o.unbind().into(),
82+
new_target.unbind(),
83+
intrinsic,
84+
gc.reborrow(),
85+
)
86+
.unbind()?
87+
.bind(gc.nogc());
88+
let mut o = Error::try_from(o).unwrap();
89+
90+
// SAFETY: not shared.
91+
let message = unsafe { scoped_message.take(agent) }.bind(gc.nogc());
5392

5493
// 3. If message is not undefined, then
55-
let message = if let Ok(message) = String::try_from(message) {
56-
Some(message.scope(agent, gc.nogc()))
94+
let msg = if let Ok(msg) = String::try_from(message) {
95+
Some(msg)
5796
} else if !message.is_undefined() {
97+
let scoped_o = o.scope(agent, gc.nogc());
5898
// a. Let msg be ? ToString(message).
59-
let scoped_options = options.scope(agent, gc.nogc());
60-
let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc()));
61-
let message = to_string(agent, message.unbind(), gc.reborrow())
99+
let ms = to_string(agent, message.unbind(), gc.reborrow())
62100
.unbind()?
63-
.scope(agent, gc.nogc());
64-
// SAFETY: Never shared.
65-
unsafe {
66-
new_target = scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc());
67-
options = scoped_options.take(agent).bind(gc.nogc());
68-
}
69-
Some(message)
101+
.bind(gc.nogc());
102+
// SAFETY: not shared.
103+
o = unsafe { scoped_o.take(agent) }.bind(gc.nogc());
104+
Some(ms)
70105
} else {
71106
None
72107
};
108+
109+
// 3. If message is not undefined, then
110+
if let Some(msg) = msg {
111+
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
112+
unwrap_try(o.try_define_own_property(
113+
agent,
114+
BUILTIN_STRING_MEMORY.message.into(),
115+
PropertyDescriptor::non_enumerable_data_descriptor(msg),
116+
None,
117+
gc.nogc(),
118+
));
119+
}
120+
121+
// SAFETY: not shared.
122+
let options = unsafe { options.take(agent) }.bind(gc.nogc());
123+
73124
// 4. Perform ? InstallErrorCause(O, options).
74125
let cause = if !options.is_object() {
75126
None
76127
} else {
77-
let scoped_new_target = new_target.map(|n| n.scope(agent, gc.nogc()));
128+
let scoped_o = o.scope(agent, gc.nogc());
78129
let cause = get_error_cause(agent, options.unbind(), gc.reborrow())
79130
.unbind()?
80131
.bind(gc.nogc());
81-
// SAFETY: Never shared.
82-
new_target = unsafe { scoped_new_target.map(|n| n.take(agent)).bind(gc.nogc()) };
83-
cause.map(|c| c.scope(agent, gc.nogc()))
132+
// SAFETY: not shared.
133+
o = unsafe { scoped_o.take(agent) }.bind(gc.nogc());
134+
cause
84135
};
136+
// 1. If options is an Object and ? HasProperty(options, "cause") is
137+
// true, then
138+
if let Some(cause) = cause {
139+
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "cause", cause).
140+
unwrap_try(o.try_define_own_property(
141+
agent,
142+
BUILTIN_STRING_MEMORY.cause.into(),
143+
PropertyDescriptor::non_enumerable_data_descriptor(cause),
144+
None,
145+
gc.nogc(),
146+
));
147+
}
85148

86-
// 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
87-
let new_target = new_target.map_or_else(
88-
|| agent.running_execution_context().function.unwrap(),
89-
|new_target| Function::try_from(new_target).unwrap(),
90-
);
91-
// 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%", « [[ErrorData]] »).
92-
let o = ordinary_create_from_constructor(
93-
agent,
94-
new_target.unbind(),
95-
ProtoIntrinsics::Error,
96-
gc.reborrow(),
97-
)
98-
.unbind()?
99-
.bind(gc.into_nogc());
100-
let o = Error::try_from(o).unwrap();
101-
// b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg).
102-
let message = message.map(|message| message.get(agent));
103-
let cause = cause.map(|c| c.get(agent));
104-
let heap_data = o.get_mut(agent);
105-
heap_data.kind = ExceptionType::Error;
106-
heap_data.message = message;
107-
heap_data.cause = cause;
108149
// 5. Return O.
109-
Ok(o.into())
150+
Ok(o.unbind())
151+
}
152+
153+
/// ### [20.5.1.1 Error ( message \[ , options \] )](https://tc39.es/ecma262/#sec-error-message)
154+
fn constructor<'gc>(
155+
agent: &mut Agent,
156+
_this_value: Value,
157+
arguments: ArgumentsList,
158+
new_target: Option<Object>,
159+
gc: GcScope<'gc, '_>,
160+
) -> JsResult<'gc, Value<'gc>> {
161+
Self::base_constructor(agent, ExceptionType::Error, arguments, new_target, gc)
162+
.map(Value::from)
110163
}
111164

112165
#[cfg(feature = "proposal-is-error")]

0 commit comments

Comments
 (0)