[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:
peterwmwong 2018-04-05 09:41:26 -05:00 committed by Commit Bot
parent 0872da3773
commit 3b39fc4dcd
23 changed files with 761 additions and 71 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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 */ \

View File

@ -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

View File

@ -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),

View File

@ -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.

View File

@ -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 {

View File

@ -29,6 +29,7 @@ class Callable;
class CallInterfaceDescriptor;
class Isolate;
class JSCollection;
class JSRegExpStringIterator;
class JSWeakCollection;
class JSWeakMap;
class JSWeakSet;

View File

@ -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:

View File

@ -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) \

View File

@ -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) \

View File

@ -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) \

View File

@ -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:

View File

@ -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());

View File

@ -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"

View File

@ -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);

View File

@ -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:

View File

@ -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) \

View 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_

View 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_

View 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);
})();

View File

@ -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',
}

View File

@ -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.