summaryrefslogtreecommitdiff
path: root/deps/v8/src/builtins/builtins-regexp-gen.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/builtins/builtins-regexp-gen.cc')
-rw-r--r--deps/v8/src/builtins/builtins-regexp-gen.cc955
1 files changed, 128 insertions, 827 deletions
diff --git a/deps/v8/src/builtins/builtins-regexp-gen.cc b/deps/v8/src/builtins/builtins-regexp-gen.cc
index f879d70c67..4bc0b6ad74 100644
--- a/deps/v8/src/builtins/builtins-regexp-gen.cc
+++ b/deps/v8/src/builtins/builtins-regexp-gen.cc
@@ -23,8 +23,6 @@ namespace v8 {
namespace internal {
using compiler::Node;
-template <class T>
-using TNode = compiler::TNode<T>;
// Tail calls the regular expression interpreter.
// static
@@ -80,7 +78,8 @@ TNode<RawPtrT> RegExpBuiltinsAssembler::LoadCodeObjectEntry(TNode<Code> code) {
TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
TNode<Context> context, TNode<Smi> length, TNode<Smi> index,
- TNode<String> input, TNode<FixedArray>* elements_out) {
+ TNode<String> input, TNode<RegExpMatchInfo> match_info,
+ TNode<FixedArray>* elements_out) {
CSA_ASSERT(this, SmiLessThanOrEqual(
length, SmiConstant(JSArray::kMaxFastArrayLength)));
CSA_ASSERT(this, SmiGreaterThan(length, SmiConstant(0)));
@@ -90,9 +89,8 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
const ElementsKind elements_kind = PACKED_ELEMENTS;
TNode<Map> map = CAST(LoadContextElement(LoadNativeContext(context),
Context::REGEXP_RESULT_MAP_INDEX));
- Node* no_allocation_site = nullptr;
+ TNode<AllocationSite> no_allocation_site = {};
TNode<IntPtrT> length_intptr = SmiUntag(length);
- TNode<IntPtrT> capacity = length_intptr;
// Note: The returned `elements` may be in young large object space, but
// `array` is guaranteed to be in new space so we could skip write barriers
@@ -100,18 +98,29 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::AllocateRegExpResult(
TNode<JSArray> array;
TNode<FixedArrayBase> elements;
std::tie(array, elements) = AllocateUninitializedJSArrayWithElements(
- elements_kind, map, length, no_allocation_site, capacity,
+ elements_kind, map, length, no_allocation_site, length_intptr,
INTPTR_PARAMETERS, kAllowLargeObjectAllocation, JSRegExpResult::kSize);
// Finish result initialization.
TNode<JSRegExpResult> result = CAST(array);
+ // Load undefined value once here to avoid multiple LoadRoots.
+ TNode<Oddball> undefined_value = UncheckedCast<Oddball>(
+ CodeAssembler::LoadRoot(RootIndex::kUndefinedValue));
+
StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kIndexOffset, index);
// TODO(jgruber,tebbi): Could skip barrier but the MemoryOptimizer complains.
StoreObjectField(result, JSRegExpResult::kInputOffset, input);
StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kGroupsOffset,
- UndefinedConstant());
+ undefined_value);
+ StoreObjectFieldNoWriteBarrier(result, JSRegExpResult::kNamesOffset,
+ undefined_value);
+
+ // Stash match_info in order to build JSRegExpResultIndices lazily when the
+ // 'indices' property is accessed.
+ StoreObjectField(result, JSRegExpResult::kCachedIndicesOrMatchInfoOffset,
+ match_info);
// Finish elements initialization.
@@ -213,7 +222,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<FixedArray> result_elements;
TNode<JSRegExpResult> result = AllocateRegExpResult(
- context, num_results, start, string, &result_elements);
+ context, num_results, start, string, match_info, &result_elements);
UnsafeStoreFixedArrayElement(result_elements, 0, first);
@@ -228,8 +237,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex + 2));
TVARIABLE(IntPtrT, var_to_cursor, IntPtrConstant(1));
- Variable* vars[] = {&var_from_cursor, &var_to_cursor};
- Label loop(this, 2, vars);
+ Label loop(this, {&var_from_cursor, &var_to_cursor});
Goto(&loop);
BIND(&loop);
@@ -289,6 +297,9 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TNode<IntPtrT> names_length = LoadAndUntagFixedArrayBaseLength(names);
CSA_ASSERT(this, IntPtrGreaterThan(names_length, IntPtrZero()));
+ // Stash names in case we need them to build the indices array later.
+ StoreObjectField(result, JSRegExpResult::kNamesOffset, names);
+
// Allocate a new object to store the named capture properties.
// TODO(jgruber): Could be optimized by adding the object map to the heap
// root list.
@@ -305,9 +316,7 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
TVARIABLE(IntPtrT, var_i, IntPtrZero());
- Variable* vars[] = {&var_i};
- const int vars_count = sizeof(vars) / sizeof(vars[0]);
- Label loop(this, vars_count, vars);
+ Label loop(this, &var_i);
Goto(&loop);
BIND(&loop);
@@ -355,9 +364,10 @@ TNode<JSRegExpResult> RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
}
void RegExpBuiltinsAssembler::GetStringPointers(
- Node* const string_data, Node* const offset, Node* const last_index,
- Node* const string_length, String::Encoding encoding,
- Variable* var_string_start, Variable* var_string_end) {
+ TNode<RawPtrT> string_data, TNode<IntPtrT> offset,
+ TNode<IntPtrT> last_index, TNode<IntPtrT> string_length,
+ String::Encoding encoding, TVariable<RawPtrT>* var_string_start,
+ TVariable<RawPtrT>* var_string_end) {
DCHECK_EQ(var_string_start->rep(), MachineType::PointerRepresentation());
DCHECK_EQ(var_string_end->rep(), MachineType::PointerRepresentation());
@@ -365,13 +375,14 @@ void RegExpBuiltinsAssembler::GetStringPointers(
? UINT8_ELEMENTS
: UINT16_ELEMENTS;
- TNode<IntPtrT> const from_offset = ElementOffsetFromIndex(
- IntPtrAdd(offset, last_index), kind, INTPTR_PARAMETERS);
- var_string_start->Bind(IntPtrAdd(string_data, from_offset));
+ TNode<IntPtrT> from_offset =
+ ElementOffsetFromIndex(IntPtrAdd(offset, last_index), kind);
+ *var_string_start =
+ ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, from_offset));
- TNode<IntPtrT> const to_offset = ElementOffsetFromIndex(
- IntPtrAdd(offset, string_length), kind, INTPTR_PARAMETERS);
- var_string_end->Bind(IntPtrAdd(string_data, to_offset));
+ TNode<IntPtrT> to_offset =
+ ElementOffsetFromIndex(IntPtrAdd(offset, string_length), kind);
+ *var_string_end = ReinterpretCast<RawPtrT>(IntPtrAdd(string_data, to_offset));
}
TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
@@ -507,27 +518,18 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
GotoIf(TaggedIsSmi(var_code.value()), &runtime);
TNode<Code> code = CAST(var_code.value());
- // Tier-up in runtime if ticks are non-zero and tier-up hasn't happened yet
- // and ensure that a RegExp stack is allocated when using compiled Irregexp.
+ // Ensure that a RegExp stack is allocated when using compiled Irregexp.
+ // TODO(jgruber): Guarantee an allocated stack and remove this check.
{
- Label next(this), check_tier_up(this);
- GotoIfNot(TaggedIsSmi(var_bytecode.value()), &check_tier_up);
+ Label next(this);
+ GotoIfNot(TaggedIsSmi(var_bytecode.value()), &next);
CSA_ASSERT(this, SmiEqual(CAST(var_bytecode.value()),
SmiConstant(JSRegExp::kUninitializedValue)));
- // Ensure RegExp stack is allocated.
TNode<IntPtrT> stack_size = UncheckedCast<IntPtrT>(
Load(MachineType::IntPtr(), regexp_stack_memory_size_address));
- GotoIf(IntPtrEqual(stack_size, IntPtrZero()), &runtime);
- Goto(&next);
-
- // Check if tier-up is requested.
- BIND(&check_tier_up);
- TNode<Smi> ticks = CAST(
- UnsafeLoadFixedArrayElement(data, JSRegExp::kIrregexpTierUpTicksIndex));
- GotoIf(SmiToInt32(ticks), &runtime);
+ Branch(IntPtrEqual(stack_size, IntPtrZero()), &runtime, &next);
- Goto(&next);
BIND(&next);
}
@@ -656,18 +658,18 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
// Fill match and capture offsets in match_info.
{
- TNode<IntPtrT> limit_offset = ElementOffsetFromIndex(
- register_count, INT32_ELEMENTS, SMI_PARAMETERS, 0);
+ TNode<IntPtrT> limit_offset =
+ ElementOffsetFromIndex(register_count, INT32_ELEMENTS, 0);
TNode<IntPtrT> to_offset = ElementOffsetFromIndex(
IntPtrConstant(RegExpMatchInfo::kFirstCaptureIndex), PACKED_ELEMENTS,
- INTPTR_PARAMETERS, RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
+ RegExpMatchInfo::kHeaderSize - kHeapObjectTag);
TVARIABLE(IntPtrT, var_to_offset, to_offset);
VariableList vars({&var_to_offset}, zone());
- BuildFastLoop(
+ BuildFastLoop<IntPtrT>(
vars, IntPtrZero(), limit_offset,
- [=, &var_to_offset](Node* offset) {
+ [&](TNode<IntPtrT> offset) {
TNode<Int32T> value = UncheckedCast<Int32T>(Load(
MachineType::Int32(), static_offsets_vector_address, offset));
TNode<Smi> smi_value = SmiFromInt32(value);
@@ -675,7 +677,7 @@ TNode<HeapObject> RegExpBuiltinsAssembler::RegExpExecInternal(
var_to_offset.value(), smi_value);
Increment(&var_to_offset, kTaggedSize);
},
- kInt32Size, INTPTR_PARAMETERS, IndexAdvanceMode::kPost);
+ kInt32Size, IndexAdvanceMode::kPost);
}
var_result = match_info;
@@ -733,7 +735,7 @@ RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
TNode<String> string, Label* if_didnotmatch, const bool is_fastpath) {
if (!is_fastpath) {
- ThrowIfNotInstanceType(context, maybe_regexp, JS_REGEXP_TYPE,
+ ThrowIfNotInstanceType(context, maybe_regexp, JS_REG_EXP_TYPE,
"RegExp.prototype.exec");
}
@@ -894,14 +896,13 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsReceiverInitialRegExpPrototype(
return TaggedEqual(receiver, initial_prototype);
}
-Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
- SloppyTNode<Context> context, SloppyTNode<Object> object,
- SloppyTNode<Map> map) {
+TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
+ TNode<Context> context, TNode<Object> object, TNode<Map> map) {
Label out(this);
- VARIABLE(var_result, MachineRepresentation::kWord32);
+ TVARIABLE(BoolT, var_result);
#ifdef V8_ENABLE_FORCE_SLOW_PATH
- var_result.Bind(Int32Constant(0));
+ var_result = Int32FalseConstant();
GotoIfForceSlowPath(&out);
#endif
@@ -912,13 +913,13 @@ Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
LoadObjectField(regexp_fun, JSFunction::kPrototypeOrInitialMapOffset);
TNode<BoolT> const has_initialmap = TaggedEqual(map, initial_map);
- var_result.Bind(has_initialmap);
+ var_result = has_initialmap;
GotoIfNot(has_initialmap, &out);
// The smi check is required to omit ToLength(lastIndex) calls with possible
// user-code execution on the fast path.
TNode<Object> last_index = FastLoadLastIndexBeforeSmiCheck(CAST(object));
- var_result.Bind(TaggedIsPositiveSmi(last_index));
+ var_result = TaggedIsPositiveSmi(last_index);
Goto(&out);
BIND(&out);
@@ -939,7 +940,7 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
GotoIfForceSlowPath(&out);
#endif
- TNode<BoolT> is_regexp = HasInstanceType(object, JS_REGEXP_TYPE);
+ TNode<BoolT> is_regexp = HasInstanceType(object, JS_REG_EXP_TYPE);
var_result = is_regexp;
GotoIfNot(is_regexp, &out);
@@ -970,8 +971,8 @@ TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpWithOriginalExec(
return var_result.value();
}
-Node* RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
- SloppyTNode<Context> context, SloppyTNode<Object> object) {
+TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExpNoPrototype(
+ TNode<Context> context, TNode<Object> object) {
CSA_ASSERT(this, TaggedIsNotSmi(object));
return IsFastRegExpNoPrototype(context, object, LoadMap(CAST(object)));
}
@@ -1046,10 +1047,9 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
if_isunmodified, if_ismodified);
}
-void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
- Node* const object,
- Label* if_isunmodified,
- Label* if_ismodified) {
+void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(
+ const TNode<Context> context, const TNode<Object> object,
+ Label* if_isunmodified, Label* if_ismodified) {
// Could be a Smi.
TNode<Map> const map = LoadReceiverMap(object);
@@ -1061,15 +1061,6 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
if_ismodified);
}
-// Slow path stub for RegExpPrototypeExec to decrease code size.
-TF_BUILTIN(RegExpPrototypeExecSlow, RegExpBuiltinsAssembler) {
- TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
- TNode<String> string = CAST(Parameter(Descriptor::kString));
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- Return(RegExpPrototypeExecBody(context, regexp, string, false));
-}
-
// Fast path stub for ATOM regexps. String matching is done by StringIndexOf,
// and {match_info} is updated on success.
// The slow path is implemented in RegExp::AtomExec.
@@ -1149,33 +1140,6 @@ TF_BUILTIN(RegExpExecInternal, RegExpBuiltinsAssembler) {
Return(RegExpExecInternal(context, regexp, string, last_index, match_info));
}
-// ES#sec-regexp.prototype.exec
-// RegExp.prototype.exec ( string )
-TF_BUILTIN(RegExpPrototypeExec, RegExpBuiltinsAssembler) {
- TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
- TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- // Ensure {maybe_receiver} is a JSRegExp.
- ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
- "RegExp.prototype.exec");
- TNode<JSRegExp> receiver = CAST(maybe_receiver);
-
- // Convert {maybe_string} to a String.
- TNode<String> string = ToString_Inline(context, maybe_string);
-
- Label if_isfastpath(this), if_isslowpath(this);
- Branch(IsFastRegExpNoPrototype(context, receiver), &if_isfastpath,
- &if_isslowpath);
-
- BIND(&if_isfastpath);
- Return(RegExpPrototypeExecBody(context, receiver, string, true));
-
- BIND(&if_isslowpath);
- Return(CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context, receiver,
- string));
-}
-
TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context,
TNode<Object> regexp,
bool is_fastpath) {
@@ -1246,8 +1210,8 @@ TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context,
{
TNode<String> const result = AllocateSeqOneByteString(var_length.value());
- VARIABLE(var_offset, MachineType::PointerRepresentation(),
- IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
+ TVARIABLE(IntPtrT, var_offset,
+ IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag));
#define CASE_FOR_FLAG(FLAG, CHAR) \
do { \
@@ -1256,7 +1220,7 @@ TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context,
TNode<Int32T> const value = Int32Constant(CHAR); \
StoreNoWriteBarrier(MachineRepresentation::kWord8, result, \
var_offset.value(), value); \
- var_offset.Bind(IntPtrAdd(var_offset.value(), int_one)); \
+ var_offset = IntPtrAdd(var_offset.value(), int_one); \
Goto(&next); \
BIND(&next); \
} while (false)
@@ -1273,64 +1237,11 @@ TNode<String> RegExpBuiltinsAssembler::FlagsGetter(TNode<Context> context,
}
}
-// ES#sec-isregexp IsRegExp ( argument )
-TNode<BoolT> RegExpBuiltinsAssembler::IsRegExp(TNode<Context> context,
- TNode<Object> maybe_receiver) {
- Label out(this), if_isregexp(this);
-
- TVARIABLE(BoolT, var_result, Int32FalseConstant());
-
- GotoIf(TaggedIsSmi(maybe_receiver), &out);
- GotoIfNot(IsJSReceiver(CAST(maybe_receiver)), &out);
-
- TNode<JSReceiver> receiver = CAST(maybe_receiver);
-
- // Check @@match.
- {
- TNode<Object> value =
- GetProperty(context, receiver, isolate()->factory()->match_symbol());
-
- Label match_isundefined(this), match_isnotundefined(this);
- Branch(IsUndefined(value), &match_isundefined, &match_isnotundefined);
-
- BIND(&match_isundefined);
- Branch(IsJSRegExp(receiver), &if_isregexp, &out);
-
- BIND(&match_isnotundefined);
- Label match_istrueish(this), match_isfalseish(this);
- BranchIfToBooleanIsTrue(value, &match_istrueish, &match_isfalseish);
-
- // The common path. Symbol.match exists, equals the RegExpPrototypeMatch
- // function (and is thus trueish), and the receiver is a JSRegExp.
- BIND(&match_istrueish);
- GotoIf(IsJSRegExp(receiver), &if_isregexp);
- CallRuntime(Runtime::kIncrementUseCounter, context,
- SmiConstant(v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp));
- Goto(&if_isregexp);
-
- BIND(&match_isfalseish);
- GotoIfNot(IsJSRegExp(receiver), &out);
- CallRuntime(Runtime::kIncrementUseCounter, context,
- SmiConstant(v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp));
- Goto(&out);
- }
-
- BIND(&if_isregexp);
- var_result = Int32TrueConstant();
- Goto(&out);
-
- BIND(&out);
- return var_result.value();
-}
-
// ES#sec-regexpinitialize
// Runtime Semantics: RegExpInitialize ( obj, pattern, flags )
-Node* RegExpBuiltinsAssembler::RegExpInitialize(Node* const context,
- Node* const regexp,
- Node* const maybe_pattern,
- Node* const maybe_flags) {
- CSA_ASSERT(this, IsJSRegExp(regexp));
-
+TNode<Object> RegExpBuiltinsAssembler::RegExpInitialize(
+ const TNode<Context> context, const TNode<JSRegExp> regexp,
+ const TNode<Object> maybe_pattern, const TNode<Object> maybe_flags) {
// Normalize pattern.
TNode<Object> const pattern = Select<Object>(
IsUndefined(maybe_pattern), [=] { return EmptyStringConstant(); },
@@ -1437,7 +1348,7 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
// Allocate.
- VARIABLE(var_regexp, MachineRepresentation::kTagged);
+ TVARIABLE(JSRegExp, var_regexp);
{
Label allocate_jsregexp(this), allocate_generic(this, Label::kDeferred),
next(this);
@@ -1448,25 +1359,23 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
{
TNode<Map> const initial_map = CAST(LoadObjectField(
regexp_function, JSFunction::kPrototypeOrInitialMapOffset));
- TNode<JSObject> const regexp = AllocateJSObjectFromMap(initial_map);
- var_regexp.Bind(regexp);
+ var_regexp = CAST(AllocateJSObjectFromMap(initial_map));
Goto(&next);
}
BIND(&allocate_generic);
{
ConstructorBuiltinsAssembler constructor_assembler(this->state());
- TNode<JSObject> const regexp = constructor_assembler.EmitFastNewObject(
- context, regexp_function, CAST(var_new_target.value()));
- var_regexp.Bind(regexp);
+ var_regexp = CAST(constructor_assembler.EmitFastNewObject(
+ context, regexp_function, CAST(var_new_target.value())));
Goto(&next);
}
BIND(&next);
}
- Node* const result = RegExpInitialize(context, var_regexp.value(),
- var_pattern.value(), var_flags.value());
+ const TNode<Object> result = RegExpInitialize(
+ context, var_regexp.value(), var_pattern.value(), var_flags.value());
Return(result);
}
@@ -1478,12 +1387,12 @@ TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
TNode<Object> maybe_flags = CAST(Parameter(Descriptor::kFlags));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
- ThrowIfNotInstanceType(context, maybe_receiver, JS_REGEXP_TYPE,
+ ThrowIfNotInstanceType(context, maybe_receiver, JS_REG_EXP_TYPE,
"RegExp.prototype.compile");
- Node* const receiver = maybe_receiver;
+ const TNode<JSRegExp> receiver = CAST(maybe_receiver);
- VARIABLE(var_flags, MachineRepresentation::kTagged, maybe_flags);
- VARIABLE(var_pattern, MachineRepresentation::kTagged, maybe_pattern);
+ TVARIABLE(Object, var_flags, maybe_flags);
+ TVARIABLE(Object, var_pattern, maybe_pattern);
// Handle a JSRegExp pattern.
{
@@ -1492,8 +1401,6 @@ TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
GotoIf(TaggedIsSmi(maybe_pattern), &next);
GotoIfNot(IsJSRegExp(CAST(maybe_pattern)), &next);
- Node* const pattern = maybe_pattern;
-
// {maybe_flags} must be undefined in this case, otherwise throw.
{
Label next(this);
@@ -1504,19 +1411,20 @@ TF_BUILTIN(RegExpPrototypeCompile, RegExpBuiltinsAssembler) {
BIND(&next);
}
- TNode<String> const new_flags = FlagsGetter(context, CAST(pattern), true);
+ const TNode<JSRegExp> pattern = CAST(maybe_pattern);
+ TNode<String> const new_flags = FlagsGetter(context, pattern, true);
TNode<Object> const new_pattern =
LoadObjectField(pattern, JSRegExp::kSourceOffset);
- var_flags.Bind(new_flags);
- var_pattern.Bind(new_pattern);
+ var_flags = new_flags;
+ var_pattern = new_pattern;
Goto(&next);
BIND(&next);
}
- Node* const result = RegExpInitialize(context, receiver, var_pattern.value(),
- var_flags.value());
+ const TNode<Object> result = RegExpInitialize(
+ context, receiver, var_pattern.value(), var_flags.value());
Return(result);
}
@@ -1586,54 +1494,6 @@ TNode<BoolT> RegExpBuiltinsAssembler::FlagGetter(TNode<Context> context,
: SlowFlagGetter(context, regexp, flag);
}
-// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
-TNode<Object> RegExpBuiltinsAssembler::RegExpExec(TNode<Context> context,
- Node* regexp, Node* string) {
- TVARIABLE(Object, var_result);
- Label out(this);
-
- // Take the slow path of fetching the exec property, calling it, and
- // verifying its return value.
-
- // Get the exec property.
- TNode<Object> const exec =
- GetProperty(context, regexp, isolate()->factory()->exec_string());
-
- // Is {exec} callable?
- Label if_iscallable(this), if_isnotcallable(this);
-
- GotoIf(TaggedIsSmi(exec), &if_isnotcallable);
-
- TNode<Map> const exec_map = LoadMap(CAST(exec));
- Branch(IsCallableMap(exec_map), &if_iscallable, &if_isnotcallable);
-
- BIND(&if_iscallable);
- {
- Callable call_callable = CodeFactory::Call(isolate());
- var_result = CAST(CallJS(call_callable, context, exec, regexp, string));
-
- GotoIf(IsNull(var_result.value()), &out);
-
- ThrowIfNotJSReceiver(context, var_result.value(),
- MessageTemplate::kInvalidRegExpExecResult, "");
-
- Goto(&out);
- }
-
- BIND(&if_isnotcallable);
- {
- ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
- "RegExp.prototype.exec");
-
- var_result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context,
- regexp, string);
- Goto(&out);
- }
-
- BIND(&out);
- return var_result.value();
-}
-
TNode<Number> RegExpBuiltinsAssembler::AdvanceStringIndex(
SloppyTNode<String> string, SloppyTNode<Number> index,
SloppyTNode<BoolT> is_unicode, bool is_fastpath) {
@@ -1717,7 +1577,7 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
{
var_result = is_fastpath ? RegExpPrototypeExecBody(context, CAST(regexp),
string, true)
- : RegExpExec(context, regexp, string);
+ : RegExpExec(context, CAST(regexp), string);
Goto(&done);
}
@@ -1735,9 +1595,9 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
// Loop preparations. Within the loop, collect results from RegExpExec
// and store match strings in the array.
- Variable* vars[] = {array.var_array(), array.var_length(),
- array.var_capacity()};
- Label loop(this, 3, vars), out(this);
+ Label loop(this,
+ {array.var_array(), array.var_length(), array.var_capacity()}),
+ out(this);
// Check if the regexp is an ATOM type. If then, keep the literal string to
// search for so that we can avoid calling substring in the loop below.
@@ -1758,7 +1618,7 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
BIND(&loop);
{
- VARIABLE(var_match, MachineRepresentation::kTagged);
+ TVARIABLE(String, var_match);
Label if_didmatch(this), if_didnotmatch(this);
if (is_fastpath) {
@@ -1776,24 +1636,24 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
match_indices, RegExpMatchInfo::kFirstCaptureIndex);
TNode<Object> const match_to = UnsafeLoadFixedArrayElement(
match_indices, RegExpMatchInfo::kFirstCaptureIndex + 1);
- var_match.Bind(CallBuiltin(Builtins::kSubString, context, string,
- match_from, match_to));
+ var_match = CAST(CallBuiltin(Builtins::kSubString, context, string,
+ match_from, match_to));
Goto(&if_didmatch);
}
BIND(&donotsubstring);
- var_match.Bind(var_search_string.value());
+ var_match = var_search_string.value();
Goto(&if_didmatch);
} else {
DCHECK(!is_fastpath);
- TNode<Object> const result = RegExpExec(context, regexp, string);
+ TNode<Object> const result = RegExpExec(context, CAST(regexp), string);
Label load_match(this);
Branch(IsNull(result), &if_didnotmatch, &load_match);
BIND(&load_match);
- var_match.Bind(
- ToString_Inline(context, GetProperty(context, result, SmiZero())));
+ var_match =
+ ToString_Inline(context, GetProperty(context, result, SmiZero()));
Goto(&if_didmatch);
}
@@ -1807,11 +1667,11 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
BIND(&if_didmatch);
{
- Node* match = var_match.value();
+ TNode<String> match = var_match.value();
// Store the match, growing the fixed array if needed.
- array.Push(CAST(match));
+ array.Push(match);
// Advance last index if the match is the empty string.
@@ -1855,128 +1715,11 @@ TNode<Object> RegExpBuiltinsAssembler::RegExpPrototypeMatchBody(
return var_result.value();
}
-void RegExpMatchAllAssembler::Generate(TNode<Context> context,
- TNode<Context> native_context,
- TNode<Object> receiver,
- TNode<Object> maybe_string) {
- // 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. Let S be ? ToString(O).
- TNode<String> string = ToString_Inline(context, maybe_string);
-
- TVARIABLE(Object, var_matcher);
- TVARIABLE(BoolT, var_global);
- TVARIABLE(BoolT, var_unicode);
- Label create_iterator(this), if_fast_regexp(this),
- if_slow_regexp(this, Label::kDeferred);
-
- // Strict, because following code uses the flags property.
- // TODO(jgruber): Handle slow flag accesses on the fast path and make this
- // permissive.
- BranchIfFastRegExp_Strict(context, CAST(receiver), &if_fast_regexp,
- &if_slow_regexp);
-
- BIND(&if_fast_regexp);
- {
- TNode<JSRegExp> fast_regexp = CAST(receiver);
- TNode<Object> source =
- LoadObjectField(fast_regexp, JSRegExp::kSourceOffset);
-
- // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
- // 5. Let flags be ? ToString(? Get(R, "flags")).
- // 6. Let matcher be ? Construct(C, « R, flags »).
- TNode<String> flags = FlagsGetter(context, fast_regexp, true);
- var_matcher = RegExpCreate(context, native_context, source, flags);
- CSA_ASSERT(this,
- IsFastRegExpPermissive(context, CAST(var_matcher.value())));
-
- // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
- // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
- FastStoreLastIndex(CAST(var_matcher.value()),
- FastLoadLastIndex(fast_regexp));
-
- // 9. If flags contains "g", let global be true.
- // 10. Else, let global be false.
- var_global = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kGlobal);
-
- // 11. If flags contains "u", let fullUnicode be true.
- // 12. Else, let fullUnicode be false.
- var_unicode = FastFlagGetter(CAST(var_matcher.value()), JSRegExp::kUnicode);
- Goto(&create_iterator);
- }
-
- BIND(&if_slow_regexp);
- {
- // 4. Let C be ? SpeciesConstructor(R, %RegExp%).
- TNode<JSFunction> regexp_fun = CAST(
- LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX));
- TNode<JSReceiver> species_constructor =
- SpeciesConstructor(native_context, receiver, regexp_fun);
-
- // 5. Let flags be ? ToString(? Get(R, "flags")).
- TNode<Object> flags =
- GetProperty(context, receiver, isolate()->factory()->flags_string());
- TNode<String> flags_string = ToString_Inline(context, flags);
-
- // 6. Let matcher be ? Construct(C, « R, flags »).
- var_matcher =
- Construct(context, species_constructor, receiver, flags_string);
-
- // 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
- TNode<Number> last_index =
- ToLength_Inline(context, SlowLoadLastIndex(context, receiver));
-
- // 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
- SlowStoreLastIndex(context, var_matcher.value(), last_index);
-
- // 9. If flags contains "g", let global be true.
- // 10. Else, let global be false.
- TNode<String> global_char_string = StringConstant("g");
- TNode<Smi> global_ix =
- CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
- global_char_string, SmiZero()));
- var_global = SmiNotEqual(global_ix, SmiConstant(-1));
-
- // 11. If flags contains "u", let fullUnicode be true.
- // 12. Else, let fullUnicode be false.
- TNode<String> unicode_char_string = StringConstant("u");
- TNode<Smi> unicode_ix =
- CAST(CallBuiltin(Builtins::kStringIndexOf, context, flags_string,
- unicode_char_string, SmiZero()));
- var_unicode = SmiNotEqual(unicode_ix, SmiConstant(-1));
- Goto(&create_iterator);
- }
-
- BIND(&create_iterator);
- {
- {
- // UseCounter for matchAll with non-g RegExp.
- // https://crbug.com/v8/9551
- Label next(this);
- GotoIf(var_global.value(), &next);
- CallRuntime(Runtime::kIncrementUseCounter, context,
- SmiConstant(v8::Isolate::kRegExpMatchAllWithNonGlobalRegExp));
- Goto(&next);
- BIND(&next);
- }
-
- // 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
- TNode<Object> iterator =
- CreateRegExpStringIterator(native_context, var_matcher.value(), string,
- var_global.value(), var_unicode.value());
- Return(iterator);
- }
-}
-
// ES#sec-createregexpstringiterator
// CreateRegExpStringIterator ( R, S, global, fullUnicode )
TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
- TNode<Context> native_context, TNode<Object> regexp, TNode<String> string,
- TNode<BoolT> global, TNode<BoolT> full_unicode) {
+ TNode<NativeContext> native_context, TNode<Object> regexp,
+ TNode<String> string, TNode<BoolT> global, TNode<BoolT> full_unicode) {
TNode<Map> map = CAST(LoadContextElement(
native_context,
Context::INITIAL_REGEXP_STRING_ITERATOR_PROTOTYPE_MAP_INDEX));
@@ -2016,164 +1759,11 @@ TNode<Object> RegExpMatchAllAssembler::CreateRegExpStringIterator(
return iterator;
}
-// https://tc39.github.io/proposal-string-matchall/
-// RegExp.prototype [ @@matchAll ] ( string )
-TF_BUILTIN(RegExpPrototypeMatchAll, RegExpMatchAllAssembler) {
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
- TNode<NativeContext> native_context = LoadNativeContext(context);
- TNode<Object> receiver = CAST(Parameter(Descriptor::kReceiver));
- TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
- Generate(context, native_context, receiver, maybe_string);
-}
-
-void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
- TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string) {
- CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
-
- // Grab the initial value of last index.
- TNode<Smi> previous_last_index = FastLoadLastIndex(regexp);
-
- // Ensure last index is 0.
- FastStoreLastIndex(regexp, SmiZero());
-
- // Call exec.
- Label if_didnotmatch(this);
- TNode<RegExpMatchInfo> match_indices = RegExpPrototypeExecBodyWithoutResult(
- context, regexp, string, &if_didnotmatch, true);
-
- // Successful match.
- {
- // Reset last index.
- FastStoreLastIndex(regexp, previous_last_index);
-
- // Return the index of the match.
- TNode<Object> const index = LoadFixedArrayElement(
- match_indices, RegExpMatchInfo::kFirstCaptureIndex);
- Return(index);
- }
-
- BIND(&if_didnotmatch);
- {
- // Reset last index and return -1.
- FastStoreLastIndex(regexp, previous_last_index);
- Return(SmiConstant(-1));
- }
-}
-
-void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodySlow(
- TNode<Context> context, Node* const regexp, Node* const string) {
- CSA_ASSERT(this, IsJSReceiver(regexp));
- CSA_ASSERT(this, IsString(string));
-
- Isolate* const isolate = this->isolate();
-
- TNode<Smi> const smi_zero = SmiZero();
-
- // Grab the initial value of last index.
- TNode<Object> const previous_last_index =
- SlowLoadLastIndex(context, CAST(regexp));
-
- // Ensure last index is 0.
- {
- Label next(this), slow(this, Label::kDeferred);
- BranchIfSameValue(previous_last_index, smi_zero, &next, &slow);
-
- BIND(&slow);
- SlowStoreLastIndex(context, regexp, smi_zero);
- Goto(&next);
- BIND(&next);
- }
-
- // Call exec.
- TNode<Object> const exec_result = RegExpExec(context, regexp, string);
-
- // Reset last index if necessary.
- {
- Label next(this), slow(this, Label::kDeferred);
- TNode<Object> const current_last_index =
- SlowLoadLastIndex(context, CAST(regexp));
-
- BranchIfSameValue(current_last_index, previous_last_index, &next, &slow);
-
- BIND(&slow);
- SlowStoreLastIndex(context, regexp, previous_last_index);
- Goto(&next);
- BIND(&next);
- }
-
- // Return -1 if no match was found.
- {
- Label next(this);
- GotoIfNot(IsNull(exec_result), &next);
- Return(SmiConstant(-1));
- BIND(&next);
- }
-
- // Return the index of the match.
- {
- Label fast_result(this), slow_result(this, Label::kDeferred);
- BranchIfFastRegExpResult(context, exec_result, &fast_result, &slow_result);
-
- BIND(&fast_result);
- {
- TNode<Object> const index =
- LoadObjectField(CAST(exec_result), JSRegExpResult::kIndexOffset);
- Return(index);
- }
-
- BIND(&slow_result);
- {
- Return(GetProperty(context, exec_result,
- isolate->factory()->index_string()));
- }
- }
-}
-
-// ES#sec-regexp.prototype-@@search
-// RegExp.prototype [ @@search ] ( string )
-TF_BUILTIN(RegExpPrototypeSearch, RegExpBuiltinsAssembler) {
- TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
- TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- // Ensure {maybe_receiver} is a JSReceiver.
- ThrowIfNotJSReceiver(context, maybe_receiver,
- MessageTemplate::kIncompatibleMethodReceiver,
- "RegExp.prototype.@@search");
- TNode<JSReceiver> receiver = CAST(maybe_receiver);
-
- // Convert {maybe_string} to a String.
- TNode<String> const string = ToString_Inline(context, maybe_string);
-
- Label fast_path(this), slow_path(this);
- BranchIfFastRegExp_Permissive(context, receiver, &fast_path, &slow_path);
-
- BIND(&fast_path);
- // TODO(pwong): Could be optimized to remove the overhead of calling the
- // builtin (at the cost of a larger builtin).
- Return(CallBuiltin(Builtins::kRegExpSearchFast, context, receiver, string));
-
- BIND(&slow_path);
- RegExpPrototypeSearchBodySlow(context, receiver, string);
-}
-
-// Helper that skips a few initial checks. and assumes...
-// 1) receiver is a "fast" RegExp
-// 2) pattern is a string
-TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) {
- TNode<JSRegExp> receiver = CAST(Parameter(Descriptor::kReceiver));
- TNode<String> string = CAST(Parameter(Descriptor::kPattern));
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- RegExpPrototypeSearchBodyFast(context, receiver, string);
-}
-
// Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
// JSRegExp, {string} is a String, and {limit} is a Smi.
-void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
- TNode<JSRegExp> regexp,
- TNode<String> string,
- TNode<Smi> const limit) {
+TNode<JSArray> RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
+ TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string,
+ TNode<Smi> const limit) {
CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
@@ -2182,11 +1772,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
const ElementsKind kind = PACKED_ELEMENTS;
const ParameterMode mode = CodeStubAssembler::INTPTR_PARAMETERS;
- Node* const allocation_site = nullptr;
+ TNode<AllocationSite> allocation_site = {};
TNode<NativeContext> const native_context = LoadNativeContext(context);
TNode<Map> array_map = LoadJSArrayElementsMap(kind, native_context);
Label return_empty_array(this, Label::kDeferred);
+ TVARIABLE(JSArray, var_result);
+ Label done(this);
// If limit is zero, return an empty array.
{
@@ -2220,13 +1812,13 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
{
TNode<Smi> length = SmiConstant(1);
TNode<IntPtrT> capacity = IntPtrConstant(1);
- TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity,
- length, allocation_site, mode);
+ var_result = AllocateJSArray(kind, array_map, capacity, length,
+ allocation_site, mode);
- TNode<FixedArray> fixed_array = CAST(LoadElements(result));
+ TNode<FixedArray> fixed_array = CAST(LoadElements(var_result.value()));
UnsafeStoreFixedArrayElement(fixed_array, 0, string);
- Return(result);
+ Goto(&done);
}
}
@@ -2240,11 +1832,9 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
TVARIABLE(Smi, var_last_matched_until, SmiZero());
TVARIABLE(Smi, var_next_search_from, SmiZero());
- Variable* vars[] = {array.var_array(), array.var_length(),
- array.var_capacity(), &var_last_matched_until,
- &var_next_search_from};
- const int vars_count = sizeof(vars) / sizeof(vars[0]);
- Label loop(this, vars_count, vars), push_suffix_and_out(this), out(this);
+ Label loop(this, {array.var_array(), array.var_length(), array.var_capacity(),
+ &var_last_matched_until, &var_next_search_from}),
+ push_suffix_and_out(this), out(this);
Goto(&loop);
BIND(&loop);
@@ -2321,19 +1911,17 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
match_indices, RegExpMatchInfo::kNumberOfCapturesIndex));
TNode<IntPtrT> const int_num_registers = SmiUntag(num_registers);
- VARIABLE(var_reg, MachineType::PointerRepresentation());
- var_reg.Bind(IntPtrConstant(2));
+ TVARIABLE(IntPtrT, var_reg, IntPtrConstant(2));
- Variable* vars[] = {array.var_array(), array.var_length(),
- array.var_capacity(), &var_reg};
- const int vars_count = sizeof(vars) / sizeof(vars[0]);
- Label nested_loop(this, vars_count, vars), nested_loop_out(this);
+ Label nested_loop(this, {array.var_array(), array.var_length(),
+ array.var_capacity(), &var_reg}),
+ nested_loop_out(this);
Branch(IntPtrLessThan(var_reg.value(), int_num_registers), &nested_loop,
&nested_loop_out);
BIND(&nested_loop);
{
- Node* const reg = var_reg.value();
+ const TNode<IntPtrT> reg = var_reg.value();
TNode<Object> const from = LoadFixedArrayElement(
match_indices, reg,
RegExpMatchInfo::kFirstCaptureIndex * kTaggedSize, mode);
@@ -2342,30 +1930,30 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
(RegExpMatchInfo::kFirstCaptureIndex + 1) * kTaggedSize, mode));
Label select_capture(this), select_undefined(this), store_value(this);
- VARIABLE(var_value, MachineRepresentation::kTagged);
+ TVARIABLE(Object, var_value);
Branch(SmiEqual(to, SmiConstant(-1)), &select_undefined,
&select_capture);
BIND(&select_capture);
{
- var_value.Bind(
- CallBuiltin(Builtins::kSubString, context, string, from, to));
+ var_value =
+ CallBuiltin(Builtins::kSubString, context, string, from, to);
Goto(&store_value);
}
BIND(&select_undefined);
{
- var_value.Bind(UndefinedConstant());
+ var_value = UndefinedConstant();
Goto(&store_value);
}
BIND(&store_value);
{
- array.Push(CAST(var_value.value()));
+ array.Push(var_value.value());
GotoIf(WordEqual(array.length(), int_limit), &out);
- TNode<WordT> const new_reg = IntPtrAdd(reg, IntPtrConstant(2));
- var_reg.Bind(new_reg);
+ const TNode<IntPtrT> new_reg = IntPtrAdd(reg, IntPtrConstant(2));
+ var_reg = new_reg;
Branch(IntPtrLessThan(new_reg, int_num_registers), &nested_loop,
&nested_loop_out);
@@ -2382,316 +1970,29 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
BIND(&push_suffix_and_out);
{
- TNode<Smi> const from = var_last_matched_until.value();
- Node* const to = string_length;
+ const TNode<Smi> from = var_last_matched_until.value();
+ const TNode<Smi> to = string_length;
array.Push(CallBuiltin(Builtins::kSubString, context, string, from, to));
Goto(&out);
}
BIND(&out);
{
- TNode<JSArray> const result = array.ToJSArray(context);
- Return(result);
+ var_result = array.ToJSArray(context);
+ Goto(&done);
}
BIND(&return_empty_array);
{
TNode<Smi> length = SmiZero();
TNode<IntPtrT> capacity = IntPtrZero();
- TNode<JSArray> result = AllocateJSArray(kind, array_map, capacity, length,
- allocation_site, mode);
- Return(result);
- }
-}
-
-// Helper that skips a few initial checks.
-TF_BUILTIN(RegExpSplit, RegExpBuiltinsAssembler) {
- TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kRegExp));
- TNode<String> string = CAST(Parameter(Descriptor::kString));
- TNode<Object> maybe_limit = CAST(Parameter(Descriptor::kLimit));
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
- BranchIfFastRegExp_Strict(context, regexp, ok, not_ok);
- });
-
- // Verify {maybe_limit}.
-
- VARIABLE(var_limit, MachineRepresentation::kTagged, maybe_limit);
- Label if_limitissmimax(this), runtime(this, Label::kDeferred);
-
- {
- Label next(this);
-
- GotoIf(IsUndefined(maybe_limit), &if_limitissmimax);
- Branch(TaggedIsPositiveSmi(maybe_limit), &next, &runtime);
-
- // We need to be extra-strict and require the given limit to be either
- // undefined or a positive smi. We can't call ToUint32(maybe_limit) since
- // that might move us onto the slow path, resulting in ordering spec
- // violations (see https://crbug.com/801171).
-
- BIND(&if_limitissmimax);
- {
- // TODO(jgruber): In this case, we can probably avoid generation of limit
- // checks in Generate_RegExpPrototypeSplitBody.
- var_limit.Bind(SmiConstant(Smi::kMaxValue));
- Goto(&next);
- }
-
- BIND(&next);
- }
-
- // Due to specific shortcuts we take on the fast path (specifically, we don't
- // allocate a new regexp instance as specced), we need to ensure that the
- // given regexp is non-sticky to avoid invalid results. See crbug.com/v8/6706.
-
- GotoIf(FastFlagGetter(regexp, JSRegExp::kSticky), &runtime);
-
- // We're good to go on the fast path, which is inlined here.
-
- RegExpPrototypeSplitBody(context, regexp, string, CAST(var_limit.value()));
-
- BIND(&runtime);
- Return(CallRuntime(Runtime::kRegExpSplit, context, regexp, string,
- var_limit.value()));
-}
-
-// ES#sec-regexp.prototype-@@split
-// RegExp.prototype [ @@split ] ( string, limit )
-TF_BUILTIN(RegExpPrototypeSplit, RegExpBuiltinsAssembler) {
- const int kStringArg = 0;
- const int kLimitArg = 1;
-
- TNode<IntPtrT> argc =
- ChangeInt32ToIntPtr(Parameter(Descriptor::kJSActualArgumentsCount));
- CodeStubArguments args(this, argc);
-
- TNode<Object> maybe_receiver = args.GetReceiver();
- TNode<Object> maybe_string = args.GetOptionalArgumentValue(kStringArg);
- TNode<Object> maybe_limit = args.GetOptionalArgumentValue(kLimitArg);
- TNode<Context> context = CAST(Parameter(Descriptor::kContext));
-
- // Ensure {maybe_receiver} is a JSReceiver.
- ThrowIfNotJSReceiver(context, maybe_receiver,
- MessageTemplate::kIncompatibleMethodReceiver,
- "RegExp.prototype.@@split");
- TNode<JSReceiver> receiver = CAST(maybe_receiver);
-
- // Convert {maybe_string} to a String.
- TNode<String> string = ToString_Inline(context, maybe_string);
-
- // Strict: Reads the flags property.
- // TODO(jgruber): Handle slow flag accesses on the fast path and make this
- // permissive.
- Label stub(this), runtime(this, Label::kDeferred);
- BranchIfFastRegExp_Strict(context, receiver, &stub, &runtime);
-
- BIND(&stub);
- args.PopAndReturn(CallBuiltin(Builtins::kRegExpSplit, context, receiver,
- string, maybe_limit));
-
- BIND(&runtime);
- args.PopAndReturn(CallRuntime(Runtime::kRegExpSplit, context, receiver,
- string, maybe_limit));
-}
-
-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) {
- const char* method_name = "%RegExpStringIterator%.prototype.next";
- 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);
-
- // 1. Let O be the this value.
- // 2. If Type(O) is not Object, throw a TypeError exception.
- // 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.
- ThrowIfNotInstanceType(context, maybe_receiver,
- JS_REGEXP_STRING_ITERATOR_TYPE, method_name);
- TNode<HeapObject> receiver = CAST(maybe_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<JSReceiver> iterating_regexp = CAST(LoadObjectField(
- receiver, JSRegExpStringIterator::kIteratingRegExpOffset));
-
- // For extra safety, also check the type in release mode.
- CSA_CHECK(this, IsJSReceiver(iterating_regexp));
-
- // 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);
- TVARIABLE(BoolT, var_is_fast_regexp);
- {
- Label if_fast(this), if_slow(this, Label::kDeferred);
- BranchIfFastRegExp_Permissive(context, iterating_regexp, &if_fast,
- &if_slow);
-
- BIND(&if_fast);
- {
- TNode<RegExpMatchInfo> match_indices =
- RegExpPrototypeExecBodyWithoutResult(
- context, iterating_regexp, iterating_string, &if_no_match, true);
- var_match = ConstructNewResultFromMatchInfo(
- context, iterating_regexp, match_indices, iterating_string);
- var_is_fast_regexp = Int32TrueConstant();
- Goto(&if_match);
- }
-
- BIND(&if_slow);
- {
- var_match = RegExpExec(context, iterating_regexp, iterating_string);
- var_is_fast_regexp = Int32FalseConstant();
- Branch(IsNull(var_match.value()), &if_no_match, &if_match);
- }
- }
-
- // 10. If match is null, then
- BIND(&if_no_match);
- {
- // a. Set O.[[Done]] to true.
- SetDoneFlag(receiver, flags);
-
- // b. Return ! CreateIterResultObject(undefined, true).
- Goto(&return_empty_done_result);
+ var_result = AllocateJSArray(kind, array_map, capacity, length,
+ allocation_site, mode);
+ Goto(&done);
}
- // 11. Else,
- BIND(&if_match);
- {
- Label if_global(this), if_not_global(this, Label::kDeferred),
- return_result(this);
-
- // a. If global is true,
- Branch(HasGlobalFlag(flags), &if_global, &if_not_global);
- BIND(&if_global);
- {
- Label if_fast(this), if_slow(this, Label::kDeferred);
- // ii. If matchStr is the empty string,
- Branch(var_is_fast_regexp.value(), &if_fast, &if_slow);
- BIND(&if_fast);
- {
- // i. Let matchStr be ? ToString(? Get(match, "0")).
- CSA_ASSERT_BRANCH(this, [&](Label* ok, Label* not_ok) {
- BranchIfFastRegExpResult(context, var_match.value(), ok, not_ok);
- });
- CSA_ASSERT(this,
- SmiNotEqual(LoadFastJSArrayLength(CAST(var_match.value())),
- SmiZero()));
- TNode<FixedArray> result_fixed_array =
- CAST(LoadElements(CAST(var_match.value())));
- TNode<String> match_str =
- CAST(LoadFixedArrayElement(result_fixed_array, 0));
-
- // When iterating_regexp is fast, we assume it stays fast even after
- // accessing the first match from the RegExp result.
- CSA_ASSERT(this, IsFastRegExpPermissive(context, iterating_regexp));
- GotoIfNot(IsEmptyString(match_str), &return_result);
-
- // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
- TNode<Smi> this_index = FastLoadLastIndex(CAST(iterating_regexp));
-
- // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
- TNode<Smi> next_index = AdvanceStringIndexFast(
- iterating_string, this_index, HasUnicodeFlag(flags));
-
- // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
- FastStoreLastIndex(CAST(iterating_regexp), next_index);
-
- // iii. Return ! CreateIterResultObject(match, false).
- Goto(&return_result);
- }
- BIND(&if_slow);
- {
- // i. Let matchStr be ? ToString(? Get(match, "0")).
- TNode<String> match_str = ToString_Inline(
- context, GetProperty(context, var_match.value(), SmiZero()));
-
- GotoIfNot(IsEmptyString(match_str), &return_result);
-
- // 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
- TNode<Object> last_index = SlowLoadLastIndex(context, iterating_regexp);
- TNode<Number> this_index = ToLength_Inline(context, last_index);
-
- // 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex, fullUnicode).
- TNode<Number> next_index = AdvanceStringIndex(
- iterating_string, this_index, HasUnicodeFlag(flags), false);
-
- // 3. Perform ? Set(R, "lastIndex", nextIndex, true).
- SlowStoreLastIndex(context, iterating_regexp, next_index);
-
- // iii. Return ! CreateIterResultObject(match, false).
- Goto(&return_result);
- }
- }
- // b. Else,
- BIND(&if_not_global);
- {
- // i. Set O.[[Done]] to true.
- SetDoneFlag(receiver, flags);
-
- // ii. Return ! CreateIterResultObject(match, false).
- Goto(&return_result);
- }
- BIND(&return_result);
- {
- Return(AllocateJSIteratorResult(context, var_match.value(),
- FalseConstant()));
- }
- }
- BIND(&return_empty_done_result);
- Return(
- AllocateJSIteratorResult(context, UndefinedConstant(), TrueConstant()));
+ BIND(&done);
+ return var_result.value();
}
} // namespace internal