blob: 48fd8a42bf7a85be830373ae1dc278bbacc0aced [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 {
const kATOM: constexpr int31
generates 'JSRegExp::ATOM';
const kTagIndex: constexpr int31
generates 'JSRegExp::kTagIndex';
const kAtomPatternIndex: constexpr int31
generates 'JSRegExp::kAtomPatternIndex';
extern transitioning macro RegExpBuiltinsAssembler::FlagGetter(
implicit context: Context)(Object, constexpr Flag, constexpr bool): bool;
extern macro UnsafeLoadFixedArrayElement(
RegExpMatchInfo, constexpr int31): Object;
transitioning macro RegExpPrototypeMatchBody(implicit context: Context)(
regexp: JSReceiver, string: String, isFastPath: constexpr bool): JSAny {
if constexpr (isFastPath) {
assert(Is<FastJSRegExp>(regexp));
}
const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath);
if (!isGlobal) {
return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) :
RegExpExec(regexp, string);
}
assert(isGlobal);
const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath);
StoreLastIndex(regexp, 0, isFastPath);
// Allocate an array to store the resulting match strings.
let array = growable_fixed_array::NewGrowableFixedArray();
// Check if the regexp is an ATOM type. If so, then keep the literal string
// to search for so that we can avoid calling substring in the loop below.
let atom: bool = false;
let searchString: String = EmptyStringConstant();
if constexpr (isFastPath) {
const maybeAtomRegexp = UnsafeCast<JSRegExp>(regexp);
const data = UnsafeCast<FixedArray>(maybeAtomRegexp.data);
if (UnsafeCast<Smi>(data.objects[kTagIndex]) == kATOM) {
searchString = UnsafeCast<String>(data.objects[kAtomPatternIndex]);
atom = true;
}
}
while (true) {
let match: String = EmptyStringConstant();
try {
if constexpr (isFastPath) {
// On the fast path, grab the matching string from the raw match index
// array.
const matchIndices: RegExpMatchInfo =
RegExpPrototypeExecBodyWithoutResultFast(
UnsafeCast<JSRegExp>(regexp), string) otherwise IfDidNotMatch;
if (atom) {
match = searchString;
} else {
const matchFrom = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex);
const matchTo = UnsafeLoadFixedArrayElement(
matchIndices, kRegExpMatchInfoFirstCaptureIndex + 1);
match = SubString(
string, UnsafeCast<Smi>(matchFrom), UnsafeCast<Smi>(matchTo));
}
} else {
assert(!isFastPath);
const resultTemp = RegExpExec(regexp, string);
if (resultTemp == Null) {
goto IfDidNotMatch;
}
match = ToString_Inline(GetProperty(resultTemp, SmiConstant(0)));
}
goto IfDidMatch;
} label IfDidNotMatch {
return array.length == 0 ? Null : array.ToJSArray();
} label IfDidMatch {
// Store the match, growing the fixed array if needed.
array.Push(match);
// Advance last index if the match is the empty string.
const matchLength: Smi = match.length_smi;
if (matchLength != 0) {
continue;
}
let lastIndex = LoadLastIndex(regexp, isFastPath);
if constexpr (isFastPath) {
assert(TaggedIsPositiveSmi(lastIndex));
} else {
lastIndex = ToLength_Inline(lastIndex);
}
const newLastIndex: Number = AdvanceStringIndex(
string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath);
if constexpr (isFastPath) {
// On the fast path, we can be certain that lastIndex can never be
// incremented to overflow the Smi range since the maximal string
// length is less than the maximal Smi value.
const kMaxStringLengthFitsSmi: constexpr bool =
kStringMaxLengthUintptr < kSmiMaxValue;
static_assert(kMaxStringLengthFitsSmi);
assert(TaggedIsPositiveSmi(newLastIndex));
}
StoreLastIndex(regexp, newLastIndex, isFastPath);
}
}
VerifiedUnreachable();
}
transitioning macro FastRegExpPrototypeMatchBody(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, true);
}
transitioning macro SlowRegExpPrototypeMatchBody(implicit context: Context)(
receiver: JSReceiver, string: String): JSAny {
return RegExpPrototypeMatchBody(receiver, string, false);
}
// Helper that skips a few initial checks. and assumes...
// 1) receiver is a "fast" RegExp
// 2) pattern is a string
transitioning builtin RegExpMatchFast(implicit context: Context)(
receiver: FastJSRegExp, string: String): JSAny {
return FastRegExpPrototypeMatchBody(receiver, string);
}
// ES#sec-regexp.prototype-@@match
// RegExp.prototype [ @@match ] ( string )
transitioning javascript builtin RegExpPrototypeMatch(
js-implicit context: NativeContext, receiver: JSAny)(string: JSAny): JSAny {
ThrowIfNotJSReceiver(
receiver, MessageTemplate::kIncompatibleMethodReceiver,
'RegExp.prototype.@@match');
const receiver = UnsafeCast<JSReceiver>(receiver);
const string: String = ToString_Inline(string);
// Strict: Reads global and unicode properties.
// TODO(jgruber): Handle slow flag accesses on the fast path and make this
// permissive.
const fastRegExp = Cast<FastJSRegExp>(receiver)
otherwise return SlowRegExpPrototypeMatchBody(receiver, string);
// TODO(pwong): Could be optimized to remove the overhead of calling the
// builtin (at the cost of a larger builtin).
return RegExpMatchFast(fastRegExp, string);
}
}