Calls to `super` inherently bind to itself.
This requires some subclassing nonsense to be able to smuggle a self-reference into `SuperObject`s. When successfully smuggled, all calls to `call` will be invoked with the `super` object as `this`. This allows constructor chaining to work. Note that not all `Object` trait methods are implemented on `SuperObject`, so things like `delete this.x` in super constructors will randomly fail. This should be fixed.
This commit is contained in:
parent
548f19ffbb
commit
854526923e
|
@ -237,6 +237,13 @@ impl<'gc> Executable<'gc> {
|
||||||
None
|
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 {
|
let effective_ver = if avm.current_swf_version() > 5 {
|
||||||
af.swf_version()
|
af.swf_version()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -227,6 +227,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
|
||||||
/// Get the underlying script object, if it exists.
|
/// Get the underlying script object, if it exists.
|
||||||
fn as_script_object(&self) -> Option<ScriptObject<'gc>>;
|
fn as_script_object(&self) -> Option<ScriptObject<'gc>>;
|
||||||
|
|
||||||
|
/// Get the underlying super object, if it exists.
|
||||||
|
fn as_super_object(&self) -> Option<SuperObject<'gc>> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the underlying display node for this object, if it exists.
|
/// Get the underlying display node for this object, if it exists.
|
||||||
fn as_display_object(&self) -> Option<DisplayObject<'gc>>;
|
fn as_display_object(&self) -> Option<DisplayObject<'gc>>;
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ pub struct SuperObjectData<'gc> {
|
||||||
child: Object<'gc>,
|
child: Object<'gc>,
|
||||||
proto: Option<Object<'gc>>,
|
proto: Option<Object<'gc>>,
|
||||||
constr: Option<Object<'gc>>,
|
constr: Option<Object<'gc>>,
|
||||||
|
this: Option<Object<'gc>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'gc> SuperObject<'gc> {
|
impl<'gc> SuperObject<'gc> {
|
||||||
|
@ -33,10 +34,11 @@ impl<'gc> SuperObject<'gc> {
|
||||||
avm: &mut Avm1<'gc>,
|
avm: &mut Avm1<'gc>,
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
let parent_proto = child.proto().and_then(|pr| pr.proto());
|
let child_proto = child.proto();
|
||||||
let parent_constr = if let Some(parent_proto) = parent_proto {
|
let parent_proto = child_proto.and_then(|pr| pr.proto());
|
||||||
|
let parent_constr = if let Some(child_proto) = child_proto {
|
||||||
Some(
|
Some(
|
||||||
parent_proto
|
child_proto
|
||||||
.get("constructor", avm, context)?
|
.get("constructor", avm, context)?
|
||||||
.resolve(avm, context)?
|
.resolve(avm, context)?
|
||||||
.as_object()?,
|
.as_object()?,
|
||||||
|
@ -51,9 +53,18 @@ impl<'gc> SuperObject<'gc> {
|
||||||
child,
|
child,
|
||||||
proto: parent_proto,
|
proto: parent_proto,
|
||||||
constr: parent_constr,
|
constr: parent_constr,
|
||||||
|
this: None,
|
||||||
},
|
},
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
|
@ -86,7 +97,7 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error> {
|
) -> Result<ReturnValue<'gc>, Error> {
|
||||||
if let Some(constr) = self.0.read().constr {
|
if let Some(constr) = self.0.read().constr {
|
||||||
constr.call(avm, context, this, args)
|
constr.call(avm, context, self.0.read().this.unwrap_or(this), args)
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
}
|
}
|
||||||
|
@ -224,6 +235,10 @@ impl<'gc> TObject<'gc> for SuperObject<'gc> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn as_super_object(&self) -> Option<SuperObject<'gc>> {
|
||||||
|
Some(*self)
|
||||||
|
}
|
||||||
|
|
||||||
fn as_display_object(&self) -> Option<DisplayObject<'gc>> {
|
fn as_display_object(&self) -> Option<DisplayObject<'gc>> {
|
||||||
self.0.read().child.as_display_object()
|
self.0.read().child.as_display_object()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue