[esnext] Implement String.prototype.matchAll
Proposal repo: https://github.com/tc39/proposal-string-matchall - Add new builtins StringPrototypeMatchAll and RegExpPrototypeMatchAll - Add new object RegExpStringIterator Bug: v8:6890 Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: I9fad71900cf30e8632258c309df1c7a638ea4600 Reviewed-on: https://chromium-review.googlesource.com/981893 Commit-Queue: Peter Wong <peter.wm.wong@gmail.com> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#52403}
This commit is contained in:
parent
0872da3773
commit
3b39fc4dcd
4
BUILD.gn
4
BUILD.gn
@ -798,6 +798,8 @@ action("postmortem-metadata") {
|
|||||||
"src/objects/js-array.h",
|
"src/objects/js-array.h",
|
||||||
"src/objects/js-regexp-inl.h",
|
"src/objects/js-regexp-inl.h",
|
||||||
"src/objects/js-regexp.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.h",
|
||||||
"src/objects/map-inl.h",
|
"src/objects/map-inl.h",
|
||||||
"src/objects/script.h",
|
"src/objects/script.h",
|
||||||
@ -1955,6 +1957,8 @@ v8_source_set("v8_base") {
|
|||||||
"src/objects/js-promise-inl.h",
|
"src/objects/js-promise-inl.h",
|
||||||
"src/objects/js-promise.h",
|
"src/objects/js-promise.h",
|
||||||
"src/objects/js-regexp-inl.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/js-regexp.h",
|
||||||
"src/objects/literal-objects-inl.h",
|
"src/objects/literal-objects-inl.h",
|
||||||
"src/objects/literal-objects.cc",
|
"src/objects/literal-objects.cc",
|
||||||
|
@ -4205,6 +4205,64 @@ void Genesis::InitializeGlobal_harmony_array_flatten() {
|
|||||||
Builtins::kArrayPrototypeFlatMap, 1, false, DONT_ENUM);
|
Builtins::kArrayPrototypeFlatMap, 1, false, DONT_ENUM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Genesis::InitializeGlobal_harmony_string_matchall() {
|
||||||
|
if (!FLAG_harmony_string_matchall) return;
|
||||||
|
|
||||||
|
{ // String.prototype.matchAll
|
||||||
|
Handle<JSFunction> string_fun(native_context()->string_function());
|
||||||
|
Handle<JSObject> string_prototype(
|
||||||
|
JSObject::cast(string_fun->instance_prototype()));
|
||||||
|
|
||||||
|
SimpleInstallFunction(string_prototype, "matchAll",
|
||||||
|
Builtins::kStringPrototypeMatchAll, 1, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // RegExp.prototype[@@matchAll]
|
||||||
|
Handle<JSFunction> regexp_fun(native_context()->regexp_function());
|
||||||
|
Handle<JSObject> regexp_prototype(
|
||||||
|
JSObject::cast(regexp_fun->instance_prototype()));
|
||||||
|
SimpleInstallFunction(regexp_prototype, factory()->match_all_symbol(),
|
||||||
|
"[Symbol.matchAll]",
|
||||||
|
Builtins::kRegExpPrototypeMatchAll, 1, true);
|
||||||
|
Handle<Map> 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<JSObject> iterator_prototype(
|
||||||
|
native_context()->initial_iterator_prototype());
|
||||||
|
|
||||||
|
Handle<JSObject> 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<PropertyAttributes>(DONT_ENUM | READ_ONLY));
|
||||||
|
|
||||||
|
SimpleInstallFunction(regexp_string_iterator_prototype, "next",
|
||||||
|
Builtins::kRegExpStringIteratorPrototypeNext, 0,
|
||||||
|
true);
|
||||||
|
|
||||||
|
Handle<JSFunction> 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<JSFunction> symbol_fun(native_context()->symbol_function());
|
||||||
|
InstallConstant(isolate(), symbol_fun, "matchAll",
|
||||||
|
factory()->match_all_symbol());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Genesis::InitializeGlobal_harmony_promise_finally() {
|
void Genesis::InitializeGlobal_harmony_promise_finally() {
|
||||||
if (!FLAG_harmony_promise_finally) return;
|
if (!FLAG_harmony_promise_finally) return;
|
||||||
|
|
||||||
|
@ -920,6 +920,8 @@ namespace internal {
|
|||||||
TFJ(RegExpPrototypeIgnoreCaseGetter, 0) \
|
TFJ(RegExpPrototypeIgnoreCaseGetter, 0) \
|
||||||
/* ES #sec-regexp.prototype-@@match */ \
|
/* ES #sec-regexp.prototype-@@match */ \
|
||||||
TFJ(RegExpPrototypeMatch, 1, kString) \
|
TFJ(RegExpPrototypeMatch, 1, kString) \
|
||||||
|
/* https://tc39.github.io/proposal-string-matchall/ */ \
|
||||||
|
TFJ(RegExpPrototypeMatchAll, 1, kString) \
|
||||||
/* ES #sec-get-regexp.prototype.multiline */ \
|
/* ES #sec-get-regexp.prototype.multiline */ \
|
||||||
TFJ(RegExpPrototypeMultilineGetter, 0) \
|
TFJ(RegExpPrototypeMultilineGetter, 0) \
|
||||||
/* ES #sec-regexp.prototype-@@search */ \
|
/* ES #sec-regexp.prototype-@@search */ \
|
||||||
@ -947,6 +949,10 @@ namespace internal {
|
|||||||
TFS(RegExpSearchFast, kReceiver, kPattern) \
|
TFS(RegExpSearchFast, kReceiver, kPattern) \
|
||||||
TFS(RegExpSplit, kRegExp, kString, kLimit) \
|
TFS(RegExpSplit, kRegExp, kString, kLimit) \
|
||||||
\
|
\
|
||||||
|
/* RegExp String Iterator */ \
|
||||||
|
/* https://tc39.github.io/proposal-string-matchall/ */ \
|
||||||
|
TFJ(RegExpStringIteratorPrototypeNext, 0) \
|
||||||
|
\
|
||||||
/* Set */ \
|
/* Set */ \
|
||||||
TFJ(SetConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
TFJ(SetConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
|
||||||
TFJ(SetPrototypeHas, 1, kKey) \
|
TFJ(SetPrototypeHas, 1, kKey) \
|
||||||
@ -1024,6 +1030,8 @@ namespace internal {
|
|||||||
TFJ(StringPrototypeLink, 1, kValue) \
|
TFJ(StringPrototypeLink, 1, kValue) \
|
||||||
/* ES6 #sec-string.prototype.match */ \
|
/* ES6 #sec-string.prototype.match */ \
|
||||||
TFJ(StringPrototypeMatch, 1, kRegexp) \
|
TFJ(StringPrototypeMatch, 1, kRegexp) \
|
||||||
|
/* ES #sec-string.prototype.matchAll */ \
|
||||||
|
TFJ(StringPrototypeMatchAll, 1, kRegexp) \
|
||||||
/* ES6 #sec-string.prototype.localecompare */ \
|
/* ES6 #sec-string.prototype.localecompare */ \
|
||||||
CPP(StringPrototypeLocaleCompare) \
|
CPP(StringPrototypeLocaleCompare) \
|
||||||
/* ES6 #sec-string.prototype.padEnd */ \
|
/* ES6 #sec-string.prototype.padEnd */ \
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "src/code-stub-assembler.h"
|
#include "src/code-stub-assembler.h"
|
||||||
#include "src/counters.h"
|
#include "src/counters.h"
|
||||||
#include "src/factory-inl.h"
|
#include "src/factory-inl.h"
|
||||||
|
#include "src/objects/js-regexp-string-iterator.h"
|
||||||
#include "src/objects/js-regexp.h"
|
#include "src/objects/js-regexp.h"
|
||||||
#include "src/objects/regexp-match-info.h"
|
#include "src/objects/regexp-match-info.h"
|
||||||
#include "src/regexp/regexp-macro-assembler.h"
|
#include "src/regexp/regexp-macro-assembler.h"
|
||||||
@ -20,6 +21,8 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
using compiler::Node;
|
using compiler::Node;
|
||||||
|
template <class T>
|
||||||
|
using TNode = compiler::TNode<T>;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ES6 section 21.2 RegExp Objects
|
// ES6 section 21.2 RegExp Objects
|
||||||
@ -88,6 +91,28 @@ Node* RegExpBuiltinsAssembler::AllocateRegExpResult(Node* context, Node* length,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(
|
||||||
|
TNode<Context> context, TNode<Context> native_context,
|
||||||
|
TNode<Object> maybe_string, TNode<String> flags) {
|
||||||
|
TNode<JSFunction> regexp_function =
|
||||||
|
CAST(LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
|
||||||
|
TNode<Map> initial_map = CAST(LoadObjectField(
|
||||||
|
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||||
|
return RegExpCreate(context, initial_map, maybe_string, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<Object> RegExpBuiltinsAssembler::RegExpCreate(TNode<Context> context,
|
||||||
|
TNode<Map> initial_map,
|
||||||
|
TNode<Object> maybe_string,
|
||||||
|
TNode<String> flags) {
|
||||||
|
TNode<String> pattern = Select<String>(
|
||||||
|
IsUndefined(maybe_string), [=] { return EmptyStringConstant(); },
|
||||||
|
[=] { return ToString_Inline(context, maybe_string); });
|
||||||
|
TNode<Object> regexp = CAST(AllocateJSObjectFromMap(initial_map));
|
||||||
|
return CallRuntime(Runtime::kRegExpInitializeAndCompile, context, regexp,
|
||||||
|
pattern, flags);
|
||||||
|
}
|
||||||
|
|
||||||
Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) {
|
Node* RegExpBuiltinsAssembler::FastLoadLastIndex(Node* regexp) {
|
||||||
// Load the in-object field.
|
// Load the in-object field.
|
||||||
static const int field_offset =
|
static const int field_offset =
|
||||||
@ -1949,6 +1974,163 @@ TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
|
|||||||
RegExpPrototypeMatchBody(context, receiver, string, false);
|
RegExpPrototypeMatchBody(context, receiver, string, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TNode<Object> RegExpBuiltinsAssembler::MatchAllIterator(
|
||||||
|
TNode<Context> context, TNode<Context> native_context,
|
||||||
|
TNode<Object> maybe_regexp, TNode<Object> 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> 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<Object> regexp_fun =
|
||||||
|
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
|
||||||
|
TNode<Object> 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<Object> flags = GetProperty(context, maybe_regexp,
|
||||||
|
isolate()->factory()->flags_string());
|
||||||
|
TNode<Object> 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<Int32T>(
|
||||||
|
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<Int32T>(
|
||||||
|
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<Number> last_index = UncheckedCast<Number>(
|
||||||
|
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<Object> 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> 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<Object> 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<Int32T> zero = Int32Constant(0);
|
||||||
|
TNode<Int32T> 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<Word32T> global_flag = Word32Shl(
|
||||||
|
var_global.value(), Int32Constant(JSRegExpStringIterator::kGlobalBit));
|
||||||
|
TNode<Word32T> unicode_flag =
|
||||||
|
Word32Shl(var_unicode.value(),
|
||||||
|
Int32Constant(JSRegExpStringIterator::kUnicodeBit));
|
||||||
|
TNode<Word32T> 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> context = CAST(Parameter(Descriptor::kContext));
|
||||||
|
TNode<Context> native_context = LoadNativeContext(context);
|
||||||
|
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||||
|
TNode<Object> 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...
|
// Helper that skips a few initial checks. and assumes...
|
||||||
// 1) receiver is a "fast" RegExp
|
// 1) receiver is a "fast" RegExp
|
||||||
// 2) pattern is a string
|
// 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<Smi> LoadFlags(TNode<HeapObject> iterator) {
|
||||||
|
return LoadObjectField<Smi>(iterator, JSRegExpStringIterator::kFlagsOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<BoolT> HasDoneFlag(TNode<Smi> flags) {
|
||||||
|
return UncheckedCast<BoolT>(
|
||||||
|
IsSetSmi(flags, 1 << JSRegExpStringIterator::kDoneBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<BoolT> HasGlobalFlag(TNode<Smi> flags) {
|
||||||
|
return UncheckedCast<BoolT>(
|
||||||
|
IsSetSmi(flags, 1 << JSRegExpStringIterator::kGlobalBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<BoolT> HasUnicodeFlag(TNode<Smi> flags) {
|
||||||
|
return UncheckedCast<BoolT>(
|
||||||
|
IsSetSmi(flags, 1 << JSRegExpStringIterator::kUnicodeBit));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDoneFlag(TNode<HeapObject> iterator, TNode<Smi> flags) {
|
||||||
|
TNode<Smi> 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> context = CAST(Parameter(Descriptor::kContext));
|
||||||
|
TNode<Object> 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<HeapObject> 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<Smi> flags = LoadFlags(receiver);
|
||||||
|
GotoIf(HasDoneFlag(flags), &return_empty_done_result);
|
||||||
|
|
||||||
|
// 5. Let R be O.[[IteratingRegExp]].
|
||||||
|
TNode<Object> iterating_regexp =
|
||||||
|
LoadObjectField(receiver, JSRegExpStringIterator::kIteratingRegExpOffset);
|
||||||
|
|
||||||
|
// 6. Let S be O.[[IteratedString]].
|
||||||
|
TNode<String> 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<Object> 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<Object> last_index =
|
||||||
|
CAST(SlowLoadLastIndex(context, iterating_regexp));
|
||||||
|
TNode<Number> this_index = ToLength_Inline(context, last_index);
|
||||||
|
|
||||||
|
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
|
||||||
|
TNode<Object> 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 internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -19,6 +19,19 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
Node* const map, Label* const if_isunmodified,
|
Node* const map, Label* const if_isunmodified,
|
||||||
Label* const if_ismodified);
|
Label* const if_ismodified);
|
||||||
|
|
||||||
|
// Create and initialize a RegExp object.
|
||||||
|
TNode<Object> RegExpCreate(TNode<Context> context,
|
||||||
|
TNode<Context> native_context,
|
||||||
|
TNode<Object> regexp_string, TNode<String> flags);
|
||||||
|
|
||||||
|
TNode<Object> RegExpCreate(TNode<Context> context, TNode<Map> initial_map,
|
||||||
|
TNode<Object> regexp_string, TNode<String> flags);
|
||||||
|
|
||||||
|
TNode<Object> MatchAllIterator(TNode<Context> context,
|
||||||
|
TNode<Context> native_context,
|
||||||
|
TNode<Object> regexp, TNode<Object> string,
|
||||||
|
char const* method_name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Allocate a RegExpResult with the given length (the number of captures,
|
// Allocate a RegExpResult with the given length (the number of captures,
|
||||||
// including the match itself), index (the index where the match starts),
|
// including the match itself), index (the index where the match starts),
|
||||||
|
@ -1024,7 +1024,7 @@ void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context,
|
|||||||
void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
||||||
Node* const context, Node* const object, Node* const maybe_string,
|
Node* const context, Node* const object, Node* const maybe_string,
|
||||||
Handle<Symbol> symbol, const NodeFunction0& regexp_call,
|
Handle<Symbol> symbol, const NodeFunction0& regexp_call,
|
||||||
const NodeFunction1& generic_call, CodeStubArguments* args) {
|
const NodeFunction1& generic_call) {
|
||||||
Label out(this);
|
Label out(this);
|
||||||
|
|
||||||
// Smis definitely don't have an attached symbol.
|
// Smis definitely don't have an attached symbol.
|
||||||
@ -1069,12 +1069,7 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
|||||||
|
|
||||||
BIND(&stub_call);
|
BIND(&stub_call);
|
||||||
// TODO(jgruber): Add a no-JS scope once it exists.
|
// TODO(jgruber): Add a no-JS scope once it exists.
|
||||||
Node* const result = regexp_call();
|
regexp_call();
|
||||||
if (args == nullptr) {
|
|
||||||
Return(result);
|
|
||||||
} else {
|
|
||||||
args->PopAndReturn(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&slow_lookup);
|
BIND(&slow_lookup);
|
||||||
}
|
}
|
||||||
@ -1094,12 +1089,7 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol(
|
|||||||
GotoIf(IsNull(maybe_func), &out);
|
GotoIf(IsNull(maybe_func), &out);
|
||||||
|
|
||||||
// Attempt to call the function.
|
// Attempt to call the function.
|
||||||
Node* const result = generic_call(maybe_func);
|
generic_call(maybe_func);
|
||||||
if (args == nullptr) {
|
|
||||||
Return(result);
|
|
||||||
} else {
|
|
||||||
args->PopAndReturn(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
BIND(&out);
|
BIND(&out);
|
||||||
}
|
}
|
||||||
@ -1294,12 +1284,12 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) {
|
|||||||
MaybeCallFunctionAtSymbol(
|
MaybeCallFunctionAtSymbol(
|
||||||
context, search, receiver, isolate()->factory()->replace_symbol(),
|
context, search, receiver, isolate()->factory()->replace_symbol(),
|
||||||
[=]() {
|
[=]() {
|
||||||
return CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
|
Return(CallBuiltin(Builtins::kRegExpReplace, context, search, receiver,
|
||||||
replace);
|
replace));
|
||||||
},
|
},
|
||||||
[=](Node* fn) {
|
[=](Node* fn) {
|
||||||
Callable call_callable = CodeFactory::Call(isolate());
|
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.
|
// Convert {receiver} and {search} to strings.
|
||||||
@ -1439,8 +1429,9 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler {
|
|||||||
protected:
|
protected:
|
||||||
enum Variant { kMatch, kSearch };
|
enum Variant { kMatch, kSearch };
|
||||||
|
|
||||||
void Generate(Variant variant, const char* method_name, Node* const receiver,
|
void Generate(Variant variant, const char* method_name,
|
||||||
Node* maybe_regexp, Node* const context) {
|
TNode<Object> receiver, TNode<Object> maybe_regexp,
|
||||||
|
TNode<Context> context) {
|
||||||
Label call_regexp_match_search(this);
|
Label call_regexp_match_search(this);
|
||||||
|
|
||||||
Builtins::Name builtin;
|
Builtins::Name builtin;
|
||||||
@ -1457,32 +1448,24 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler {
|
|||||||
|
|
||||||
MaybeCallFunctionAtSymbol(
|
MaybeCallFunctionAtSymbol(
|
||||||
context, maybe_regexp, receiver, symbol,
|
context, maybe_regexp, receiver, symbol,
|
||||||
[=] { return CallBuiltin(builtin, context, maybe_regexp, receiver); },
|
[=] { Return(CallBuiltin(builtin, context, maybe_regexp, receiver)); },
|
||||||
[=](Node* fn) {
|
[=](Node* fn) {
|
||||||
Callable call_callable = CodeFactory::Call(isolate());
|
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.
|
// maybe_regexp is not a RegExp nor has [@@match / @@search] property.
|
||||||
{
|
{
|
||||||
RegExpBuiltinsAssembler regexp_asm(state());
|
RegExpBuiltinsAssembler regexp_asm(state());
|
||||||
|
|
||||||
Node* const receiver_string = ToString_Inline(context, receiver);
|
TNode<String> receiver_string = ToString_Inline(context, receiver);
|
||||||
TNode<Object> const pattern = Select<Object>(
|
TNode<Context> native_context = LoadNativeContext(context);
|
||||||
IsUndefined(maybe_regexp), [=] { return EmptyStringConstant(); },
|
TNode<HeapObject> regexp_function = CAST(
|
||||||
[=] { return ToString_Inline(context, maybe_regexp); });
|
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
|
||||||
|
TNode<Map> initial_map = CAST(LoadObjectField(
|
||||||
// Create RegExp
|
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||||
// TODO(pwong): This could be factored out as a helper (RegExpCreate) that
|
TNode<Object> regexp = regexp_asm.RegExpCreate(
|
||||||
// also does the "is fast" checks.
|
context, initial_map, maybe_regexp, EmptyStringConstant());
|
||||||
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());
|
|
||||||
|
|
||||||
Label fast_path(this), slow_path(this);
|
Label fast_path(this), slow_path(this);
|
||||||
regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path,
|
regexp_asm.BranchIfFastRegExp(context, regexp, initial_map, &fast_path,
|
||||||
@ -1493,7 +1476,7 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler {
|
|||||||
|
|
||||||
BIND(&slow_path);
|
BIND(&slow_path);
|
||||||
{
|
{
|
||||||
Node* const maybe_func = GetProperty(context, regexp, symbol);
|
TNode<Object> maybe_func = GetProperty(context, regexp, symbol);
|
||||||
Callable call_callable = CodeFactory::Call(isolate());
|
Callable call_callable = CodeFactory::Call(isolate());
|
||||||
Return(CallJS(call_callable, context, maybe_func, regexp,
|
Return(CallJS(call_callable, context, maybe_func, regexp,
|
||||||
receiver_string));
|
receiver_string));
|
||||||
@ -1504,13 +1487,52 @@ class StringMatchSearchAssembler : public StringBuiltinsAssembler {
|
|||||||
|
|
||||||
// ES6 #sec-string.prototype.match
|
// ES6 #sec-string.prototype.match
|
||||||
TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
|
TF_BUILTIN(StringPrototypeMatch, StringMatchSearchAssembler) {
|
||||||
Node* const receiver = Parameter(Descriptor::kReceiver);
|
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||||
Node* const maybe_regexp = Parameter(Descriptor::kRegexp);
|
TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
|
||||||
Node* const context = Parameter(Descriptor::kContext);
|
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||||
|
|
||||||
Generate(kMatch, "String.prototype.match", receiver, maybe_regexp, context);
|
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> context = CAST(Parameter(Descriptor::kContext));
|
||||||
|
TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
|
||||||
|
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||||
|
TNode<Context> 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<Object> iterator = regexp_asm.MatchAllIterator(
|
||||||
|
context, native_context, maybe_regexp, receiver, method_name);
|
||||||
|
Return(iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class StringPadAssembler : public StringBuiltinsAssembler {
|
class StringPadAssembler : public StringBuiltinsAssembler {
|
||||||
public:
|
public:
|
||||||
explicit StringPadAssembler(compiler::CodeAssemblerState* state)
|
explicit StringPadAssembler(compiler::CodeAssemblerState* state)
|
||||||
@ -1639,9 +1661,9 @@ TF_BUILTIN(StringPrototypePadStart, StringPadAssembler) {
|
|||||||
|
|
||||||
// ES6 #sec-string.prototype.search
|
// ES6 #sec-string.prototype.search
|
||||||
TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
|
TF_BUILTIN(StringPrototypeSearch, StringMatchSearchAssembler) {
|
||||||
Node* const receiver = Parameter(Descriptor::kReceiver);
|
TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||||
Node* const maybe_regexp = Parameter(Descriptor::kRegexp);
|
TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
|
||||||
Node* const context = Parameter(Descriptor::kContext);
|
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||||
Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
|
Generate(kSearch, "String.prototype.search", receiver, maybe_regexp, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1718,15 +1740,15 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) {
|
|||||||
|
|
||||||
MaybeCallFunctionAtSymbol(
|
MaybeCallFunctionAtSymbol(
|
||||||
context, separator, receiver, isolate()->factory()->split_symbol(),
|
context, separator, receiver, isolate()->factory()->split_symbol(),
|
||||||
[=]() {
|
[&]() {
|
||||||
return CallBuiltin(Builtins::kRegExpSplit, context, separator, receiver,
|
args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context,
|
||||||
limit);
|
separator, receiver, limit));
|
||||||
},
|
},
|
||||||
[=](Node* fn) {
|
[&](Node* fn) {
|
||||||
Callable call_callable = CodeFactory::Call(isolate());
|
Callable call_callable = CodeFactory::Call(isolate());
|
||||||
return CallJS(call_callable, context, fn, separator, receiver, limit);
|
args.PopAndReturn(
|
||||||
},
|
CallJS(call_callable, context, fn, separator, receiver, limit));
|
||||||
&args);
|
});
|
||||||
|
|
||||||
// String and integer conversions.
|
// String and integer conversions.
|
||||||
|
|
||||||
|
@ -95,14 +95,13 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
|
|||||||
//
|
//
|
||||||
// Contains fast paths for Smi and RegExp objects.
|
// Contains fast paths for Smi and RegExp objects.
|
||||||
// Important: {regexp_call} may not contain any code that can call into JS.
|
// Important: {regexp_call} may not contain any code that can call into JS.
|
||||||
typedef std::function<Node*()> NodeFunction0;
|
typedef std::function<void()> NodeFunction0;
|
||||||
typedef std::function<Node*(Node* fn)> NodeFunction1;
|
typedef std::function<void(Node* fn)> NodeFunction1;
|
||||||
void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
|
void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
|
||||||
Node* const maybe_string,
|
Node* const maybe_string,
|
||||||
Handle<Symbol> symbol,
|
Handle<Symbol> symbol,
|
||||||
const NodeFunction0& regexp_call,
|
const NodeFunction0& regexp_call,
|
||||||
const NodeFunction1& generic_call,
|
const NodeFunction1& generic_call);
|
||||||
CodeStubArguments* args = nullptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler {
|
class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler {
|
||||||
|
@ -29,6 +29,7 @@ class Callable;
|
|||||||
class CallInterfaceDescriptor;
|
class CallInterfaceDescriptor;
|
||||||
class Isolate;
|
class Isolate;
|
||||||
class JSCollection;
|
class JSCollection;
|
||||||
|
class JSRegExpStringIterator;
|
||||||
class JSWeakCollection;
|
class JSWeakCollection;
|
||||||
class JSWeakMap;
|
class JSWeakMap;
|
||||||
class JSWeakSet;
|
class JSWeakSet;
|
||||||
|
@ -223,6 +223,7 @@ Type::bitset BitsetType::Lub(i::Map* map) {
|
|||||||
case JS_ARRAY_BUFFER_TYPE:
|
case JS_ARRAY_BUFFER_TYPE:
|
||||||
case JS_ARRAY_ITERATOR_TYPE:
|
case JS_ARRAY_ITERATOR_TYPE:
|
||||||
case JS_REGEXP_TYPE: // TODO(rossberg): there should be a RegExp 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_TYPED_ARRAY_TYPE:
|
||||||
case JS_DATA_VIEW_TYPE:
|
case JS_DATA_VIEW_TYPE:
|
||||||
case JS_SET_TYPE:
|
case JS_SET_TYPE:
|
||||||
|
@ -244,6 +244,8 @@ enum ContextLookupFlags {
|
|||||||
V(REGEXP_INTERNAL_MATCH_INFO_INDEX, RegExpMatchInfo, \
|
V(REGEXP_INTERNAL_MATCH_INFO_INDEX, RegExpMatchInfo, \
|
||||||
regexp_internal_match_info) \
|
regexp_internal_match_info) \
|
||||||
V(REGEXP_PROTOTYPE_MAP_INDEX, Map, regexp_prototype_map) \
|
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(REGEXP_RESULT_MAP_INDEX, Map, regexp_result_map) \
|
||||||
V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table) \
|
V(SCRIPT_CONTEXT_TABLE_INDEX, ScriptContextTable, script_context_table) \
|
||||||
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
|
V(SCRIPT_FUNCTION_INDEX, JSFunction, script_function) \
|
||||||
|
@ -213,7 +213,8 @@ DEFINE_IMPLICATION(harmony_class_fields, harmony_private_fields)
|
|||||||
V(harmony_do_expressions, "harmony do-expressions") \
|
V(harmony_do_expressions, "harmony do-expressions") \
|
||||||
V(harmony_class_fields, "harmony fields in class literals") \
|
V(harmony_class_fields, "harmony fields in class literals") \
|
||||||
V(harmony_static_fields, "harmony static 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).
|
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||||
#define HARMONY_STAGED(V) \
|
#define HARMONY_STAGED(V) \
|
||||||
|
@ -264,6 +264,7 @@
|
|||||||
V(async_iterator_symbol, Symbol.asyncIterator) \
|
V(async_iterator_symbol, Symbol.asyncIterator) \
|
||||||
V(iterator_symbol, Symbol.iterator) \
|
V(iterator_symbol, Symbol.iterator) \
|
||||||
V(intl_fallback_symbol, IntlFallback) \
|
V(intl_fallback_symbol, IntlFallback) \
|
||||||
|
V(match_all_symbol, Symbol.matchAll) \
|
||||||
V(match_symbol, Symbol.match) \
|
V(match_symbol, Symbol.match) \
|
||||||
V(replace_symbol, Symbol.replace) \
|
V(replace_symbol, Symbol.replace) \
|
||||||
V(search_symbol, Symbol.search) \
|
V(search_symbol, Symbol.search) \
|
||||||
|
@ -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_KEY_VALUE_ITERATOR_TYPE:
|
||||||
case JS_MAP_VALUE_ITERATOR_TYPE:
|
case JS_MAP_VALUE_ITERATOR_TYPE:
|
||||||
case JS_STRING_ITERATOR_TYPE:
|
case JS_STRING_ITERATOR_TYPE:
|
||||||
|
case JS_REGEXP_STRING_ITERATOR_TYPE:
|
||||||
case JS_REGEXP_TYPE:
|
case JS_REGEXP_TYPE:
|
||||||
case JS_GLOBAL_PROXY_TYPE:
|
case JS_GLOBAL_PROXY_TYPE:
|
||||||
case JS_GLOBAL_OBJECT_TYPE:
|
case JS_GLOBAL_OBJECT_TYPE:
|
||||||
|
@ -276,6 +276,9 @@ void HeapObject::HeapObjectVerify() {
|
|||||||
case JS_REGEXP_TYPE:
|
case JS_REGEXP_TYPE:
|
||||||
JSRegExp::cast(this)->JSRegExpVerify();
|
JSRegExp::cast(this)->JSRegExpVerify();
|
||||||
break;
|
break;
|
||||||
|
case JS_REGEXP_STRING_ITERATOR_TYPE:
|
||||||
|
JSRegExpStringIterator::cast(this)->JSRegExpStringIteratorVerify();
|
||||||
|
break;
|
||||||
case FILLER_TYPE:
|
case FILLER_TYPE:
|
||||||
break;
|
break;
|
||||||
case JS_PROXY_TYPE:
|
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() {
|
void JSProxy::JSProxyVerify() {
|
||||||
CHECK(IsJSProxy());
|
CHECK(IsJSProxy());
|
||||||
VerifyPointer(target());
|
VerifyPointer(target());
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
#include "src/objects/js-collection-inl.h"
|
#include "src/objects/js-collection-inl.h"
|
||||||
#include "src/objects/js-promise-inl.h"
|
#include "src/objects/js-promise-inl.h"
|
||||||
#include "src/objects/js-regexp-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/literal-objects.h"
|
||||||
#include "src/objects/module-inl.h"
|
#include "src/objects/module-inl.h"
|
||||||
#include "src/objects/regexp-match-info.h"
|
#include "src/objects/regexp-match-info.h"
|
||||||
|
@ -165,6 +165,9 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
|
|||||||
case JS_REGEXP_TYPE:
|
case JS_REGEXP_TYPE:
|
||||||
JSRegExp::cast(this)->JSRegExpPrint(os);
|
JSRegExp::cast(this)->JSRegExpPrint(os);
|
||||||
break;
|
break;
|
||||||
|
case JS_REGEXP_STRING_ITERATOR_TYPE:
|
||||||
|
JSRegExpStringIterator::cast(this)->JSRegExpStringIteratorPrint(os);
|
||||||
|
break;
|
||||||
case ODDBALL_TYPE:
|
case ODDBALL_TYPE:
|
||||||
Oddball::cast(this)->to_string()->Print(os);
|
Oddball::cast(this)->to_string()->Print(os);
|
||||||
break;
|
break;
|
||||||
@ -619,6 +622,16 @@ void JSRegExp::JSRegExpPrint(std::ostream& os) { // NOLINT
|
|||||||
JSObjectPrintBody(os, this);
|
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
|
void Symbol::SymbolPrint(std::ostream& os) { // NOLINT
|
||||||
HeapObject::PrintHeader(os, "Symbol");
|
HeapObject::PrintHeader(os, "Symbol");
|
||||||
@ -1079,7 +1092,6 @@ void JSCollectionIterator::JSCollectionIteratorPrint(
|
|||||||
os << "\n";
|
os << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void JSSetIterator::JSSetIteratorPrint(std::ostream& os) { // NOLINT
|
void JSSetIterator::JSSetIteratorPrint(std::ostream& os) { // NOLINT
|
||||||
JSObjectPrintHeader(os, this, "JSSetIterator");
|
JSObjectPrintHeader(os, this, "JSSetIterator");
|
||||||
JSCollectionIteratorPrint(os);
|
JSCollectionIteratorPrint(os);
|
||||||
|
@ -59,6 +59,7 @@
|
|||||||
#include "src/objects/debug-objects-inl.h"
|
#include "src/objects/debug-objects-inl.h"
|
||||||
#include "src/objects/frame-array-inl.h"
|
#include "src/objects/frame-array-inl.h"
|
||||||
#include "src/objects/hash-table.h"
|
#include "src/objects/hash-table.h"
|
||||||
|
#include "src/objects/js-regexp-string-iterator.h"
|
||||||
#include "src/objects/map.h"
|
#include "src/objects/map.h"
|
||||||
#include "src/objects/microtask-inl.h"
|
#include "src/objects/microtask-inl.h"
|
||||||
#include "src/objects/promise-inl.h"
|
#include "src/objects/promise-inl.h"
|
||||||
@ -1369,6 +1370,8 @@ int JSObject::GetHeaderSize(InstanceType type,
|
|||||||
return JSPromise::kSize;
|
return JSPromise::kSize;
|
||||||
case JS_REGEXP_TYPE:
|
case JS_REGEXP_TYPE:
|
||||||
return JSRegExp::kSize;
|
return JSRegExp::kSize;
|
||||||
|
case JS_REGEXP_STRING_ITERATOR_TYPE:
|
||||||
|
return JSRegExpStringIterator::kSize;
|
||||||
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
case JS_CONTEXT_EXTENSION_OBJECT_TYPE:
|
||||||
return JSObject::kHeaderSize;
|
return JSObject::kHeaderSize;
|
||||||
case JS_MESSAGE_OBJECT_TYPE:
|
case JS_MESSAGE_OBJECT_TYPE:
|
||||||
@ -3100,6 +3103,7 @@ VisitorId Map::GetVisitorId(Map* map) {
|
|||||||
case JS_STRING_ITERATOR_TYPE:
|
case JS_STRING_ITERATOR_TYPE:
|
||||||
case JS_PROMISE_TYPE:
|
case JS_PROMISE_TYPE:
|
||||||
case JS_REGEXP_TYPE:
|
case JS_REGEXP_TYPE:
|
||||||
|
case JS_REGEXP_STRING_ITERATOR_TYPE:
|
||||||
case WASM_GLOBAL_TYPE:
|
case WASM_GLOBAL_TYPE:
|
||||||
case WASM_MEMORY_TYPE:
|
case WASM_MEMORY_TYPE:
|
||||||
case WASM_MODULE_TYPE:
|
case WASM_MODULE_TYPE:
|
||||||
|
@ -464,6 +464,7 @@ const int kStubMinorKeyBits = kSmiValueSize - kStubMajorKeyBits - 1;
|
|||||||
V(JS_MESSAGE_OBJECT_TYPE) \
|
V(JS_MESSAGE_OBJECT_TYPE) \
|
||||||
V(JS_PROMISE_TYPE) \
|
V(JS_PROMISE_TYPE) \
|
||||||
V(JS_REGEXP_TYPE) \
|
V(JS_REGEXP_TYPE) \
|
||||||
|
V(JS_REGEXP_STRING_ITERATOR_TYPE) \
|
||||||
V(JS_SET_TYPE) \
|
V(JS_SET_TYPE) \
|
||||||
V(JS_SET_KEY_VALUE_ITERATOR_TYPE) \
|
V(JS_SET_KEY_VALUE_ITERATOR_TYPE) \
|
||||||
V(JS_SET_VALUE_ITERATOR_TYPE) \
|
V(JS_SET_VALUE_ITERATOR_TYPE) \
|
||||||
@ -839,6 +840,7 @@ enum InstanceType : uint16_t {
|
|||||||
JS_MESSAGE_OBJECT_TYPE,
|
JS_MESSAGE_OBJECT_TYPE,
|
||||||
JS_PROMISE_TYPE,
|
JS_PROMISE_TYPE,
|
||||||
JS_REGEXP_TYPE,
|
JS_REGEXP_TYPE,
|
||||||
|
JS_REGEXP_STRING_ITERATOR_TYPE,
|
||||||
JS_SET_TYPE,
|
JS_SET_TYPE,
|
||||||
JS_SET_KEY_VALUE_ITERATOR_TYPE,
|
JS_SET_KEY_VALUE_ITERATOR_TYPE,
|
||||||
JS_SET_VALUE_ITERATOR_TYPE,
|
JS_SET_VALUE_ITERATOR_TYPE,
|
||||||
@ -1073,6 +1075,7 @@ template <class C> inline bool Is(Object* obj);
|
|||||||
V(JSProxy) \
|
V(JSProxy) \
|
||||||
V(JSReceiver) \
|
V(JSReceiver) \
|
||||||
V(JSRegExp) \
|
V(JSRegExp) \
|
||||||
|
V(JSRegExpStringIterator) \
|
||||||
V(JSSet) \
|
V(JSSet) \
|
||||||
V(JSSetIterator) \
|
V(JSSetIterator) \
|
||||||
V(JSSloppyArgumentsObject) \
|
V(JSSloppyArgumentsObject) \
|
||||||
|
35
src/objects/js-regexp-string-iterator-inl.h
Normal file
35
src/objects/js-regexp-string-iterator-inl.h
Normal file
@ -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_
|
59
src/objects/js-regexp-string-iterator.h
Normal file
59
src/objects/js-regexp-string-iterator.h
Normal file
@ -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_
|
100
test/mjsunit/harmony/string-matchAll.js
Normal file
100
test/mjsunit/harmony/string-matchAll.js
Normal file
@ -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);
|
||||||
|
})();
|
@ -51,6 +51,8 @@ FEATURE_FLAGS = {
|
|||||||
'class-fields-private': '--harmony-private-fields',
|
'class-fields-private': '--harmony-private-fields',
|
||||||
'Array.prototype.flatten': '--harmony-array-flatten',
|
'Array.prototype.flatten': '--harmony-array-flatten',
|
||||||
'Array.prototype.flatMap': '--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',
|
'numeric-separator-literal': '--harmony-numeric-separator',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,21 +137,22 @@ INSTANCE_TYPES = {
|
|||||||
1072: "JS_MESSAGE_OBJECT_TYPE",
|
1072: "JS_MESSAGE_OBJECT_TYPE",
|
||||||
1073: "JS_PROMISE_TYPE",
|
1073: "JS_PROMISE_TYPE",
|
||||||
1074: "JS_REGEXP_TYPE",
|
1074: "JS_REGEXP_TYPE",
|
||||||
1075: "JS_SET_TYPE",
|
1075: "JS_REGEXP_STRING_ITERATOR_TYPE",
|
||||||
1076: "JS_SET_KEY_VALUE_ITERATOR_TYPE",
|
1076: "JS_SET_TYPE",
|
||||||
1077: "JS_SET_VALUE_ITERATOR_TYPE",
|
1077: "JS_SET_KEY_VALUE_ITERATOR_TYPE",
|
||||||
1078: "JS_STRING_ITERATOR_TYPE",
|
1078: "JS_SET_VALUE_ITERATOR_TYPE",
|
||||||
1079: "JS_WEAK_MAP_TYPE",
|
1079: "JS_STRING_ITERATOR_TYPE",
|
||||||
1080: "JS_WEAK_SET_TYPE",
|
1080: "JS_WEAK_MAP_TYPE",
|
||||||
1081: "JS_TYPED_ARRAY_TYPE",
|
1081: "JS_WEAK_SET_TYPE",
|
||||||
1082: "JS_DATA_VIEW_TYPE",
|
1082: "JS_TYPED_ARRAY_TYPE",
|
||||||
1083: "WASM_GLOBAL_TYPE",
|
1083: "JS_DATA_VIEW_TYPE",
|
||||||
1084: "WASM_INSTANCE_TYPE",
|
1084: "WASM_GLOBAL_TYPE",
|
||||||
1085: "WASM_MEMORY_TYPE",
|
1085: "WASM_INSTANCE_TYPE",
|
||||||
1086: "WASM_MODULE_TYPE",
|
1086: "WASM_MEMORY_TYPE",
|
||||||
1087: "WASM_TABLE_TYPE",
|
1087: "WASM_MODULE_TYPE",
|
||||||
1088: "JS_BOUND_FUNCTION_TYPE",
|
1088: "WASM_TABLE_TYPE",
|
||||||
1089: "JS_FUNCTION_TYPE",
|
1089: "JS_BOUND_FUNCTION_TYPE",
|
||||||
|
1090: "JS_FUNCTION_TYPE",
|
||||||
}
|
}
|
||||||
|
|
||||||
# List of known V8 maps.
|
# List of known V8 maps.
|
||||||
|
Loading…
Reference in New Issue
Block a user