mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 23:07:35 +00:00
LibJS: Implement (mostly) String.prototype.match
JavaScript has a couple of different ways to run a regular expression on a string. This adds support for one more. :^)
This commit is contained in:
parent
2c24c0e451
commit
1db943e146
7 changed files with 91 additions and 1 deletions
|
@ -168,4 +168,30 @@ JS_DEFINE_NATIVE_SETTER(RegExpObject::set_last_index)
|
|||
regexp_object->regex().start_offset = index;
|
||||
}
|
||||
|
||||
RegExpObject* regexp_create(GlobalObject& global_object, Value pattern, Value flags)
|
||||
{
|
||||
// https://tc39.es/ecma262/#sec-regexpcreate
|
||||
String p;
|
||||
if (pattern.is_undefined()) {
|
||||
p = String::empty();
|
||||
} else {
|
||||
p = pattern.to_string(global_object);
|
||||
if (p.is_null())
|
||||
return nullptr;
|
||||
}
|
||||
String f;
|
||||
if (flags.is_empty()) {
|
||||
f = String::empty();
|
||||
} else {
|
||||
f = flags.to_string(global_object);
|
||||
if (f.is_null())
|
||||
return nullptr;
|
||||
}
|
||||
// FIXME: This is awkward: the RegExpObject C++ constructor may throw a VM exception.
|
||||
auto* obj = RegExpObject::create(global_object, move(p), move(f));
|
||||
if (global_object.vm().exception())
|
||||
return nullptr;
|
||||
return obj;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ struct Flags {
|
|||
|
||||
namespace JS {
|
||||
|
||||
RegExpObject* regexp_create(GlobalObject&, Value pattern, Value flags);
|
||||
|
||||
class RegExpObject : public Object {
|
||||
JS_OBJECT(RegExpObject, Object);
|
||||
|
||||
|
|
|
@ -49,6 +49,8 @@ void RegExpPrototype::initialize(GlobalObject& global_object)
|
|||
define_native_function(vm.names.test, test, 1, attr);
|
||||
define_native_function(vm.names.exec, exec, 1, attr);
|
||||
|
||||
define_native_function(vm.well_known_symbol_match(), symbol_match, 1, attr);
|
||||
|
||||
u8 readable_attr = Attribute::Configurable;
|
||||
define_native_property(vm.names.flags, flags, {}, readable_attr);
|
||||
define_native_property(vm.names.source, source, {}, readable_attr);
|
||||
|
@ -254,4 +256,26 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
|
|||
return js_string(vm, String::formatted("/{}/{}", pattern, flags));
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
|
||||
{
|
||||
// https://tc39.es/ecma262/#sec-regexp.prototype-@@match
|
||||
auto* rx = this_object_from(vm, global_object);
|
||||
if (!rx)
|
||||
return {};
|
||||
auto string = vm.argument(0);
|
||||
auto s = string.to_string(global_object);
|
||||
auto global_value = rx->get(vm.names.global);
|
||||
if (global_value.is_empty())
|
||||
return {};
|
||||
bool global = global_value.to_boolean();
|
||||
auto* exec = get_method(global_object, rx, vm.names.exec);
|
||||
if (!exec)
|
||||
return {};
|
||||
if (!global)
|
||||
return vm.call(*exec, rx, string);
|
||||
|
||||
// FIXME: This should exec the RegExp repeatedly while updating "lastIndex"
|
||||
return vm.call(*exec, rx, string);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(exec);
|
||||
JS_DECLARE_NATIVE_FUNCTION(test);
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
JS_DECLARE_NATIVE_FUNCTION(symbol_match);
|
||||
|
||||
#define __JS_ENUMERATE(_, flag_name, ...) \
|
||||
JS_DECLARE_NATIVE_GETTER(flag_name);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
|
@ -33,6 +33,7 @@
|
|||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
#include <LibJS/Runtime/RegExpObject.h>
|
||||
#include <LibJS/Runtime/StringIterator.h>
|
||||
#include <LibJS/Runtime/StringObject.h>
|
||||
#include <LibJS/Runtime/StringPrototype.h>
|
||||
|
@ -106,6 +107,7 @@ void StringPrototype::initialize(GlobalObject& global_object)
|
|||
define_native_function(vm.names.split, split, 2, attr);
|
||||
define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr);
|
||||
define_native_function(vm.names.at, at, 1, attr);
|
||||
define_native_function(vm.names.match, match, 1, attr);
|
||||
define_native_function(vm.well_known_symbol_iterator(), symbol_iterator, 0, attr);
|
||||
}
|
||||
|
||||
|
@ -651,4 +653,32 @@ JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
|
|||
return StringIterator::create(global_object, string);
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_FUNCTION(StringPrototype::match)
|
||||
{
|
||||
// https://tc39.es/ecma262/#sec-string.prototype.match
|
||||
auto this_object = vm.this_value(global_object);
|
||||
if (this_object.is_nullish()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::ToObjectNullOrUndefined);
|
||||
return {};
|
||||
}
|
||||
auto regexp = vm.argument(0);
|
||||
if (!regexp.is_nullish()) {
|
||||
if (auto* matcher = get_method(global_object, regexp, vm.well_known_symbol_match()))
|
||||
return vm.call(*matcher, regexp, this_object);
|
||||
}
|
||||
auto s = this_object.to_primitive_string(global_object);
|
||||
if (!s)
|
||||
return {};
|
||||
auto regexp_string = regexp.to_string(global_object);
|
||||
if (regexp_string.is_null())
|
||||
return {};
|
||||
auto rx = regexp_create(global_object, regexp, js_undefined());
|
||||
if (!rx)
|
||||
return {};
|
||||
auto* matcher = get_method(global_object, rx, vm.well_known_symbol_match());
|
||||
if (!matcher)
|
||||
return {};
|
||||
return vm.call(*matcher, rx, this_object);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,6 +63,7 @@ private:
|
|||
JS_DECLARE_NATIVE_FUNCTION(split);
|
||||
JS_DECLARE_NATIVE_FUNCTION(last_index_of);
|
||||
JS_DECLARE_NATIVE_FUNCTION(at);
|
||||
JS_DECLARE_NATIVE_FUNCTION(match);
|
||||
|
||||
JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
|
||||
};
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
test("basic functionality", () => {
|
||||
expect(String.prototype.match).toHaveLength(1);
|
||||
|
||||
expect("hello friends".match(/hello/)).not.toBeNull();
|
||||
expect("hello friends".match(/enemies/)).toBeNull();
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue