Implement `getsuper` and `setsuper`.
This required the reintroduction of dedicated reciever parameters to `Object.get_property_local` and `Object.set_property`, which I had removed from the AVM1 code I copied it from. It turns out being able to change the reciever was actually necessary in order to make super set/get work.
This commit is contained in:
parent
785832b7f3
commit
665d7a4342
|
@ -457,6 +457,8 @@ impl<'gc> Avm2<'gc> {
|
|||
Op::SetProperty { index } => self.op_set_property(context, index),
|
||||
Op::InitProperty { index } => self.op_init_property(context, index),
|
||||
Op::DeleteProperty { index } => self.op_delete_property(context, index),
|
||||
Op::GetSuper { index } => self.op_get_super(context, index),
|
||||
Op::SetSuper { index } => self.op_set_super(context, index),
|
||||
Op::PushScope => self.op_push_scope(context),
|
||||
Op::PushWith => self.op_push_with(context),
|
||||
Op::PopScope => self.op_pop_scope(context),
|
||||
|
@ -637,7 +639,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let base_proto = receiver.get_base_proto(&name);
|
||||
let function = base_proto
|
||||
.unwrap_or(receiver)
|
||||
.get_property_local(&name, self, context)?
|
||||
.get_property_local(receiver, &name, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -661,7 +663,7 @@ impl<'gc> Avm2<'gc> {
|
|||
.resolve_multiname(&multiname)
|
||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||
let function = receiver
|
||||
.get_property(&name?, self, context)?
|
||||
.get_property(receiver, &name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -686,7 +688,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let base_proto = receiver.get_base_proto(&name);
|
||||
let function = base_proto
|
||||
.unwrap_or(receiver)
|
||||
.get_property_local(&name, self, context)?
|
||||
.get_property_local(receiver, &name, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -747,7 +749,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let base_proto = base_proto?;
|
||||
|
||||
let function = base_proto
|
||||
.get_property(&name?, self, context)?
|
||||
.get_property(receiver, &name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -784,7 +786,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let base_proto = base_proto?;
|
||||
|
||||
let function = base_proto
|
||||
.get_property(&name?, self, context)?
|
||||
.get_property(receiver, &name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -818,7 +820,7 @@ impl<'gc> Avm2<'gc> {
|
|||
});
|
||||
|
||||
let value = object
|
||||
.get_property(&name?, self, context)?
|
||||
.get_property(object, &name?, self, context)?
|
||||
.resolve(self, context)?;
|
||||
self.push(value);
|
||||
|
||||
|
@ -835,7 +837,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let object = self.pop().as_object()?;
|
||||
|
||||
if let Some(name) = object.resolve_multiname(&multiname) {
|
||||
object.set_property(&name, value, self, context)
|
||||
object.set_property(object, &name, value, self, context)
|
||||
} else {
|
||||
//TODO: Non-dynamic objects should fail
|
||||
//TODO: This should only work if the public namespace is present
|
||||
|
@ -843,7 +845,7 @@ impl<'gc> Avm2<'gc> {
|
|||
.local_name()
|
||||
.ok_or_else(|| "Cannot set property using any name".into());
|
||||
let name = QName::dynamic_name(local_name?);
|
||||
object.set_property(&name, value, self, context)
|
||||
object.set_property(object, &name, value, self, context)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -886,6 +888,72 @@ impl<'gc> Avm2<'gc> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn op_get_super(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMultiname>,
|
||||
) -> Result<(), Error> {
|
||||
let multiname = self.pool_multiname(index)?;
|
||||
let object = self.pop().as_object()?;
|
||||
let base_proto: Result<Object<'gc>, Error> = self
|
||||
.current_stack_frame()
|
||||
.unwrap()
|
||||
.read()
|
||||
.base_proto()
|
||||
.and_then(|p| p.proto())
|
||||
.ok_or_else(|| "Attempted to get property on non-existent super object".into());
|
||||
let base_proto = base_proto?;
|
||||
|
||||
let name: Result<QName, Error> =
|
||||
base_proto.resolve_multiname(&multiname).ok_or_else(|| {
|
||||
format!(
|
||||
"Could not resolve {:?} as super property",
|
||||
multiname.local_name()
|
||||
)
|
||||
.into()
|
||||
});
|
||||
|
||||
let value = base_proto
|
||||
.get_property(object, &name?, self, context)?
|
||||
.resolve(self, context)?;
|
||||
|
||||
self.push(value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_set_super(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
index: Index<AbcMultiname>,
|
||||
) -> Result<(), Error> {
|
||||
let multiname = self.pool_multiname(index)?;
|
||||
let object = self.pop().as_object()?;
|
||||
let base_proto: Result<Object<'gc>, Error> = self
|
||||
.current_stack_frame()
|
||||
.unwrap()
|
||||
.read()
|
||||
.base_proto()
|
||||
.and_then(|p| p.proto())
|
||||
.ok_or_else(|| "Attempted to get property on non-existent super object".into());
|
||||
let base_proto = base_proto?;
|
||||
|
||||
let name: Result<QName, Error> =
|
||||
base_proto.resolve_multiname(&multiname).ok_or_else(|| {
|
||||
format!(
|
||||
"Could not resolve {:?} as super property",
|
||||
multiname.local_name()
|
||||
)
|
||||
.into()
|
||||
});
|
||||
|
||||
let value = self.pop();
|
||||
|
||||
base_proto.set_property(object, &name?, value, self, context)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn op_push_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||
let object = self.pop().as_object()?;
|
||||
let activation = self.current_stack_frame().unwrap();
|
||||
|
@ -1071,6 +1139,7 @@ impl<'gc> Avm2<'gc> {
|
|||
|
||||
let proto = ctor
|
||||
.get_property(
|
||||
ctor,
|
||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||
self,
|
||||
context,
|
||||
|
@ -1102,11 +1171,12 @@ impl<'gc> Avm2<'gc> {
|
|||
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
||||
});
|
||||
let ctor = source
|
||||
.get_property(&ctor_name?, self, context)?
|
||||
.get_property(source, &ctor_name?, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
let proto = ctor
|
||||
.get_property(
|
||||
ctor,
|
||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||
self,
|
||||
context,
|
||||
|
@ -1145,7 +1215,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let base_proto = base_proto?;
|
||||
|
||||
let function = base_proto
|
||||
.get_property_local(&name, self, context)?
|
||||
.get_property_local(receiver, &name, self, context)?
|
||||
.resolve(self, context)?
|
||||
.as_object()?;
|
||||
|
||||
|
@ -1174,6 +1244,7 @@ impl<'gc> Avm2<'gc> {
|
|||
let name = self.pop();
|
||||
|
||||
object.set_property(
|
||||
object,
|
||||
&QName::new(Namespace::public_namespace(), name.as_string()?),
|
||||
value,
|
||||
self,
|
||||
|
|
|
@ -268,6 +268,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
) -> Result<(Object<'gc>, Object<'gc>), Error> {
|
||||
let super_proto: Result<Object<'gc>, Error> = base_class
|
||||
.get_property(
|
||||
base_class,
|
||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||
avm,
|
||||
context,
|
||||
|
@ -432,6 +433,7 @@ impl<'gc> FunctionObject<'gc> {
|
|||
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||
fn get_property_local(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -439,11 +441,12 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.0
|
||||
.read()
|
||||
.base
|
||||
.get_property_local(name, avm, context, self.into())
|
||||
.get_property_local(reciever, name, avm, context)
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm2<'gc>,
|
||||
|
@ -452,7 +455,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
|||
self.0
|
||||
.write(context.gc_context)
|
||||
.base
|
||||
.set_property(name, value, avm, context, self.into())
|
||||
.set_property(reciever, name, value, avm, context)
|
||||
}
|
||||
|
||||
fn init_property(
|
||||
|
|
|
@ -29,6 +29,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// into account.
|
||||
fn get_property_local(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -37,16 +38,17 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Retrieve a property by it's QName.
|
||||
fn get_property(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if self.has_own_property(name) {
|
||||
return self.get_property_local(name, avm, context);
|
||||
return self.get_property_local(reciever, name, avm, context);
|
||||
}
|
||||
|
||||
if let Some(proto) = self.proto() {
|
||||
return proto.get_property(name, avm, context);
|
||||
return proto.get_property(reciever, name, avm, context);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
|
@ -68,6 +70,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
/// Set a property by it's QName.
|
||||
fn set_property(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm2<'gc>,
|
||||
|
@ -263,8 +266,9 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
|||
&type_entry.abc(),
|
||||
type_entry.instance().super_name.clone(),
|
||||
)?;
|
||||
let reciever: Object<'gc> = (*self).into();
|
||||
let super_class: Result<Object<'gc>, Error> = self
|
||||
.get_property(&super_name, avm, context)?
|
||||
.get_property(reciever, &super_name, avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_object()
|
||||
.map_err(|_e| {
|
||||
|
|
|
@ -128,7 +128,10 @@ impl<'gc> Scope<'gc> {
|
|||
) -> Option<Result<ReturnValue<'gc>, Error>> {
|
||||
if let Some(qname) = self.locals().resolve_multiname(name) {
|
||||
if self.locals().has_property(&qname) {
|
||||
return Some(self.locals().get_property(&qname, avm, context));
|
||||
return Some(
|
||||
self.locals()
|
||||
.get_property(self.values, &qname, avm, context),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -37,17 +37,19 @@ pub struct ScriptObjectData<'gc> {
|
|||
impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||
fn get_property_local(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
self.0
|
||||
.read()
|
||||
.get_property_local(name, avm, context, self.into())
|
||||
.get_property_local(reciever, name, avm, context)
|
||||
}
|
||||
|
||||
fn set_property(
|
||||
self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm2<'gc>,
|
||||
|
@ -55,7 +57,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
|||
) -> Result<(), Error> {
|
||||
self.0
|
||||
.write(context.gc_context)
|
||||
.set_property(name, value, avm, context, self.into())
|
||||
.set_property(reciever, name, value, avm, context)
|
||||
}
|
||||
|
||||
fn init_property(
|
||||
|
@ -213,15 +215,15 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
|
||||
pub fn get_property_local(
|
||||
&self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let prop = self.values.get(name);
|
||||
|
||||
if let Some(prop) = prop {
|
||||
prop.get(avm, context, this)
|
||||
prop.get(avm, context, reciever)
|
||||
} else {
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
@ -229,17 +231,17 @@ impl<'gc> ScriptObjectData<'gc> {
|
|||
|
||||
pub fn set_property(
|
||||
&mut self,
|
||||
reciever: Object<'gc>,
|
||||
name: &QName,
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm2<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
) -> Result<(), Error> {
|
||||
if let Some(prop) = self.values.get_mut(name) {
|
||||
if let Some(slot_id) = prop.slot_id() {
|
||||
self.set_slot(slot_id, value, context.gc_context)?;
|
||||
} else {
|
||||
prop.set(avm, context, this, value)?;
|
||||
prop.set(avm, context, reciever, value)?;
|
||||
}
|
||||
} else {
|
||||
//TODO: Not all classes are dynamic like this
|
||||
|
|
Loading…
Reference in New Issue