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 29d20760d0..14e7880547 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 a570a68ad0..16d481c9f8 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]"); +});