diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 46bdf6910e..8edf12ba18 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -13,6 +13,7 @@ pub mod string_iterator; #[cfg(test)] mod tests; +use crate::property::DataDescriptor; use crate::{ builtins::{string::string_iterator::StringIterator, BuiltIn, RegExp}, object::{ConstructorBuilder, Object, ObjectData}, @@ -140,9 +141,11 @@ impl String { None => RcString::default(), }; - let length = string.encode_utf16().count(); - - this.set_field("length", Value::from(length as i32)); + let length = DataDescriptor::new( + Value::from(string.encode_utf16().count()), + Attribute::NON_ENUMERABLE, + ); + this.set_property("length", length); this.set_data(ObjectData::String(string.clone())); diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index 611d0ecac4..a4f9409e38 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -39,6 +39,20 @@ fn new_string_has_length() { assert_eq!(forward(&mut context, "a.length"), "4"); } +#[test] +fn new_string_has_length_not_enumerable() { + let mut context = Context::new(); + let init = r#" + let a = new String("1234"); + "#; + + forward(&mut context, init); + assert_eq!( + forward(&mut context, "a.propertyIsEnumerable('length')"), + "false" + ); +} + #[test] fn new_utf8_string_has_length() { let mut context = Context::new(); diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 69ac236ee0..dcc1a64384 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -664,7 +664,11 @@ impl Value { ObjectData::String(string.clone()), )); // Make sure the correct length is set on our new string object - object.set("length".into(), string.chars().count().into()); + object.insert_property( + PropertyKey::String("length".into()), + Value::from(string.encode_utf16().count()), + Attribute::NON_ENUMERABLE, + ); Ok(object) } Value::Symbol(ref symbol) => { diff --git a/boa/src/value/tests.rs b/boa/src/value/tests.rs index bf0afd9e46..8cafe5320f 100644 --- a/boa/src/value/tests.rs +++ b/boa/src/value/tests.rs @@ -251,6 +251,37 @@ fn to_string() { assert_eq!(f64_to_str(3e50), "3e+50"); } +#[test] +fn string_length_is_not_enumerable() { + let mut context = Context::new(); + + let object = Value::from("foo").to_object(&mut context).unwrap(); + let length_desc = object + .get_own_property(&PropertyKey::from("length")) + .unwrap(); + assert!(!length_desc.enumerable()); +} + +#[test] +fn string_length_is_in_utf16_codeunits() { + let mut context = Context::new(); + + // 😀 is one Unicode code point, but 2 UTF-16 code units + let object = Value::from("😀").to_object(&mut context).unwrap(); + let length_desc = object + .get_own_property(&PropertyKey::from("length")) + .unwrap(); + assert_eq!( + length_desc + .as_data_descriptor() + .unwrap() + .value() + .to_integer_or_infinity(&mut context) + .unwrap(), + IntegerOrInfinity::Integer(2) + ); +} + #[test] fn add_number_and_number() { let mut context = Context::new();