[builtins] Port RegExp StringIteratorNext to Torque

Bug: v8:8976
Change-Id: I3f13980ce49e1ca0f5caf5712c5181908054d649
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1834882
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64170}
This commit is contained in:
Z Nguyen-Huu 2019-10-08 09:10:20 -07:00 committed by Commit Bot
parent cc5016e1b7
commit 25e551eff9
9 changed files with 174 additions and 206 deletions

View File

@ -1494,7 +1494,7 @@ transient type FastJSRegExpResult extends JSRegExpResult;
@generateCppClass
extern class JSRegExpStringIterator extends JSObject {
// The [[IteratingRegExp]] internal property.
iterating_reg_exp: JSAny;
iterating_reg_exp: JSReceiver;
// The [[IteratedString]] internal property.
iterated_string: String;
flags: Smi;
@ -2303,6 +2303,14 @@ Cast<JSStringIterator>(o: HeapObject): JSStringIterator
return HeapObjectToJSStringIterator(o) otherwise CastError;
}
Cast<JSRegExpStringIterator>(o: HeapObject): JSRegExpStringIterator
labels CastError {
if (IsJSRegExpStringIterator(o)) {
return %RawDownCast<JSRegExpStringIterator>(o);
}
goto CastError;
}
Cast<JSTypedArray>(o: HeapObject): JSTypedArray
labels CastError {
if (IsJSTypedArray(o)) return %RawDownCast<JSTypedArray>(o);
@ -3342,6 +3350,7 @@ extern macro IsFeedbackVector(HeapObject): bool;
extern macro IsJSArray(HeapObject): bool;
extern macro IsJSProxy(HeapObject): bool;
extern macro IsJSRegExp(HeapObject): bool;
extern macro IsJSRegExpStringIterator(HeapObject): bool;
extern macro IsMap(HeapObject): bool;
extern macro IsJSFunction(HeapObject): bool;
extern macro IsJSObject(HeapObject): bool;

View File

@ -858,10 +858,6 @@ namespace internal {
TFS(RegExpExecInternal, kRegExp, kString, kLastIndex, kMatchInfo) \
ASM(RegExpInterpreterTrampoline, CCall) \
\
/* RegExp String Iterator */ \
/* https://tc39.github.io/proposal-string-matchall/ */ \
TFJ(RegExpStringIteratorPrototypeNext, 0, kReceiver) \
\
/* Set */ \
TFJ(SetConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFJ(SetPrototypeHas, 1, kReceiver, kKey) \

View File

@ -2104,203 +2104,5 @@ TNode<JSArray> RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(
return var_result.value();
}
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);
}
// 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()));
}
} // namespace internal
} // namespace v8

View File

@ -186,6 +186,11 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
return CAST(AdvanceStringIndex(string, index, is_unicode, true));
}
TNode<Smi> AdvanceStringIndexSlow(TNode<String> string, TNode<Number> index,
TNode<BoolT> is_unicode) {
return CAST(AdvanceStringIndex(string, index, is_unicode, false));
}
TNode<Object> RegExpPrototypeMatchBody(TNode<Context> context,
TNode<Object> regexp,
TNode<String> const string,

View File

@ -107,4 +107,152 @@ namespace regexp {
const nativeContext: NativeContext = LoadNativeContext(context);
return RegExpPrototypeMatchAllImpl(nativeContext, receiver, string);
}
const kJSRegExpStringIteratorDone:
constexpr int31 generates '1 << JSRegExpStringIterator::kDoneBit';
const kJSRegExpStringIteratorGlobal: constexpr int31
generates '1 << JSRegExpStringIterator::kGlobalBit';
const kJSRegExpStringIteratorUnicode: constexpr int31
generates '1 << JSRegExpStringIterator::kUnicodeBit';
extern macro IsSetSmi(Smi, constexpr int31): bool;
macro HasDoneFlag(flags: Smi): bool {
return IsSetSmi(flags, kJSRegExpStringIteratorDone);
}
macro HasGlobalFlag(flags: Smi): bool {
return IsSetSmi(flags, kJSRegExpStringIteratorGlobal);
}
macro HasUnicodeFlag(flags: Smi): bool {
return IsSetSmi(flags, kJSRegExpStringIteratorUnicode);
}
macro SetDoneFlag(iterator: JSRegExpStringIterator, flags: Smi) {
const newFlags: Smi = flags | kJSRegExpStringIteratorDone;
iterator.flags = newFlags;
}
extern macro RegExpBuiltinsAssembler::ConstructNewResultFromMatchInfo(
implicit context: Context)(JSReceiver, RegExpMatchInfo, String):
JSRegExpResult;
// https://tc39.github.io/proposal-string-matchall/
// %RegExpStringIteratorPrototype%.next ( )
transitioning javascript builtin RegExpStringIteratorPrototypeNext(
js-implicit context: Context, receiver: JSAny)(): JSAny {
// 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.
const methodName: constexpr string =
'%RegExpStringIterator%.prototype.next';
const receiver = Cast<JSRegExpStringIterator>(receiver) otherwise
ThrowTypeError(kIncompatibleMethodReceiver, methodName, receiver);
try {
// 4. If O.[[Done]] is true, then
// a. Return ! CreateIterResultObject(undefined, true).
const flags: Smi = receiver.flags;
if (HasDoneFlag(flags)) goto ReturnEmptyDoneResult;
// 5. Let R be O.[[iteratingRegExp]].
const iteratingRegExp: JSReceiver = receiver.iterating_reg_exp;
// 6. Let S be O.[[IteratedString]].
const iteratingString: String = receiver.iterated_string;
// 7. Let global be O.[[Global]].
// 8. Let fullUnicode be O.[[Unicode]].
// 9. Let match be ? RegExpExec(R, S).
let match: Object;
let isFastRegExp: bool = false;
try {
if (IsFastRegExpPermissive(iteratingRegExp)) {
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(iteratingRegExp), iteratingString)
otherwise IfNoMatch;
match = ConstructNewResultFromMatchInfo(
iteratingRegExp, matchIndices, iteratingString);
isFastRegExp = true;
} else {
match = RegExpExec(context, iteratingRegExp, iteratingString);
if (match == Null) {
goto IfNoMatch;
}
}
// 11. Else,
// b. Else, handle non-global case first.
if (!HasGlobalFlag(flags)) {
// i. Set O.[[Done]] to true.
SetDoneFlag(receiver, flags);
// ii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(UnsafeCast<JSAny>(match), False);
}
// a. If global is true,
assert(HasGlobalFlag(flags));
if (isFastRegExp) {
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<FastJSRegExpResult>(match);
const resultFixedArray = UnsafeCast<FixedArray>(match.elements);
const matchStr = UnsafeCast<String>(resultFixedArray.objects[0]);
// When iterating_regexp is fast, we assume it stays fast even after
// accessing the first match from the RegExp result.
assert(IsFastRegExpPermissive(iteratingRegExp));
const iteratingRegExp = UnsafeCast<JSRegExp>(iteratingRegExp);
if (matchStr == kEmptyString) {
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
const thisIndex: Smi = FastLoadLastIndex(iteratingRegExp);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Smi = AdvanceStringIndexFast(
iteratingString, thisIndex, HasUnicodeFlag(flags));
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
FastStoreLastIndex(iteratingRegExp, nextIndex);
}
// iii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(match, False);
}
assert(!isFastRegExp);
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<JSAny>(match);
const matchStr =
ToString_Inline(context, GetProperty(match, SmiConstant(0)));
if (matchStr == kEmptyString) {
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: JSAny = SlowLoadLastIndex(iteratingRegExp);
const thisIndex: Number = ToLength_Inline(context, lastIndex);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Number = AdvanceStringIndexSlow(
iteratingString, thisIndex, HasUnicodeFlag(flags));
// 3. Perform ? Set(R, "lastIndex", nextIndex, true).
SlowStoreLastIndex(iteratingRegExp, nextIndex);
}
// iii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(match, False);
}
// 10. If match is null, then
label IfNoMatch {
// a. Set O.[[Done]] to true.
SetDoneFlag(receiver, flags);
// b. Return ! CreateIterResultObject(undefined, true).
goto ReturnEmptyDoneResult;
}
}
label ReturnEmptyDoneResult {
return AllocateJSIteratorResult(Undefined, True);
}
}
}

View File

@ -19,9 +19,6 @@ namespace regexp {
StringReplaceNonGlobalRegExpWithFunction(implicit context: Context)(
String, JSRegExp, Callable): String;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi;
transitioning macro RegExpReplaceCallableNoExplicitCaptures(implicit context:
Context)(
matchesElements: FixedArray, matchesLength: intptr, string: String,

View File

@ -172,4 +172,9 @@ namespace regexp {
extern builtin
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi;
extern macro
RegExpBuiltinsAssembler::AdvanceStringIndexSlow(String, Number, bool): Smi;
}

View File

@ -6300,6 +6300,11 @@ TNode<BoolT> CodeStubAssembler::IsJSStringIterator(
return HasInstanceType(object, JS_STRING_ITERATOR_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsJSRegExpStringIterator(
SloppyTNode<HeapObject> object) {
return HasInstanceType(object, JS_REGEXP_STRING_ITERATOR_TYPE);
}
TNode<BoolT> CodeStubAssembler::IsMap(SloppyTNode<HeapObject> map) {
return IsMetaMap(LoadMap(map));
}

View File

@ -2455,6 +2455,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> IsJSPromise(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSProxy(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSStringIterator(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSRegExpStringIterator(SloppyTNode<HeapObject> object);
TNode<BoolT> IsJSReceiverInstanceType(SloppyTNode<Int32T> instance_type);
TNode<BoolT> IsJSReceiverMap(SloppyTNode<Map> map);
TNode<BoolT> IsJSReceiver(SloppyTNode<HeapObject> object);