From 2ec21bdd07a3528250348c4bca2e6e55d0e15747 Mon Sep 17 00:00:00 2001 From: paq <89paku@gmail.com> Date: Sat, 6 Feb 2021 18:55:28 +0900 Subject: [PATCH] avm2: Implement String.length --- core/src/avm2/globals/string.rs | 33 +++++++++++++++--- core/tests/regression_tests.rs | 1 + core/tests/swfs/avm2/string_length/Test.as | 20 +++++++++++ core/tests/swfs/avm2/string_length/output.txt | 16 +++++++++ core/tests/swfs/avm2/string_length/test.fla | Bin 0 -> 4031 bytes core/tests/swfs/avm2/string_length/test.swf | Bin 0 -> 1154 bytes 6 files changed, 66 insertions(+), 4 deletions(-) create mode 100644 core/tests/swfs/avm2/string_length/Test.as create mode 100644 core/tests/swfs/avm2/string_length/output.txt create mode 100644 core/tests/swfs/avm2/string_length/test.fla create mode 100644 core/tests/swfs/avm2/string_length/test.swf diff --git a/core/src/avm2/globals/string.rs b/core/src/avm2/globals/string.rs index c235a6919..629061996 100644 --- a/core/src/avm2/globals/string.rs +++ b/core/src/avm2/globals/string.rs @@ -1,12 +1,12 @@ //! `String` impl -use crate::avm2::activation::Activation; -use crate::avm2::class::Class; +use crate::avm2::class::{Class, ClassAttributes}; use crate::avm2::method::Method; use crate::avm2::names::{Namespace, QName}; use crate::avm2::object::{Object, TObject}; use crate::avm2::value::Value; use crate::avm2::Error; +use crate::avm2::{activation::Activation, traits::Trait}; use gc_arena::{GcCell, MutationContext}; /// Implements `String`'s instance initializer. @@ -39,13 +39,38 @@ pub fn class_init<'gc>( Ok(Value::Undefined) } +/// Implements `length` property's getter +fn length<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(this) = this { + if let Value::String(s) = this.value_of(activation.context.gc_context)? { + return Ok(s.encode_utf16().count().into()); + } + } + + Ok(Value::Undefined) +} + /// Construct `String`'s class. pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { - Class::new( + let class = Class::new( QName::new(Namespace::public(), "String"), Some(QName::new(Namespace::public(), "Object").into()), Method::from_builtin(instance_init), Method::from_builtin(class_init), mc, - ) + ); + + let mut write = class.write(mc); + write.set_attributes(ClassAttributes::FINAL | ClassAttributes::SEALED); + + write.define_instance_trait(Trait::from_getter( + QName::new(Namespace::public(), "length"), + Method::from_builtin(length), + )); + + class } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index 61597be45..003a2ccff 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -505,6 +505,7 @@ swf_tests! { (as3_movieclip_dispatchevent_target, "avm2/movieclip_dispatchevent_target", 1), (as3_movieclip_dispatchevent_selfadd, "avm2/movieclip_dispatchevent_selfadd", 1), (as3_string_constr, "avm2/string_constr", 1), + (as3_string_length, "avm2/string_length", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/core/tests/swfs/avm2/string_length/Test.as b/core/tests/swfs/avm2/string_length/Test.as new file mode 100644 index 000000000..915700d8e --- /dev/null +++ b/core/tests/swfs/avm2/string_length/Test.as @@ -0,0 +1,20 @@ +package { + public class Test {} +} + +trace("//\"\".length;"); +trace("".length); +trace("//\"\\n\\r\".length;"); +trace("\n\r".length); +trace("//\"\\t\".length;"); +trace("\t".length); +trace("//\"abc012aáâ\".length;"); +trace("abc012aáâ".length); +trace("//\"你好こんにちは\".length;"); +trace("你好こんにちは".length); +trace("//\"مَرحَبًا\".length;"); +trace("مَرحَبًا".length); +trace("//\"😀\".length;"); +trace("😀".length); +trace("//\"👨‍👨‍👧‍👦\".length;"); +trace("👨‍👨‍👧‍👦".length); diff --git a/core/tests/swfs/avm2/string_length/output.txt b/core/tests/swfs/avm2/string_length/output.txt new file mode 100644 index 000000000..bac529ea4 --- /dev/null +++ b/core/tests/swfs/avm2/string_length/output.txt @@ -0,0 +1,16 @@ +//"".length; +0 +//"\n\r".length; +2 +//"\t".length; +1 +//"abc012aáâ".length; +9 +//"你好こんにちは".length; +7 +//"مَرحَبًا".length; +8 +//"😀".length; +2 +//"👨‍👨‍👧‍👦".length; +11 diff --git a/core/tests/swfs/avm2/string_length/test.fla b/core/tests/swfs/avm2/string_length/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..d382fbe3e8ab454910c617c4d2ff9d879207277d GIT binary patch literal 4031 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�enB{snK({K zVZ&%<&Ue0Z?wRwrceW9~fl%l%LUD}dBO*d*jyXCynvEee7)B_WNiNMMjYjE%`9;f^ zzjysZy*x9$yt;O8DuZ4`ec+GLYLOUZD1*?8o^8P<;0mLZE$$-eKCx)pevMIhZIpF2 zyR2%)eXT}x%^>METQ=1i$xQx-S0~HaVLdB_k4TYHC58AhOX{N%k?dd z%;{PqdTuSiHTtk-hpX1T1BtET0mT(NGFoE^lij0&yZylH2bQZ275lhZebtCTjf>9ks^7l<7=J+nHUo>G)+>B6R_Q_bM1 zsTPSpogNuUxp6#>3}|ql4Wa4|bm%?Ec*Lj-G4ohjsg(?|-%T%id4$ezyO`-qZGy@T(R(-PX6? zJZO7xwl;Rme*Nr^2Vb6wr;hlc?b`cVmw}VHr8?)9b@vXFRD4crH>svu-OljUVv$&u zR?u{f?wo80Vtq{O4Zoui)2y4;iLyluY6V?on-bGdb$3}AW<*TO>u42Huesl-==FlC zYoDo9s~bLttC5nXN)9#%4YX0JWY*mH4cIU+^-5c>SU4<@*4zsDb`5pJj&2PJ=$Icv;Z2PG6oaI*`WlEA~$$3XQCvmQB zP#6v|2hTb!8PGemNVP5EIQIQZkp9kLWbfF*wyU&QC1y}9l@{!YuNF# zDU+DyC*8g;=PH!{(%dFL&GEdXmV`);-!f$0rLX(2&^b@Ucuv+cr1As zDa=Y;ti*HDZBFuYLU>l-<^*nD;O+?Ag8Q3ikR$Dfg@;mcv*;7$nhYJAgwLuU-!r?*y`)m*f(C=^& zfCDz@0&v0Mx&aQ_pa;NESy~YB!{2|q3)NyQd7bK@Sg$Vxr3|;&b?i8!bS8emP`Hpa1{> literal 0 HcmV?d00001