summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTimothy Flynn <trflynn89@pm.me>2021-12-17 10:39:53 -0500
committerLinus Groh <mail@linusgroh.de>2021-12-21 14:56:00 +0100
commit5eb4d2e96eb4dc093638cb61ddbef50f3444b440 (patch)
tree1eb9e229cd83e2fd2234ce62a41af4469dcd24e4
parent26294a2d273f1a65e328b5186e8d6f7fc550b59a (diff)
downloadserenity-5eb4d2e96eb4dc093638cb61ddbef50f3444b440.zip
LibJS: Add spec comments to RegExp.prototype [ @@match ]
In doing so, this fixes a few minor issues: 1. We were accessing the "unicode" and "lastIndex" properties out of order. This is somewhat frequently tested by test262, but not in this case. 2. We were doing a Value::to_object() followed by Object::get(), rather than just Value::get() as the spec dictates. 3. We were TRYing a step (CreateDataPropertyOrThrow) that should never fail, and should have been a MUST.
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp51
1 files changed, 42 insertions, 9 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp
index c5bf50f21b..2d4038e7dd 100644
--- a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp
+++ b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp
@@ -68,14 +68,19 @@ size_t advance_string_index(Utf16View const& string, size_t index, bool unicode)
return index + code_point.code_unit_count;
}
+// Non-standard abstraction around steps used by multiple prototypes.
static ThrowCompletionOr<void> increment_last_index(GlobalObject& global_object, Object& regexp_object, Utf16View const& string, bool unicode)
{
auto& vm = global_object.vm();
+ // Let thisIndex be ℝ(? ToLength(? Get(rx, "lastIndex"))).
auto last_index_value = TRY(regexp_object.get(vm.names.lastIndex));
auto last_index = TRY(last_index_value.to_length(global_object));
+ // Let nextIndex be AdvanceStringIndex(S, thisIndex, fullUnicode).
last_index = advance_string_index(string, last_index, unicode);
+
+ // Perform ? Set(rx, "lastIndex", 𝔽(nextIndex), true).
TRY(regexp_object.set(vm.names.lastIndex, Value(last_index), Object::ShouldThrowExceptions::Yes));
return {};
}
@@ -357,40 +362,68 @@ JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
// 22.2.5.7 RegExp.prototype [ @@match ] ( string ), https://tc39.es/ecma262/#sec-regexp.prototype-@@match
JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::symbol_match)
{
+ // 1. Let rx be the this value.
+ // 2. If Type(rx) is not Object, throw a TypeError exception.
auto* regexp_object = TRY(this_object(global_object));
+
+ // 3. Let S be ? ToString(string).
auto string = TRY(vm.argument(0).to_utf16_string(global_object));
+ // 4. Let global be ! ToBoolean(? Get(rx, "global")).
bool global = TRY(regexp_object->get(vm.names.global)).to_boolean();
- if (!global)
+ // 5. If global is false, then
+ if (!global) {
+ // a. Return ? RegExpExec(rx, S).
return TRY(regexp_exec(global_object, *regexp_object, move(string)));
+ }
+
+ // 6. Else,
+ // a. Assert: global is true.
+ // b. Let fullUnicode be ! ToBoolean(? Get(rx, "unicode")).
+ bool full_unicode = TRY(regexp_object->get(vm.names.unicode)).to_boolean();
+
+ // c. Perform ? Set(rx, "lastIndex", +0𝔽, true).
TRY(regexp_object->set(vm.names.lastIndex, Value(0), Object::ShouldThrowExceptions::Yes));
+ // d. Let A be ! ArrayCreate(0).
auto* array = MUST(Array::create(global_object, 0));
- bool unicode = TRY(regexp_object->get(vm.names.unicode)).to_boolean();
-
+ // e. Let n be 0.
size_t n = 0;
+ // f. Repeat,
while (true) {
+ // i. Let result be ? RegExpExec(rx, S).
auto result = TRY(regexp_exec(global_object, *regexp_object, string));
+ // ii. If result is null, then
if (result.is_null()) {
+ // 1. If n = 0, return null.
if (n == 0)
return js_null();
+
+ // 2. Return A.
return array;
}
- auto* result_object = TRY(result.to_object(global_object));
- auto match_object = TRY(result_object->get(0));
- auto match_str = TRY(match_object.to_string(global_object));
+ // iii. Else,
- TRY(array->create_data_property_or_throw(n, js_string(vm, match_str)));
+ // 1. Let matchStr be ? ToString(? Get(result, "0")).
+ auto match_value = TRY(result.get(global_object, 0));
+ auto match_str = TRY(match_value.to_string(global_object));
- if (match_str.is_empty())
- TRY(increment_last_index(global_object, *regexp_object, string.view(), unicode));
+ // 2. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(n)), matchStr).
+ MUST(array->create_data_property_or_throw(n, js_string(vm, match_str)));
+
+ // 3. If matchStr is the empty String, then
+ if (match_str.is_empty()) {
+ // Stepsp 3a-3c are implemented by increment_last_index.
+ TRY(increment_last_index(global_object, *regexp_object, string.view(), full_unicode));
+ }
+ // 4. Set n to n + 1.
++n;
}
}