avm2: Implement String.length

This commit is contained in:
paq 2021-02-06 18:55:28 +09:00 committed by Mike Welsh
parent 4a87d707f0
commit 2ec21bdd07
6 changed files with 66 additions and 4 deletions

View File

@ -1,12 +1,12 @@
//! `String` impl //! `String` impl
use crate::avm2::activation::Activation; use crate::avm2::class::{Class, ClassAttributes};
use crate::avm2::class::Class;
use crate::avm2::method::Method; use crate::avm2::method::Method;
use crate::avm2::names::{Namespace, QName}; use crate::avm2::names::{Namespace, QName};
use crate::avm2::object::{Object, TObject}; use crate::avm2::object::{Object, TObject};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::Error; use crate::avm2::Error;
use crate::avm2::{activation::Activation, traits::Trait};
use gc_arena::{GcCell, MutationContext}; use gc_arena::{GcCell, MutationContext};
/// Implements `String`'s instance initializer. /// Implements `String`'s instance initializer.
@ -39,13 +39,38 @@ pub fn class_init<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
/// Implements `length` property's getter
fn length<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, 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. /// Construct `String`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
Class::new( let class = Class::new(
QName::new(Namespace::public(), "String"), QName::new(Namespace::public(), "String"),
Some(QName::new(Namespace::public(), "Object").into()), Some(QName::new(Namespace::public(), "Object").into()),
Method::from_builtin(instance_init), Method::from_builtin(instance_init),
Method::from_builtin(class_init), Method::from_builtin(class_init),
mc, 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
} }

View File

@ -505,6 +505,7 @@ swf_tests! {
(as3_movieclip_dispatchevent_target, "avm2/movieclip_dispatchevent_target", 1), (as3_movieclip_dispatchevent_target, "avm2/movieclip_dispatchevent_target", 1),
(as3_movieclip_dispatchevent_selfadd, "avm2/movieclip_dispatchevent_selfadd", 1), (as3_movieclip_dispatchevent_selfadd, "avm2/movieclip_dispatchevent_selfadd", 1),
(as3_string_constr, "avm2/string_constr", 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. // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.

View File

@ -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);

View File

@ -0,0 +1,16 @@
//"".length;
0
//"\n\r".length;
2
//"\t".length;
1
//"abc012aáâ".length;
9
//"你好こんにちは".length;
7
//"مَرحَبًا".length;
8
//"😀".length;
2
//"👨‍👨‍👧‍👦".length;
11

Binary file not shown.

Binary file not shown.