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