diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs index 2ff2abd89..84634696e 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/array_objects/array_prototype.rs @@ -5,7 +5,7 @@ use core::cmp::Ordering; use crate::ecmascript::abstract_operations::operations_on_objects::{ - try_create_data_property_or_throw, try_length_of_array_like, + invoke, try_create_data_property_or_throw, try_length_of_array_like, }; use crate::ecmascript::abstract_operations::type_conversion::{ try_to_integer_or_infinity, try_to_string, @@ -3474,13 +3474,88 @@ impl ArrayPrototype { Ok(a.get(agent).into_value()) } + /// ### [23.1.3.32 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.tolocalestring) + /// An ECMAScript implementation that includes the ECMA-402 Internationalization + /// API must implement this method as specified in the ECMA-402 specification. + /// If an ECMAScript implementation does not include the ECMA-402 API the + /// following specification of this method is used. + /// + /// > #### Note 1 + /// > The first edition of ECMA-402 did not include a replacement specification + /// > for this method. The meanings of the optional parameters to this method + /// > are defined in the ECMA-402 specification; implementations that do not + /// > include ECMA-402 support must not use those parameter positions for + /// > anything else. + /// + /// > #### Note 2 + /// > This method converts the elements of the array to Strings using their + /// > toLocaleString methods, and then concatenates these Strings, separated + /// > by occurrences of an implementation-defined locale-sensitive separator + /// > String. This method is analogous to toString except that it is intended + /// > to yield a locale-sensitive result corresponding with conventions of + /// > the host environment's current locale. + /// + /// > #### Note 3 + /// > This method is intentionally generic; it does not require that its this + /// > value be an Array. Therefore it can be transferred to other kinds of + /// > objects for use as a method. fn to_locale_string<'gc>( agent: &mut Agent, - _this_value: Value, + this_value: Value, _: ArgumentsList, - gc: GcScope<'gc, '_>, + mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Err(agent.todo("Array.prototype.toLocaleString", gc.into_nogc())) + // 1. Let array be ? ToObject(this value). + let array = to_object(agent, this_value, gc.nogc()) + .unbind()? + .bind(gc.nogc()); + // 2. Let len be ? LengthOfArrayLike(array). + let array = array.scope(agent, gc.nogc()); + let len = length_of_array_like(agent, array.get(agent), gc.reborrow()).unbind()?; + // 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as ", "). + let separator = ", "; + // 4. Let R be the empty String. + let mut r = std::string::String::new(); + // 5. Let k be 0. + let mut k = 0; + // 6. Repeat, while k < len, + while k < len { + // a. If k > 0, set R to the string-concatenation of R and separator. + if k > 0 { + r.push_str(separator); + }; + // b. Let element be ? Get(array, ! ToString(𝔽(k))). + let element = get( + agent, + array.get(agent), + PropertyKey::Integer(k.try_into().unwrap()), + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + // c. If element is neither undefined nor null, then + if !element.is_undefined() && !element.is_null() { + // i. Let S be ? ToString(? Invoke(element, "toLocaleString")). + let argument = invoke( + agent, + element.unbind(), + BUILTIN_STRING_MEMORY.toLocaleString.into(), + None, + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + let s = to_string(agent, argument.unbind(), gc.reborrow()) + .unbind()? + .bind(gc.nogc()); + // ii. Set R to the string-concatenation of R and S. + r.push_str(s.as_str(agent)); + }; + // d. Set k to k + 1. + k += 1; + } + // 7. Return R. + Ok(Value::from_string(agent, r, gc.into_nogc()).into_value()) } fn to_reversed<'gc>( diff --git a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs index baa0bac85..7de98607f 100644 --- a/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs +++ b/nova_vm/src/ecmascript/builtins/indexed_collections/typed_array_objects/typed_array_intrinsic_object.rs @@ -10,8 +10,8 @@ use crate::{ abstract_operations::{ operations_on_iterator_objects::{get_iterator_from_method, iterator_to_list}, operations_on_objects::{ - call_function, get, get_method, length_of_array_like, set, throw_not_callable, - try_get, try_set, + call_function, get, get_method, invoke, length_of_array_like, set, + throw_not_callable, try_get, try_set, }, testing_and_comparison::{is_array, is_callable, is_constructor, same_value_zero}, type_conversion::{ @@ -2060,13 +2060,81 @@ impl TypedArrayPrototype { res.map(|v| v.into_value()) } + /// ### [23.2.3.31 %TypedArray%.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-%typedarray%.prototype.tolocalestring) + /// This is a distinct method that implements the same algorithm as Array.prototype.toLocaleString + /// as defined in 23.1.3.32 except that TypedArrayLength is called in place of performing a + /// [[Get]] of "length". The implementation of the algorithm may be optimized with the knowledge + /// that the this value has a fixed length when the underlying buffer is not resizable and whose + /// integer-indexed properties are not sparse. However, such optimization must not introduce any + /// observable changes in the specified behaviour of the algorithm. This method is not generic. + /// ValidateTypedArray is called with the this value and seq-cst as arguments prior to evaluating + /// the algorithm. If its result is an abrupt completion that exception is thrown instead of + /// evaluating the algorithm. + /// + /// > #### Note + /// > If the ECMAScript implementation includes the ECMA-402 Internationalization API this method is + /// > based upon the algorithm for Array.prototype.toLocaleString that is in the ECMA-402 specification. fn to_locale_string<'gc>( agent: &mut Agent, - _this_value: Value, + this_value: Value, _: ArgumentsList, - gc: GcScope<'gc, '_>, + mut gc: GcScope<'gc, '_>, ) -> JsResult<'gc, Value<'gc>> { - Err(agent.todo("TypedArray.prototype.toLocaleString", gc.into_nogc())) + // 1. Let array be ? ToObject(this value). + let ta_record = validate_typed_array(agent, this_value, Ordering::SeqCst, gc.nogc()) + .unbind()? + .bind(gc.nogc()); + let o = ta_record.object; + let o = o.scope(agent, gc.nogc()); + // 2. Let len be ? LengthOfArrayLike(array). + let len = with_typed_array_viewable!( + o.get(agent), + typed_array_length::(agent, &ta_record, gc.nogc()) + ) as i64; + // 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as ", "). + let separator = ", "; + // 4. Let R be the empty String. + let mut r = std::string::String::new(); + // 5. Let k be 0. + let mut k = 0; + // 6. Repeat, while k < len, + while k < len { + // a. If k > 0, set R to the string-concatenation of R and separator. + if k > 0 { + r.push_str(separator); + }; + // b. Let element be ? Get(array, ! ToString(𝔽(k))). + let element = get( + agent, + o.get(agent), + PropertyKey::Integer(k.try_into().unwrap()), + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + // c. If element is neither undefined nor null, then + if !element.is_undefined() && !element.is_null() { + // i. Let S be ? ToString(? Invoke(element, "toLocaleString")). + let argument = invoke( + agent, + element.unbind(), + BUILTIN_STRING_MEMORY.toLocaleString.into(), + None, + gc.reborrow(), + ) + .unbind()? + .bind(gc.nogc()); + let s = to_string(agent, argument.unbind(), gc.reborrow()) + .unbind()? + .bind(gc.nogc()); + // ii. Set R to the string-concatenation of R and S. + r.push_str(s.as_str(agent)); + }; + // d. Set k to k + 1. + k += 1; + } + // 7. Return R. + Ok(Value::from_string(agent, r, gc.into_nogc())) } /// ### [23.2.3.32 %TypedArray%.prototype.toReversed ( )](https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.tospliced) diff --git a/tests/expectations.json b/tests/expectations.json index 25089e4ae..3ac5f5503 100644 --- a/tests/expectations.json +++ b/tests/expectations.json @@ -173,7 +173,6 @@ "built-ins/Array/prototype/map/15.4.4.19-8-b-16.js": "FAIL", "built-ins/Array/prototype/map/15.4.4.19-8-c-i-19.js": "FAIL", "built-ins/Array/prototype/map/create-revoked-proxy.js": "FAIL", - "built-ins/Array/prototype/methods-called-as-functions.js": "FAIL", "built-ins/Array/prototype/reduce/15.4.4.21-1-12.js": "FAIL", "built-ins/Array/prototype/reduce/15.4.4.21-8-b-iii-1-20.js": "FAIL", "built-ins/Array/prototype/reduce/15.4.4.21-9-b-16.js": "FAIL", @@ -195,14 +194,6 @@ "built-ins/Array/prototype/some/15.4.4.17-7-c-iii-22.js": "FAIL", "built-ins/Array/prototype/sort/resizable-buffer-default-comparator.js": "FAIL", "built-ins/Array/prototype/splice/create-revoked-proxy.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/S15.4.4.3_A1_T1.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/S15.4.4.3_A3_T1.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/invoke-element-tolocalestring.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/primitive_this_value.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/primitive_this_value_getter.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/resizable-buffer.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/user-provided-tolocalestring-grow.js": "FAIL", - "built-ins/Array/prototype/toLocaleString/user-provided-tolocalestring-shrink.js": "FAIL", "built-ins/Array/prototype/toString/non-callable-join-string-tag.js": "FAIL", "built-ins/Array/prototype/unshift/set-length-array-is-frozen.js": "FAIL", "built-ins/Array/prototype/unshift/set-length-array-length-is-non-writable.js": "FAIL", @@ -8719,41 +8710,6 @@ "built-ins/TypedArray/prototype/set/typedarray-arg-target-byteoffset-internal.js": "FAIL", "built-ins/TypedArray/prototype/set/typedarray-arg-target-out-of-bounds.js": "FAIL", "built-ins/TypedArray/prototype/set/typedarray-arg-targetbuffer-detached-during-tointeger-offset-throws.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/calls-tolocalestring-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/calls-tostring-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/calls-valueof-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/detached-buffer.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/empty-instance-returns-empty-string.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/get-length-uses-internal-arraylength.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-firstelement-tolocalestring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-firstelement-tostring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-firstelement-valueof.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-nextelement-tolocalestring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-nextelement-tostring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-nextelement-valueof.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-abrupt-from-this-out-of-bounds.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/BigInt/return-result.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/calls-tolocalestring-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/calls-tostring-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/calls-valueof-from-each-value.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/detached-buffer.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/empty-instance-returns-empty-string.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/get-length-uses-internal-arraylength.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/invoked-as-func.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/invoked-as-method.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/resizable-buffer.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-tolocalestring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-tostring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-firstelement-valueof.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-tolocalestring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-tostring.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-nextelement-valueof.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-abrupt-from-this-out-of-bounds.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/return-result.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/this-is-not-object.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/this-is-not-typedarray-instance.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/user-provided-tolocalestring-grow.js": "FAIL", - "built-ins/TypedArray/prototype/toLocaleString/user-provided-tolocalestring-shrink.js": "FAIL", "built-ins/TypedArray/prototype/values/resizable-buffer-grow-mid-iteration.js": "FAIL", "built-ins/TypedArray/prototype/values/resizable-buffer-shrink-mid-iteration.js": "FAIL", "built-ins/TypedArray/prototype/values/resizable-buffer.js": "FAIL", @@ -13519,8 +13475,6 @@ "staging/sm/Array/sort-non-function.js": "FAIL", "staging/sm/Array/species.js": "FAIL", "staging/sm/Array/toLocaleString-01.js": "FAIL", - "staging/sm/Array/toLocaleString-nointl.js": "FAIL", - "staging/sm/Array/toLocaleString.js": "FAIL", "staging/sm/Array/toSpliced-dense.js": "FAIL", "staging/sm/Array/unscopables.js": "CRASH", "staging/sm/Array/values.js": "CRASH", @@ -13578,7 +13532,6 @@ "staging/sm/Function/has-instance-jitted.js": "FAIL", "staging/sm/Function/implicit-this-in-parameter-expression.js": "CRASH", "staging/sm/Function/invalid-parameter-list.js": "FAIL", - "staging/sm/Function/redefine-arguments-length.js": "FAIL", "staging/sm/Function/return-finally.js": "FAIL", "staging/sm/Function/strict-arguments.js": "FAIL", "staging/sm/Iterator/from/Iterator.from-descriptor.js": "FAIL", @@ -14192,4 +14145,4 @@ "staging/upsert/WeakMap/getOrInsertComputed/returns-value-if-key-is-present-symbol-key.js": "FAIL", "staging/upsert/WeakMap/getOrInsertComputed/this-not-object-throw.js": "FAIL", "staging/upsert/WeakMap/getOrInsertComputed/throw-if-key-cannot-be-held-weakly.js": "FAIL" -} \ No newline at end of file +} diff --git a/tests/metrics.json b/tests/metrics.json index 9da07aede..c6a25175b 100644 --- a/tests/metrics.json +++ b/tests/metrics.json @@ -1,8 +1,8 @@ { "results": { "crash": 2032, - "fail": 12143, - "pass": 33720, + "fail": 12096, + "pass": 33767, "skip": 2695, "timeout": 4, "unresolved": 0