avm2: Alter basic object operations to use more descriptive error messages.

This commit is contained in:
David Wendt 2022-02-11 17:53:14 -05:00 committed by Mike Welsh
parent 4185acc5a8
commit 0f2b77c138
2 changed files with 55 additions and 23 deletions

View File

@ -274,7 +274,8 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
let result = self let result = self
.base() .base()
.get_property_local(multiname, activation)? .get_property_local(multiname, activation)?
.coerce_to_object(activation)?; .as_callable(activation, Some(multiname), Some(self.into()))?;
result.call(Some(self.into()), arguments, activation) result.call(Some(self.into()), arguments, activation)
} }
@ -292,10 +293,12 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) { match self.vtable().and_then(|vtable| vtable.get_trait(multiname)) {
Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => { Some(Property::Slot { slot_id }) | Some(Property::ConstSlot { slot_id }) => {
let obj = self let obj = self.base().get_slot(slot_id)?.as_callable(
.base() activation,
.get_slot(slot_id)? Some(multiname),
.coerce_to_object(activation)?; Some(self.into()),
)?;
obj.call(Some(self.into()), arguments, activation) obj.call(Some(self.into()), arguments, activation)
} }
Some(Property::Method { disp_id }) => { Some(Property::Method { disp_id }) => {
@ -328,9 +331,12 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
} }
} }
Some(Property::Virtual { get: Some(get), .. }) => { Some(Property::Virtual { get: Some(get), .. }) => {
let obj = self let obj = self.call_method(get, &[], activation)?.as_callable(
.call_method(get, &[], activation)? activation,
.coerce_to_object(activation)?; Some(multiname),
Some(self.into()),
)?;
obj.call(Some(self.into()), arguments, activation) obj.call(Some(self.into()), arguments, activation)
} }
Some(Property::Virtual { get: None, .. }) => { Some(Property::Virtual { get: None, .. }) => {
@ -642,9 +648,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
args: &[Value<'gc>], args: &[Value<'gc>],
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Object<'gc>, Error> { ) -> Result<Object<'gc>, Error> {
let ctor = self let ctor = self.get_property(multiname, activation)?.as_callable(
.get_property(multiname, activation)? activation,
.coerce_to_object(activation)?; Some(multiname),
Some(self.into()),
)?;
ctor.construct(activation, args) ctor.construct(activation, args)
} }
@ -739,9 +747,13 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let type_proto = class let type_proto = class
.get_property(&QName::dynamic_name("prototype").into(), activation)? .get_property(&QName::dynamic_name("prototype").into(), activation)?
.coerce_to_object(activation)?; .as_object();
if let Some(type_proto) = type_proto {
self.has_prototype_in_chain(type_proto) self.has_prototype_in_chain(type_proto)
} else {
Ok(false)
}
} }
/// Determine if this object has a given prototype in its prototype chain. /// Determine if this object has a given prototype in its prototype chain.

View File

@ -138,13 +138,21 @@ impl<'gc> ScriptObjectData<'gc> {
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<Value<'gc>, Error> { ) -> Result<Value<'gc>, Error> {
if !multiname.contains_public_namespace() { if !multiname.contains_public_namespace() {
return Err( return Err(format!(
format!("Non-public property `{:?}` not found on Object", multiname).into(), "Non-public property {} not found on Object",
); multiname.to_qualified_name(activation.context.gc_context)
)
.into());
} }
let local_name = match multiname.local_name() { let local_name = match multiname.local_name() {
None => return Err("Unnamed property not found on Object".into()), None => {
return Err(format!(
"Unnamed property {} not found on Object",
multiname.to_qualified_name(activation.context.gc_context)
)
.into())
}
Some(name) => name, Some(name) => name,
}; };
@ -174,24 +182,36 @@ impl<'gc> ScriptObjectData<'gc> {
&mut self, &mut self,
multiname: &Multiname<'gc>, multiname: &Multiname<'gc>,
value: Value<'gc>, value: Value<'gc>,
_activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
) -> Result<(), Error> { ) -> Result<(), Error> {
if self if self
.instance_of() .instance_of()
.map(|cls| cls.inner_class_definition().read().is_sealed()) .map(|cls| cls.inner_class_definition().read().is_sealed())
.unwrap_or(false) .unwrap_or(false)
{ {
return Err( return Err(format!(
format!("Cannot set undefined property {:?}", multiname.local_name()).into(), "Cannot set undefined property {}",
); multiname.to_qualified_name(activation.context.gc_context)
)
.into());
} }
if !multiname.contains_public_namespace() { if !multiname.contains_public_namespace() {
return Err("Non-public property not found on Object".into()); return Err(format!(
"Non-public property {} not found on Object",
multiname.to_qualified_name(activation.context.gc_context)
)
.into());
} }
let local_name = match multiname.local_name() { let local_name = match multiname.local_name() {
None => return Err("Unnamed property not found on Object".into()), None => {
return Err(format!(
"Unnamed property {} not found on Object",
multiname.to_qualified_name(activation.context.gc_context)
)
.into())
}
Some(name) => name, Some(name) => name,
}; };