blob: a3606dc4414053092e4537ee1c59115aa0a5e2d0 [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 uint8
generates 'static_cast<uint8_t>(RegExpData::Type::ATOM)';
const kIRREGEXP: constexpr uint8
generates 'static_cast<uint8_t>(RegExpData::Type::IRREGEXP)';
const kEXPERIMENTAL: constexpr uint8
generates 'static_cast<uint8_t>(RegExpData::Type::EXPERIMENTAL)';
const kRegExpDataIndirectPointerTag:
constexpr uint64 generates 'kRegExpDataIndirectPointerTag';
extern transitioning macro RegExpBuiltinsAssembler::FlagGetter(
implicit context: Context)(Object, constexpr Flag, constexpr bool): bool;
extern macro LoadTrustedPointerFromObject(
HeapObject, constexpr int31, constexpr uint64): TrustedObject;
extern transitioning macro RegExpBuiltinsAssembler::RegExpMatchGlobal(
implicit context: Context)(JSRegExp, String, RegExpData): JSAny;
namespace runtime {
extern runtime RegExpMatchGlobalAtom(
implicit context: Context)(JSRegExp, String, RegExpData): JSAny;
}
transitioning macro RegExpPrototypeMatchBody(
implicit context: Context)(regexp: JSReceiver, string: String,
isFastPath: constexpr bool): JSAny {
if constexpr (isFastPath) {
dcheck(Is<FastJSRegExp>(regexp));
}
const isGlobal: bool = FlagGetter(regexp, Flag::kGlobal, isFastPath);
if (!isGlobal) {
return isFastPath ? RegExpPrototypeExecBodyFast(regexp, string) :
RegExpExec(regexp, string);
}
dcheck(isGlobal);
// The fast paths:
if constexpr (isFastPath) {
const jsregexp = UnsafeCast<JSRegExp>(regexp);
const data: RegExpData =
UnsafeCast<RegExpData>(LoadTrustedPointerFromObject(
jsregexp, kJSRegExpRegExpDataOffset,
kRegExpDataIndirectPointerTag));
if (data.type_tag == kATOM) {
// TODO(jgruber): We could merge this path with
// RegExpMatchGlobal; but then we'd lose the caching.
return runtime::RegExpMatchGlobalAtom(jsregexp, string, data);
}
return RegExpMatchGlobal(jsregexp, string, data);
} else {
// .. and the generic slow path.
dcheck(!isFastPath);
const isUnicode: bool = FlagGetter(regexp, Flag::kUnicode, isFastPath) ||
FlagGetter(regexp, Flag::kUnicodeSets, isFastPath);
StoreLastIndex(regexp, 0, isFastPath);
let array = growable_fixed_array::NewGrowableFixedArray();
while (true) {
let match: String = EmptyStringConstant();
try {
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);
lastIndex = ToLength_Inline(lastIndex);
const newLastIndex: Number = AdvanceStringIndex(
string, UnsafeCast<Number>(lastIndex), isUnicode, isFastPath);
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);
}
}