From 2c1936db18c31cae4fb8505c1bd8ef364d0a86bd Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Tue, 7 Mar 2023 14:06:49 -0600 Subject: [PATCH] avm2: Fix QName::from_qualified_name for Vector. We weren't handling names like `__AS3__.vec::Vector.` --- core/src/avm2/domain.rs | 5 ++-- core/src/avm2/qname.rs | 25 +++++++++++++++--- tests/tests/swfs/avm2/vector_class/Test.as | 23 ++++++++++++++++ tests/tests/swfs/avm2/vector_class/output.txt | 8 ++++++ tests/tests/swfs/avm2/vector_class/test.swf | Bin 1091 -> 1402 bytes 5 files changed, 56 insertions(+), 5 deletions(-) diff --git a/core/src/avm2/domain.rs b/core/src/avm2/domain.rs index 17ce935c4..da88a3510 100644 --- a/core/src/avm2/domain.rs +++ b/core/src/avm2/domain.rs @@ -192,7 +192,8 @@ impl<'gc> Domain<'gc> { // to a lookup of `Vector,` a lookup of `SomeType`, and `vector_class.apply(some_type_class)` let mut type_name = None; if (name.namespace() == activation.avm2().vector_public_namespace - || name.namespace() == activation.avm2().vector_internal_namespace) + || name.namespace() == activation.avm2().vector_internal_namespace + || name.namespace() == activation.avm2().public_namespace) && (name.local_name().starts_with(b"Vector.<".as_slice()) && name.local_name().ends_with(b">".as_slice())) { @@ -201,7 +202,7 @@ impl<'gc> Domain<'gc> { activation.context.gc_context, &local_name["Vector.<".len()..(local_name.len() - 1)], )); - name = QName::new(name.namespace(), "Vector"); + name = QName::new(activation.avm2().vector_public_namespace, "Vector"); } let res = self.get_defined_value(activation, name); diff --git a/core/src/avm2/qname.rs b/core/src/avm2/qname.rs index a68d2aa2c..e76cbd1a9 100644 --- a/core/src/avm2/qname.rs +++ b/core/src/avm2/qname.rs @@ -79,9 +79,28 @@ impl<'gc> QName<'gc> { /// LOCAL_NAME (Use the public namespace) pub fn from_qualified_name(name: AvmString<'gc>, activation: &mut Activation<'_, 'gc>) -> Self { let mc = activation.context.gc_context; - let parts = name - .rsplit_once(WStr::from_units(b"::")) - .or_else(|| name.rsplit_once(WStr::from_units(b"."))); + // If we have a type like 'some::namespace::Vector.', + // we want to look at 'some::namespace::Vector' when splitting out the namespace + let before_type_param = if let Some(type_param_start) = name.find(WStr::from_units(b".<")) { + &name[..type_param_start] + } else { + &name + }; + + // We unfortunately can't use 'rsplit' here, because we would need to split + // the entire string, but only before the first '.<' + // + // Get the last '::' or '.', only considering the string before '.<' (if any). + // This will ignore any namespaces that are part of a type parameter (e.g 'Vector.'). + // The type parameter will stay combined with the type name (so we'll have 'Vector.') + // in some namespace, depending on whether or not anything comes before 'Vector') + let parts = if let Some(last_separator) = before_type_param.rfind(WStr::from_units(b"::")) { + Some((&name[..last_separator], &name[(last_separator + 2)..])) + } else if let Some(last_separator) = before_type_param.rfind(b".".as_slice()) { + Some((&name[..last_separator], &name[(last_separator + 1)..])) + } else { + None + }; if let Some((package_name, local_name)) = parts { Self { diff --git a/tests/tests/swfs/avm2/vector_class/Test.as b/tests/tests/swfs/avm2/vector_class/Test.as index b86649163..0e93af52a 100644 --- a/tests/tests/swfs/avm2/vector_class/Test.as +++ b/tests/tests/swfs/avm2/vector_class/Test.as @@ -3,6 +3,8 @@ import flash.utils.getQualifiedClassName; import flash.system.ApplicationDomain; + + public class Test { public function Test() { var vec; @@ -43,6 +45,27 @@ trace("Vector.: " + getDefinitionByName(name)); trace("ApplicationDomain.hasDefinition Vector.: " + ApplicationDomain.currentDomain.hasDefinition(name)); trace("ApplicationDomain.getDefinition Vector.: " + ApplicationDomain.currentDomain.getDefinition(name)); + + import rs.ruffle.CustomClass; + + trace("Vector.: " + getDefinitionByName("Vector.")); + trace("__AS3__.vec::Vector.: " + getDefinitionByName("__AS3__.vec::Vector.")); + + vec = new Vector.([]); + + name = getQualifiedClassName(vec); + trace("Vector. name: " + name); + trace("Vector.: " + getDefinitionByName(name)); + trace("ApplicationDomain.hasDefinition Vector.: " + ApplicationDomain.currentDomain.hasDefinition(name)); + trace("ApplicationDomain.getDefinition Vector.: " + ApplicationDomain.currentDomain.getDefinition(name)); + + trace("Vector. without namespace" + getDefinitionByName("Vector.")); + try { + trace("Vector without namespace: " + getDefinitionByName("Vector")); + } catch (e) { + trace("Caught error: " + e); + } + } } } \ No newline at end of file diff --git a/tests/tests/swfs/avm2/vector_class/output.txt b/tests/tests/swfs/avm2/vector_class/output.txt index 75f108931..3303705e0 100644 --- a/tests/tests/swfs/avm2/vector_class/output.txt +++ b/tests/tests/swfs/avm2/vector_class/output.txt @@ -19,3 +19,11 @@ Vector. name: __AS3__.vec::Vector. Vector.: [class Vector.] ApplicationDomain.hasDefinition Vector.: true ApplicationDomain.getDefinition Vector.: [class Vector.] +Vector.: [class Vector.] +__AS3__.vec::Vector.: [class Vector.] +Vector. name: __AS3__.vec::Vector. +Vector.: [class Vector.] +ApplicationDomain.hasDefinition Vector.: true +ApplicationDomain.getDefinition Vector.: [class Vector.] +Vector. without namespace[class Vector.] +Caught error: ReferenceError: Error #1065: Variable Vector is not defined. diff --git a/tests/tests/swfs/avm2/vector_class/test.swf b/tests/tests/swfs/avm2/vector_class/test.swf index bb173f74af30506799cfc6ca2e6ddbe1dde40788..4c023d41ec74dbf8101e3fca85c0a3b7dde6f215 100644 GIT binary patch literal 1402 zcmV-=1%>)US5qq|4FCXmoRyZ_R@+7thG!&MW7+rwST+!YNt_c9TR=*QNJ+pPlC%j) zot0)~;bjXmj+M%XCCQ}R_62&?``q`ww|NEkqJ52;7ihXA*^Z@{gm_^|`a*21%G>oOZRODW zco1?iDpY8Lrk4?VKl#U)1>h4!S?g3l!gFevIqNX58(FUZl0(#uc@v0NNwJ+n|&o8TH{9VXuPc&hssHPdNi}TK3_AukG9pz=byCeM^qR4*a`b?Vje7~moMt) za}}c<4JyLBimsiewPy3@c1uoA4?RM%+|RZi!p(tgXE%LfyNAER_U9@_+a6R*x~zGB zPaA&4?|FXyXdAu!K|E+7*`XE!3A3M1xHWRI&zo%VE3#+$!^5z*O5&(7IlXq##rk5| z=rwbf&3pI2Th**J+otW&XhBnocrvD)_|rsqhVx4MbD9R*sMNtgAvrS6k+lbCQRnUJs-v(CCcFu65dhmx*Fl zYE%#HIPVqbA&)(5m=wCRsh&`Wh_3N`vth!_RObsVT{Y>o3*GABxQLUy=uZa3pcqO< z5?Qv~w_6T%9_H%9m{@kfOTOmKokrU;%U zkrauh37#kTGQk;wuM)gK@Gc4Nk>I|-GlIYp{D{OJ6aER|4+#I1@XrFye+dlZoUJd5 zK3;S1^*PrpW&{Q(;cw|*JdOm4=YOqZ`^dn+L(~mXTqb3I0cUWAdBJRB81IHDE<&z`_XKQZ zMv8%@8Hky82LarTLUgGZwBo%WwowewYs|8brJ!YIVqEr{0K zT3JBfdbM%@*S%V+0PhroOLD*THh>KPcl%(?cEf&jv+i+o2P(cBxRD{+bi3IAbkFPN zF2MUr1G>>o{`Fzq_PkGUNR}?Q{&El>?Wb)-o$jT<&13J=st}6 I4|fUQBt&4rJpcdz literal 1091 zcmV-J1ibr0S5qrG2><|ioRwDVQrkup-lN<4e!;eENJvaDGz20GC}|>6(u7Nf&Lm97 zDMLGWY+=^565qy>Y5B{O5*l-7BMN&SlV;G{myrev}bqx2N4T| zgx?dA0V4HXG1z9+XeP$$NlppMuiBwKZ}y+& z?`88)KdG%8o?xOzt58BwbA8>;uIMI@s8Gj1X}>q^>*JZ#$1@enYHHc6e%v@>N~uxH zKF_UYS5);;_NZ-CEu-!)6c8#D6cpi&y5)BYZd*pxO#H^IpW1rWC>!i})6V%Cvxrtp zFR_@DOL>Yh8)-YcmJW?#S~`Z|TdZu<4a;cMU!2;(%JXKkYLs*v*=p4A-Ia=N{>MtY zB?3-0=Ba71TJ*EnV5!||vAWgwTuNgyx$3!+Dd?IM+K9#njP}SohFzeR&ffHeuIG<0 ztIwBSw`)hNB@bZ79aIy&0&D5zo7VHCi18H-BSPDHt9qKQHX85RO)Wh&(n6XxVB1Z& zF)Z8JBcGMshrgoiFGY+mdl(USm3DPKd*D~B=k@&YW%T*egN?6@ZhWnO_=NQ1;~m?| zm0CvAn!ScR>h-q>JHdTmhPUlu3r`!}Dpgo3xaAx?&Nqne)-8sc*D_9+gUGJ=%|_iq zW!92%&6Z)Y`5T=>hY=Z4qD*CP%Acg7C`OaunV1sgn4FMr%TIU@^?=vM`2zqc3L*uG zA}08#6!1oXLYxL=3P~EAqM>ODcPQMYFhk)Ug>SqfN8vjf)~K*fg{M^bo(db(IpaCv zjP}jrEjTs@oR>p`7d#>nK?Hk(CmaWWh6KC-KndW;chr{{BJhIv4}THt(vCWF_HpLy zZ?tyFCkPSGCkTVL>n}=0nu82vxP9&goPF#D@}9qTJb=_--n*1Kf4}F2ZV1iEyzhL+ z2R%QG{;i_F95~+zI!FYOw1b2kWCEqbF6r1%I*#mFM>>IKW<**>f6gVHL}cD2J&Dp0 zm-N(7`WCY9JJPq&To{p_M*o3J`VJzCF6lHPOE$9fa6p>DY@93Gym3cOgSM zWpQtU>&;3i?_KPujM&mKwHQ~63AHGz#iUvk@OJ>y&Q;+ro^?|g@+>yh@9D9dAOF!1 J@-MQk$=uTtBDnwn