diff --git a/BUILD.gn b/BUILD.gn index 009601ffcf..2f6450ea67 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -798,6 +798,8 @@ action("postmortem-metadata") { "src/objects/js-array.h", "src/objects/js-regexp-inl.h", "src/objects/js-regexp.h", + "src/objects/js-regexp-string-iterator-inl.h", + "src/objects/js-regexp-string-iterator.h", "src/objects/map.h", "src/objects/map-inl.h", "src/objects/script.h", @@ -1955,6 +1957,8 @@ v8_source_set("v8_base") { "src/objects/js-promise-inl.h", "src/objects/js-promise.h", "src/objects/js-regexp-inl.h", + "src/objects/js-regexp-string-iterator-inl.h", + "src/objects/js-regexp-string-iterator.h", "src/objects/js-regexp.h", "src/objects/literal-objects-inl.h", "src/objects/literal-objects.cc", diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 23c500f719..5ce3a05369 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -4205,6 +4205,64 @@ void Genesis::InitializeGlobal_harmony_array_flatten() { Builtins::kArrayPrototypeFlatMap, 1, false, DONT_ENUM); } +void Genesis::InitializeGlobal_harmony_string_matchall() { + if (!FLAG_harmony_string_matchall) return; + + { // String.prototype.matchAll + Handle string_fun(native_context()->string_function()); + Handle string_prototype( + JSObject::cast(string_fun->instance_prototype())); + + SimpleInstallFunction(string_prototype, "matchAll", + Builtins::kStringPrototypeMatchAll, 1, true); + } + + { // RegExp.prototype[@@matchAll] + Handle regexp_fun(native_context()->regexp_function()); + Handle regexp_prototype( + JSObject::cast(regexp_fun->instance_prototype())); + SimpleInstallFunction(regexp_prototype, factory()->match_all_symbol(), + "[Symbol.matchAll]", + Builtins::kRegExpPrototypeMatchAll, 1, true); + Handle regexp_prototype_map(regexp_prototype->map()); + Map::SetShouldBeFastPrototypeMap(regexp_prototype_map, true, isolate()); + native_context()->set_regexp_prototype_map(*regexp_prototype_map); + } + + { // --- R e g E x p S t r i n g I t e r a t o r --- + Handle iterator_prototype( + native_context()->initial_iterator_prototype()); + + Handle regexp_string_iterator_prototype = + factory()->NewJSObject(isolate()->object_function(), TENURED); + JSObject::ForceSetPrototype(regexp_string_iterator_prototype, + iterator_prototype); + + JSObject::AddProperty( + regexp_string_iterator_prototype, factory()->to_string_tag_symbol(), + factory()->NewStringFromAsciiChecked("RegExp String Iterator"), + static_cast(DONT_ENUM | READ_ONLY)); + + SimpleInstallFunction(regexp_string_iterator_prototype, "next", + Builtins::kRegExpStringIteratorPrototypeNext, 0, + true); + + Handle regexp_string_iterator_function = CreateFunction( + isolate(), factory()->NewStringFromAsciiChecked("RegExpStringIterator"), + JS_REGEXP_STRING_ITERATOR_TYPE, JSRegExpStringIterator::kSize, 0, + regexp_string_iterator_prototype, Builtins::kIllegal); + regexp_string_iterator_function->shared()->set_native(false); + native_context()->set_initial_regexp_string_iterator_prototype_map_index( + regexp_string_iterator_function->initial_map()); + } + + { // @@matchAll Symbol + Handle symbol_fun(native_context()->symbol_function()); + InstallConstant(isolate(), symbol_fun, "matchAll", + factory()->match_all_symbol()); + } +} + void Genesis::InitializeGlobal_harmony_promise_finally() { if (!FLAG_harmony_promise_finally) return; diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h index 9e9c87da51..9a3e541450 100644 --- a/src/builtins/builtins-definitions.h +++ b/src/builtins/builtins-definitions.h @@ -920,6 +920,8 @@ namespace internal { TFJ(RegExpPrototypeIgnoreCaseGetter, 0) \ /* ES #sec-regexp.prototype-@@match */ \ TFJ(RegExpPrototypeMatch, 1, kString) \ + /* https://tc39.github.io/proposal-string-matchall/ */ \ + TFJ(RegExpPrototypeMatchAll, 1, kString) \ /* ES #sec-get-regexp.prototype.multiline */ \ TFJ(RegExpPrototypeMultilineGetter, 0) \ /* ES #sec-regexp.prototype-@@search */ \ @@ -947,6 +949,10 @@ namespace internal { TFS(RegExpSearchFast, kReceiver, kPattern) \ TFS(RegExpSplit, kRegExp, kString, kLimit) \ \ + /* RegExp String Iterator */ \ + /* https://tc39.github.io/proposal-string-matchall/ */ \ + TFJ(RegExpStringIteratorPrototypeNext, 0) \ + \ /* Set */ \ TFJ(SetConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ TFJ(SetPrototypeHas, 1, kKey) \ @@ -1024,6 +1030,8 @@ namespace internal { TFJ(StringPrototypeLink, 1, kValue) \ /* ES6 #sec-string.prototype.match */ \ TFJ(StringPrototypeMatch, 1, kRegexp) \ + /* ES #sec-string.prototype.matchAll */ \ + TFJ(StringPrototypeMatchAll, 1, kRegexp) \ /* ES6 #sec-string.prototype.localecompare */ \ CPP(StringPrototypeLocaleCompare) \ /* ES6 #sec-string.prototype.padEnd */ \ diff --git a/src/builtins/builtins-regexp-gen.cc b/src/builtins/builtins-regexp-gen.cc index bb3a78239b..70520d222b 100644 --- a/src/builtins/builtins-regexp-gen.cc +++ b/src/builtins/builtins-regexp-gen.cc @@ -12,6 +12,7 @@ #include "src/code-stub-assembler.h" #include "src/counters.h" #include "src/factory-inl.h" +#include "src/objects/js-regexp-string-iterator.h" #include "src/objects/js-regexp.h" #include "src/objects/regexp-match-info.h" #include "src/regexp/regexp-macro-assembler.h" @@ -20,6 +21,8 @@ namespace v8 { namespace internal { using compiler::Node; +template +using TNode = compiler::TNode; // ----------------------------------------------------------------------------- // ES6 section 21.2 RegExp Objects @@ -88,6 +91,28 @@ Node* RegExpBuiltinsAssembler::AllocateRegExpResult(Node* context, Node* length, return result; } +TNode RegExpBuiltinsAssembler::RegExpCreate( + TNode context, TNode native_context, + TNode maybe_string, TNode flags) { + TNode regexp_function = + CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); + TNode initial_map = CAST(LoadObjectField( + regexp_function, JSFunction::kPrototypeOrInitialMapOffset)); + return RegExpCreate(context, initial_map, maybe_string, flags); +} + +TNode RegExpBuiltinsAssembler::RegExpCreate(TNode context, + TNode initial_map, + TNode maybe_string, + TNode flags) { + TNode pattern = Select( + IsUndefined(maybe_string), [=] { return EmptyStringConstant(); }, + [=] { return ToString_Inline(context, maybe_string); }); + TNode regexp = CAST(AllocateJSObjectFromMap(initial_map)); + return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp, + pattern, flags); +} + Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) { // Load the in-object field. static const int field_offset = @@ -1949,6 +1974,163 @@ TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) { RegExpPrototypeMatchBody(context, receiver, string, false); } +TNode RegExpBuiltinsAssembler::MatchAllIterator( + TNode context, TNode native_context, + TNode maybe_regexp, TNode maybe_string, + char const* method_name) { + Label create_iterator(this), if_regexp(this), if_not_regexp(this), + throw_type_error(this, Label::kDeferred); + + // 1. Let S be ? ToString(O). + TNode string = ToString_Inline(context, maybe_string); + TVARIABLE(Object, var_matcher); + TVARIABLE(Int32T, var_global); + TVARIABLE(Int32T, var_unicode); + + // 2. If ? IsRegExp(R) is true, then + Branch(IsRegExp(context, maybe_regexp), &if_regexp, &if_not_regexp); + BIND(&if_regexp); + { + // a. Let C be ? SpeciesConstructor(R, %RegExp%). + TNode regexp_fun = + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); + TNode species_constructor = + CAST(SpeciesConstructor(native_context, maybe_regexp, regexp_fun)); + + // b. Let flags be ? ToString(? Get(R, "flags")). + // TODO(pwong): Add fast path to avoid property lookup. + TNode flags = GetProperty(context, maybe_regexp, + isolate()->factory()->flags_string()); + TNode flags_string = ToString_Inline(context, flags); + + // c. Let matcher be ? Construct(C, « R, flags »). + var_matcher = + CAST(ConstructJS(CodeFactory::Construct(isolate()), context, + species_constructor, maybe_regexp, flags_string)); + + // d. Let global be ? ToBoolean(? Get(matcher, "global")). + // TODO(pwong): Add fast path for loading flags. + var_global = UncheckedCast( + SlowFlagGetter(context, var_matcher.value(), JSRegExp::kGlobal)); + + // e. Let fullUnicode be ? ToBoolean(? Get(matcher, "unicode"). + // TODO(pwong): Add fast path for loading flags. + var_unicode = UncheckedCast( + SlowFlagGetter(context, var_matcher.value(), JSRegExp::kUnicode)); + + // f. Let lastIndex be ? ToLength(? Get(R, "lastIndex")). + // TODO(pwong): Add fast path for loading last index. + TNode last_index = UncheckedCast( + ToLength_Inline(context, SlowLoadLastIndex(context, maybe_regexp))); + + // g. Perform ? Set(matcher, "lastIndex", lastIndex, true). + // TODO(pwong): Add fast path for storing last index. + SlowStoreLastIndex(context, var_matcher.value(), last_index); + + Goto(&create_iterator); + } + // 3. Else, + BIND(&if_not_regexp); + { + // a. Let flags be "g". + // b. Let matcher be ? RegExpCreate(R, flags). + var_matcher = RegExpCreate(context, native_context, maybe_regexp, + StringConstant("g")); + + // c. If ? IsRegExp(matcher) is not true, throw a TypeError exception. + GotoIfNot(IsRegExp(context, var_matcher.value()), &throw_type_error); + + // d. Let global be true. + var_global = Int32Constant(1); + + // e. Let fullUnicode be false. + var_unicode = Int32Constant(0); + + // f. If ? Get(matcher, "lastIndex") is not 0, throw a TypeError exception. + TNode last_index = + CAST(LoadLastIndex(context, var_matcher.value(), false)); + Branch(SmiEqual(SmiConstant(0), last_index), &create_iterator, + &throw_type_error); + } + BIND(&throw_type_error); + { + ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver, + StringConstant(method_name), maybe_regexp); + } + // 4. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode). + // CreateRegExpStringIterator ( R, S, global, fullUnicode ) + BIND(&create_iterator); + { + TNode map = CAST(LoadContextElement( + native_context, + Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX)); + + // 4. Let iterator be ObjectCreate(%RegExpStringIteratorPrototype%, « + // [[IteratingRegExp]], [[IteratedString]], [[Global]], [[Unicode]], + // [[Done]] »). + TNode iterator = CAST(Allocate(JSRegExpStringIterator::kSize)); + StoreMapNoWriteBarrier(iterator, map); + StoreObjectFieldRoot(iterator, + JSRegExpStringIterator::kPropertiesOrHashOffset, + Heap::kEmptyFixedArrayRootIndex); + StoreObjectFieldRoot(iterator, JSRegExpStringIterator::kElementsOffset, + Heap::kEmptyFixedArrayRootIndex); + + // 5. Set iterator.[[IteratingRegExp]] to R. + StoreObjectFieldNoWriteBarrier( + iterator, JSRegExpStringIterator::kIteratingRegExpOffset, + var_matcher.value()); + + // 6. Set iterator.[[IteratedString]] to S. + StoreObjectFieldNoWriteBarrier( + iterator, JSRegExpStringIterator::kIteratedStringOffset, string); + +#ifdef DEBUG + // Verify global and unicode can be bitwise shifted without masking. + TNode zero = Int32Constant(0); + TNode one = Int32Constant(1); + CSA_ASSERT(this, Word32Or(Word32Equal(var_global.value(), zero), + Word32Equal(var_global.value(), one))); + CSA_ASSERT(this, Word32Or(Word32Equal(var_unicode.value(), zero), + Word32Equal(var_unicode.value(), one))); +#endif // DEBUG + + // 7. Set iterator.[[Global]] to global. + // 8. Set iterator.[[Unicode]] to fullUnicode. + // 9. Set iterator.[[Done]] to false. + TNode global_flag = Word32Shl( + var_global.value(), Int32Constant(JSRegExpStringIterator::kGlobalBit)); + TNode unicode_flag = + Word32Shl(var_unicode.value(), + Int32Constant(JSRegExpStringIterator::kUnicodeBit)); + TNode iterator_flags = Word32Or(global_flag, unicode_flag); + StoreObjectFieldNoWriteBarrier(iterator, + JSRegExpStringIterator::kFlagsOffset, + SmiFromInt32(Signed(iterator_flags))); + + return iterator; + } +} + +// https://tc39.github.io/proposal-string-matchall/ +// RegExp.prototype [ @@matchAll ] ( string ) +TF_BUILTIN(RegExpPrototypeMatchAll, RegExpBuiltinsAssembler) { + TNode context = CAST(Parameter(Descriptor::kContext)); + TNode native_context = LoadNativeContext(context); + TNode receiver = CAST(Parameter(Descriptor::kReceiver)); + TNode string = CAST(Parameter(Descriptor::kString)); + + // 1. Let R be the this value. + // 2. If Type(R) is not Object, throw a TypeError exception. + ThrowIfNotJSReceiver(context, receiver, + MessageTemplate::kIncompatibleMethodReceiver, + "RegExp.prototype.@@matchAll"); + + // 3. Return ? MatchAllIterator(R, string). + Return(MatchAllIterator(context, native_context, receiver, string, + "RegExp.prototype.@@matchAll")); +} + // Helper that skips a few initial checks. and assumes... // 1) receiver is a "fast" RegExp // 2) pattern is a string @@ -2886,5 +3068,174 @@ TF_BUILTIN(RegExpInternalMatch, RegExpBuiltinsAssembler) { } } +class RegExpStringIteratorAssembler : public RegExpBuiltinsAssembler { + public: + explicit RegExpStringIteratorAssembler(compiler::CodeAssemblerState* state) + : RegExpBuiltinsAssembler(state) {} + + protected: + TNode LoadFlags(TNode iterator) { + return LoadObjectField(iterator, JSRegExpStringIterator::kFlagsOffset); + } + + TNode HasDoneFlag(TNode flags) { + return UncheckedCast( + IsSetSmi(flags, 1 << JSRegExpStringIterator::kDoneBit)); + } + + TNode HasGlobalFlag(TNode flags) { + return UncheckedCast( + IsSetSmi(flags, 1 << JSRegExpStringIterator::kGlobalBit)); + } + + TNode HasUnicodeFlag(TNode flags) { + return UncheckedCast( + IsSetSmi(flags, 1 << JSRegExpStringIterator::kUnicodeBit)); + } + + void SetDoneFlag(TNode iterator, TNode flags) { + TNode new_flags = + SmiOr(flags, SmiConstant(1 << JSRegExpStringIterator::kDoneBit)); + StoreObjectFieldNoWriteBarrier( + iterator, JSRegExpStringIterator::kFlagsOffset, new_flags); + } +}; + +// https://tc39.github.io/proposal-string-matchall/ +// %RegExpStringIteratorPrototype%.next ( ) +TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) { + TNode context = CAST(Parameter(Descriptor::kContext)); + TNode maybe_receiver = CAST(Parameter(Descriptor::kReceiver)); + + Label if_match(this), if_no_match(this, Label::kDeferred), + return_empty_done_result(this, Label::kDeferred), + throw_bad_receiver(this, Label::kDeferred); + + // 1. Let O be the this value. + // 2. If Type(O) is not Object, throw a TypeError exception. + GotoIf(TaggedIsSmi(maybe_receiver), &throw_bad_receiver); + GotoIfNot(IsJSReceiver(maybe_receiver), &throw_bad_receiver); + TNode receiver = CAST(maybe_receiver); + + // 3. If O does not have all of the internal slots of a RegExp String Iterator + // Object Instance (see 5.3), throw a TypeError exception. + GotoIfNot(InstanceTypeEqual(LoadInstanceType(receiver), + JS_REGEXP_STRING_ITERATOR_TYPE), + &throw_bad_receiver); + + // 4. If O.[[Done]] is true, then + // a. Return ! CreateIterResultObject(undefined, true). + TNode flags = LoadFlags(receiver); + GotoIf(HasDoneFlag(flags), &return_empty_done_result); + + // 5. Let R be O.[[IteratingRegExp]]. + TNode iterating_regexp = + LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset); + + // 6. Let S be O.[[IteratedString]]. + TNode iterating_string = CAST( + LoadObjectField(receiver, JSRegExpStringIterator::kIteratedStringOffset)); + + // 7. Let global be O.[[Global]]. + // See if_match. + + // 8. Let fullUnicode be O.[[Unicode]]. + // See if_global. + + // 9. Let match be ? RegExpExec(R, S). + TVARIABLE(Object, var_match); + { + Label if_fast(this), if_slow(this), next(this); + BranchIfFastRegExp(context, iterating_regexp, &if_fast, &if_slow); + BIND(&if_fast); + { + var_match = CAST(RegExpPrototypeExecBody(context, iterating_regexp, + iterating_string, true)); + Goto(&next); + } + BIND(&if_slow); + { + var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string)); + Goto(&next); + } + BIND(&next); + } + + // 10. If match is null, then + Branch(IsNull(var_match.value()), &if_no_match, &if_match); + BIND(&if_no_match); + { + // a. Set O.[[Done]] to true. + SetDoneFlag(receiver, flags); + + // b. Return ! CreateIterResultObject(undefined, true). + Goto(&return_empty_done_result); + } + // 11. Else, + BIND(&if_match); + { + Label if_global(this), if_not_global(this, Label::kDeferred); + + // a. If global is true, + Branch(HasGlobalFlag(flags), &if_global, &if_not_global); + BIND(&if_global); + { + // i. Let matchStr be ? ToString(? Get(match, "0")). + // TODO(pwong): Add fast path for fast regexp results. See + // BranchIfFastRegExpResult(). + TNode match_str = ToString_Inline( + context, GetProperty(context, var_match.value(), + isolate()->factory()->zero_string())); + + // ii. If matchStr is the empty string, + { + Label next(this); + GotoIfNot(IsEmptyString(match_str), &next); + + // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")). + // TODO(pwong): Add fast path for loading last index. + TNode last_index = + CAST(SlowLoadLastIndex(context, iterating_regexp)); + TNode this_index = ToLength_Inline(context, last_index); + + // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode). + TNode next_index = CAST(AdvanceStringIndex( + iterating_string, this_index, HasUnicodeFlag(flags), false)); + + // 3. Perform ? Set(R, "lastIndex", nextIndex, true). + // TODO(pwong): Add fast path for storing last index. + SlowStoreLastIndex(context, iterating_regexp, next_index); + + Goto(&next); + BIND(&next); + } + + // iii. Return ! CreateIterResultObject(match, false). + Return(AllocateJSIteratorResult(context, var_match.value(), + FalseConstant())); + } + // b. Else, + BIND(&if_not_global); + { + // i. Set O.[[Done]] to true. + SetDoneFlag(receiver, flags); + + // ii. Return ! CreateIterResultObject(match, false). + Return(AllocateJSIteratorResult(context, var_match.value(), + FalseConstant())); + } + } + BIND(&return_empty_done_result); + Return( + AllocateJSIteratorResult(context, UndefinedConstant(), TrueConstant())); + + BIND(&throw_bad_receiver); + { + ThrowTypeError(context, MessageTemplate::kIncompatibleMethodReceiver, + StringConstant("%RegExpStringIterator%.prototype.next"), + receiver); + } +} + } // namespace internal } // namespace v8 diff --git a/src/builtins/builtins-regexp-gen.h b/src/builtins/builtins-regexp-gen.h index b57b90acf9..2146da5c0e 100644 --- a/src/builtins/builtins-regexp-gen.h +++ b/src/builtins/builtins-regexp-gen.h @@ -19,6 +19,19 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler { Node* const map, Label* const if_isunmodified, Label* const if_ismodified); + // Create and initialize a RegExp object. + TNode RegExpCreate(TNode context, + TNode native_context, + TNode regexp_string, TNode flags); + + TNode RegExpCreate(TNode context, TNode initial_map, + TNode regexp_string, TNode flags); + + TNode MatchAllIterator(TNode context, + TNode native_context, + TNode regexp, TNode string, + char const* method_name); + protected: // Allocate a RegExpResult with the given length (the number of captures, // including the match itself), index (the index where the match starts), diff --git a/src/builtins/builtins-string-gen.cc b/src/builtins/builtins-string-gen.cc index 7c9a265b5d..e50702b9ab 100644 --- a/src/builtins/builtins-string-gen.cc +++ b/src/builtins/builtins-string-gen.cc @@ -1024,7 +1024,7 @@ void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( Node* const context, Node* const object, Node* const maybe_string, Handle symbol, const NodeFunction0& regexp_call, - const NodeFunction1& generic_call, CodeStubArguments* args) { + const NodeFunction1& generic_call) { Label out(this); // Smis definitely don't have an attached symbol. @@ -1069,12 +1069,7 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( BIND(&stub_call); // TODO(jgruber): Add a no-JS scope once it exists. - Node* const result = regexp_call(); - if (args == nullptr) { - Return(result); - } else { - args->PopAndReturn(result); - } + regexp_call(); BIND(&slow_lookup); } @@ -1094,12 +1089,7 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( GotoIf(IsNull(maybe_func), &out); // Attempt to call the function. - Node* const result = generic_call(maybe_func); - if (args == nullptr) { - Return(result); - } else { - args->PopAndReturn(result); - } + generic_call(maybe_func); BIND(&out); } @@ -1294,12 +1284,12 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) { MaybeCallFunctionAtSymbol( context, search, receiver, isolate()->factory()->replace_symbol(), [=]() { - return CallBuiltin(Builtins::kRegExpReplace, context, search, receiver, - replace); + Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver, + replace)); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); - return CallJS(call_callable, context, fn, search, receiver, replace); + Return(CallJS(call_callable, context, fn, search, receiver, replace)); }); // Convert {receiver} and {search} to strings. @@ -1439,8 +1429,9 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler { protected: enum Variant { kMatch, kSearch }; - void Generate(Variant variant, const char* method_name, Node* const receiver, - Node* maybe_regexp, Node* const context) { + void Generate(Variant variant, const char* method_name, + TNode receiver, TNode maybe_regexp, + TNode context) { Label call_regexp_match_search(this); Builtins::Name builtin; @@ -1457,32 +1448,24 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler { MaybeCallFunctionAtSymbol( context, maybe_regexp, receiver, symbol, - [=] { return CallBuiltin(builtin, context, maybe_regexp, receiver); }, + [=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); - return CallJS(call_callable, context, fn, maybe_regexp, receiver); + Return(CallJS(call_callable, context, fn, maybe_regexp, receiver)); }); // maybe_regexp is not a RegExp nor has [@@match / @@search] property. { RegExpBuiltinsAssembler regexp_asm(state()); - Node* const receiver_string = ToString_Inline(context, receiver); - TNode const pattern = Select( - IsUndefined(maybe_regexp), [=] { return EmptyStringConstant(); }, - [=] { return ToString_Inline(context, maybe_regexp); }); - - // Create RegExp - // TODO(pwong): This could be factored out as a helper (RegExpCreate) that - // also does the "is fast" checks. - Node* const native_context = LoadNativeContext(context); - Node* const regexp_function = - LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX); - Node* const initial_map = LoadObjectField( - regexp_function, JSFunction::kPrototypeOrInitialMapOffset); - Node* const regexp = CallRuntime( - Runtime::kRegExpInitializeAndCompile, context, - AllocateJSObjectFromMap(initial_map), pattern, EmptyStringConstant()); + TNode receiver_string = ToString_Inline(context, receiver); + TNode native_context = LoadNativeContext(context); + TNode regexp_function = CAST( + LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX)); + TNode initial_map = CAST(LoadObjectField( + regexp_function, JSFunction::kPrototypeOrInitialMapOffset)); + TNode regexp = regexp_asm.RegExpCreate( + context, initial_map, maybe_regexp, EmptyStringConstant()); Label fast_path(this), slow_path(this); regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path, @@ -1493,7 +1476,7 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler { BIND(&slow_path); { - Node* const maybe_func = GetProperty(context, regexp, symbol); + TNode maybe_func = GetProperty(context, regexp, symbol); Callable call_callable = CodeFactory::Call(isolate()); Return(CallJS(call_callable, context, maybe_func, regexp, receiver_string)); @@ -1504,13 +1487,52 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler { // ES6 #sec-string.prototype.match TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) { - Node* const receiver = Parameter(Descriptor::kReceiver); - Node* const maybe_regexp = Parameter(Descriptor::kRegexp); - Node* const context = Parameter(Descriptor::kContext); + TNode receiver = CAST(Parameter(Descriptor::kReceiver)); + TNode maybe_regexp = CAST(Parameter(Descriptor::kRegexp)); + TNode context = CAST(Parameter(Descriptor::kContext)); Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context); } +// ES #sec-string.prototype.matchAll +TF_BUILTIN(StringPrototypeMatchAll, StringBuiltinsAssembler) { + char const* method_name = "String.prototype.matchAll"; + + TNode context = CAST(Parameter(Descriptor::kContext)); + TNode maybe_regexp = CAST(Parameter(Descriptor::kRegexp)); + TNode receiver = CAST(Parameter(Descriptor::kReceiver)); + TNode native_context = LoadNativeContext(context); + + // 1. Let O be ? RequireObjectCoercible(this value). + RequireObjectCoercible(context, receiver, method_name); + + // 2. If regexp is neither undefined nor null, then + Label return_match_all_iterator(this); + GotoIf(IsNullOrUndefined(maybe_regexp), &return_match_all_iterator); + { + // a. Let matcher be ? GetMethod(regexp, @@matchAll). + // b. If matcher is not undefined, then + // i. Return ? Call(matcher, regexp, « O »). + auto if_regexp_call = [&] { Goto(&return_match_all_iterator); }; + auto if_generic_call = [=](Node* fn) { + Callable call_callable = CodeFactory::Call(isolate()); + Return(CallJS(call_callable, context, fn, maybe_regexp, receiver)); + }; + MaybeCallFunctionAtSymbol(context, maybe_regexp, receiver, + isolate()->factory()->match_all_symbol(), + if_regexp_call, if_generic_call); + Goto(&return_match_all_iterator); + } + BIND(&return_match_all_iterator); + { + // 3. Return ? MatchAllIterator(regexp, O). + RegExpBuiltinsAssembler regexp_asm(state()); + TNode iterator = regexp_asm.MatchAllIterator( + context, native_context, maybe_regexp, receiver, method_name); + Return(iterator); + } +} + class StringPadAssembler : public StringBuiltinsAssembler { public: explicit StringPadAssembler(compiler::CodeAssemblerState* state) @@ -1639,9 +1661,9 @@ TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) { // ES6 #sec-string.prototype.search TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) { - Node* const receiver = Parameter(Descriptor::kReceiver); - Node* const maybe_regexp = Parameter(Descriptor::kRegexp); - Node* const context = Parameter(Descriptor::kContext); + TNode receiver = CAST(Parameter(Descriptor::kReceiver)); + TNode maybe_regexp = CAST(Parameter(Descriptor::kRegexp)); + TNode context = CAST(Parameter(Descriptor::kContext)); Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context); } @@ -1718,15 +1740,15 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { MaybeCallFunctionAtSymbol( context, separator, receiver, isolate()->factory()->split_symbol(), - [=]() { - return CallBuiltin(Builtins::kRegExpSplit, context, separator, receiver, - limit); + [&]() { + args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, + separator, receiver, limit)); }, - [=](Node* fn) { + [&](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); - return CallJS(call_callable, context, fn, separator, receiver, limit); - }, - &args); + args.PopAndReturn( + CallJS(call_callable, context, fn, separator, receiver, limit)); + }); // String and integer conversions. diff --git a/src/builtins/builtins-string-gen.h b/src/builtins/builtins-string-gen.h index 1bd5429fdb..4147b3fc0c 100644 --- a/src/builtins/builtins-string-gen.h +++ b/src/builtins/builtins-string-gen.h @@ -95,14 +95,13 @@ class StringBuiltinsAssembler : public CodeStubAssembler { // // Contains fast paths for Smi and RegExp objects. // Important: {regexp_call} may not contain any code that can call into JS. - typedef std::function NodeFunction0; - typedef std::function NodeFunction1; + typedef std::function NodeFunction0; + typedef std::function NodeFunction1; void MaybeCallFunctionAtSymbol(Node* const context, Node* const object, Node* const maybe_string, Handle symbol, const NodeFunction0& regexp_call, - const NodeFunction1& generic_call, - CodeStubArguments* args = nullptr); + const NodeFunction1& generic_call); }; class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler { diff --git a/src/compiler/code-assembler.h b/src/compiler/code-assembler.h index 5079c8f117..9d0e057855 100644 --- a/src/compiler/code-assembler.h +++ b/src/compiler/code-assembler.h @@ -29,6 +29,7 @@ class Callable; class CallInterfaceDescriptor; class Isolate; class JSCollection; +class JSRegExpStringIterator; class JSWeakCollection; class JSWeakMap; class JSWeakSet; diff --git a/src/compiler/types.cc b/src/compiler/types.cc index 88e6e7ee30..0512d2d5d9 100644 --- a/src/compiler/types.cc +++ b/src/compiler/types.cc @@ -223,6 +223,7 @@ Type::bitset BitsetType::Lub(i::Map* map) { case JS_ARRAY_BUFFER_TYPE: case JS_ARRAY_ITERATOR_TYPE: case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp type. + case JS_REGEXP_STRING_ITERATOR_TYPE: case JS_TYPED_ARRAY_TYPE: case JS_DATA_VIEW_TYPE: case JS_SET_TYPE: diff --git a/src/contexts.h b/src/contexts.h index 8d6afa9543..c0f8c39b73 100644 --- a/src/contexts.h +++ b/src/contexts.h @@ -244,6 +244,8 @@ enum ContextLookupFlags { V(REGEXP_INTERNAL_MATCH_INFO_INDEX, RegExpMatchInfo, \ regexp_internal_match_info) \ V(REGEXP_PROTOTYPE_MAP_INDEX, Map, regexp_prototype_map) \ + V(INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX, Map, \ + initial_regexp_string_iterator_prototype_map_index) \ V(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \ V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table) \ V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \ diff --git a/src/flag-definitions.h b/src/flag-definitions.h index 46aadee9ac..93d3225cd1 100644 --- a/src/flag-definitions.h +++ b/src/flag-definitions.h @@ -213,7 +213,8 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields) V(harmony_do_expressions, "harmony do-expressions") \ V(harmony_class_fields, "harmony fields in class literals") \ V(harmony_static_fields, "harmony static fields in class literals") \ - V(harmony_array_flatten, "harmony Array.prototype.flat{ten,Map}") + V(harmony_array_flatten, "harmony Array.prototype.flat{ten,Map}") \ + V(harmony_string_matchall, "harmony String.prototype.matchAll") // Features that are complete (but still behind --harmony/es-staging flag). #define HARMONY_STAGED(V) \ diff --git a/src/heap-symbols.h b/src/heap-symbols.h index a1918cde5c..5c0df1adf8 100644 --- a/src/heap-symbols.h +++ b/src/heap-symbols.h @@ -264,6 +264,7 @@ V(async_iterator_symbol, Symbol.asyncIterator) \ V(iterator_symbol, Symbol.iterator) \ V(intl_fallback_symbol, IntlFallback) \ + V(match_all_symbol, Symbol.matchAll) \ V(match_symbol, Symbol.match) \ V(replace_symbol, Symbol.replace) \ V(search_symbol, Symbol.search) \ diff --git a/src/objects-body-descriptors-inl.h b/src/objects-body-descriptors-inl.h index b57264c08b..e248fc8444 100644 --- a/src/objects-body-descriptors-inl.h +++ b/src/objects-body-descriptors-inl.h @@ -586,6 +586,7 @@ ReturnType BodyDescriptorApply(InstanceType type, T1 p1, T2 p2, T3 p3, T4 p4) { case JS_MAP_KEY_VALUE_ITERATOR_TYPE: case JS_MAP_VALUE_ITERATOR_TYPE: case JS_STRING_ITERATOR_TYPE: + case JS_REGEXP_STRING_ITERATOR_TYPE: case JS_REGEXP_TYPE: case JS_GLOBAL_PROXY_TYPE: case JS_GLOBAL_OBJECT_TYPE: diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 7adc78e824..77da0123d2 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -276,6 +276,9 @@ void HeapObject::HeapObjectVerify() { case JS_REGEXP_TYPE: JSRegExp::cast(this)->JSRegExpVerify(); break; + case JS_REGEXP_STRING_ITERATOR_TYPE: + JSRegExpStringIterator::cast(this)->JSRegExpStringIteratorVerify(); + break; case FILLER_TYPE: break; case JS_PROXY_TYPE: @@ -1315,6 +1318,14 @@ void JSRegExp::JSRegExpVerify() { } } +void JSRegExpStringIterator::JSRegExpStringIteratorVerify() { + CHECK(IsJSRegExpStringIterator()); + JSObjectVerify(); + CHECK(iterating_string()->IsString()); + CHECK(iterating_regexp()->IsObject()); + VerifySmiField(kFlagsOffset); +} + void JSProxy::JSProxyVerify() { CHECK(IsJSProxy()); VerifyPointer(target()); diff --git a/src/objects-inl.h b/src/objects-inl.h index bd7c7b9b98..db7c0bcaf4 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -42,6 +42,7 @@ #include "src/objects/js-collection-inl.h" #include "src/objects/js-promise-inl.h" #include "src/objects/js-regexp-inl.h" +#include "src/objects/js-regexp-string-iterator-inl.h" #include "src/objects/literal-objects.h" #include "src/objects/module-inl.h" #include "src/objects/regexp-match-info.h" diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 2a9662ae45..d3995e6042 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -165,6 +165,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT case JS_REGEXP_TYPE: JSRegExp::cast(this)->JSRegExpPrint(os); break; + case JS_REGEXP_STRING_ITERATOR_TYPE: + JSRegExpStringIterator::cast(this)->JSRegExpStringIteratorPrint(os); + break; case ODDBALL_TYPE: Oddball::cast(this)->to_string()->Print(os); break; @@ -619,6 +622,16 @@ void JSRegExp::JSRegExpPrint(std::ostream& os) { // NOLINT JSObjectPrintBody(os, this); } +void JSRegExpStringIterator::JSRegExpStringIteratorPrint( + std::ostream& os) { // NOLINT + JSObjectPrintHeader(os, this, "JSRegExpStringIterator"); + os << "\n - regex: " << Brief(iterating_regexp()); + os << "\n - string: " << Brief(iterating_string()); + os << "\n - done: " << done(); + os << "\n - global: " << global(); + os << "\n - unicode: " << unicode(); + JSObjectPrintBody(os, this); +} void Symbol::SymbolPrint(std::ostream& os) { // NOLINT HeapObject::PrintHeader(os, "Symbol"); @@ -1079,7 +1092,6 @@ void JSCollectionIterator::JSCollectionIteratorPrint( os << "\n"; } - void JSSetIterator::JSSetIteratorPrint(std::ostream& os) { // NOLINT JSObjectPrintHeader(os, this, "JSSetIterator"); JSCollectionIteratorPrint(os); diff --git a/src/objects.cc b/src/objects.cc index 79657825e4..f6602bca02 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -59,6 +59,7 @@ #include "src/objects/debug-objects-inl.h" #include "src/objects/frame-array-inl.h" #include "src/objects/hash-table.h" +#include "src/objects/js-regexp-string-iterator.h" #include "src/objects/map.h" #include "src/objects/microtask-inl.h" #include "src/objects/promise-inl.h" @@ -1369,6 +1370,8 @@ int JSObject::GetHeaderSize(InstanceType type, return JSPromise::kSize; case JS_REGEXP_TYPE: return JSRegExp::kSize; + case JS_REGEXP_STRING_ITERATOR_TYPE: + return JSRegExpStringIterator::kSize; case JS_CONTEXT_EXTENSION_OBJECT_TYPE: return JSObject::kHeaderSize; case JS_MESSAGE_OBJECT_TYPE: @@ -3100,6 +3103,7 @@ VisitorId Map::GetVisitorId(Map* map) { case JS_STRING_ITERATOR_TYPE: case JS_PROMISE_TYPE: case JS_REGEXP_TYPE: + case JS_REGEXP_STRING_ITERATOR_TYPE: case WASM_GLOBAL_TYPE: case WASM_MEMORY_TYPE: case WASM_MODULE_TYPE: diff --git a/src/objects.h b/src/objects.h index f461051169..000110c73c 100644 --- a/src/objects.h +++ b/src/objects.h @@ -464,6 +464,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1; V(JS_MESSAGE_OBJECT_TYPE) \ V(JS_PROMISE_TYPE) \ V(JS_REGEXP_TYPE) \ + V(JS_REGEXP_STRING_ITERATOR_TYPE) \ V(JS_SET_TYPE) \ V(JS_SET_KEY_VALUE_ITERATOR_TYPE) \ V(JS_SET_VALUE_ITERATOR_TYPE) \ @@ -839,6 +840,7 @@ enum InstanceType : uint16_t { JS_MESSAGE_OBJECT_TYPE, JS_PROMISE_TYPE, JS_REGEXP_TYPE, + JS_REGEXP_STRING_ITERATOR_TYPE, JS_SET_TYPE, JS_SET_KEY_VALUE_ITERATOR_TYPE, JS_SET_VALUE_ITERATOR_TYPE, @@ -1073,6 +1075,7 @@ template inline bool Is(Object* obj); V(JSProxy) \ V(JSReceiver) \ V(JSRegExp) \ + V(JSRegExpStringIterator) \ V(JSSet) \ V(JSSetIterator) \ V(JSSloppyArgumentsObject) \ diff --git a/src/objects/js-regexp-string-iterator-inl.h b/src/objects/js-regexp-string-iterator-inl.h new file mode 100644 index 0000000000..1d6a64ec0c --- /dev/null +++ b/src/objects/js-regexp-string-iterator-inl.h @@ -0,0 +1,35 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_INL_H_ +#define V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_INL_H_ + +#include "src/objects/js-regexp-string-iterator.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +TYPE_CHECKER(JSRegExpStringIterator, JS_REGEXP_STRING_ITERATOR_TYPE) + +ACCESSORS(JSRegExpStringIterator, iterating_regexp, Object, + kIteratingRegExpOffset) +ACCESSORS(JSRegExpStringIterator, iterating_string, String, + kIteratedStringOffset) + +SMI_ACCESSORS(JSRegExpStringIterator, flags, kFlagsOffset) +BOOL_ACCESSORS(JSRegExpStringIterator, flags, done, kDoneBit) +BOOL_ACCESSORS(JSRegExpStringIterator, flags, global, kGlobalBit) +BOOL_ACCESSORS(JSRegExpStringIterator, flags, unicode, kUnicodeBit) + +CAST_ACCESSOR(JSRegExpStringIterator) + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_INL_H_ diff --git a/src/objects/js-regexp-string-iterator.h b/src/objects/js-regexp-string-iterator.h new file mode 100644 index 0000000000..9821e33efb --- /dev/null +++ b/src/objects/js-regexp-string-iterator.h @@ -0,0 +1,59 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_ +#define V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_ + +#include "src/objects.h" + +// Has to be the last include (doesn't have include guards): +#include "src/objects/object-macros.h" + +namespace v8 { +namespace internal { + +class JSRegExpStringIterator : public JSObject { + public: + // [regexp]: the [[IteratingRegExp]] internal property. + DECL_ACCESSORS(iterating_regexp, Object) + + // [string]: The [[IteratedString]] internal property. + DECL_ACCESSORS(iterating_string, String) + + DECL_INT_ACCESSORS(flags) + + // [boolean]: The [[Done]] internal property. + DECL_BOOLEAN_ACCESSORS(done) + + // [boolean]: The [[Global]] internal property. + DECL_BOOLEAN_ACCESSORS(global) + + // [boolean]: The [[Unicode]] internal property. + DECL_BOOLEAN_ACCESSORS(unicode) + + DECL_CAST(JSRegExpStringIterator) + DECL_PRINTER(JSRegExpStringIterator) + DECL_VERIFIER(JSRegExpStringIterator) + + static const int kIteratingRegExpOffset = JSObject::kHeaderSize; + static const int kIteratedStringOffset = + kIteratingRegExpOffset + kPointerSize; + static const int kFlagsOffset = kIteratedStringOffset + kPointerSize; + + static const int kSize = kFlagsOffset + kPointerSize; + + static const int kDoneBit = 0; + static const int kGlobalBit = 1; + static const int kUnicodeBit = 2; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(JSRegExpStringIterator); +}; + +} // namespace internal +} // namespace v8 + +#include "src/objects/object-macros-undef.h" + +#endif // V8_OBJECTS_JS_REGEXP_STRING_ITERATOR_H_ diff --git a/test/mjsunit/harmony/string-matchAll.js b/test/mjsunit/harmony/string-matchAll.js new file mode 100644 index 0000000000..e8b212529a --- /dev/null +++ b/test/mjsunit/harmony/string-matchAll.js @@ -0,0 +1,100 @@ +// Copyright 2018 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --harmony-string-matchall + +(function TestReceiverNonString() { + const iter = 'a'.matchAll(/./); + assertThrows( + () => iter.next.call(0), + TypeError + ); +})(); + + +(function TestAncestry() { + const iterProto = Object.getPrototypeOf('a'.matchAll(/./)); + const arrProto = Object.getPrototypeOf([][Symbol.iterator]()); + + assertSame(Object.getPrototypeOf(iterProto), Object.getPrototypeOf(arrProto)); +})(); + + +function TestNoMatch(string, regex_or_string) { + const next_result = string.matchAll(regex_or_string).next(); + assertSame(undefined, next_result.value); + assertTrue(next_result.done); +} +TestNoMatch('a', /b/); +TestNoMatch('a', /b/g); +TestNoMatch('a', 'b'); + + +(function NonGlobalRegex() { + const iter = 'ab'.matchAll(/./); + let next_result = iter.next(); + assertEquals(['a'], next_result.value); + assertFalse(next_result.done); + + next_result = iter.next(); + assertEquals(undefined, next_result.value); + assertTrue(next_result.done); +})(); + + +function TestGlobalRegex(regex_or_string) { + const iter = 'ab'.matchAll(/./g); + let next_result = iter.next(); + assertEquals(['a'], next_result.value); + assertFalse(next_result.done); + + next_result = iter.next(); + assertEquals(['b'], next_result.value); + assertFalse(next_result.done); + + next_result = iter.next(); + assertSame(undefined, next_result.value); + assertTrue(next_result.done); +} +TestGlobalRegex(/./g); +TestGlobalRegex('.'); + + +function TestEmptyGlobalRegExp(regex_or_string) { + const iter = 'd'.matchAll(regex_or_string); + let next_result = iter.next(); + assertEquals([''], next_result.value); + assertFalse(next_result.done); + + next_result = iter.next(); + assertEquals([''], next_result.value); + assertFalse(next_result.done); + + next_result = iter.next(); + assertSame(undefined, next_result.value); + assertTrue(next_result.done); +} +TestEmptyGlobalRegExp(undefined); +TestEmptyGlobalRegExp(/(?:)/g); +TestEmptyGlobalRegExp(''); + + +(function TestGlobalRegExpLastIndex() { + const regex = /./g; + const string = 'abc'; + regex.exec(string); + assertSame(1, regex.lastIndex); + + const iter = string.matchAll(regex); + + // Verify an internal RegExp is created and mutations to the provided RegExp + // are not abservered. + regex.exec(string); + assertSame(2, regex.lastIndex); + + let next_result = iter.next(); + assertEquals(['b'], next_result.value); + assertFalse(next_result.done); + assertSame(2, regex.lastIndex); +})(); diff --git a/test/test262/testcfg.py b/test/test262/testcfg.py index 86703a7ce5..edea2e8d95 100644 --- a/test/test262/testcfg.py +++ b/test/test262/testcfg.py @@ -51,6 +51,8 @@ FEATURE_FLAGS = { 'class-fields-private': '--harmony-private-fields', 'Array.prototype.flatten': '--harmony-array-flatten', 'Array.prototype.flatMap': '--harmony-array-flatten', + 'String.prototype.matchAll': '--harmony-string-matchall', + 'Symbol.matchAll': '--harmony-string-matchall', 'numeric-separator-literal': '--harmony-numeric-separator', } diff --git a/tools/v8heapconst.py b/tools/v8heapconst.py index e0e1300361..6d1aae880f 100644 --- a/tools/v8heapconst.py +++ b/tools/v8heapconst.py @@ -137,21 +137,22 @@ INSTANCE_TYPES = { 1072: "JS_MESSAGE_OBJECT_TYPE", 1073: "JS_PROMISE_TYPE", 1074: "JS_REGEXP_TYPE", - 1075: "JS_SET_TYPE", - 1076: "JS_SET_KEY_VALUE_ITERATOR_TYPE", - 1077: "JS_SET_VALUE_ITERATOR_TYPE", - 1078: "JS_STRING_ITERATOR_TYPE", - 1079: "JS_WEAK_MAP_TYPE", - 1080: "JS_WEAK_SET_TYPE", - 1081: "JS_TYPED_ARRAY_TYPE", - 1082: "JS_DATA_VIEW_TYPE", - 1083: "WASM_GLOBAL_TYPE", - 1084: "WASM_INSTANCE_TYPE", - 1085: "WASM_MEMORY_TYPE", - 1086: "WASM_MODULE_TYPE", - 1087: "WASM_TABLE_TYPE", - 1088: "JS_BOUND_FUNCTION_TYPE", - 1089: "JS_FUNCTION_TYPE", + 1075: "JS_REGEXP_STRING_ITERATOR_TYPE", + 1076: "JS_SET_TYPE", + 1077: "JS_SET_KEY_VALUE_ITERATOR_TYPE", + 1078: "JS_SET_VALUE_ITERATOR_TYPE", + 1079: "JS_STRING_ITERATOR_TYPE", + 1080: "JS_WEAK_MAP_TYPE", + 1081: "JS_WEAK_SET_TYPE", + 1082: "JS_TYPED_ARRAY_TYPE", + 1083: "JS_DATA_VIEW_TYPE", + 1084: "WASM_GLOBAL_TYPE", + 1085: "WASM_INSTANCE_TYPE", + 1086: "WASM_MEMORY_TYPE", + 1087: "WASM_MODULE_TYPE", + 1088: "WASM_TABLE_TYPE", + 1089: "JS_BOUND_FUNCTION_TYPE", + 1090: "JS_FUNCTION_TYPE", } # List of known V8 maps.