blob: 932972d8440632d8405e3a73f633485bd8d52bf9 [file] [log] [blame]
// Copyright 2019 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.
#include 'src/builtins/builtins-regexp-gen.h'
namespace regexp {
extern transitioning macro
RegExpMatchAllAssembler::CreateRegExpStringIterator(
NativeContext, Object, String, bool, bool): JSAny;
@export
transitioning macro RegExpPrototypeMatchAllImpl(implicit context: Context)(
nativeContext: NativeContext, receiver: JSAny, string: JSAny): JSAny {
// 1. Let R be the this value.
// 2. If Type(R) is not Object, throw a TypeError exception.
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@matchAll');
const receiver = UnsafeCast<JSReceiver>(receiver);
// 3. Let S be ? ToString(O).
const string: String = ToString_Inline(string);
let matcher: Object;
let global: bool;
let unicode: bool;
// 'FastJSRegExp' uses the strict fast path check because following code
// uses the flags property.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
typeswitch (receiver) {
case (fastRegExp: FastJSRegExp): {
const source = fastRegExp.source;
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
// 5. Let flags be ? ToString(? Get(R, "flags")).
// 6. Let matcher be ? Construct(C, « R, flags »).
const flags: String = FastFlagsGetter(fastRegExp);
matcher = RegExpCreate(nativeContext, source, flags);
const matcherRegExp = UnsafeCast<JSRegExp>(matcher);
assert(IsFastRegExpPermissive(matcherRegExp));
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
const fastRegExp = UnsafeCast<FastJSRegExp>(receiver);
FastStoreLastIndex(matcherRegExp, fastRegExp.lastIndex);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
global = FastFlagGetter(matcherRegExp, Flag::kGlobal);
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
unicode = FastFlagGetter(matcherRegExp, Flag::kUnicode);
}
case (Object): {
// 4. Let C be ? SpeciesConstructor(R, %RegExp%).
const regexpFun = LoadRegExpFunction(nativeContext);
const speciesConstructor =
UnsafeCast<Constructor>(SpeciesConstructor(receiver, regexpFun));
// 5. Let flags be ? ToString(? Get(R, "flags")).
const flags = GetProperty(receiver, 'flags');
const flagsString = ToString_Inline(flags);
// 6. Let matcher be ? Construct(C, « R, flags »).
matcher = Construct(speciesConstructor, receiver, flagsString);
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
const lastIndex: Number = ToLength_Inline(SlowLoadLastIndex(receiver));
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
SlowStoreLastIndex(UnsafeCast<JSReceiver>(matcher), lastIndex);
// 9. If flags contains "g", let global be true.
// 10. Else, let global be false.
const globalCharString: String = StringConstant('g');
const globalIndex: Smi = StringIndexOf(flagsString, globalCharString, 0);
global = globalIndex != -1;
// 11. If flags contains "u", let fullUnicode be true.
// 12. Else, let fullUnicode be false.
const unicodeCharString = StringConstant('u');
const unicodeIndex: Smi =
StringIndexOf(flagsString, unicodeCharString, 0);
unicode = unicodeIndex != -1;
}
}
// 13. Return ! CreateRegExpStringIterator(matcher, S, global, fullUnicode).
return CreateRegExpStringIterator(
nativeContext, matcher, string, global, unicode);
}
// https://tc39.github.io/proposal-string-matchall/
// RegExp.prototype [ @@matchAll ] ( string )
transitioning javascript builtin RegExpPrototypeMatchAll(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
return RegExpPrototypeMatchAllImpl(context, receiver, string);
}
// https://tc39.github.io/proposal-string-matchall/
// %RegExpStringIteratorPrototype%.next ( )
transitioning javascript builtin RegExpStringIteratorPrototypeNext(
js-implicit context: NativeContext, 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(
MessageTemplate::kIncompatibleMethodReceiver, methodName, receiver);
try {
// 4. If O.[[Done]] is true, then
// a. Return ! CreateIterResultObject(undefined, true).
const flags: SmiTagged<JSRegExpStringIteratorFlags> = receiver.flags;
if (flags.done) 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 regexp = UnsafeCast<JSRegExp>(iteratingRegExp);
const lastIndex = LoadLastIndexAsLength(regexp, true);
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
regexp, iteratingString, lastIndex)
otherwise IfNoMatch;
match = ConstructNewResultFromMatchInfo(
regexp, matchIndices, iteratingString, lastIndex);
isFastRegExp = true;
} else {
match = RegExpExec(iteratingRegExp, iteratingString);
if (match == Null) {
goto IfNoMatch;
}
}
// 11. Else,
// b. Else, handle non-global case first.
if (!flags.global) {
// i. Set O.[[Done]] to true.
receiver.flags.done = true;
// ii. Return ! CreateIterResultObject(match, false).
return AllocateJSIteratorResult(UnsafeCast<JSAny>(match), False);
}
// a. If global is true,
assert(flags.global);
if (isFastRegExp) {
// i. Let matchStr be ? ToString(? Get(match, "0")).
const match = UnsafeCast<JSRegExpResult>(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, flags.unicode);
// 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(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(lastIndex);
// 2. Let nextIndex be ! AdvanceStringIndex(S, thisIndex,
// fullUnicode).
const nextIndex: Number =
AdvanceStringIndexSlow(iteratingString, thisIndex, flags.unicode);
// 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.
receiver.flags.done = true;
// b. Return ! CreateIterResultObject(undefined, true).
goto ReturnEmptyDoneResult;
}
} label ReturnEmptyDoneResult {
return AllocateJSIteratorResult(Undefined, True);
}
}
}