From 33e9713279accedf125b656700e44d035a4445e2 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 11 Apr 2023 21:05:32 -0400 Subject: [PATCH] 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. --- core/src/avm2/object/class_object.rs | 97 +++++++++++------- .../swfs/avm2/super_get_call/Subclass.as | 36 +++++++ .../swfs/avm2/super_get_call/Superclass.as | 19 ++++ .../tests/swfs/avm2/super_get_call/output.txt | 12 +++ tests/tests/swfs/avm2/super_get_call/test.fla | Bin 0 -> 3822 bytes tests/tests/swfs/avm2/super_get_call/test.swf | Bin 0 -> 1238 bytes .../tests/swfs/avm2/super_get_call/test.toml | 1 + 7 files changed, 129 insertions(+), 36 deletions(-) create mode 100644 tests/tests/swfs/avm2/super_get_call/Subclass.as create mode 100644 tests/tests/swfs/avm2/super_get_call/Superclass.as create mode 100644 tests/tests/swfs/avm2/super_get_call/output.txt create mode 100644 tests/tests/swfs/avm2/super_get_call/test.fla create mode 100644 tests/tests/swfs/avm2/super_get_call/test.swf create mode 100644 tests/tests/swfs/avm2/super_get_call/test.toml 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 0000000000000000000000000000000000000000..b3f61f1ab3b58df7f8c090483de010ac33c02c21 GIT binary patch literal 3822 zcmbVPc{o(<8y?FbdzRFQ>|6G+FImQxY%xBjh{2d4jAe+jgpe(}EK@|XB}=6&*|RU% zq7>OPvP2n8$#3TKl}}fFfBc?vJ#*f3&U3%dbLP6wb=|kw845}^0DuMnXpf2gt1fvu z@)!UBASLk@z}>|ifeG+Lz&$n_9ypAQtim%ZU{~gH&#GTH9R9947VWD$>Trx=<~N(fEizFt*f|cS8-Q?SYUmgD61ZhLC;j80eF9j)k);l3F#sx9pB5T&^YA#-VL@- zg;4!BPd^70$55swEHjg-J~Yv4l6~m=5If5FMAR|F0z^0RWXoRyu{4YIW)N;TPoNzc4dz|}G$tv=ezGQ4f(^AirY|6sI`#M#^L>=o-W zE4(R<$>+h|X!VqhmE!ObrjZiPFZPkA)0+i0_=n>Lz_uJ1B&y7hjd4Y*

g3wwB;l z==Lm^Po!_b@WnOAf@8i|Q^&T#Y1U%X59+PHD{DiFizgI<#A+Vl8ho(h!8;Wgf$K~1 zo(6rJkMxfRlr4jS-(ZHrWNkWBswXCH?1t?aI#)k z*+HbDv8I86S}cSR)F&tNC6esY-jdBL6F7&wV@KOlPqrH%v1QxV}E#{mFVBE)Qu&lU&_ z#>E5aLjpX9?V@K?Sax@I*lBK=TpG&frKD9GH()cIuvqdwgZ~^{`o_L#B5_Xn-lMN$ zQ*n*g#+!y*tuB>}^aiTk{utK*HhC}QkX+6UzAb5Ow7LGI0SLVyG62g8EFmC2y9)AN zBowRU(N#t3K4x)ODvaKSvIjy+r_pJKA+-T9um(_JpmAGj;j#I!!Fp}h4YuKc(`?rk z4F|bUeUQdASUpQpX|s&32=ftqydK{?llY2(2_4$pPx{+|e>%xV`m7KhVl+-MYy#-@28p=O{T4 zeH4df0DX@|LP_1zql8)KoqE4XkfLi}BvV${<%bH~a;mLrGwPiN& zT@>^!TkRN;Z@0~uT8*QyzGXgUj#JSvAH&}mnediqh#XP2jNXMJf^~p%g3OAs&2(@X z!@TJe4CiS*l665;@l(s&V|2JI6qYj&alMJTfj6#VN?xaf*&T9JJ$RT|F997#_`YTf z!O#yXwAJvQi$sC=q;*=E8~AX}MU0T(lV$=%fM)L38=Sd!quK>R+dV_oo1gW!AHCj=gk2DA3FZRcDD;cqpsdJc`<x!{ zu+tRvvYd?$j$cm}moa+*eb}6{@Ki8N)NSxVBSd$6AiN zhC1kqZD&=@8(!;Ed6%Vdp|YSpNJP6_9?p03V#+mg^srnjPfuZ(bC{=$U^ce@Y;CV= z=yefc_B&(A-dkMSMh-4M40rUNMZKGW(jHNs`I^h}9Us|dR3FjTfHaVo&NKA_se@lp z^Y}l#uii^3lUX=%TIJ?NDX(*^Jr6VLjZsz2X{j(rT!b!$zF&kNSQxps1QIGLOWC3D z2AIU^)0n2rch@0IvFnQHM1;)K>jfpU6Hc)12*r#-(-BSqdRoCUIb3{Ge%I{SF`<<( z2}PTjD`WVS^QJkJw=%B$<;!7HFma_SkVh?Z?o5D8Ws-Mjb!>&NWqWo>LP@Ki&=H54@tkY?Y5!C|rOO=PY z$o#ox+)R*5LYXIb-P>Fv;b4)H8ktS8SW~r#N7NdllV?;=*iaRujTPwFYgijhC{E=? zAkE7Oqda8Q-J9i4KRZ6S_>F9YC5NT5yH(qy@-pyYllDMUEd_fRPN{j<=348(rP=IK zjqgXN7@fxw!prevIgM}L*ai{4+GRsONjI~PrVzTpu}-hVZzghBbn^~gD@ms|v%=1O zQJCMX5O~Jc`91n0$^2ArnI4ggM$p^QC@HV< zL}Z8}&)u`{D{d17f97sIB%@)h*q;z>aN>`sJKjhrmzY=?Ljn z%nfEiIU?4#u*2CEueManBn2d832M_dtufWAAsuKsR=&Kn`$P^8+l83MY0aQVJ(N5D9+f~cyQ7_Ziz6Im0 z6m|wr#P}kb^4YO#u7d1zTotV!hy5l$+3eUhg7V_T*}XqitZO%>Ta600u8BWHeo)v} zmuy(>$?^I)P}D5K^QN-#+=@a-j_T6tEpPuFlP90nOh+q%7j`k+Sb|fjyNLlbGt2bT zZmE;hO4hgdcZD|UJIb($5c{s(QOhro?$cJE2fz#ZUFtkcyL3Nf!=8fwDKm%*7Lw9H zJGi(Z{;e7gs_S(vmWQ4M4j&lcSIqO#M_J1lmK!79pEt0 z8k`_rlcJ%Fs7em#|Np~2kddM+@EW>2~mM|CMn2_zfcdH}v~4z@I$o{stQK Y`;OJ@43LWSgqC-qmd-`5xbsx+o;j0k~iTML}&UOC{I9-q!(bq*6xe@E%dgW0xm|K z0kx>IfzaF1mKYk~%A*!yN(kLSLdSCq>xkU8ZIkL&u``=eXJG2Ct4?0;Q*Ue!tDo5J zQO~1}dPF_$*fu@`J2B`2s5?g+jpL zqYr3p&r;lvm@@CQtzf0T1D-HJN&G14x(w~js4{ZwNdjb#b@SX;eUz7mH&*ulk52mY zK3Kc|T)JyGuBSjpId5#ZiD6lar)Tjx+e#K35{zsj*80}<)v@PI+p4M$?LKV{?1{SF z*-|$(?W)?J8K!4gIT%WK9CdB08)nNV_EDZnBxnC~G3BA*z-V=$N=cti&DY_Ik5<)% zV*R9E#ZNSjp~hFZv9^u*2(^+U)IR$tvifj%H(YfFjxqJtzCup!(($a)V`_PB?oRmF zg}K$e!I(Ok-FSrKkEXC^K^^+QGmdF&seFR(*p>&8sUvizj^WYm|2hY1u7YcYTCK8D zY1GbDSNIyQRk66STw5+weq%8fMJ&lFHl0}^m?4-YSRlAS@DjmAf=dLK39b-aCAda# zop9d}{#!y6g4d)1mMRQMU6X2<;9p4gCgE=pew*;O3BQvM4_XFchInyDNbS$;nF}`#^Ndx zrSnLV*N~jaiUP{w97Y+01e8WBk2wyZ6i#sn%oz?dd4w^`vA%W>+*wGncDfn}zV;rA zNO+IWV<8}i`R4k&ljen!zfKPFI1qu#Fqq>@K$U$hNCDv@AkFWB^nIw5WjMYLSS}3n zJ<0`nAY6JEITyg0!{7{#{vHBW2`oGVRTO;s#J3F$>9kRZ(u{z!@v$*{@#T)aucrsZ=&w8{E^{vG7NqP`m~#YOqe z&9NLT`OPg}4vLUUyp;HsdcPSN z3^y9%6u@Z=6B}!i!B|d#(5E*7e3<$@g)RjQn6oUHE0M&9<Xb!?l zSA314Fmy%hRkdDC>(#a1vewJO{fueE*En;!8`Qw%W^ws!Cck(bpj&|a1Esr$f*!4D ANdN!< literal 0 HcmV?d00001 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