From c7c025277fd146c593969ae3382587d8b6020f3b Mon Sep 17 00:00:00 2001 From: paq <89paku@gmail.com> Date: Sat, 6 Feb 2021 19:50:08 +0900 Subject: [PATCH] avm2: Implement String.charAt --- core/src/avm2/globals/string.rs | 37 ++++++++++++++++++ core/tests/regression_tests.rs | 1 + core/tests/swfs/avm2/string_char_at/Test.as | 32 +++++++++++++++ .../tests/swfs/avm2/string_char_at/output.txt | 28 +++++++++++++ core/tests/swfs/avm2/string_char_at/test.fla | Bin 0 -> 4031 bytes core/tests/swfs/avm2/string_char_at/test.swf | Bin 0 -> 1288 bytes 6 files changed, 98 insertions(+) create mode 100644 core/tests/swfs/avm2/string_char_at/Test.as create mode 100644 core/tests/swfs/avm2/string_char_at/output.txt create mode 100644 core/tests/swfs/avm2/string_char_at/test.fla create mode 100644 core/tests/swfs/avm2/string_char_at/test.swf diff --git a/core/src/avm2/globals/string.rs b/core/src/avm2/globals/string.rs index 629061996..889069bb5 100644 --- a/core/src/avm2/globals/string.rs +++ b/core/src/avm2/globals/string.rs @@ -4,9 +4,11 @@ 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::string::AvmString; use crate::avm2::value::Value; use crate::avm2::Error; use crate::avm2::{activation::Activation, traits::Trait}; +use crate::string_utils; use gc_arena::{GcCell, MutationContext}; /// Implements `String`'s instance initializer. @@ -54,6 +56,36 @@ fn length<'gc>( Ok(Value::Undefined) } +/// Implements `String.charAt` +fn char_at<'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)? { + // This function takes Number, so if we use coerce_to_i32 instead of coerce_to_number, the value may overflow. + let n = args + .get(0) + .unwrap_or(&Value::Number(0.0)) + .coerce_to_number(activation)?; + if n < 0.0 { + return Ok("".into()); + } + + let index = if !n.is_nan() { n as usize } else { 0 }; + let ret = s + .encode_utf16() + .nth(index) + .map(|c| string_utils::utf16_code_unit_to_char(c).to_string()) + .unwrap_or_default(); + return Ok(AvmString::new(activation.context.gc_context, ret).into()); + } + } + + Ok(Value::Undefined) +} + /// Construct `String`'s class. pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { let class = Class::new( @@ -72,5 +104,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> Method::from_builtin(length), )); + write.define_instance_trait(Trait::from_method( + QName::new(Namespace::as3_namespace(), "charAt"), + Method::from_builtin(char_at), + )); + class } diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index 003a2ccff..0cfbafe18 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -506,6 +506,7 @@ swf_tests! { (as3_movieclip_dispatchevent_selfadd, "avm2/movieclip_dispatchevent_selfadd", 1), (as3_string_constr, "avm2/string_constr", 1), (as3_string_length, "avm2/string_length", 1), + (as3_string_char_at, "avm2/string_char_at", 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_char_at/Test.as b/core/tests/swfs/avm2/string_char_at/Test.as new file mode 100644 index 000000000..954f523ad --- /dev/null +++ b/core/tests/swfs/avm2/string_char_at/Test.as @@ -0,0 +1,32 @@ +package { + public class Test {} +} + +trace("//\"abcdefg\".charAt();") +trace("abcdefg".charAt()); +trace("//\"abcdefg\".charAt(1);") +trace("abcdefg".charAt(1)); +trace("//\"abcdefg\".charAt(1.1);") +trace("abcdefg".charAt(1.1)); +trace("//\"abcdefg\".charAt(1.5);") +trace("abcdefg".charAt(1.5)); +trace("//\"abcdefg\".charAt(7);") +trace("abcdefg".charAt(7)); +trace("//\"abcdefg\".charAt(-1);") +trace("abcdefg".charAt(-1)); +trace("//\"abcdefg\".charAt(NaN);") +trace("abcdefg".charAt(NaN)); +trace("//\"abcdefg\".charAt(1.79e+308);") +trace("abcdefg".charAt(1.79e+308)); +trace("//\"abcdefg\".charAt(Infinity);") +trace("abcdefg".charAt(Infinity)); +trace("//\"abcdefg\".charAt(-Infinity);") +trace("abcdefg".charAt(-Infinity)); +trace("//\"あいうえお\".charAt(1);") +trace("あいうえお".charAt(1)); +trace("//\"مَرحَبًا\".charAt(1);") +trace("مَرحَبًا".charAt(1)); +trace("//\"👨‍👨‍👧‍👦\".charAt(0);") +trace("👨‍👨‍👧‍👦".charAt(0)); +trace("//\"\".charAt(0);") +trace("".charAt(0)); diff --git a/core/tests/swfs/avm2/string_char_at/output.txt b/core/tests/swfs/avm2/string_char_at/output.txt new file mode 100644 index 000000000..c8832200c --- /dev/null +++ b/core/tests/swfs/avm2/string_char_at/output.txt @@ -0,0 +1,28 @@ +//"abcdefg".charAt(); +a +//"abcdefg".charAt(1); +b +//"abcdefg".charAt(1.1); +b +//"abcdefg".charAt(1.5); +b +//"abcdefg".charAt(7); + +//"abcdefg".charAt(-1); + +//"abcdefg".charAt(NaN); +a +//"abcdefg".charAt(1.79e+308); + +//"abcdefg".charAt(Infinity); + +//"abcdefg".charAt(-Infinity); + +//"あいうえお".charAt(1); +い +//"مَرحَبًا".charAt(1); +َ +//"👨‍👨‍👧‍👦".charAt(0); +� +//"".charAt(0); + diff --git a/core/tests/swfs/avm2/string_char_at/test.fla b/core/tests/swfs/avm2/string_char_at/test.fla new file mode 100644 index 0000000000000000000000000000000000000000..d382fbe3e8ab454910c617c4d2ff9d879207277d GIT binary patch literal 4031 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 yLJ++izmg3o*`Si`R