[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-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",
|
||||
|
@ -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<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() {
|
||||
if (!FLAG_harmony_promise_finally) return;
|
||||
|
||||
|
@ -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 */ \
|
||||
|
@ -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 <class T>
|
||||
using TNode = compiler::TNode<T>;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 21.2 RegExp Objects
|
||||
@ -88,6 +91,28 @@ Node* RegExpBuiltinsAssembler::AllocateRegExpResult(Node* context, Node* length,
|
||||
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) {
|
||||
// Load the in-object field.
|
||||
static const int field_offset =
|
||||
@ -1949,6 +1974,163 @@ TF_BUILTIN(RegExpPrototypeMatch, RegExpBuiltinsAssembler) {
|
||||
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...
|
||||
// 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<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 v8
|
||||
|
@ -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<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:
|
||||
// Allocate a RegExpResult with the given length (the number of captures,
|
||||
// including the match itself), index (the index where the match starts),
|
||||
|
@ -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> 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<Object> receiver, TNode<Object> maybe_regexp,
|
||||
TNode<Context> 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<Object> const pattern = Select<Object>(
|
||||
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<String> receiver_string = ToString_Inline(context, receiver);
|
||||
TNode<Context> native_context = LoadNativeContext(context);
|
||||
TNode<HeapObject> regexp_function = CAST(
|
||||
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
|
||||
TNode<Map> initial_map = CAST(LoadObjectField(
|
||||
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
|
||||
TNode<Object> 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<Object> 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<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
|
||||
TNode<Context> 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> 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 {
|
||||
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<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
TNode<Object> maybe_regexp = CAST(Parameter(Descriptor::kRegexp));
|
||||
TNode<Context> 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.
|
||||
|
||||
|
@ -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<Node*()> NodeFunction0;
|
||||
typedef std::function<Node*(Node* fn)> NodeFunction1;
|
||||
typedef std::function<void()> NodeFunction0;
|
||||
typedef std::function<void(Node* fn)> NodeFunction1;
|
||||
void MaybeCallFunctionAtSymbol(Node* const context, Node* const object,
|
||||
Node* const maybe_string,
|
||||
Handle<Symbol> symbol,
|
||||
const NodeFunction0& regexp_call,
|
||||
const NodeFunction1& generic_call,
|
||||
CodeStubArguments* args = nullptr);
|
||||
const NodeFunction1& generic_call);
|
||||
};
|
||||
|
||||
class StringIncludesIndexOfAssembler : public StringBuiltinsAssembler {
|
||||
|
@ -29,6 +29,7 @@ class Callable;
|
||||
class CallInterfaceDescriptor;
|
||||
class Isolate;
|
||||
class JSCollection;
|
||||
class JSRegExpStringIterator;
|
||||
class JSWeakCollection;
|
||||
class JSWeakMap;
|
||||
class JSWeakSet;
|
||||
|
@ -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:
|
||||
|
@ -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) \
|
||||
|
@ -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) \
|
||||
|
@ -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) \
|
||||
|
@ -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:
|
||||
|
@ -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());
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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 <class C> inline bool Is(Object* obj);
|
||||
V(JSProxy) \
|
||||
V(JSReceiver) \
|
||||
V(JSRegExp) \
|
||||
V(JSRegExpStringIterator) \
|
||||
V(JSSet) \
|
||||
V(JSSetIterator) \
|
||||
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',
|
||||
'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',
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user