avm2: Implement String.charAt
This commit is contained in:
parent
2ec21bdd07
commit
c7c025277f
|
@ -4,9 +4,11 @@ use crate::avm2::class::{Class, ClassAttributes};
|
||||||
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::string::AvmString;
|
||||||
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 crate::avm2::{activation::Activation, traits::Trait};
|
||||||
|
use crate::string_utils;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `String`'s instance initializer.
|
/// Implements `String`'s instance initializer.
|
||||||
|
@ -54,6 +56,36 @@ fn length<'gc>(
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements `String.charAt`
|
||||||
|
fn char_at<'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)? {
|
||||||
|
// 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.
|
/// 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>> {
|
||||||
let class = Class::new(
|
let class = Class::new(
|
||||||
|
@ -72,5 +104,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
||||||
Method::from_builtin(length),
|
Method::from_builtin(length),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
write.define_instance_trait(Trait::from_method(
|
||||||
|
QName::new(Namespace::as3_namespace(), "charAt"),
|
||||||
|
Method::from_builtin(char_at),
|
||||||
|
));
|
||||||
|
|
||||||
class
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -506,6 +506,7 @@ swf_tests! {
|
||||||
(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),
|
(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.
|
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||||
|
|
|
@ -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));
|
|
@ -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);
|
||||||
|
<EFBFBD>
|
||||||
|
//"".charAt(0);
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue