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