diff --git a/Userland/Libraries/LibJS/Runtime/JSONObject.cpp b/Userland/Libraries/LibJS/Runtime/JSONObject.cpp index f1e014c9bb..f79e376cd8 100644 --- a/Userland/Libraries/LibJS/Runtime/JSONObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/JSONObject.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -354,7 +355,6 @@ ThrowCompletionOr JSONObject::serialize_json_array(GlobalObject& global_ // 25.5.2.2 QuoteJSONString ( value ), https://tc39.es/ecma262/#sec-quotejsonstring String JSONObject::quote_json_string(String string) { - // FIXME: Handle UTF16 StringBuilder builder; builder.append('"'); auto utf_view = Utf8View(string); @@ -382,7 +382,7 @@ String JSONObject::quote_json_string(String string) builder.append("\\\\"); break; default: - if (code_point < 0x20) { + if (code_point < 0x20 || Utf16View::is_high_surrogate(code_point) || Utf16View::is_low_surrogate(code_point)) { builder.appendff("\\u{:04x}", code_point); } else { builder.append_code_point(code_point); diff --git a/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.stringify.js b/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.stringify.js index e1630ee7a0..8e3325d87d 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.stringify.js +++ b/Userland/Libraries/LibJS/Tests/builtins/JSON/JSON.stringify.js @@ -63,6 +63,14 @@ describe("correct behavior", () => { o[sym] = "qux"; expect(JSON.stringify(o)).toBe('{"foo":"bar"}'); }); + + test("escape surrogate codepoints in strings", () => { + expect(JSON.stringify("\ud83d\ude04")).toBe('"😄"'); + expect(JSON.stringify("\ud83d")).toBe('"\\ud83d"'); + expect(JSON.stringify("\ude04")).toBe('"\\ude04"'); + expect(JSON.stringify("\ud83d\ud83d\ude04\ud83d\ude04\ude04")).toBe('"\\ud83d😄😄\\ude04"'); + expect(JSON.stringify("\ude04\ud83d\ude04\ud83d\ude04\ud83d")).toBe('"\\ude04😄😄\\ud83d"'); + }); }); describe("errors", () => {