From fd281cc7155aa0d4e760aaf92951980c4590e4fd Mon Sep 17 00:00:00 2001 From: Adrian Wielgosik Date: Wed, 3 Apr 2024 00:31:48 +0200 Subject: [PATCH] avm2: Add IS_QNAME flag to Multiname, improve XML name matching --- core/src/avm2/e4x.rs | 12 ++++--- core/src/avm2/multiname.rs | 25 +++++++++++--- .../avm2/xml_getdescendants_qname/output.txt | 21 ++++++++++++ .../avm2/xml_getdescendants_qname/test.as | 32 ++++++++++++++++++ .../avm2/xml_getdescendants_qname/test.swf | Bin 0 -> 878 bytes .../avm2/xml_getdescendants_qname/test.toml | 1 + 6 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 tests/tests/swfs/avm2/xml_getdescendants_qname/output.txt create mode 100644 tests/tests/swfs/avm2/xml_getdescendants_qname/test.as create mode 100644 tests/tests/swfs/avm2/xml_getdescendants_qname/test.swf create mode 100644 tests/tests/swfs/avm2/xml_getdescendants_qname/test.toml diff --git a/core/src/avm2/e4x.rs b/core/src/avm2/e4x.rs index fbcea4e6b..28c346d00 100644 --- a/core/src/avm2/e4x.rs +++ b/core/src/avm2/e4x.rs @@ -1055,14 +1055,18 @@ impl<'gc> E4XNode<'gc> { return false; } + // The Multiname is not a QName, so an any name matches everything. + // See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/Multiname.cpp#L59 + if name.is_any_name() && !name.is_qname() { + return true; + } + if !name.is_any_name() && self.local_name() != name.local_name() { return false; } - // The Multiname is not a QName, so an any name matches everything. - // See https://github.com/adobe/avmplus/blob/858d034a3bd3a54d9b70909386435cf4aec81d21/core/Multiname.cpp#L59 - if name.is_any_name() && name.namespace_set().len() > 1 { - return true; + if self.local_name().is_none() { + return false; } if name.is_any_namespace() { diff --git a/core/src/avm2/multiname.rs b/core/src/avm2/multiname.rs index 866c98ad0..9b91bfc70 100644 --- a/core/src/avm2/multiname.rs +++ b/core/src/avm2/multiname.rs @@ -68,6 +68,12 @@ bitflags! { const HAS_LAZY_NAME = 1 << 1; /// Whether this was a 'MultinameA' - used for XML attribute lookups const ATTRIBUTE = 1 << 2; + + /// Represents the XML concept of "qualified name". + /// This also distinguishes a QName(x, y) from Multiname(x, [y]) + /// Basically, marks multinames that come from multinames of kind `(RT)QName(L)(A)` + /// (and dynamically-generated multinames that are supposed to be equivalent to one). + const IS_QNAME = 1 << 3; } } @@ -121,6 +127,15 @@ impl<'gc> Multiname<'gc> { self.flags.set(MultinameFlags::ATTRIBUTE, is_attribute); } + #[inline(always)] + pub fn is_qname(&self) -> bool { + self.flags.contains(MultinameFlags::IS_QNAME) + } + + pub fn set_is_qname(&mut self, is_qname: bool) { + self.flags.set(MultinameFlags::IS_QNAME, is_qname); + } + /// Read a namespace set from the ABC constant pool, and return a list of /// copied namespaces. pub fn abc_namespace_set( @@ -175,7 +190,7 @@ impl<'gc> Multiname<'gc> { .pool_string_option(name.0, &mut context.borrow_gc())? .map(|v| v.into()), param: None, - flags: Default::default(), + flags: MultinameFlags::IS_QNAME, } } AbcMultiname::RTQName { name } | AbcMultiname::RTQNameA { name } => Self { @@ -184,13 +199,15 @@ impl<'gc> Multiname<'gc> { .pool_string_option(name.0, &mut context.borrow_gc())? .map(|v| v.into()), param: None, - flags: MultinameFlags::HAS_LAZY_NS, + flags: MultinameFlags::HAS_LAZY_NS | MultinameFlags::IS_QNAME, }, AbcMultiname::RTQNameL | AbcMultiname::RTQNameLA => Self { ns: NamespaceSet::multiple(vec![], mc), name: None, param: None, - flags: MultinameFlags::HAS_LAZY_NS | MultinameFlags::HAS_LAZY_NAME, + flags: MultinameFlags::HAS_LAZY_NS + | MultinameFlags::HAS_LAZY_NAME + | MultinameFlags::IS_QNAME, }, AbcMultiname::Multiname { namespace_set, @@ -287,7 +304,7 @@ impl<'gc> Multiname<'gc> { ns, name, param: self.param, - flags: self.flags & MultinameFlags::ATTRIBUTE, + flags: self.flags & (MultinameFlags::ATTRIBUTE | MultinameFlags::IS_QNAME), }) } diff --git a/tests/tests/swfs/avm2/xml_getdescendants_qname/output.txt b/tests/tests/swfs/avm2/xml_getdescendants_qname/output.txt new file mode 100644 index 000000000..12392a437 --- /dev/null +++ b/tests/tests/swfs/avm2/xml_getdescendants_qname/output.txt @@ -0,0 +1,21 @@ +getdescendants QName(null, null) +element +element + +getdescendants Multiname(null, [Namespace("")]) +text +element +text +element +text +text +text + +getdescendants Multiname(null, [Namespace(""), Namespace("x")]) +text +element +text +element +text +text +text diff --git a/tests/tests/swfs/avm2/xml_getdescendants_qname/test.as b/tests/tests/swfs/avm2/xml_getdescendants_qname/test.as new file mode 100644 index 000000000..0482a771f --- /dev/null +++ b/tests/tests/swfs/avm2/xml_getdescendants_qname/test.as @@ -0,0 +1,32 @@ +// compiled with mxmlc, with bytecode modifications in FFDEC + +import flash.utils.getQualifiedClassName; +import flash.utils.getTimer; + +class C{ + public function test(x){ + trace('getdescendants QName(null, null)') + var a = x; // note: put `getdescendants QName(null, null)` here + for each(var i in a) { trace(i.nodeKind()); } + trace(); + trace('getdescendants Multiname(null, [Namespace("")])') + var b = x; // note: put `getdescendants Multiname(null, [Namespace("")])` here + for each(var i in b) { trace(i.nodeKind()); } + trace(); + trace('getdescendants Multiname(null, [Namespace(""), Namespace("x")])') + var c = x; // note: put `getdescendants Multiname(null, [Namespace(""), Namespace("x")])` here + for each(var i in c) { trace(i.nodeKind()); } + } +} +XML.ignoreWhitespace = false; +var s = " x y "; +var x = new XML(s); +new C().test(x); + +package { + import flash.display.MovieClip; + public class Test extends MovieClip { + public function Test(){ + } + } +} diff --git a/tests/tests/swfs/avm2/xml_getdescendants_qname/test.swf b/tests/tests/swfs/avm2/xml_getdescendants_qname/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..c8da3a07783671b79fb3c9cc1e9ba2aaae841d3b GIT binary patch literal 878 zcmV-!1Cjh%S5qtN1pokD0{{SB001BW06YKujv4yj=Vl0?JwAo?b#KsqVKo~Xixy4a z1v37mOh!q!duw(1D_l1z3z0VgHxB9Y;ryOt+H4UaGwq-bc5tB0m@amJ@Zeci1_!QZnT84W^-kDu!b~qNjlRVSVp3_xhTH3JNC+2)1J*>=-p>|kv2*_BK)@TdzxW~s5 zRX~U5S-?);A+G44YL3M0*f=<)^i{biPBGOIR$lYO&{zPAusrYQ+MNi=aD#T4!$ zPkS*y6Mr~&jb2>K)qny&rD&(ESY3$E?lK{?1zUms&{I3Vb0P)`iuV~L@IwOVhR;E|5AGtNh#33F zpOVVPP#sH#Q1lr8duTOx2yJ9p_5)#*Y`*}Tg@K-%R8w+zWQlGC2-5h6veew?xBSK5 z9O7#)Gis+4GXn*`a=IMft20rLn^q6^d@~EZvV%}P>Jrr`v{2a1HYSzb8jN-r@d#># zUvFCP4I*AkaJJj?P!I+S1AclZAE~W;K_g9(BflLk=a;;RHttwMVE+EkXD_w^wU+bOUr2<_0h<5?+UjXIv{|eHbnXmF7 zX7+Bk2KB2F+a~qfP4pa<`Q>mhbBs&mD6dj&#d0d09p#L?XOu{?pD|FPyL&hAa2Zx} z{9s?A-$_*eYVLo@uZp%4+ygCX#6N>?CNm(}zMD6A4jVKKfSPcZv7m{Zuerfe$^rHR zdXb)HRVJ$X5?!y`a3~Jcj?whX*c$OEmu@s8p^$IEC&cKS2cTn-6FqMRtUr;$pB!RE z6qVZkAMKHX4ssKq_O@oLdxy+wHU2*>J`gKs`r_eO?*2MPcw#)}l*DrUoU@Ysmg|Jk82 E1w{(4vH$=8 literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm2/xml_getdescendants_qname/test.toml b/tests/tests/swfs/avm2/xml_getdescendants_qname/test.toml new file mode 100644 index 000000000..dbee897f5 --- /dev/null +++ b/tests/tests/swfs/avm2/xml_getdescendants_qname/test.toml @@ -0,0 +1 @@ +num_frames = 1