11cb0ef41Sopenharmony_ci// Copyright 2019 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include 'src/builtins/builtins-regexp-gen.h'
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_cinamespace regexp {
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst kATOM: constexpr int31
101cb0ef41Sopenharmony_ci    generates 'JSRegExp::ATOM';
111cb0ef41Sopenharmony_ciconst kTagIndex: constexpr int31
121cb0ef41Sopenharmony_ci    generates 'JSRegExp::kTagIndex';
131cb0ef41Sopenharmony_ciconst kAtomPatternIndex: constexpr int31
141cb0ef41Sopenharmony_ci    generates 'JSRegExp::kAtomPatternIndex';
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_ciextern transitioning macro RegExpBuiltinsAssembler::FlagGetter(
171cb0ef41Sopenharmony_ci    implicit context: Context)(Object, constexpr Flag, constexpr bool): bool;
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ciextern macro UnsafeLoadFixedArrayElement(
201cb0ef41Sopenharmony_ci    RegExpMatchInfo, constexpr int31): Object;
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_citransitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
231cb0ef41Sopenharmony_ci    regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
241cb0ef41Sopenharmony_ci  if constexpr (isFastPath) {
251cb0ef41Sopenharmony_ci    dcheck(Is<FastJSRegExp>(regexp));
261cb0ef41Sopenharmony_ci  }
271cb0ef41Sopenharmony_ci
281cb0ef41Sopenharmony_ci  const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath);
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci  if (!isGlobal) {
311cb0ef41Sopenharmony_ci    return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) :
321cb0ef41Sopenharmony_ci                        RegExpExec(regexp, string);
331cb0ef41Sopenharmony_ci  }
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci  dcheck(isGlobal);
361cb0ef41Sopenharmony_ci  const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath);
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  StoreLastIndex(regexp, 0, isFastPath);
391cb0ef41Sopenharmony_ci
401cb0ef41Sopenharmony_ci  // Allocate an array to store the resulting match strings.
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci  let array = growable_fixed_array::NewGrowableFixedArray();
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci  // Check if the regexp is an ATOM type. If so, then keep the literal string
451cb0ef41Sopenharmony_ci  // to search for so that we can avoid calling substring in the loop below.
461cb0ef41Sopenharmony_ci  let atom: bool = false;
471cb0ef41Sopenharmony_ci  let searchString: String = EmptyStringConstant();
481cb0ef41Sopenharmony_ci  if constexpr (isFastPath) {
491cb0ef41Sopenharmony_ci    const maybeAtomRegexp = UnsafeCast<JSRegExp>(regexp);
501cb0ef41Sopenharmony_ci    const data = UnsafeCast<FixedArray>(maybeAtomRegexp.data);
511cb0ef41Sopenharmony_ci    if (UnsafeCast<Smi>(data.objects[kTagIndex]) == kATOM) {
521cb0ef41Sopenharmony_ci      searchString = UnsafeCast<String>(data.objects[kAtomPatternIndex]);
531cb0ef41Sopenharmony_ci      atom = true;
541cb0ef41Sopenharmony_ci    }
551cb0ef41Sopenharmony_ci  }
561cb0ef41Sopenharmony_ci
571cb0ef41Sopenharmony_ci  while (true) {
581cb0ef41Sopenharmony_ci    let match: String = EmptyStringConstant();
591cb0ef41Sopenharmony_ci    try {
601cb0ef41Sopenharmony_ci      if constexpr (isFastPath) {
611cb0ef41Sopenharmony_ci        // On the fast path, grab the matching string from the raw match index
621cb0ef41Sopenharmony_ci        // array.
631cb0ef41Sopenharmony_ci        const matchIndices: RegExpMatchInfo =
641cb0ef41Sopenharmony_ci            RegExpPrototypeExecBodyWithoutResultFast(
651cb0ef41Sopenharmony_ci                UnsafeCast<JSRegExp>(regexp), string) otherwise IfDidNotMatch;
661cb0ef41Sopenharmony_ci        if (atom) {
671cb0ef41Sopenharmony_ci          match = searchString;
681cb0ef41Sopenharmony_ci        } else {
691cb0ef41Sopenharmony_ci          const matchFrom = UnsafeLoadFixedArrayElement(
701cb0ef41Sopenharmony_ci              matchIndices, kRegExpMatchInfoFirstCaptureIndex);
711cb0ef41Sopenharmony_ci          const matchTo = UnsafeLoadFixedArrayElement(
721cb0ef41Sopenharmony_ci              matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1);
731cb0ef41Sopenharmony_ci          match = SubString(
741cb0ef41Sopenharmony_ci              string, UnsafeCast<Smi>(matchFrom), UnsafeCast<Smi>(matchTo));
751cb0ef41Sopenharmony_ci        }
761cb0ef41Sopenharmony_ci      } else {
771cb0ef41Sopenharmony_ci        dcheck(!isFastPath);
781cb0ef41Sopenharmony_ci        const resultTemp = RegExpExec(regexp, string);
791cb0ef41Sopenharmony_ci        if (resultTemp == Null) {
801cb0ef41Sopenharmony_ci          goto IfDidNotMatch;
811cb0ef41Sopenharmony_ci        }
821cb0ef41Sopenharmony_ci        match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0)));
831cb0ef41Sopenharmony_ci      }
841cb0ef41Sopenharmony_ci      goto IfDidMatch;
851cb0ef41Sopenharmony_ci    } label IfDidNotMatch {
861cb0ef41Sopenharmony_ci      return array.length == 0 ? Null : array.ToJSArray();
871cb0ef41Sopenharmony_ci    } label IfDidMatch {
881cb0ef41Sopenharmony_ci      // Store the match, growing the fixed array if needed.
891cb0ef41Sopenharmony_ci
901cb0ef41Sopenharmony_ci      array.Push(match);
911cb0ef41Sopenharmony_ci
921cb0ef41Sopenharmony_ci      // Advance last index if the match is the empty string.
931cb0ef41Sopenharmony_ci      const matchLength: Smi = match.length_smi;
941cb0ef41Sopenharmony_ci      if (matchLength != 0) {
951cb0ef41Sopenharmony_ci        continue;
961cb0ef41Sopenharmony_ci      }
971cb0ef41Sopenharmony_ci      let lastIndex = LoadLastIndex(regexp, isFastPath);
981cb0ef41Sopenharmony_ci      if constexpr (isFastPath) {
991cb0ef41Sopenharmony_ci        dcheck(TaggedIsPositiveSmi(lastIndex));
1001cb0ef41Sopenharmony_ci      } else {
1011cb0ef41Sopenharmony_ci        lastIndex = ToLength_Inline(lastIndex);
1021cb0ef41Sopenharmony_ci      }
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci      const newLastIndex: Number = AdvanceStringIndex(
1051cb0ef41Sopenharmony_ci          string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath);
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci      if constexpr (isFastPath) {
1081cb0ef41Sopenharmony_ci        // On the fast path, we can be certain that lastIndex can never be
1091cb0ef41Sopenharmony_ci        // incremented to overflow the Smi range since the maximal string
1101cb0ef41Sopenharmony_ci        // length is less than the maximal Smi value.
1111cb0ef41Sopenharmony_ci        StaticAssertStringLengthFitsSmi();
1121cb0ef41Sopenharmony_ci        dcheck(TaggedIsPositiveSmi(newLastIndex));
1131cb0ef41Sopenharmony_ci      }
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci      StoreLastIndex(regexp, newLastIndex, isFastPath);
1161cb0ef41Sopenharmony_ci    }
1171cb0ef41Sopenharmony_ci  }
1181cb0ef41Sopenharmony_ci
1191cb0ef41Sopenharmony_ci  VerifiedUnreachable();
1201cb0ef41Sopenharmony_ci}
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_citransitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)(
1231cb0ef41Sopenharmony_ci    receiver: FastJSRegExp, string: String): JSAny {
1241cb0ef41Sopenharmony_ci  return RegExpPrototypeMatchBody(receiver, string, true);
1251cb0ef41Sopenharmony_ci}
1261cb0ef41Sopenharmony_ci
1271cb0ef41Sopenharmony_citransitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)(
1281cb0ef41Sopenharmony_ci    receiver: JSReceiver, string: String): JSAny {
1291cb0ef41Sopenharmony_ci  return RegExpPrototypeMatchBody(receiver, string, false);
1301cb0ef41Sopenharmony_ci}
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ci// Helper that skips a few initial checks. and assumes...
1331cb0ef41Sopenharmony_ci// 1) receiver is a "fast" RegExp
1341cb0ef41Sopenharmony_ci// 2) pattern is a string
1351cb0ef41Sopenharmony_citransitioning builtin RegExpMatchFast(implicit context: Context)(
1361cb0ef41Sopenharmony_ci    receiver: FastJSRegExp, string: String): JSAny {
1371cb0ef41Sopenharmony_ci  return FastRegExpPrototypeMatchBody(receiver, string);
1381cb0ef41Sopenharmony_ci}
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci// ES#sec-regexp.prototype-@@match
1411cb0ef41Sopenharmony_ci// RegExp.prototype [ @@match ] ( string )
1421cb0ef41Sopenharmony_citransitioning javascript builtin RegExpPrototypeMatch(
1431cb0ef41Sopenharmony_ci    js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
1441cb0ef41Sopenharmony_ci  ThrowIfNotJSReceiver(
1451cb0ef41Sopenharmony_ci      receiver, MessageTemplate::kIncompatibleMethodReceiver,
1461cb0ef41Sopenharmony_ci      'RegExp.prototype.@@match');
1471cb0ef41Sopenharmony_ci  const receiver = UnsafeCast<JSReceiver>(receiver);
1481cb0ef41Sopenharmony_ci  const string: String = ToString_Inline(string);
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ci  // Strict: Reads global and unicode properties.
1511cb0ef41Sopenharmony_ci  // TODO(jgruber): Handle slow flag accesses on the fast path and make this
1521cb0ef41Sopenharmony_ci  // permissive.
1531cb0ef41Sopenharmony_ci  const fastRegExp = Cast<FastJSRegExp>(receiver)
1541cb0ef41Sopenharmony_ci      otherwise return SlowRegExpPrototypeMatchBody(receiver, string);
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci  // TODO(pwong): Could be optimized to remove the overhead of calling the
1571cb0ef41Sopenharmony_ci  //              builtin (at the cost of a larger builtin).
1581cb0ef41Sopenharmony_ci  return RegExpMatchFast(fastRegExp, string);
1591cb0ef41Sopenharmony_ci}
1601cb0ef41Sopenharmony_ci}
161