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::SetProperty { index } => self.op_set_property(context, index),
|
||||||
Op::InitProperty { index } => self.op_init_property(context, index),
|
Op::InitProperty { index } => self.op_init_property(context, index),
|
||||||
Op::DeleteProperty { index } => self.op_delete_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::PushScope => self.op_push_scope(context),
|
||||||
Op::PushWith => self.op_push_with(context),
|
Op::PushWith => self.op_push_with(context),
|
||||||
Op::PopScope => self.op_pop_scope(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 base_proto = receiver.get_base_proto(&name);
|
||||||
let function = base_proto
|
let function = base_proto
|
||||||
.unwrap_or(receiver)
|
.unwrap_or(receiver)
|
||||||
.get_property_local(&name, self, context)?
|
.get_property_local(receiver, &name, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -661,7 +663,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
.resolve_multiname(&multiname)
|
.resolve_multiname(&multiname)
|
||||||
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
.ok_or_else(|| format!("Could not find method {:?}", multiname.local_name()).into());
|
||||||
let function = receiver
|
let function = receiver
|
||||||
.get_property(&name?, self, context)?
|
.get_property(receiver, &name?, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -686,7 +688,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let base_proto = receiver.get_base_proto(&name);
|
let base_proto = receiver.get_base_proto(&name);
|
||||||
let function = base_proto
|
let function = base_proto
|
||||||
.unwrap_or(receiver)
|
.unwrap_or(receiver)
|
||||||
.get_property_local(&name, self, context)?
|
.get_property_local(receiver, &name, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -747,7 +749,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let base_proto = base_proto?;
|
let base_proto = base_proto?;
|
||||||
|
|
||||||
let function = base_proto
|
let function = base_proto
|
||||||
.get_property(&name?, self, context)?
|
.get_property(receiver, &name?, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -784,7 +786,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let base_proto = base_proto?;
|
let base_proto = base_proto?;
|
||||||
|
|
||||||
let function = base_proto
|
let function = base_proto
|
||||||
.get_property(&name?, self, context)?
|
.get_property(receiver, &name?, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -818,7 +820,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let value = object
|
let value = object
|
||||||
.get_property(&name?, self, context)?
|
.get_property(object, &name?, self, context)?
|
||||||
.resolve(self, context)?;
|
.resolve(self, context)?;
|
||||||
self.push(value);
|
self.push(value);
|
||||||
|
|
||||||
|
@ -835,7 +837,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let object = self.pop().as_object()?;
|
let object = self.pop().as_object()?;
|
||||||
|
|
||||||
if let Some(name) = object.resolve_multiname(&multiname) {
|
if let Some(name) = object.resolve_multiname(&multiname) {
|
||||||
object.set_property(&name, value, self, context)
|
object.set_property(object, &name, value, self, context)
|
||||||
} else {
|
} else {
|
||||||
//TODO: Non-dynamic objects should fail
|
//TODO: Non-dynamic objects should fail
|
||||||
//TODO: This should only work if the public namespace is present
|
//TODO: This should only work if the public namespace is present
|
||||||
|
@ -843,7 +845,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
.local_name()
|
.local_name()
|
||||||
.ok_or_else(|| "Cannot set property using any name".into());
|
.ok_or_else(|| "Cannot set property using any name".into());
|
||||||
let name = QName::dynamic_name(local_name?);
|
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(())
|
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> {
|
fn op_push_scope(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<(), Error> {
|
||||||
let object = self.pop().as_object()?;
|
let object = self.pop().as_object()?;
|
||||||
let activation = self.current_stack_frame().unwrap();
|
let activation = self.current_stack_frame().unwrap();
|
||||||
|
@ -1071,6 +1139,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
|
|
||||||
let proto = ctor
|
let proto = ctor
|
||||||
.get_property(
|
.get_property(
|
||||||
|
ctor,
|
||||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||||
self,
|
self,
|
||||||
context,
|
context,
|
||||||
|
@ -1102,11 +1171,12 @@ impl<'gc> Avm2<'gc> {
|
||||||
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
format!("Could not resolve property {:?}", multiname.local_name()).into()
|
||||||
});
|
});
|
||||||
let ctor = source
|
let ctor = source
|
||||||
.get_property(&ctor_name?, self, context)?
|
.get_property(source, &ctor_name?, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
let proto = ctor
|
let proto = ctor
|
||||||
.get_property(
|
.get_property(
|
||||||
|
ctor,
|
||||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||||
self,
|
self,
|
||||||
context,
|
context,
|
||||||
|
@ -1145,7 +1215,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let base_proto = base_proto?;
|
let base_proto = base_proto?;
|
||||||
|
|
||||||
let function = base_proto
|
let function = base_proto
|
||||||
.get_property_local(&name, self, context)?
|
.get_property_local(receiver, &name, self, context)?
|
||||||
.resolve(self, context)?
|
.resolve(self, context)?
|
||||||
.as_object()?;
|
.as_object()?;
|
||||||
|
|
||||||
|
@ -1174,6 +1244,7 @@ impl<'gc> Avm2<'gc> {
|
||||||
let name = self.pop();
|
let name = self.pop();
|
||||||
|
|
||||||
object.set_property(
|
object.set_property(
|
||||||
|
object,
|
||||||
&QName::new(Namespace::public_namespace(), name.as_string()?),
|
&QName::new(Namespace::public_namespace(), name.as_string()?),
|
||||||
value,
|
value,
|
||||||
self,
|
self,
|
||||||
|
|
|
@ -268,6 +268,7 @@ impl<'gc> FunctionObject<'gc> {
|
||||||
) -> Result<(Object<'gc>, Object<'gc>), Error> {
|
) -> Result<(Object<'gc>, Object<'gc>), Error> {
|
||||||
let super_proto: Result<Object<'gc>, Error> = base_class
|
let super_proto: Result<Object<'gc>, Error> = base_class
|
||||||
.get_property(
|
.get_property(
|
||||||
|
base_class,
|
||||||
&QName::new(Namespace::public_namespace(), "prototype"),
|
&QName::new(Namespace::public_namespace(), "prototype"),
|
||||||
avm,
|
avm,
|
||||||
context,
|
context,
|
||||||
|
@ -432,6 +433,7 @@ impl<'gc> FunctionObject<'gc> {
|
||||||
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
fn get_property_local(
|
fn get_property_local(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
@ -439,11 +441,12 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.base
|
.base
|
||||||
.get_property_local(name, avm, context, self.into())
|
.get_property_local(reciever, name, avm, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(
|
fn set_property(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
|
@ -452,7 +455,7 @@ impl<'gc> TObject<'gc> for FunctionObject<'gc> {
|
||||||
self.0
|
self.0
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
.base
|
.base
|
||||||
.set_property(name, value, avm, context, self.into())
|
.set_property(reciever, name, value, avm, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_property(
|
fn init_property(
|
||||||
|
|
|
@ -29,6 +29,7 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// into account.
|
/// into account.
|
||||||
fn get_property_local(
|
fn get_property_local(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, '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.
|
/// Retrieve a property by it's QName.
|
||||||
fn get_property(
|
fn get_property(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
if self.has_own_property(name) {
|
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() {
|
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())
|
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.
|
/// Set a property by it's QName.
|
||||||
fn set_property(
|
fn set_property(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm2<'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.abc(),
|
||||||
type_entry.instance().super_name.clone(),
|
type_entry.instance().super_name.clone(),
|
||||||
)?;
|
)?;
|
||||||
|
let reciever: Object<'gc> = (*self).into();
|
||||||
let super_class: Result<Object<'gc>, Error> = self
|
let super_class: Result<Object<'gc>, Error> = self
|
||||||
.get_property(&super_name, avm, context)?
|
.get_property(reciever, &super_name, avm, context)?
|
||||||
.resolve(avm, context)?
|
.resolve(avm, context)?
|
||||||
.as_object()
|
.as_object()
|
||||||
.map_err(|_e| {
|
.map_err(|_e| {
|
||||||
|
|
|
@ -128,7 +128,10 @@ impl<'gc> Scope<'gc> {
|
||||||
) -> Option<Result<ReturnValue<'gc>, Error>> {
|
) -> Option<Result<ReturnValue<'gc>, Error>> {
|
||||||
if let Some(qname) = self.locals().resolve_multiname(name) {
|
if let Some(qname) = self.locals().resolve_multiname(name) {
|
||||||
if self.locals().has_property(&qname) {
|
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> {
|
impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
fn get_property_local(
|
fn get_property_local(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
self.0
|
self.0
|
||||||
.read()
|
.read()
|
||||||
.get_property_local(name, avm, context, self.into())
|
.get_property_local(reciever, name, avm, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_property(
|
fn set_property(
|
||||||
self,
|
self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
|
@ -55,7 +57,7 @@ impl<'gc> TObject<'gc> for ScriptObject<'gc> {
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
self.0
|
self.0
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
.set_property(name, value, avm, context, self.into())
|
.set_property(reciever, name, value, avm, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_property(
|
fn init_property(
|
||||||
|
@ -213,15 +215,15 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
|
|
||||||
pub fn get_property_local(
|
pub fn get_property_local(
|
||||||
&self,
|
&self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
let prop = self.values.get(name);
|
let prop = self.values.get(name);
|
||||||
|
|
||||||
if let Some(prop) = prop {
|
if let Some(prop) = prop {
|
||||||
prop.get(avm, context, this)
|
prop.get(avm, context, reciever)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
}
|
}
|
||||||
|
@ -229,17 +231,17 @@ impl<'gc> ScriptObjectData<'gc> {
|
||||||
|
|
||||||
pub fn set_property(
|
pub fn set_property(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
reciever: Object<'gc>,
|
||||||
name: &QName,
|
name: &QName,
|
||||||
value: Value<'gc>,
|
value: Value<'gc>,
|
||||||
avm: &mut Avm2<'gc>,
|
avm: &mut Avm2<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
if let Some(prop) = self.values.get_mut(name) {
|
if let Some(prop) = self.values.get_mut(name) {
|
||||||
if let Some(slot_id) = prop.slot_id() {
|
if let Some(slot_id) = prop.slot_id() {
|
||||||
self.set_slot(slot_id, value, context.gc_context)?;
|
self.set_slot(slot_id, value, context.gc_context)?;
|
||||||
} else {
|
} else {
|
||||||
prop.set(avm, context, this, value)?;
|
prop.set(avm, context, reciever, value)?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//TODO: Not all classes are dynamic like this
|
//TODO: Not all classes are dynamic like this
|
||||||
|
|
Loading…
Reference in New Issue