diff --git a/core/src/avm2/object/class_object.rs b/core/src/avm2/object/class_object.rs index 65bc57037..20764af53 100644 --- a/core/src/avm2/object/class_object.rs +++ b/core/src/avm2/object/class_object.rs @@ -596,29 +596,48 @@ impl<'gc> ClassObject<'gc> { activation: &mut Activation<'_, 'gc>, ) -> Result, Error<'gc>> { 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", multiname.local_name() ) - .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) + .into()), } } @@ -662,24 +681,30 @@ impl<'gc> ClassObject<'gc> { ) .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(()) - } else { - receiver.set_property(multiname, value, activation) + callee.call(Some(receiver), &[value], activation)?; + Ok(()) + } + 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()) + } } } diff --git a/tests/tests/swfs/avm2/super_get_call/Subclass.as b/tests/tests/swfs/avm2/super_get_call/Subclass.as new file mode 100644 index 000000000..b17dd308b --- /dev/null +++ b/tests/tests/swfs/avm2/super_get_call/Subclass.as @@ -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); + } + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/super_get_call/Superclass.as b/tests/tests/swfs/avm2/super_get_call/Superclass.as new file mode 100644 index 000000000..8a0a2408a --- /dev/null +++ b/tests/tests/swfs/avm2/super_get_call/Superclass.as @@ -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); + } + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/super_get_call/output.txt b/tests/tests/swfs/avm2/super_get_call/output.txt new file mode 100644 index 000000000..7ad9b7312 --- /dev/null +++ b/tests/tests/swfs/avm2/super_get_call/output.txt @@ -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 diff --git a/tests/tests/swfs/avm2/super_get_call/test.fla b/tests/tests/swfs/avm2/super_get_call/test.fla new file mode 100644 index 000000000..b3f61f1ab Binary files /dev/null and b/tests/tests/swfs/avm2/super_get_call/test.fla differ diff --git a/tests/tests/swfs/avm2/super_get_call/test.swf b/tests/tests/swfs/avm2/super_get_call/test.swf new file mode 100644 index 000000000..b66654078 Binary files /dev/null and b/tests/tests/swfs/avm2/super_get_call/test.swf differ diff --git a/tests/tests/swfs/avm2/super_get_call/test.toml b/tests/tests/swfs/avm2/super_get_call/test.toml new file mode 100644 index 000000000..dbee897f5 --- /dev/null +++ b/tests/tests/swfs/avm2/super_get_call/test.toml @@ -0,0 +1 @@ +num_frames = 1