Преглед изворни кода

LibJS: Evaluate replacement value before searching source string

The String.prototype.replace spec requires evaluating the replacement
value (if it is not a function) before searching the source string.

Fixes 4 test262 tests.
Timothy Flynn пре 4 година
родитељ
комит
81fec49ac3

+ 10 - 0
Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp

@@ -286,6 +286,16 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_replace)
     if (vm.exception())
         return {};
 
+    if (!replace_value.is_function()) {
+        auto replace_string = replace_value.to_string(global_object);
+        if (vm.exception())
+            return {};
+
+        replace_value = js_string(vm, move(replace_string));
+        if (vm.exception())
+            return {};
+    }
+
     auto global_value = rx->get(vm.names.global);
     if (vm.exception())
         return {};

+ 21 - 0
Userland/Libraries/LibJS/Runtime/StringPrototype.cpp

@@ -800,6 +800,17 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace)
     auto search_string = search_value.to_string(global_object);
     if (vm.exception())
         return {};
+
+    if (!replace_value.is_function()) {
+        auto replace_string = replace_value.to_string(global_object);
+        if (vm.exception())
+            return {};
+
+        replace_value = js_string(vm, move(replace_string));
+        if (vm.exception())
+            return {};
+    }
+
     Optional<size_t> position = string.find(search_string);
     if (!position.has_value())
         return js_string(vm, string);
@@ -877,6 +888,16 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::replace_all)
     if (vm.exception())
         return {};
 
+    if (!replace_value.is_function()) {
+        auto replace_string = replace_value.to_string(global_object);
+        if (vm.exception())
+            return {};
+
+        replace_value = js_string(vm, move(replace_string));
+        if (vm.exception())
+            return {};
+    }
+
     Vector<size_t> match_positions;
     size_t advance_by = max(1u, search_string.length());
     auto position = string.find(search_string);

+ 18 - 0
Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replace.js

@@ -158,3 +158,21 @@ test("replacement with substitution and 'groups' coerced to an object", () => {
 
     expect(r[Symbol.replace]("ab", "[$<length>]")).toBe("a[3]");
 });
+
+test("replacement value is evaluated before searching the source string", () => {
+    var calls = 0;
+    var replaceValue = {
+        toString: function () {
+            calls += 1;
+            return "b";
+        },
+    };
+
+    var newString = "".replace("a", replaceValue);
+    expect(newString).toBe("");
+    expect(calls).toBe(1);
+
+    newString = "".replace(/a/g, replaceValue);
+    expect(newString).toBe("");
+    expect(calls).toBe(2);
+});

+ 18 - 0
Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.replaceAll.js

@@ -104,3 +104,21 @@ test("functional regex replacement", () => {
         })
     ).toBe("xd");
 });
+
+test("replacement value is evaluated before searching the source string", () => {
+    var calls = 0;
+    var replaceValue = {
+        toString: function () {
+            calls += 1;
+            return "b";
+        },
+    };
+
+    var newString = "".replaceAll("a", replaceValue);
+    expect(newString).toBe("");
+    expect(calls).toBe(1);
+
+    newString = "".replaceAll(/a/g, replaceValue);
+    expect(newString).toBe("");
+    expect(calls).toBe(2);
+});