// 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); } } }