1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 05:37:34 +00:00

LibJS: Implement ECMA-402 Array.prototype.toLocaleString

Turns out the only difference between our existing implementation and
the ECMA-402 implementation is we weren't passing the locales and
options list to each element.toLocaleString invocation.

This also adds spec comments to the definition.
This commit is contained in:
Timothy Flynn 2021-11-16 22:06:09 -05:00 committed by Linus Groh
parent c19c3205ff
commit 39ab1a8999
2 changed files with 41 additions and 8 deletions

View file

@ -388,9 +388,13 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_string)
return TRY(vm.call(join_function.as_function(), this_object)); return TRY(vm.call(join_function.as_function(), this_object));
} }
// 23.1.3.30 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-array.prototype.tolocalestring // 18.5.1 Array.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-array.prototype.tolocalestring
JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string) JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string)
{ {
auto locales = vm.argument(0);
auto options = vm.argument(1);
// 1. Let array be ? ToObject(this value).
auto* this_object = TRY(vm.this_value(global_object).to_object(global_object)); auto* this_object = TRY(vm.this_value(global_object).to_object(global_object));
if (s_array_join_seen_objects.contains(this_object)) if (s_array_join_seen_objects.contains(this_object))
@ -400,20 +404,41 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string)
s_array_join_seen_objects.remove(this_object); s_array_join_seen_objects.remove(this_object);
}; };
// 2. Let len be ? ToLength(? Get(array, "length")).
auto length = TRY(length_of_array_like(global_object, *this_object)); auto length = TRY(length_of_array_like(global_object, *this_object));
String separator = ","; // NOTE: This is implementation-specific. // 3. Let separator be the String value for the list-separator String appropriate for the host environment's current locale (this is derived in an implementation-defined way).
constexpr auto separator = ","sv;
// 4. Let R be the empty String.
StringBuilder builder; StringBuilder builder;
// 5. Let k be 0.
// 6. Repeat, while k < len,
for (size_t i = 0; i < length; ++i) { for (size_t i = 0; i < length; ++i) {
if (i > 0) // a. If k > 0, then
if (i > 0) {
// i. Set R to the string-concatenation of R and separator.
builder.append(separator); builder.append(separator);
}
// b. Let nextElement be ? Get(array, ! ToString(k)).
auto value = TRY(this_object->get(i)); auto value = TRY(this_object->get(i));
if (value.is_nullish())
continue; // c. If nextElement is not undefined or null, then
auto locale_string_result = TRY(value.invoke(global_object, vm.names.toLocaleString)); if (!value.is_nullish()) {
auto string = TRY(locale_string_result.to_string(global_object)); // i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)).
builder.append(string); auto locale_string_result = TRY(value.invoke(global_object, vm.names.toLocaleString, locales, options));
// ii. Set R to the string-concatenation of R and S.
auto string = TRY(locale_string_result.to_string(global_object));
builder.append(string);
}
// d. Increase k by 1.
} }
// 7. Return R.
return js_string(vm, builder.to_string()); return js_string(vm, builder.to_string());
} }

View file

@ -59,4 +59,12 @@ describe("normal behavior", () => {
// [ "foo", <circular>, [ 1, 2, <circular> ], [ "bar" ] ] // [ "foo", <circular>, [ 1, 2, <circular> ], [ "bar" ] ]
expect(a.toLocaleString()).toBe("foo,,1,2,,bar"); expect(a.toLocaleString()).toBe("foo,,1,2,,bar");
}); });
test("with options", () => {
expect([12, 34].toLocaleString("en")).toBe("12");
expect([12, 34].toLocaleString("ar")).toBe("\u0661\u0662,\u0663\u0664");
expect([0.234].toLocaleString("en", { style: "percent" })).toBe("23%");
expect([0.234].toLocaleString("ar", { style: "percent" })).toBe("\u0662\u0663\u066a\u061c");
});
}); });