diff --git a/core/src/avm1/function.rs b/core/src/avm1/function.rs index a72057caa..b628af474 100644 --- a/core/src/avm1/function.rs +++ b/core/src/avm1/function.rs @@ -224,7 +224,7 @@ impl<'gc> Executable<'gc> { avm: &mut Avm1<'gc>, ac: &mut UpdateContext<'_, 'gc, '_>, this: Object<'gc>, - _base_proto: Option>, + base_proto: Option>, args: &[Value<'gc>], ) -> Result, Error> { match self { @@ -255,18 +255,19 @@ impl<'gc> Executable<'gc> { let argcell = arguments.into(); let super_object: Option> = 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 { diff --git a/core/src/avm1/super_object.rs b/core/src/avm1/super_object.rs index 245bc7893..56c3e8c26 100644 --- a/core/src/avm1/super_object.rs +++ b/core/src/avm1/super_object.rs @@ -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>, - constr: Option>, - this: Option>, + + /// The `proto` that all property resolution on `super` happens on. + super_proto: Option>, + + /// The object called when `super` is called. + /// + /// This should be the `constructor` property of `super_proto`. + super_constr: Option>, } 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 { - 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>, + _this: Object<'gc>, + _base_proto: Option>, args: &[Value<'gc>], ) -> Result, 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> { - self.0.read().proto + self.0.read().super_proto } fn set_proto(&self, gc_context: MutationContext<'gc, '_>, prototype: Option>) { @@ -279,7 +279,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> { fn as_executable(&self) -> Option> { //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 {