Skip to content

feat(ecmascript): Array and TypedArray prototype toLocaleString #745

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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::<T>(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)
Expand Down
49 changes: 1 addition & 48 deletions tests/expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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"
}
}
4 changes: 2 additions & 2 deletions tests/metrics.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"results": {
"crash": 2032,
"fail": 12143,
"pass": 33720,
"fail": 12096,
"pass": 33767,
"skip": 2695,
"timeout": 4,
"unresolved": 0
Expand Down
Loading