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

LibJS: Implement String.prototype.toWellFormed

This commit is contained in:
Timothy Flynn 2022-12-01 10:52:10 -05:00 committed by Linus Groh
parent 0bb46235a7
commit 3ee5217adc
4 changed files with 90 additions and 0 deletions

View file

@ -526,6 +526,7 @@ namespace JS {
P(toTimeString) \
P(toUpperCase) \
P(toUTCString) \
P(toWellFormed) \
P(toZonedDateTime) \
P(toZonedDateTimeISO) \
P(trace) \

View file

@ -171,6 +171,7 @@ void StringPrototype::initialize(Realm& realm)
define_native_function(realm, vm.names.toLowerCase, to_lowercase, 0, attr);
define_native_function(realm, vm.names.toString, to_string, 0, attr);
define_native_function(realm, vm.names.toUpperCase, to_uppercase, 0, attr);
define_native_function(realm, vm.names.toWellFormed, to_well_formed, 0, attr);
define_native_function(realm, vm.names.trim, trim, 0, attr);
define_native_function(realm, vm.names.trimEnd, trim_end, 0, attr);
define_native_function(realm, vm.names.trimStart, trim_start, 0, attr);
@ -980,6 +981,46 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_uppercase)
return js_string(vm, move(uppercase));
}
// 22.1.3.11 String.prototype.toWellFormed ( )
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_well_formed)
{
// 1. Let O be ? RequireObjectCoercible(this value).
// 2. Let S be ? ToString(O).
auto string = TRY(utf16_string_from(vm));
// 3. Let strLen be the length of S.
auto length = string.length_in_code_units();
// 4. Let k be 0.
size_t k = 0;
// 5. Let result be the empty String.
StringBuilder result;
// 6. Repeat, while k < strLen,
while (k < length) {
// a. Let cp be CodePointAt(S, k).
auto code_point = JS::code_point_at(string.view(), k);
// b. If cp.[[IsUnpairedSurrogate]] is true, then
if (code_point.is_unpaired_surrogate) {
// i. Set result to the string-concatenation of result and 0xFFFD (REPLACEMENT CHARACTER).
result.append_code_point(0xfffd);
}
// c. Else,
else {
// i. Set result to the string-concatenation of result and UTF16EncodeCodePoint(cp.[[CodePoint]]).
result.append_code_point(code_point.code_point);
}
// d. Set k to k + cp.[[CodeUnitCount]].
k += code_point.code_unit_count;
}
// 7. Return result.
return js_string(vm, result.build());
}
ThrowCompletionOr<String> trim_string(VM& vm, Value input_value, TrimMode where)
{
// 1. Let str be ? RequireObjectCoercible(string).

View file

@ -59,6 +59,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(to_lowercase);
JS_DECLARE_NATIVE_FUNCTION(to_string);
JS_DECLARE_NATIVE_FUNCTION(to_uppercase);
JS_DECLARE_NATIVE_FUNCTION(to_well_formed);
JS_DECLARE_NATIVE_FUNCTION(trim);
JS_DECLARE_NATIVE_FUNCTION(trim_end);
JS_DECLARE_NATIVE_FUNCTION(trim_start);

View file

@ -0,0 +1,47 @@
describe("errors", () => {
test("called with value that cannot be converted to a string", () => {
expect(() => {
String.prototype.toWellFormed.call(Symbol.hasInstance);
}).toThrowWithMessage(TypeError, "Cannot convert symbol to string");
});
});
describe("basic functionality", () => {
test("ascii strings", () => {
expect("".toWellFormed()).toBe("");
expect("foo".toWellFormed()).toBe("foo");
expect("abcdefghi".toWellFormed()).toBe("abcdefghi");
});
test("valid UTF-16 strings", () => {
expect("😀".toWellFormed()).toBe("😀");
expect("\ud83d\ude00".toWellFormed()).toBe("\ud83d\ude00");
});
test("invalid UTF-16 strings", () => {
expect("😀".slice(0, 1).toWellFormed()).toBe("\ufffd");
expect("😀".slice(1, 2).toWellFormed()).toBe("\ufffd");
expect("\ud83d".toWellFormed()).toBe("\ufffd");
expect("\ude00".toWellFormed()).toBe("\ufffd");
expect("a\ud83d".toWellFormed()).toBe("a\ufffd");
expect("a\ude00".toWellFormed()).toBe("a\ufffd");
expect("\ud83da".toWellFormed()).toBe("\ufffda");
expect("\ude00a".toWellFormed()).toBe("\ufffda");
expect("a\ud83da".toWellFormed()).toBe("a\ufffda");
expect("a\ude00a".toWellFormed()).toBe("a\ufffda");
});
test("object converted to a string", () => {
let toStringCalled = false;
const obj = {
toString: function () {
toStringCalled = true;
return "toString";
},
};
expect(String.prototype.toWellFormed.call(obj)).toBe("toString");
expect(toStringCalled).toBeTrue();
});
});