Refactor how `SuperObject` works to use `base_proto` and avoid handing copies of itself as `this`.
This allows supercalled functions to properly read and mutate the object they were called on.
This commit is contained in:
parent
f3b3db51cb
commit
9c5cd79e2c
|
@ -224,7 +224,7 @@ impl<'gc> Executable<'gc> {
|
|||
avm: &mut Avm1<'gc>,
|
||||
ac: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
_base_proto: Option<Object<'gc>>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
match self {
|
||||
|
@ -255,18 +255,19 @@ impl<'gc> Executable<'gc> {
|
|||
|
||||
let argcell = arguments.into();
|
||||
let super_object: Option<Object<'gc>> = if !af.suppress_super {
|
||||
Some(SuperObject::from_child_object(this, avm, ac)?.into())
|
||||
Some(
|
||||
SuperObject::from_this_and_base_proto(
|
||||
this,
|
||||
base_proto.unwrap_or(this),
|
||||
avm,
|
||||
ac,
|
||||
)?
|
||||
.into(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(super_object) = super_object {
|
||||
super_object
|
||||
.as_super_object()
|
||||
.unwrap()
|
||||
.bind_this(ac.gc_context, super_object);
|
||||
}
|
||||
|
||||
let effective_ver = if avm.current_swf_version() > 5 {
|
||||
af.swf_version()
|
||||
} else {
|
||||
|
|
|
@ -22,27 +22,36 @@ pub struct SuperObject<'gc>(GcCell<'gc, SuperObjectData<'gc>>);
|
|||
#[collect(no_drop)]
|
||||
#[derive(Clone, Collect, Debug)]
|
||||
pub struct SuperObjectData<'gc> {
|
||||
/// The object present as `this` throughout the superchain.
|
||||
child: Object<'gc>,
|
||||
proto: Option<Object<'gc>>,
|
||||
constr: Option<Object<'gc>>,
|
||||
this: Option<Object<'gc>>,
|
||||
|
||||
/// The `proto` that all property resolution on `super` happens on.
|
||||
super_proto: Option<Object<'gc>>,
|
||||
|
||||
/// The object called when `super` is called.
|
||||
///
|
||||
/// This should be the `constructor` property of `super_proto`.
|
||||
super_constr: Option<Object<'gc>>,
|
||||
}
|
||||
|
||||
impl<'gc> SuperObject<'gc> {
|
||||
pub fn from_child_object(
|
||||
child: Object<'gc>,
|
||||
/// Construct a `super` for an incoming stack frame.
|
||||
///
|
||||
/// `this` and `base_proto` must be the values provided to
|
||||
/// `Executable.exec`.
|
||||
pub fn from_this_and_base_proto(
|
||||
this: Object<'gc>,
|
||||
base_proto: Object<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Self, Error> {
|
||||
let child_proto = child.proto();
|
||||
let parent_proto = child_proto.and_then(|pr| pr.proto());
|
||||
let parent_constr = if let Some(child_proto) = child_proto {
|
||||
Some(
|
||||
child_proto
|
||||
.get("constructor", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_object()?,
|
||||
)
|
||||
let super_proto = base_proto.proto();
|
||||
let super_constr = if let Some(super_proto) = super_proto {
|
||||
super_proto
|
||||
.get("constructor", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_object()
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -50,21 +59,12 @@ impl<'gc> SuperObject<'gc> {
|
|||
Ok(Self(GcCell::allocate(
|
||||
context.gc_context,
|
||||
SuperObjectData {
|
||||
child,
|
||||
proto: parent_proto,
|
||||
constr: parent_constr,
|
||||
this: None,
|
||||
child: this,
|
||||
super_proto,
|
||||
super_constr,
|
||||
},
|
||||
)))
|
||||
}
|
||||
|
||||
/// Set `this` to a particular value.
|
||||
///
|
||||
/// This is intended to be called with a self-reference, so that future
|
||||
/// invocations of `super()` can get a `this` value one level up the chain.
|
||||
pub fn bind_this(&mut self, context: MutationContext<'gc, '_>, this: Object<'gc>) {
|
||||
self.0.write(context).this = Some(this);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||
|
@ -93,16 +93,16 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
&self,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
base_proto: Option<Object<'gc>>,
|
||||
_this: Object<'gc>,
|
||||
_base_proto: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(constr) = self.0.read().constr {
|
||||
if let Some(constr) = self.0.read().super_constr {
|
||||
constr.call(
|
||||
avm,
|
||||
context,
|
||||
self.0.read().this.unwrap_or(this),
|
||||
base_proto,
|
||||
self.0.read().child,
|
||||
self.0.read().super_proto,
|
||||
args,
|
||||
)
|
||||
} else {
|
||||
|
@ -138,7 +138,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
}
|
||||
|
||||
fn proto(&self) -> Option<Object<'gc>> {
|
||||
self.0.read().proto
|
||||
self.0.read().super_proto
|
||||
}
|
||||
|
||||
fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option<Object<'gc>>) {
|
||||
|
@ -279,7 +279,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
|||
|
||||
fn as_executable(&self) -> Option<Executable<'gc>> {
|
||||
//well, `super` *can* be called...
|
||||
self.0.read().constr.and_then(|c| c.as_executable())
|
||||
self.0.read().super_constr.and_then(|c| c.as_executable())
|
||||
}
|
||||
|
||||
fn as_ptr(&self) -> *const ObjectPtr {
|
||||
|
|
Loading…
Reference in New Issue