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

LibJS: Implement String.prototype.slice with UTF-16 code units

This also implements String.prototype.slice more closely to the spec
(such as handling indices equivalent to Infinity).
This commit is contained in:
Timothy Flynn 2021-07-19 14:57:07 -04:00 committed by Andreas Kling
parent eaa1360eee
commit 5ac964d841
2 changed files with 34 additions and 30 deletions

View file

@ -610,46 +610,40 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::includes)
// 22.1.3.20 String.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.slice // 22.1.3.20 String.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-string.prototype.slice
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
{ {
auto string = ak_string_from(vm, global_object); auto string = utf16_string_from(vm, global_object);
if (!string.has_value())
return {};
if (vm.argument_count() == 0)
return js_string(vm, *string);
// FIXME: index_start and index_end should index a UTF-16 code_point view of the string.
auto string_length = static_cast<i32>(string->length());
auto index_start = vm.argument(0).to_i32(global_object);
if (vm.exception()) if (vm.exception())
return {}; return {};
auto index_end = string_length;
auto negative_min_index = -(string_length - 1); Utf16View utf16_string_view { string };
if (index_start < negative_min_index) auto string_length = static_cast<double>(utf16_string_view.length_in_code_units());
index_start = 0;
else if (index_start < 0)
index_start = string_length + index_start;
if (vm.argument_count() >= 2) { auto int_start = vm.argument(0).to_integer_or_infinity(global_object);
index_end = vm.argument(1).to_i32(global_object); if (vm.exception())
return {};
if (Value(int_start).is_negative_infinity())
int_start = 0;
else if (int_start < 0)
int_start = max(string_length + int_start, 0);
else
int_start = min(int_start, string_length);
auto int_end = string_length;
if (!vm.argument(1).is_undefined()) {
int_end = vm.argument(1).to_integer_or_infinity(global_object);
if (vm.exception()) if (vm.exception())
return {}; return {};
if (Value(int_end).is_negative_infinity())
if (index_end < negative_min_index) int_end = 0;
return js_string(vm, String::empty()); else if (int_end < 0)
int_end = max(string_length + int_end, 0);
if (index_end > string_length) else
index_end = string_length; int_end = min(int_end, string_length);
else if (index_end < 0)
index_end = string_length + index_end;
} }
if (index_start >= index_end) if (int_start >= int_end)
return js_string(vm, String::empty()); return js_string(vm, String::empty());
auto part_length = index_end - index_start; return js_string(vm, utf16_string_view.substring_view(int_start, int_end - int_start));
auto string_part = string->substring(index_start, part_length);
return js_string(vm, string_part);
} }
// 22.1.3.21 String.prototype.split ( separator, limit ), https://tc39.es/ecma262/#sec-string.prototype.split // 22.1.3.21 String.prototype.split ( separator, limit ), https://tc39.es/ecma262/#sec-string.prototype.split

View file

@ -15,3 +15,13 @@ test("basic functionality", () => {
expect("hello friends".slice(1000)).toBe(""); expect("hello friends".slice(1000)).toBe("");
expect("hello friends".slice(-1000)).toBe("hello friends"); expect("hello friends".slice(-1000)).toBe("hello friends");
}); });
test("UTF-16", () => {
var s = "😀";
expect(s).toHaveLength(2);
expect(s.slice()).toBe("😀");
expect(s.slice(0)).toBe("😀");
expect(s.slice(1)).toBe("\ude00");
expect(s.slice(0, 1)).toBe("\ud83d");
expect(s.slice(0, 2)).toBe("😀");
});