avm2: Fix get_super and set_super with normal methods
Doing `super.someNonGetter` gives you back a function object. We were previously attempting to call normal methods as though they were getters. Additionally, we were failing to properly get the property from the superclass vtable.
This commit is contained in:
parent
30dc715c46
commit
33e9713279
|
@ -596,29 +596,48 @@ impl<'gc> ClassObject<'gc> {
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
let property = self.instance_vtable().get_trait(multiname);
|
let property = self.instance_vtable().get_trait(multiname);
|
||||||
if property.is_none() {
|
|
||||||
return Err(format!(
|
match property {
|
||||||
|
Some(
|
||||||
|
Property::Virtual {
|
||||||
|
get: Some(disp_id), ..
|
||||||
|
}
|
||||||
|
| Property::Method { disp_id },
|
||||||
|
) => {
|
||||||
|
// todo: handle errors
|
||||||
|
let ClassBoundMethod {
|
||||||
|
class,
|
||||||
|
scope,
|
||||||
|
method,
|
||||||
|
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||||
|
let callee = FunctionObject::from_method(
|
||||||
|
activation,
|
||||||
|
method,
|
||||||
|
scope,
|
||||||
|
Some(receiver),
|
||||||
|
Some(class),
|
||||||
|
);
|
||||||
|
|
||||||
|
// We call getters, but return the actual function object for normal methods
|
||||||
|
if matches!(property, Some(Property::Virtual { .. })) {
|
||||||
|
callee.call(Some(receiver), &[], activation)
|
||||||
|
} else {
|
||||||
|
Ok(callee.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(Property::Virtual { .. }) => Err(format!(
|
||||||
|
"Attempting to use get_super on non-getter property {:?}",
|
||||||
|
multiname
|
||||||
|
)
|
||||||
|
.into()),
|
||||||
|
Some(Property::Slot { .. } | Property::ConstSlot { .. }) => {
|
||||||
|
receiver.get_property(multiname, activation)
|
||||||
|
}
|
||||||
|
None => Err(format!(
|
||||||
"Attempted to supercall method {:?}, which does not exist",
|
"Attempted to supercall method {:?}, which does not exist",
|
||||||
multiname.local_name()
|
multiname.local_name()
|
||||||
)
|
)
|
||||||
.into());
|
.into()),
|
||||||
}
|
|
||||||
if let Some(Property::Virtual {
|
|
||||||
get: Some(disp_id), ..
|
|
||||||
}) = property
|
|
||||||
{
|
|
||||||
// todo: handle errors
|
|
||||||
let ClassBoundMethod {
|
|
||||||
class,
|
|
||||||
scope,
|
|
||||||
method,
|
|
||||||
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
|
||||||
let callee =
|
|
||||||
FunctionObject::from_method(activation, method, scope, Some(receiver), Some(class));
|
|
||||||
|
|
||||||
callee.call(Some(receiver), &[], activation)
|
|
||||||
} else {
|
|
||||||
receiver.get_property(multiname, activation)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,24 +681,30 @@ impl<'gc> ClassObject<'gc> {
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
if let Some(Property::Virtual {
|
|
||||||
set: Some(disp_id), ..
|
|
||||||
}) = property
|
|
||||||
{
|
|
||||||
// todo: handle errors
|
|
||||||
let ClassBoundMethod {
|
|
||||||
class,
|
|
||||||
scope,
|
|
||||||
method,
|
|
||||||
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
|
||||||
let callee =
|
|
||||||
FunctionObject::from_method(activation, method, scope, Some(receiver), Some(class));
|
|
||||||
|
|
||||||
callee.call(Some(receiver), &[value], activation)?;
|
match property {
|
||||||
|
Some(Property::Virtual {
|
||||||
|
set: Some(disp_id), ..
|
||||||
|
}) => {
|
||||||
|
// todo: handle errors
|
||||||
|
let ClassBoundMethod {
|
||||||
|
class,
|
||||||
|
scope,
|
||||||
|
method,
|
||||||
|
} = self.instance_vtable().get_full_method(disp_id).unwrap();
|
||||||
|
let callee =
|
||||||
|
FunctionObject::from_method(activation, method, scope, Some(receiver), Some(class));
|
||||||
|
|
||||||
Ok(())
|
callee.call(Some(receiver), &[value], activation)?;
|
||||||
} else {
|
Ok(())
|
||||||
receiver.set_property(multiname, value, activation)
|
}
|
||||||
|
Some(Property::Slot { .. }) => {
|
||||||
|
receiver.set_property(multiname, value, activation)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Err(format!("set_super on {receiver:?} {multiname:?} with {value:?} resolved to unexpected property {property:?}").into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
package {
|
||||||
|
public class Subclass extends Superclass {
|
||||||
|
|
||||||
|
public var subclassField:String = "Val";
|
||||||
|
|
||||||
|
public function Subclass() {
|
||||||
|
this.myMethod("First arg", true);
|
||||||
|
|
||||||
|
trace("this.myGetter: " + this.myGetter);
|
||||||
|
trace("super.myGetter: " + super.myGetter);
|
||||||
|
|
||||||
|
trace("super.superField: " + super.superField);
|
||||||
|
|
||||||
|
var obj = super;
|
||||||
|
trace("obj.myGetter: " + obj.myGetter);
|
||||||
|
|
||||||
|
this.mySetter = "setting_on_this";
|
||||||
|
super.mySetter = "setting_on_super";
|
||||||
|
}
|
||||||
|
public override function myMethod(arg1: String, arg2: Boolean) {
|
||||||
|
trace("In subclass myMethod: " + arg1 + " " + arg2);
|
||||||
|
super.myMethod("direct_arg", true);
|
||||||
|
super.myMethod.apply(this, ["apply_arg", true]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function get myGetter():String {
|
||||||
|
trace("Calling subclass getter");
|
||||||
|
return "Value from subclass"
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function set mySetter(val: String):void {
|
||||||
|
trace("Calling subclass getter with " + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package {
|
||||||
|
public class Superclass {
|
||||||
|
|
||||||
|
public var superField:Number = 20;
|
||||||
|
|
||||||
|
public function myMethod(arg1: String, arg2: Boolean) {
|
||||||
|
trace("In superclass myMethod: " + arg1 + " " + arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function get myGetter():String {
|
||||||
|
trace("Calling superclass getter");
|
||||||
|
return "Value from superclass"
|
||||||
|
}
|
||||||
|
|
||||||
|
public function set mySetter(val: String):void {
|
||||||
|
trace("Calling superclass getter with " + val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
In subclass myMethod: First arg true
|
||||||
|
In superclass myMethod: direct_arg true
|
||||||
|
In superclass myMethod: apply_arg true
|
||||||
|
Calling subclass getter
|
||||||
|
this.myGetter: Value from subclass
|
||||||
|
Calling superclass getter
|
||||||
|
super.myGetter: Value from superclass
|
||||||
|
super.superField: 20
|
||||||
|
Calling subclass getter
|
||||||
|
obj.myGetter: Value from subclass
|
||||||
|
Calling subclass getter with setting_on_this
|
||||||
|
Calling superclass getter with setting_on_super
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
||||||
|
num_frames = 1
|
Loading…
Reference in New Issue