Browse Source

LibJS: Implement RegExp.prototype.match with RegExpExec abstraction

Timothy Flynn 4 years ago
parent
commit
b6b5adb47d

+ 11 - 9
Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp

@@ -302,7 +302,7 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
 // 22.2.5.7 RegExp.prototype [ @@match ] ( string ), https://tc39.es/ecma262/#sec-regexp.prototype-@@match
 // 22.2.5.7 RegExp.prototype [ @@match ] ( string ), https://tc39.es/ecma262/#sec-regexp.prototype-@@match
 JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
 JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
 {
 {
-    auto* rx = this_object_from(vm, global_object);
+    auto* rx = regexp_object_from(vm, global_object);
     if (!rx)
     if (!rx)
         return {};
         return {};
     auto s = vm.argument(0).to_string(global_object);
     auto s = vm.argument(0).to_string(global_object);
@@ -312,16 +312,18 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
     if (vm.exception())
     if (vm.exception())
         return {};
         return {};
     bool global = global_value.to_boolean();
     bool global = global_value.to_boolean();
-    // FIXME: Implement and use RegExpExec, this does something different - https://tc39.es/ecma262/#sec-regexpexec
-    auto* exec = Value(rx).get_method(global_object, vm.names.exec);
-    if (!exec)
-        return js_undefined();
-    // FIXME end
-    if (!global)
-        return vm.call(*exec, rx, js_string(vm, s));
+    if (!global) {
+        auto result = regexp_exec(global_object, *rx, s);
+        if (vm.exception())
+            return {};
+        return result;
+    }
 
 
     // FIXME: This should exec the RegExp repeatedly while updating "lastIndex"
     // FIXME: This should exec the RegExp repeatedly while updating "lastIndex"
-    return vm.call(*exec, rx, js_string(vm, s));
+    auto result = regexp_exec(global_object, *rx, s);
+    if (vm.exception())
+        return {};
+    return result;
 }
 }
 
 
 // 22.2.5.10 RegExp.prototype [ @@replace ] ( string, replaceValue ), https://tc39.es/ecma262/#sec-regexp.prototype-@@replace
 // 22.2.5.10 RegExp.prototype [ @@replace ] ( string, replaceValue ), https://tc39.es/ecma262/#sec-regexp.prototype-@@replace

+ 35 - 0
Userland/Libraries/LibJS/Tests/builtins/String/String.prototype.match.js

@@ -4,3 +4,38 @@ test("basic functionality", () => {
     expect("hello friends".match(/hello/)).not.toBeNull();
     expect("hello friends".match(/hello/)).not.toBeNull();
     expect("hello friends".match(/enemies/)).toBeNull();
     expect("hello friends".match(/enemies/)).toBeNull();
 });
 });
+
+test("override exec with function", () => {
+    let calls = 0;
+
+    let re = /test/;
+    let oldExec = re.exec.bind(re);
+    re.exec = function (...args) {
+        ++calls;
+        return oldExec(...args);
+    };
+
+    expect("test".match(re)).not.toBeNull();
+    expect(calls).toBe(1);
+});
+
+test("override exec with bad function", () => {
+    let calls = 0;
+
+    let re = /test/;
+    re.exec = function (...args) {
+        ++calls;
+        return 4;
+    };
+
+    expect(() => {
+        "test".match(re);
+    }).toThrow(TypeError);
+    expect(calls).toBe(1);
+});
+
+test("override exec with non-function", () => {
+    let re = /test/;
+    re.exec = 3;
+    expect("test".match(re)).not.toBeNull();
+});