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

LibJS: Implement normative change in String.prototype.substr

And add spec comments while we're in the neighborhood.
This commit is contained in:
davidot 2022-09-20 23:59:11 +02:00 committed by Linus Groh
parent 60a6bae53d
commit 446a10a1ac
2 changed files with 35 additions and 3 deletions

View file

@ -568,26 +568,40 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
// B.2.2.1 String.prototype.substr ( start, length ), https://tc39.es/ecma262/#sec-string.prototype.substr // B.2.2.1 String.prototype.substr ( start, length ), https://tc39.es/ecma262/#sec-string.prototype.substr
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr) JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr)
{ {
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
auto string = TRY(utf16_string_from(vm)); auto string = TRY(utf16_string_from(vm));
// 3. Let size be the length of S.
auto size = string.length_in_code_units(); auto size = string.length_in_code_units();
// 4. Let intStart be ? ToIntegerOrInfinity(start).
auto int_start = TRY(vm.argument(0).to_integer_or_infinity(vm)); auto int_start = TRY(vm.argument(0).to_integer_or_infinity(vm));
// 5. If intStart is -∞, set intStart to 0.
if (Value(int_start).is_negative_infinity()) if (Value(int_start).is_negative_infinity())
int_start = 0; int_start = 0;
// 6. Else if intStart < 0, set intStart to max(size + intStart, 0).
else if (int_start < 0) else if (int_start < 0)
int_start = max(size + int_start, 0); int_start = max(size + int_start, 0);
// 7. Else, set intStart to min(intStart, size).
else
int_start = min(int_start, size);
// 8. If length is undefined, let intLength be size; otherwise let intLength be ? ToIntegerOrInfinity(length).
auto length = vm.argument(1); auto length = vm.argument(1);
auto int_length = length.is_undefined() ? size : TRY(length.to_integer_or_infinity(vm)); auto int_length = length.is_undefined() ? size : TRY(length.to_integer_or_infinity(vm));
if (Value(int_start).is_positive_infinity() || (int_length <= 0) || Value(int_length).is_positive_infinity())
return js_string(vm, String::empty());
// 9. Set intLength to the result of clamping intLength between 0 and size.
int_length = clamp(int_length, 0, size);
// 10. Let intEnd be min(intStart + intLength, size).
auto int_end = min((i32)(int_start + int_length), size); auto int_end = min((i32)(int_start + int_length), size);
if (int_start >= int_end) if (int_start >= int_end)
return js_string(vm, String::empty()); return js_string(vm, String::empty());
// 11. Return the substring of S from intStart to intEnd.
return js_string(vm, string.substring_view(int_start, int_end - int_start)); return js_string(vm, string.substring_view(int_start, int_end - int_start));
} }

View file

@ -21,6 +21,24 @@ test("basic functionality", () => {
expect("hello friends".substr(-3, -5)).toBe(""); expect("hello friends".substr(-3, -5)).toBe("");
}); });
test("Non numeric values", () => {
expect("a".substr(0, Infinity)).toBe("a");
expect("a".substr(0, -Infinity)).toBe("");
expect("abc".substr(0, Infinity)).toBe("abc");
expect("abc".substr(0, -Infinity)).toBe("");
expect("abc".substr(Infinity, Infinity)).toBe("");
expect("abc".substr(Infinity)).toBe("");
expect("abc".substr(-Infinity)).toBe("abc");
expect("abc".substr(-Infinity, 1)).toBe("a");
expect("abc".substr(-Infinity, Infinity)).toBe("abc");
expect("abc".substr(NaN)).toBe("abc");
expect("abc".substr(NaN, NaN)).toBe("");
expect("abc".substr(0, NaN)).toBe("");
expect("abc".substr(NaN, Infinity)).toBe("abc");
expect("abc".substr(NaN, -Infinity)).toBe("");
});
test("UTF-16", () => { test("UTF-16", () => {
var s = "😀"; var s = "😀";
expect(s).toHaveLength(2); expect(s).toHaveLength(2);