diff --git a/core/engine/src/vm/opcode/await/mod.rs b/core/engine/src/vm/opcode/await/mod.rs index 2dce3496414..69d925281c6 100644 --- a/core/engine/src/vm/opcode/await/mod.rs +++ b/core/engine/src/vm/opcode/await/mod.rs @@ -5,6 +5,7 @@ use crate::{ Promise, async_generator::AsyncGenerator, generator::GeneratorContext, promise::PromiseCapability, }, + error::PanicError, js_string, native_function::NativeFunction, object::FunctionObjectBuilder, @@ -34,9 +35,15 @@ impl Await { value.clone(), context, ) { - Ok(promise) => promise - .downcast::() - .expect("%Promise% constructor must return a `Promise` object"), + Ok(promise) => match promise.downcast::().ok() { + Some(v) => v, + None => { + return context.handle_error( + PanicError::new("%Promise% constructor must return a `Promise` object") + .into(), + ); + } + }, Err(err) => return context.handle_error(err), }; @@ -61,7 +68,7 @@ impl Await { // b. Suspend prevContext. // c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context. // d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it. - let mut r#gen = captures.take().expect("should only run once"); + let mut r#gen = captures.take().js_expect("should only run once")?; // NOTE: We need to get the object before resuming, since it could clear the stack. let async_generator = r#gen.async_generator_object()?; @@ -75,7 +82,7 @@ impl Await { if let Some(async_generator) = async_generator { async_generator .downcast_mut::() - .expect("must be async generator") + .js_expect("must be async generator")? .context = Some(r#gen); } @@ -102,7 +109,7 @@ impl Await { // d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it. // e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context. // f. Return undefined. - let mut r#gen = captures.take().expect("should only run once"); + let mut r#gen = captures.take().js_expect("should only run once")?; // NOTE: We need to get the object before resuming, since it could clear the stack. let async_generator = r#gen.async_generator_object()?; @@ -116,7 +123,7 @@ impl Await { if let Some(async_generator) = async_generator { async_generator .downcast_mut::() - .expect("must be async generator") + .js_expect("must be async generator")? .context = Some(r#gen); } diff --git a/core/engine/src/vm/opcode/environment/mod.rs b/core/engine/src/vm/opcode/environment/mod.rs index 0152358462c..b6a44373288 100644 --- a/core/engine/src/vm/opcode/environment/mod.rs +++ b/core/engine/src/vm/opcode/environment/mod.rs @@ -71,7 +71,7 @@ impl This { .unwrap_or(context.realm().global_this().clone().into()); context.vm.frame_mut().flags |= CallFrameFlags::THIS_VALUE_CACHED; context.vm.stack.set_this( - context.vm.frames.last().expect("frame must exist"), + context.vm.frames.last().js_expect("frame must exist")?, this.clone(), ); context.vm.set_register(dst.into(), this); @@ -141,13 +141,13 @@ impl SuperCall { .environments .get_this_environment(frame.realm.environment()) .as_function() - .expect("super call must be in function environment") + .js_expect("super call must be in function environment")? }; let new_target = this_env .slots() .new_target() - .expect("must have new.target") + .js_expect("must have new.target")? .clone(); context.vm.stack.push(new_target); @@ -179,12 +179,12 @@ impl SuperCallSpread { let arguments_array = context.vm.stack.pop(); let arguments_array_object = arguments_array .as_object() - .expect("arguments array in call spread function must be an object"); + .js_expect("arguments array in call spread function must be an object")?; let arguments = arguments_array_object .borrow() .properties() .to_dense_indexed_properties() - .expect("arguments array in call spread function must be dense"); + .js_expect("arguments array in call spread function must be dense")?; let super_constructor = context.vm.stack.pop(); @@ -207,13 +207,13 @@ impl SuperCallSpread { .environments .get_this_environment(frame.realm.environment()) .as_function() - .expect("super call must be in function environment") + .js_expect("super call must be in function environment")? }; let new_target = this_env .slots() .new_target() - .expect("must have new.target") + .js_expect("must have new.target")? .clone(); context.vm.stack.push(new_target); @@ -247,18 +247,18 @@ impl SuperCallDerived { .environments .get_this_environment(frame.realm.environment()) .as_function() - .expect("super call must be in function environment") + .js_expect("super call must be in function environment")? }; let new_target = this_env .slots() .new_target() - .expect("must have new target") + .js_expect("must have new target")? .clone(); let active_function = this_env.slots().function_object().clone(); let super_constructor = active_function .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context)) - .expect("function object must have prototype") - .expect("function object must have prototype"); + .js_expect("function object must have prototype")? + .js_expect("function object must have prototype")?; if !super_constructor.is_constructor() { return Err(JsNativeError::typ() @@ -304,7 +304,7 @@ impl BindThisValue { .vm .get_register(value.into()) .as_object() - .expect("construct result should be an object") + .js_expect("construct result should be an object")? .clone(); // 7. Let thisER be GetThisEnvironment(). @@ -314,7 +314,7 @@ impl BindThisValue { .environments .get_this_environment(frame.realm.environment()) .as_function() - .expect("super call must be in function environment") + .js_expect("super call must be in function environment")? }; // 8. Perform ? thisER.BindThisValue(result). diff --git a/core/engine/src/vm/opcode/generator/mod.rs b/core/engine/src/vm/opcode/generator/mod.rs index 78b1b743736..70087b7b9c0 100644 --- a/core/engine/src/vm/opcode/generator/mod.rs +++ b/core/engine/src/vm/opcode/generator/mod.rs @@ -1,11 +1,12 @@ pub(crate) mod yield_stm; use crate::{ - Context, JsObject, JsResult, + Context, JsExpect, JsObject, JsResult, builtins::{ async_generator::{AsyncGenerator as NativeAsyncGenerator, AsyncGeneratorState}, generator::{Generator as NativeGenerator, GeneratorContext, GeneratorState}, }, + error::PanicError, object::PROTOTYPE, vm::{CompletionRecord, opcode::Operation}, }; @@ -23,14 +24,16 @@ pub(crate) struct Generator; impl Generator { #[inline(always)] pub(super) fn operation((): (), context: &mut Context) -> ControlFlow { - let active_function = context.vm.stack.get_function(context.vm.frame()); - let this_function_object = - active_function.expect("active function should be set to the generator"); + let Some(this_function_object) = context.vm.stack.get_function(context.vm.frame()) else { + return context.handle_error( + PanicError::new("active function should be set to the generator").into(), + ); + }; let proto = this_function_object .get(PROTOTYPE, context) - .expect("generator must have a prototype property") - .as_object() + .ok() + .and_then(|v| v.as_object()) .unwrap_or_else(|| context.intrinsics().objects().generator()); let generator = JsObject::from_proto_and_data_with_shared_shape( @@ -67,14 +70,16 @@ pub(crate) struct AsyncGenerator; impl AsyncGenerator { #[inline(always)] pub(super) fn operation((): (), context: &mut Context) -> ControlFlow { - let active_function = context.vm.stack.get_function(context.vm.frame()); - let this_function_object = - active_function.expect("active function should be set to the generator"); + let Some(this_function_object) = context.vm.stack.get_function(context.vm.frame()) else { + return context.handle_error( + PanicError::new("active function should be set to the generator").into(), + ); + }; let proto = this_function_object .get(PROTOTYPE, context) - .expect("generator must have a prototype property") - .as_object() + .ok() + .and_then(|v| v.as_object()) .unwrap_or_else(|| context.intrinsics().objects().async_generator()); let generator = JsObject::from_proto_and_data_with_shared_shape( @@ -115,9 +120,10 @@ impl AsyncGeneratorClose { let generator = context .vm .async_generator_object() - .expect("There should be a object") + .js_expect("There should be a object")? .downcast::() - .expect("must be async generator"); + .ok() + .js_expect("must be async generator")?; let mut r#gen = generator.borrow_mut(); diff --git a/core/engine/src/vm/opcode/generator/yield_stm.rs b/core/engine/src/vm/opcode/generator/yield_stm.rs index 8e91aa3fbdf..bcf281412e7 100644 --- a/core/engine/src/vm/opcode/generator/yield_stm.rs +++ b/core/engine/src/vm/opcode/generator/yield_stm.rs @@ -3,6 +3,7 @@ use std::ops::ControlFlow; use crate::{ Context, JsValue, builtins::async_generator::{AsyncGenerator, AsyncGeneratorState}, + error::PanicError, vm::{ CompletionRecord, GeneratorResumeKind, opcode::{Operation, RegisterOperand}, @@ -54,13 +55,17 @@ impl AsyncGeneratorYield { // 2. Assert: genContext is the execution context of a generator. // 3. Let generator be the value of the Generator component of genContext. // 4. Assert: GetGeneratorKind() is async. - let async_generator_object = context - .vm - .async_generator_object() - .expect("`AsyncGeneratorYield` must only be called inside async generators"); - let async_generator_object = async_generator_object - .downcast::() - .expect("must be async generator object"); + let Some(async_generator_object) = context.vm.async_generator_object() else { + return context.handle_error( + PanicError::new( + "`AsyncGeneratorYield` must only be called inside async generators", + ) + .into(), + ); + }; + let Ok(async_generator_object) = async_generator_object.downcast::() else { + return context.handle_error(PanicError::new("must be async generator object").into()); + }; // 5. Let completion be NormalCompletion(value). let value = context.vm.get_register(value.into()); diff --git a/core/engine/src/vm/opcode/new/mod.rs b/core/engine/src/vm/opcode/new/mod.rs index a09237a4555..d54c7e2bdeb 100644 --- a/core/engine/src/vm/opcode/new/mod.rs +++ b/core/engine/src/vm/opcode/new/mod.rs @@ -1,5 +1,5 @@ use super::IndexOperand; -use crate::{Context, JsResult, error::JsNativeError, vm::opcode::Operation}; +use crate::{Context, JsExpect, JsResult, error::JsNativeError, vm::opcode::Operation}; /// `New` implements the Opcode Operation for `Opcode::New` /// @@ -48,12 +48,12 @@ impl NewSpread { let arguments_array = context.vm.stack.pop(); let arguments_array_object = arguments_array .as_object() - .expect("arguments array in call spread function must be an object"); + .js_expect("arguments array in call spread function must be an object")?; let arguments = arguments_array_object .borrow() .properties() .to_dense_indexed_properties() - .expect("arguments array in call spread function must be dense"); + .js_expect("arguments array in call spread function must be dense")?; let func = context.vm.stack.pop(); diff --git a/core/engine/src/vm/opcode/push/array.rs b/core/engine/src/vm/opcode/push/array.rs index 6c0df378d36..9f3a209d7c5 100644 --- a/core/engine/src/vm/opcode/push/array.rs +++ b/core/engine/src/vm/opcode/push/array.rs @@ -1,5 +1,5 @@ use crate::{ - Context, JsResult, JsValue, + Context, JsExpect, JsResult, JsValue, builtins::Array, string::StaticJsStrings, vm::opcode::{Operation, RegisterOperand}, @@ -42,13 +42,13 @@ impl PushValueToArray { pub(crate) fn operation( (value, array): (RegisterOperand, RegisterOperand), context: &mut Context, - ) { + ) -> JsResult<()> { let value = context.vm.get_register(value.into()).clone(); let o = context .vm .get_register(array.into()) .as_object() - .expect("should be an object"); + .js_expect("should be an object")?; // Fast path: push directly to dense indexed storage. { @@ -58,16 +58,17 @@ impl PushValueToArray { && o_mut.properties_mut().indexed_properties.push_dense(&value) { o_mut.properties_mut().storage[0] = JsValue::new(len + 1); - return; + return Ok(()); } } // Slow path: fall through to the generic property machinery. let len = o .length_of_array_like(context) - .expect("should have 'length' property"); + .js_expect("should have 'length' property")?; o.create_data_property_or_throw(len, value, context) - .expect("should be able to create new data property"); + .js_expect("should be able to create new data property")?; + Ok(()) } } @@ -88,10 +89,10 @@ impl PushElisionToArray { #[inline(always)] pub(crate) fn operation(array: RegisterOperand, context: &mut Context) -> JsResult<()> { let array = context.vm.get_register(array.into()).clone(); - let o = array.as_object().expect("should always be an object"); + let o = array.as_object().js_expect("should always be an object")?; let len = o .length_of_array_like(context) - .expect("arrays should always have a 'length' property"); + .js_expect("arrays should always have a 'length' property")?; o.set(StaticJsStrings::LENGTH, len + 1, true, context)?; o.borrow_mut() .properties_mut() @@ -123,7 +124,7 @@ impl PushIteratorToArray { .frame_mut() .iterators .pop() - .expect("iterator stack should have at least an iterator"); + .js_expect("iterator stack should have at least an iterator")?; while let Some(next) = iterator.step_value(context)? { Array::push(&array, &[next], context)?; } diff --git a/core/engine/src/vm/opcode/push/class/field.rs b/core/engine/src/vm/opcode/push/class/field.rs index 869cf090d37..f28089ff8c6 100644 --- a/core/engine/src/vm/opcode/push/class/field.rs +++ b/core/engine/src/vm/opcode/push/class/field.rs @@ -1,5 +1,5 @@ use crate::{ - Context, JsResult, + Context, JsExpect, JsResult, builtins::function::OrdinaryFunction, object::JsFunction, vm::opcode::{IndexOperand, Operation, RegisterOperand}, @@ -31,17 +31,19 @@ impl PushClassField { let name = name.to_property_key(context)?; let function = function .as_object() - .expect("field value must be function object"); - let class = class.as_object().expect("class must be function object"); + .js_expect("field value must be function object")?; + let class = class + .as_object() + .js_expect("class must be function object")?; function .downcast_mut::() - .expect("field value must be function object") + .js_expect("field value must be function object")? .set_home_object(class.clone()); class .downcast_mut::() - .expect("class must be function object") + .js_expect("class must be function object")? .push_field( name.clone(), JsFunction::from_object_unchecked(function.clone()), @@ -73,7 +75,7 @@ impl PushClassFieldPrivate { pub(crate) fn operation( (class, function, index): (RegisterOperand, RegisterOperand, IndexOperand), context: &mut Context, - ) { + ) -> JsResult<()> { let class = context.vm.get_register(class.into()); let function = context.vm.get_register(function.into()); let name = context @@ -84,21 +86,24 @@ impl PushClassFieldPrivate { let function = function .as_object() - .expect("field value must be function object"); - let class = class.as_object().expect("class must be function object"); + .js_expect("field value must be function object")?; + let class = class + .as_object() + .js_expect("class must be function object")?; function .downcast_mut::() - .expect("field value must be function object") + .js_expect("field value must be function object")? .set_home_object(class.clone()); class .downcast_mut::() - .expect("class must be function object") + .js_expect("class must be function object")? .push_field_private( class.private_name(name), JsFunction::from_object_unchecked(function.clone()), ); + Ok(()) } } diff --git a/core/engine/src/vm/opcode/push/class/mod.rs b/core/engine/src/vm/opcode/push/class/mod.rs index 9336fccc579..7b29a690305 100644 --- a/core/engine/src/vm/opcode/push/class/mod.rs +++ b/core/engine/src/vm/opcode/push/class/mod.rs @@ -1,5 +1,5 @@ use crate::{ - Context, JsResult, JsValue, + Context, JsExpect, JsResult, JsValue, error::JsNativeError, object::PROTOTYPE, vm::opcode::{Operation, RegisterOperand}, @@ -62,7 +62,7 @@ impl StoreClassPrototype { .into()); }; - let class_object = class.as_object().expect("class must be object"); + let class_object = class.as_object().js_expect("class must be object")?; if let Some(constructor_parent) = constructor_parent { class_object.set_prototype(Some(constructor_parent)); diff --git a/core/engine/src/vm/opcode/push/class/private.rs b/core/engine/src/vm/opcode/push/class/private.rs index 28212d06edf..8054effa4c3 100644 --- a/core/engine/src/vm/opcode/push/class/private.rs +++ b/core/engine/src/vm/opcode/push/class/private.rs @@ -1,5 +1,5 @@ use crate::{ - Context, + Context, JsExpect, JsResult, builtins::function::OrdinaryFunction, js_str, js_string, object::{PrivateElement, internal_methods::InternalMethodPropertyContext}, @@ -24,7 +24,7 @@ impl PushClassPrivateMethod { IndexOperand, ), context: &mut Context, - ) { + ) -> JsResult<()> { let object = context.vm.get_register(object.into()).clone(); let prototype = context.vm.get_register(prototype.into()).clone(); let value = context.vm.get_register(value.into()).clone(); @@ -34,11 +34,13 @@ impl PushClassPrivateMethod { .code_block() .constant_string(index.into()); - let value = value.as_callable().expect("method must be callable"); + let value = value.as_callable().js_expect("method must be callable")?; let prototype = prototype .as_object() - .expect("class_prototype must be function object"); - let object = object.as_object().expect("class must be function object"); + .js_expect("class_prototype must be function object")?; + let object = object + .as_object() + .js_expect("class must be function object")?; let name_string = js_string!(js_str!("#"), &name); let desc = PropertyDescriptor::builder() @@ -53,19 +55,20 @@ impl PushClassPrivateMethod { desc, &mut InternalMethodPropertyContext::new(context), ) - .expect("failed to set name property on private method"); + .js_expect("failed to set name property on private method")?; value .downcast_mut::() - .expect("method must be function object") + .js_expect("method must be function object")? .set_home_object(prototype.clone()); object .downcast_mut::() - .expect("class must be function object") + .js_expect("class must be function object")? .push_private_method( object.private_name(name), PrivateElement::Method(value.clone()), ); + Ok(()) } } @@ -87,7 +90,7 @@ impl PushClassPrivateGetter { pub(crate) fn operation( (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand), context: &mut Context, - ) { + ) -> JsResult<()> { let object = context.vm.get_register(object.into()); let value = context.vm.get_register(value.into()); let name = context @@ -96,12 +99,14 @@ impl PushClassPrivateGetter { .code_block() .constant_string(index.into()); - let value = value.as_callable().expect("getter must be callable"); - let object = object.as_object().expect("class must be function object"); + let value = value.as_callable().js_expect("getter must be callable")?; + let object = object + .as_object() + .js_expect("class must be function object")?; object .downcast_mut::() - .expect("class must be function object") + .js_expect("class must be function object")? .push_private_method( object.private_name(name), PrivateElement::Accessor { @@ -109,6 +114,7 @@ impl PushClassPrivateGetter { setter: None, }, ); + Ok(()) } } @@ -130,7 +136,7 @@ impl PushClassPrivateSetter { pub(crate) fn operation( (object, value, index): (RegisterOperand, RegisterOperand, IndexOperand), context: &mut Context, - ) { + ) -> JsResult<()> { let object = context.vm.get_register(object.into()); let value = context.vm.get_register(value.into()); let name = context @@ -139,12 +145,14 @@ impl PushClassPrivateSetter { .code_block() .constant_string(index.into()); - let value = value.as_callable().expect("getter must be callable"); - let object = object.as_object().expect("class must be function object"); + let value = value.as_callable().js_expect("getter must be callable")?; + let object = object + .as_object() + .js_expect("class must be function object")?; object .downcast_mut::() - .expect("class must be function object") + .js_expect("class must be function object")? .push_private_method( object.private_name(name), PrivateElement::Accessor { @@ -152,6 +160,7 @@ impl PushClassPrivateSetter { setter: Some(value.clone()), }, ); + Ok(()) } } diff --git a/core/engine/src/vm/opcode/push/environment.rs b/core/engine/src/vm/opcode/push/environment.rs index f18593ab029..debccae3991 100644 --- a/core/engine/src/vm/opcode/push/environment.rs +++ b/core/engine/src/vm/opcode/push/environment.rs @@ -1,5 +1,5 @@ use crate::{ - Context, JsResult, + Context, JsExpect, JsResult, builtins::function::OrdinaryFunction, environments::PrivateEnvironment, vm::opcode::{IndexOperand, Operation, RegisterOperand}, @@ -67,9 +67,9 @@ impl PushPrivateEnvironment { pub(crate) fn operation( (class, name_indices): (RegisterOperand, ThinVec), context: &mut Context, - ) { + ) -> JsResult<()> { let class = context.vm.get_register(class.into()); - let class = class.as_object().expect("should be a object"); + let class = class.as_object().js_expect("should be a object")?; let mut names = Vec::with_capacity(name_indices.len()); for index in name_indices { let name = context @@ -85,13 +85,14 @@ impl PushPrivateEnvironment { class .downcast_mut::() - .expect("class object must be function") + .js_expect("class object must be function")? .push_private_environment(environment.clone()); context .vm .frame_mut() .environments .push_private(environment); + Ok(()) } } diff --git a/core/engine/src/vm/opcode/templates/mod.rs b/core/engine/src/vm/opcode/templates/mod.rs index 6cdcb99e3f4..f01c2a1d99e 100644 --- a/core/engine/src/vm/opcode/templates/mod.rs +++ b/core/engine/src/vm/opcode/templates/mod.rs @@ -1,6 +1,6 @@ use super::RegisterOperand; use crate::{ - Context, + Context, JsExpect, JsResult, builtins::array::Array, js_string, object::IntegrityLevel, @@ -47,12 +47,12 @@ impl TemplateCreate { pub(super) fn operation( (site, dst, values): (u64, RegisterOperand, ThinVec), context: &mut Context, - ) { + ) -> JsResult<()> { let count = values.len() / 2; let template = - Array::array_create(count as u64, None, context).expect("cannot fail per spec"); + Array::array_create(count as u64, None, context).js_expect("cannot fail per spec")?; let raw_obj = - Array::array_create(count as u64, None, context).expect("cannot fail per spec"); + Array::array_create(count as u64, None, context).js_expect("cannot fail per spec")?; let mut index = 0; let mut cooked = true; @@ -69,7 +69,7 @@ impl TemplateCreate { .configurable(false), context, ) - .expect("should not fail on new array"); + .js_expect("should not fail on new array")?; } else { let raw_value = context.vm.get_register(value as usize); raw_obj @@ -82,7 +82,7 @@ impl TemplateCreate { .configurable(false), context, ) - .expect("should not fail on new array"); + .js_expect("should not fail on new array")?; index += 1; } @@ -91,7 +91,7 @@ impl TemplateCreate { raw_obj .set_integrity_level(IntegrityLevel::Frozen, context) - .expect("should never fail per spec"); + .js_expect("should never fail per spec")?; template .define_property_or_throw( js_string!("raw"), @@ -102,14 +102,15 @@ impl TemplateCreate { .configurable(false), context, ) - .expect("should never fail per spec"); + .js_expect("should never fail per spec")?; template .set_integrity_level(IntegrityLevel::Frozen, context) - .expect("should never fail per spec"); + .js_expect("should never fail per spec")?; context.realm().push_template(site, template.clone()); context.vm.set_register(dst.into(), template.into()); + Ok(()) } }