Skip to content

Commit 5586a04

Browse files
authored
fix(builtins/function/mod,function/arguments,function/bound,object/mod): convert panics to EngineError::Panic using js_expect (#5001)
Part of #3241. It changes the following: * `core/engine/src/builtins/function/arguments.rs`: converted 7 panics (7× `js_expect` on `downcast_ref::<MappedArguments>()` exotic method calls); `binding_indices` retains `.expect("binding must exist")` as a documented compiler invariant — binding existence is guaranteed by `bound_names(formals)` * `core/engine/src/builtins/function/mod.rs`: converted 10 panics (2× `downcast_ref::<OrdinaryFunction>()`, 3× `vm.frames.last()` frame existence, 1× `to_integer_or_infinity`, 1× `define_property_or_throw` length, 1× `define_property_or_throw` name, 1× `new.target` object cast, 1× `set_function_name`) to use `js_expect` * `core/engine/src/builtins/function/bound.rs`: converted 2 panics (2× `downcast_ref::<BoundFunction>()` exotic method calls) to use `js_expect` * `core/engine/src/builtins/object/mod.rs`: converted 10 panics (6× `CreateDataPropertyOrThrow` * `from_property_descriptor`, 1× `create_data_property_or_throw` in `group_by`,1× `create_data_property_or_throw` in `get_own_property_descriptors`,1× `toObject` in `to_string`, 1× `ToObject` in `assign`) to use `js_expect`
1 parent 8e6ac8f commit 5586a04

File tree

10 files changed

+73
-65
lines changed

10 files changed

+73
-65
lines changed

core/engine/src/builtins/function/arguments.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{
2-
Context, JsData, JsResult, JsValue,
2+
Context, JsData, JsExpect, JsResult, JsValue,
33
bytecompiler::ToJsString,
44
environments::DeclarativeEnvironment,
55
object::{
@@ -296,7 +296,7 @@ pub(crate) fn arguments_exotic_get_own_property(
296296
if let PropertyKey::Index(index) = key
297297
&& let Some(value) = obj
298298
.downcast_ref::<MappedArguments>()
299-
.expect("arguments exotic method must only be callable from arguments objects")
299+
.js_expect("arguments exotic method must only be callable from arguments objects")?
300300
.get(index.get())
301301
{
302302
// a. Set desc.[[Value]] to Get(map, P).
@@ -331,7 +331,7 @@ pub(crate) fn arguments_exotic_define_own_property(
331331
let mapped = if let &PropertyKey::Index(index) = &key {
332332
// 1. Let map be args.[[ParameterMap]].
333333
obj.downcast_ref::<MappedArguments>()
334-
.expect("arguments exotic method must only be callable from arguments objects")
334+
.js_expect("arguments exotic method must only be callable from arguments objects")?
335335
.get(index.get())
336336
.map(|value| (index, value))
337337
} else {
@@ -376,7 +376,7 @@ pub(crate) fn arguments_exotic_define_own_property(
376376
// 1. Let map be args.[[ParameterMap]].
377377
let mut map = obj
378378
.downcast_mut::<MappedArguments>()
379-
.expect("arguments exotic method must only be callable from arguments objects");
379+
.js_expect("arguments exotic method must only be callable from arguments objects")?;
380380

381381
// a. If IsAccessorDescriptor(Desc) is true, then
382382
if desc.is_accessor_descriptor() {
@@ -425,7 +425,7 @@ pub(crate) fn arguments_exotic_try_get(
425425
if let PropertyKey::Index(index) = key
426426
&& let Some(value) = obj
427427
.downcast_ref::<MappedArguments>()
428-
.expect("arguments exotic method must only be callable from arguments objects")
428+
.js_expect("arguments exotic method must only be callable from arguments objects")?
429429
.get(index.get())
430430
{
431431
// a. Assert: map contains a formal parameter mapping for P.
@@ -455,7 +455,7 @@ pub(crate) fn arguments_exotic_get(
455455
if let PropertyKey::Index(index) = key
456456
&& let Some(value) = obj
457457
.downcast_ref::<MappedArguments>()
458-
.expect("arguments exotic method must only be callable from arguments objects")
458+
.js_expect("arguments exotic method must only be callable from arguments objects")?
459459
.get(index.get())
460460
{
461461
// a. Assert: map contains a formal parameter mapping for P.
@@ -493,7 +493,7 @@ pub(crate) fn arguments_exotic_set(
493493
// a. Let setStatus be Set(map, P, V, false).
494494
// b. Assert: setStatus is true because formal parameters mapped by argument objects are always writable.
495495
obj.downcast_ref::<MappedArguments>()
496-
.expect("arguments exotic method must only be callable from arguments objects")
496+
.js_expect("arguments exotic method must only be callable from arguments objects")?
497497
.set(index.get(), &value);
498498
}
499499

@@ -521,7 +521,7 @@ pub(crate) fn arguments_exotic_delete(
521521
// 4. If result is true and isMapped is true, then
522522
// a. Call map.[[Delete]](P).
523523
obj.downcast_mut::<MappedArguments>()
524-
.expect("arguments exotic method must only be callable from arguments objects")
524+
.js_expect("arguments exotic method must only be callable from arguments objects")?
525525
.delete(index.get());
526526
}
527527

core/engine/src/builtins/function/bound.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use boa_gc::{Finalize, Trace};
22

33
use crate::{
4-
Context, JsObject, JsResult, JsValue,
4+
Context, JsExpect, JsObject, JsResult, JsValue,
55
object::{
66
JsData,
77
internal_methods::{
@@ -108,9 +108,9 @@ fn bound_function_exotic_call(
108108
argument_count: usize,
109109
context: &mut InternalMethodCallContext<'_>,
110110
) -> JsResult<CallValue> {
111-
let bound_function = obj
112-
.downcast_ref::<BoundFunction>()
113-
.expect("bound function exotic method should only be callable from bound function objects");
111+
let bound_function = obj.downcast_ref::<BoundFunction>().js_expect(
112+
"bound function exotic method should only be callable from bound function objects",
113+
)?;
114114

115115
// 1. Let target be F.[[BoundTargetFunction]].
116116
let target = bound_function.target_function();
@@ -155,9 +155,9 @@ fn bound_function_exotic_construct(
155155

156156
debug_assert!(new_target.is_object(), "new.target should be an object");
157157

158-
let bound_function = function_object
159-
.downcast_ref::<BoundFunction>()
160-
.expect("bound function exotic method should only be callable from bound function objects");
158+
let bound_function = function_object.downcast_ref::<BoundFunction>().js_expect(
159+
"bound function exotic method should only be callable from bound function objects",
160+
)?;
161161

162162
// 1. Let target be F.[[BoundTargetFunction]].
163163
let target = bound_function.target_function();

core/engine/src/builtins/function/mod.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
1313
1414
use crate::{
15-
Context, JsArgs, JsResult, JsStr, JsString, JsValue, SpannedSourceText,
15+
Context, JsArgs, JsExpect, JsResult, JsStr, JsString, JsValue, SpannedSourceText,
1616
builtins::{
1717
BuiltInBuilder, BuiltInConstructor, BuiltInObject, IntrinsicObject, OrdinaryObject,
1818
},
@@ -762,7 +762,7 @@ impl BuiltInFunctionObject {
762762
// 1. Let targetLenAsInt be ! ToIntegerOrInfinity(targetLen).
763763
match target_len
764764
.to_integer_or_infinity(context)
765-
.expect("to_integer_or_infinity cannot fail for a number")
765+
.js_expect("to_integer_or_infinity cannot fail for a number")?
766766
{
767767
// i. If targetLen is +∞𝔽, set L to +∞.
768768
IntegerOrInfinity::PositiveInfinity => l = f64::INFINITY.into(),
@@ -789,7 +789,7 @@ impl BuiltInFunctionObject {
789789
.configurable(true),
790790
context,
791791
)
792-
.expect("defining the `length` property for a new object should not fail");
792+
.js_expect("defining the `length` property for a new object should not fail")?;
793793

794794
// 8. Let targetName be ? Get(Target, "name").
795795
let target_name = target.get(js_string!("name"), context)?;
@@ -798,7 +798,7 @@ impl BuiltInFunctionObject {
798798
let target_name = target_name.as_string().unwrap_or_default();
799799

800800
// 10. Perform SetFunctionName(F, targetName, "bound").
801-
set_function_name(&f, &target_name.into(), Some(js_str!("bound")), context);
801+
set_function_name(&f, &target_name.into(), Some(js_str!("bound")), context)?;
802802

803803
// 11. Return F.
804804
Ok(f.into())
@@ -922,7 +922,7 @@ pub(crate) fn set_function_name(
922922
name: &PropertyKey,
923923
prefix: Option<JsStr<'_>>,
924924
context: &mut Context,
925-
) {
925+
) -> JsResult<()> {
926926
// 1. Assert: F is an extensible object that does not have a "name" own property.
927927
// 2. If Type(name) is Symbol, then
928928
let mut name = match name {
@@ -967,7 +967,9 @@ pub(crate) fn set_function_name(
967967
.configurable(true),
968968
context,
969969
)
970-
.expect("defining the `name` property must not fail per the spec");
970+
.js_expect("defining the `name` property must not fail per the spec")?;
971+
972+
Ok(())
971973
}
972974

973975
/// Call this object.
@@ -990,7 +992,7 @@ pub(crate) fn function_call(
990992

991993
let function = function_object
992994
.downcast_ref::<OrdinaryFunction>()
993-
.expect("not a function");
995+
.js_expect("not a function")?;
994996
let realm = function.realm().clone();
995997

996998
if function.code.is_class_constructor() {
@@ -1041,18 +1043,18 @@ pub(crate) fn function_call(
10411043
context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;
10421044
let this: JsValue = context.realm().global_this().clone().into();
10431045
context.vm.stack.set_this(
1044-
context.vm.frames.last().expect("frame must exist"),
1046+
context.vm.frames.last().js_expect("frame must exist")?,
10451047
this.clone(),
10461048
);
10471049
ThisBindingStatus::Initialized(this)
10481050
} else {
10491051
let this: JsValue = this
10501052
.to_object(context)
1051-
.expect("conversion cannot fail")
1053+
.js_expect("conversion cannot fail")?
10521054
.into();
10531055
context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED;
10541056
context.vm.stack.set_this(
1055-
context.vm.frames.last().expect("frame must exist"),
1057+
context.vm.frames.last().js_expect("frame must exist")?,
10561058
this.clone(),
10571059
);
10581060
ThisBindingStatus::Initialized(this)
@@ -1106,7 +1108,7 @@ fn function_construct(
11061108

11071109
let function = this_function_object
11081110
.downcast_ref::<OrdinaryFunction>()
1109-
.expect("not a function");
1111+
.js_expect("not a function")?;
11101112
let realm = function.realm().clone();
11111113

11121114
debug_assert!(
@@ -1198,7 +1200,7 @@ fn function_construct(
11981200
Some(
11991201
new_target
12001202
.as_object()
1201-
.expect("new.target should be an object")
1203+
.js_expect("new.target should be an object")?
12021204
.clone(),
12031205
),
12041206
),
@@ -1208,7 +1210,7 @@ fn function_construct(
12081210

12091211
let context = context.context();
12101212
context.vm.stack.set_this(
1211-
context.vm.frames.last().expect("frame must exist"),
1213+
context.vm.frames.last().js_expect("frame must exist")?,
12121214
this.map(JsValue::new).unwrap_or_default(),
12131215
);
12141216

core/engine/src/builtins/object/mod.rs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use super::{
1919
use crate::builtins::function::arguments::{MappedArguments, UnmappedArguments};
2020
use crate::value::JsVariant;
2121
use crate::{
22-
Context, JsArgs, JsData, JsResult, JsString,
22+
Context, JsArgs, JsData, JsExpect, JsResult, JsString,
2323
builtins::{BuiltInObject, iterable::IteratorHint, map},
2424
context::intrinsics::{Intrinsics, StandardConstructor, StandardConstructors},
2525
error::JsNativeError,
@@ -169,7 +169,7 @@ impl BuiltInConstructor for OrdinaryObject {
169169
.unwrap_or_else(|| context.intrinsics().constructors().object().constructor())
170170
.into()
171171
{
172-
// a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%").
172+
// a. Return ? OrdinaryCreateFromConstructor(NewTarget, "%Object.prototype%").
173173
let prototype =
174174
get_prototype_from_constructor(new_target, StandardConstructors::object, context)?;
175175
let object = JsObject::from_proto_and_data_with_shared_shape(
@@ -186,7 +186,7 @@ impl BuiltInConstructor for OrdinaryObject {
186186
if value.is_null_or_undefined() {
187187
Ok(JsObject::with_object_proto(context.intrinsics()).into())
188188
} else {
189-
// 3. Return ! ToObject(value).
189+
// 3. Return ! ToObject(value).
190190
value.to_object(context).map(JsValue::from)
191191
}
192192
}
@@ -511,7 +511,7 @@ impl OrdinaryObject {
511511
obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
512512

513513
// 4. Return FromPropertyDescriptor(desc).
514-
Ok(Self::from_property_descriptor(desc, context))
514+
Self::from_property_descriptor(desc, context)
515515
}
516516

517517
/// `Object.getOwnPropertyDescriptors( object )`
@@ -547,14 +547,14 @@ impl OrdinaryObject {
547547
obj.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
548548

549549
// b. Let descriptor be FromPropertyDescriptor(desc).
550-
let descriptor = Self::from_property_descriptor(desc, context);
550+
let descriptor = Self::from_property_descriptor(desc, context)?;
551551

552552
// c. If descriptor is not undefined,
553553
// perform ! CreateDataPropertyOrThrow(descriptors, key, descriptor).
554554
if !descriptor.is_undefined() {
555555
descriptors
556556
.create_data_property_or_throw(key, descriptor, context)
557-
.expect("should not fail according to spec");
557+
.js_expect("should not fail according to spec")?;
558558
}
559559
}
560560

@@ -570,10 +570,10 @@ impl OrdinaryObject {
570570
pub(crate) fn from_property_descriptor(
571571
desc: Option<PropertyDescriptor>,
572572
context: &mut Context,
573-
) -> JsValue {
573+
) -> JsResult<JsValue> {
574574
// 1. If Desc is undefined, return undefined.
575575
let Some(desc) = desc else {
576-
return JsValue::undefined();
576+
return Ok(JsValue::undefined());
577577
};
578578

579579
// 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
@@ -584,46 +584,46 @@ impl OrdinaryObject {
584584
if let Some(value) = desc.value() {
585585
// a. Perform ! CreateDataPropertyOrThrow(obj, "value", Desc.[[Value]]).
586586
obj.create_data_property_or_throw(js_string!("value"), value.clone(), context)
587-
.expect("CreateDataPropertyOrThrow cannot fail here");
587+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
588588
}
589589

590590
// 5. If Desc has a [[Writable]] field, then
591591
if let Some(writable) = desc.writable() {
592592
// a. Perform ! CreateDataPropertyOrThrow(obj, "writable", Desc.[[Writable]]).
593593
obj.create_data_property_or_throw(js_string!("writable"), writable, context)
594-
.expect("CreateDataPropertyOrThrow cannot fail here");
594+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
595595
}
596596

597597
// 6. If Desc has a [[Get]] field, then
598598
if let Some(get) = desc.get() {
599599
// a. Perform ! CreateDataPropertyOrThrow(obj, "get", Desc.[[Get]]).
600600
obj.create_data_property_or_throw(js_string!("get"), get.clone(), context)
601-
.expect("CreateDataPropertyOrThrow cannot fail here");
601+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
602602
}
603603

604604
// 7. If Desc has a [[Set]] field, then
605605
if let Some(set) = desc.set() {
606606
// a. Perform ! CreateDataPropertyOrThrow(obj, "set", Desc.[[Set]]).
607607
obj.create_data_property_or_throw(js_string!("set"), set.clone(), context)
608-
.expect("CreateDataPropertyOrThrow cannot fail here");
608+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
609609
}
610610

611611
// 8. If Desc has an [[Enumerable]] field, then
612612
if let Some(enumerable) = desc.enumerable() {
613613
// a. Perform ! CreateDataPropertyOrThrow(obj, "enumerable", Desc.[[Enumerable]]).
614614
obj.create_data_property_or_throw(js_string!("enumerable"), enumerable, context)
615-
.expect("CreateDataPropertyOrThrow cannot fail here");
615+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
616616
}
617617

618618
// 9. If Desc has a [[Configurable]] field, then
619619
if let Some(configurable) = desc.configurable() {
620620
// a. Perform ! CreateDataPropertyOrThrow(obj, "configurable", Desc.[[Configurable]]).
621621
obj.create_data_property_or_throw(js_string!("configurable"), configurable, context)
622-
.expect("CreateDataPropertyOrThrow cannot fail here");
622+
.js_expect("CreateDataPropertyOrThrow cannot fail here")?;
623623
}
624624

625625
// 10. Return obj.
626-
obj.into()
626+
Ok(obj.into())
627627
}
628628

629629
/// Uses the `SameValue` algorithm to check equality of objects
@@ -841,7 +841,9 @@ impl OrdinaryObject {
841841
return Ok(js_string!("[object Null]").into());
842842
}
843843
// 3. Let O be ! ToObject(this value).
844-
let o = this.to_object(context).expect("toObject cannot fail here");
844+
let o = this
845+
.to_object(context)
846+
.js_expect("toObject cannot fail here")?;
845847

846848
// 4. Let isArray be ? IsArray(O).
847849
// 5. If isArray is true, let builtinTag be "Array".
@@ -993,7 +995,7 @@ impl OrdinaryObject {
993995
// 3.a.i. Let from be ! ToObject(nextSource).
994996
let from = source
995997
.to_object(context)
996-
.expect("this ToObject call must not fail");
998+
.js_expect("this ToObject call must not fail")?;
997999
// 3.a.ii. Let keys be ? from.[[OwnPropertyKeys]]().
9981000
let keys =
9991001
from.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
@@ -1446,7 +1448,7 @@ impl OrdinaryObject {
14461448

14471449
// b. Perform ! CreateDataPropertyOrThrow(obj, g.[[Key]], elements).
14481450
obj.create_data_property_or_throw(key, elements, context)
1449-
.expect("cannot fail for a newly created object");
1451+
.js_expect("cannot fail for a newly created object")?;
14501452
}
14511453

14521454
// 4. Return obj.

core/engine/src/builtins/proxy/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ pub(crate) fn proxy_exotic_define_own_property(
612612
};
613613

614614
// 7. Let descObj be FromPropertyDescriptor(Desc).
615-
let desc_obj = OrdinaryObject::from_property_descriptor(Some(desc.clone()), context);
615+
let desc_obj = OrdinaryObject::from_property_descriptor(Some(desc.clone()), context)?;
616616

617617
// 8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).
618618
// 9. If booleanTrapResult is false, return false.

core/engine/src/object/operations.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::internal_methods::InternalMethodPropertyContext;
22
use crate::js_error;
33
use crate::value::JsVariant;
44
use crate::{
5-
Context, JsResult, JsSymbol, JsValue,
5+
Context, JsExpect, JsResult, JsSymbol, JsValue,
66
builtins::{
77
Array, Proxy,
88
function::{BoundFunction, ClassFieldDefinition, OrdinaryFunction, set_function_name},
@@ -1238,11 +1238,11 @@ impl JsObject {
12381238
set_function_name(
12391239
&init_value
12401240
.as_object()
1241-
.expect("init value must be a function object"),
1241+
.js_expect("init value must be a function object")?,
12421242
function_name,
12431243
None,
12441244
context,
1245-
);
1245+
)?;
12461246
}
12471247

12481248
// a. Assert: IsPropertyKey(fieldName) is true.

0 commit comments

Comments
 (0)