From 8fcdc57ae19d9c3e74e83239b7404ffc630a5af6 Mon Sep 17 00:00:00 2001 From: Timothy Flynn Date: Tue, 6 Jul 2021 09:06:49 -0400 Subject: [PATCH] LibJS: Coerce named captures to an object before calling GetSubstitution Per the spec, before invoking the GetSubstitution abstraction, the named capture groups (if not undefined) should be coerced to an object via the ToObject abstraction. --- .../Libraries/LibJS/Runtime/RegExpPrototype.cpp | 9 ++++++++- .../builtins/String/String.prototype.replace.js | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp index 29d20760d06..14e7880547e 100644 --- a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp @@ -399,7 +399,14 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace) if (vm.exception()) return {}; } else { - replacement = get_substitution(global_object, matched, string, position, captures, named_captures, replace_value); + auto named_captures_object = js_undefined(); + if (!named_captures.is_undefined()) { + named_captures_object = named_captures.to_object(global_object); + if (vm.exception()) + return {}; + } + + replacement = get_substitution(global_object, matched, string, position, captures, named_captures_object, replace_value); if (vm.exception()) return {}; } diff --git a/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replace.js b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replace.js index a570a68ad0b..16d481c9f89 100644 --- a/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replace.js +++ b/Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replace.js @@ -139,3 +139,19 @@ test("replacement with substitution", () => { expect("abc".replace(/(?a)b(?c)/, "$")).toBe("c"); expect("abc".replace(/(?a)b(?c)/, "$b$")).toBe("cba"); }); + +test("replacement with substitution and 'groups' coerced to an object", () => { + var r = /./; + var coercibleValue = { + length: 1, + 0: "b", + index: 1, + groups: "123", + }; + + r.exec = function () { + return coercibleValue; + }; + + expect(r[Symbol.replace]("ab", "[$]")).toBe("a[3]"); +});