Reland "[builtins] Port RegExpTest to Torque"
This is a reland of f54f92dda1
.
Fix IsFastRegExpPermissive to call BranchIfFastRegExp_Permissive.
Original change's description:
> [builtins] Port RegExpTest to Torque
>
> Bug: v8:8976
> Change-Id: Ia4dc120a31eb363599b47b22b749a3146a9c7c73
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1746083
> Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Tobias Tebbi <tebbi@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#63211}
Bug: v8:8976, chromium:994041
Change-Id: I86c9c66b060f47164515e29f914b95456c233d30
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1756390
Commit-Queue: Z Nguyen-Huu <duongn@microsoft.com>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63255}
This commit is contained in:
parent
8e065dbe67
commit
bc1c36ee56
1
BUILD.gn
1
BUILD.gn
@ -994,6 +994,7 @@ torque_files = [
|
||||
"src/builtins/proxy.tq",
|
||||
"src/builtins/reflect.tq",
|
||||
"src/builtins/regexp-replace.tq",
|
||||
"src/builtins/regexp-test.tq",
|
||||
"src/builtins/regexp.tq",
|
||||
"src/builtins/string.tq",
|
||||
"src/builtins/string-endswith.tq",
|
||||
|
@ -865,8 +865,6 @@ namespace internal {
|
||||
TFJ(RegExpPrototypeSourceGetter, 0, kReceiver) \
|
||||
/* ES #sec-get-regexp.prototype.sticky */ \
|
||||
TFJ(RegExpPrototypeStickyGetter, 0, kReceiver) \
|
||||
/* ES #sec-regexp.prototype.test */ \
|
||||
TFJ(RegExpPrototypeTest, 1, kReceiver, kString) \
|
||||
TFS(RegExpPrototypeTestFast, kReceiver, kString) \
|
||||
CPP(RegExpPrototypeToString) \
|
||||
/* ES #sec-get-regexp.prototype.unicode */ \
|
||||
|
@ -843,8 +843,8 @@ RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResult(
|
||||
|
||||
TNode<RegExpMatchInfo>
|
||||
RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast(
|
||||
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
|
||||
TNode<String> string, Label* if_didnotmatch) {
|
||||
TNode<Context> context, TNode<JSRegExp> maybe_regexp, TNode<String> string,
|
||||
Label* if_didnotmatch) {
|
||||
return RegExpPrototypeExecBodyWithoutResult(context, maybe_regexp, string,
|
||||
if_didnotmatch, true);
|
||||
}
|
||||
@ -1052,25 +1052,6 @@ void RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
|
||||
if_isunmodified, if_ismodified);
|
||||
}
|
||||
|
||||
TNode<BoolT> RegExpBuiltinsAssembler::IsFastRegExp_Permissive(
|
||||
SloppyTNode<Context> context, SloppyTNode<Object> object) {
|
||||
Label yup(this), nope(this), out(this);
|
||||
TVARIABLE(BoolT, var_result);
|
||||
|
||||
BranchIfFastRegExp_Permissive(context, CAST(object), &yup, &nope);
|
||||
|
||||
BIND(&yup);
|
||||
var_result = Int32TrueConstant();
|
||||
Goto(&out);
|
||||
|
||||
BIND(&nope);
|
||||
var_result = Int32FalseConstant();
|
||||
Goto(&out);
|
||||
|
||||
BIND(&out);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void RegExpBuiltinsAssembler::BranchIfFastRegExpResult(Node* const context,
|
||||
Node* const object,
|
||||
Label* if_isunmodified,
|
||||
@ -1790,9 +1771,9 @@ TF_BUILTIN(RegExpPrototypeUnicodeGetter, RegExpBuiltinsAssembler) {
|
||||
}
|
||||
|
||||
// ES#sec-regexpexec Runtime Semantics: RegExpExec ( R, S )
|
||||
Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
|
||||
Node* string) {
|
||||
VARIABLE(var_result, MachineRepresentation::kTagged);
|
||||
TNode<Object> RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
|
||||
Node* string) {
|
||||
TVARIABLE(Object, var_result);
|
||||
Label out(this);
|
||||
|
||||
// Take the slow path of fetching the exec property, calling it, and
|
||||
@ -1813,12 +1794,11 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
|
||||
BIND(&if_iscallable);
|
||||
{
|
||||
Callable call_callable = CodeFactory::Call(isolate());
|
||||
Node* const result = CallJS(call_callable, context, exec, regexp, string);
|
||||
var_result = CAST(CallJS(call_callable, context, exec, regexp, string));
|
||||
|
||||
var_result.Bind(result);
|
||||
GotoIf(IsNull(result), &out);
|
||||
GotoIf(IsNull(var_result.value()), &out);
|
||||
|
||||
ThrowIfNotJSReceiver(context, result,
|
||||
ThrowIfNotJSReceiver(context, var_result.value(),
|
||||
MessageTemplate::kInvalidRegExpExecResult, "");
|
||||
|
||||
Goto(&out);
|
||||
@ -1829,9 +1809,8 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
|
||||
ThrowIfNotInstanceType(context, regexp, JS_REGEXP_TYPE,
|
||||
"RegExp.prototype.exec");
|
||||
|
||||
Node* const result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow,
|
||||
context, regexp, string);
|
||||
var_result.Bind(result);
|
||||
var_result = CallBuiltin(Builtins::kRegExpPrototypeExecSlow, context,
|
||||
regexp, string);
|
||||
Goto(&out);
|
||||
}
|
||||
|
||||
@ -1839,47 +1818,6 @@ Node* RegExpBuiltinsAssembler::RegExpExec(Node* context, Node* regexp,
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
// ES#sec-regexp.prototype.test
|
||||
// RegExp.prototype.test ( S )
|
||||
TF_BUILTIN(RegExpPrototypeTest, RegExpBuiltinsAssembler) {
|
||||
TNode<Object> maybe_receiver = CAST(Parameter(Descriptor::kReceiver));
|
||||
TNode<Object> maybe_string = CAST(Parameter(Descriptor::kString));
|
||||
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
|
||||
|
||||
// Ensure {maybe_receiver} is a JSReceiver.
|
||||
ThrowIfNotJSReceiver(context, maybe_receiver,
|
||||
MessageTemplate::kIncompatibleMethodReceiver,
|
||||
"RegExp.prototype.test");
|
||||
TNode<JSReceiver> receiver = CAST(maybe_receiver);
|
||||
|
||||
// Convert {maybe_string} to a String.
|
||||
TNode<String> string = ToString_Inline(context, maybe_string);
|
||||
|
||||
Label fast_path(this), slow_path(this);
|
||||
BranchIfFastRegExp_Permissive(context, receiver, &fast_path, &slow_path);
|
||||
|
||||
BIND(&fast_path);
|
||||
{
|
||||
Label if_didnotmatch(this);
|
||||
RegExpPrototypeExecBodyWithoutResult(context, receiver, string,
|
||||
&if_didnotmatch, true);
|
||||
Return(TrueConstant());
|
||||
|
||||
BIND(&if_didnotmatch);
|
||||
Return(FalseConstant());
|
||||
}
|
||||
|
||||
BIND(&slow_path);
|
||||
{
|
||||
// Call exec.
|
||||
TNode<HeapObject> match_indices =
|
||||
CAST(RegExpExec(context, receiver, string));
|
||||
|
||||
// Return true iff exec matched successfully.
|
||||
Return(SelectBooleanConstant(IsNotNull(match_indices)));
|
||||
}
|
||||
}
|
||||
|
||||
TF_BUILTIN(RegExpPrototypeTestFast, RegExpBuiltinsAssembler) {
|
||||
TNode<JSRegExp> regexp = CAST(Parameter(Descriptor::kReceiver));
|
||||
TNode<String> string = CAST(Parameter(Descriptor::kString));
|
||||
@ -2179,7 +2117,8 @@ void RegExpMatchAllAssembler::Generate(TNode<Context> context,
|
||||
// 6. Let matcher be ? Construct(C, « R, flags »).
|
||||
TNode<String> flags = CAST(FlagsGetter(context, fast_regexp, true));
|
||||
var_matcher = RegExpCreate(context, native_context, source, flags);
|
||||
CSA_ASSERT(this, IsFastRegExp_Permissive(context, var_matcher.value()));
|
||||
CSA_ASSERT(this,
|
||||
IsFastRegExpPermissive(context, CAST(var_matcher.value())));
|
||||
|
||||
// 7. Let lastIndex be ? ToLength(? Get(R, "lastIndex")).
|
||||
// 8. Perform ? Set(matcher, "lastIndex", lastIndex, true).
|
||||
@ -2336,7 +2275,7 @@ TF_BUILTIN(RegExpMatchFast, RegExpBuiltinsAssembler) {
|
||||
|
||||
void RegExpBuiltinsAssembler::RegExpPrototypeSearchBodyFast(
|
||||
TNode<Context> context, TNode<JSRegExp> regexp, TNode<String> string) {
|
||||
CSA_ASSERT(this, IsFastRegExp_Permissive(context, regexp));
|
||||
CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
|
||||
|
||||
// Grab the initial value of last index.
|
||||
TNode<Smi> previous_last_index = FastLoadLastIndex(regexp);
|
||||
@ -2478,13 +2417,12 @@ TF_BUILTIN(RegExpSearchFast, RegExpBuiltinsAssembler) {
|
||||
|
||||
// Generates the fast path for @@split. {regexp} is an unmodified, non-sticky
|
||||
// JSRegExp, {string} is a String, and {limit} is a Smi.
|
||||
void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
|
||||
Node* const regexp,
|
||||
void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(TNode<Context> context,
|
||||
TNode<JSRegExp> regexp,
|
||||
TNode<String> string,
|
||||
TNode<Smi> const limit) {
|
||||
CSA_ASSERT(this, IsFastRegExp_Permissive(context, regexp));
|
||||
CSA_ASSERT(this,
|
||||
Word32BinaryNot(FastFlagGetter(CAST(regexp), JSRegExp::kSticky)));
|
||||
CSA_ASSERT(this, IsFastRegExpPermissive(context, regexp));
|
||||
CSA_ASSERT(this, Word32BinaryNot(FastFlagGetter(regexp, JSRegExp::kSticky)));
|
||||
|
||||
TNode<IntPtrT> const int_limit = SmiUntag(limit);
|
||||
|
||||
@ -2606,7 +2544,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
|
||||
GotoIfNot(SmiEqual(match_to, next_search_from), &next);
|
||||
GotoIfNot(SmiEqual(match_to, last_matched_until), &next);
|
||||
|
||||
Node* const is_unicode = FastFlagGetter(CAST(regexp), JSRegExp::kUnicode);
|
||||
Node* const is_unicode = FastFlagGetter(regexp, JSRegExp::kUnicode);
|
||||
Node* const new_next_search_from =
|
||||
AdvanceStringIndex(string, next_search_from, is_unicode, true);
|
||||
var_next_search_from = CAST(new_next_search_from);
|
||||
@ -2698,7 +2636,7 @@ void RegExpBuiltinsAssembler::RegExpPrototypeSplitBody(Node* const context,
|
||||
|
||||
BIND(&out);
|
||||
{
|
||||
Node* const result = array.ToJSArray(CAST(context));
|
||||
Node* const result = array.ToJSArray(context);
|
||||
Return(result);
|
||||
}
|
||||
|
||||
@ -2898,7 +2836,7 @@ TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) {
|
||||
|
||||
BIND(&if_slow);
|
||||
{
|
||||
var_match = CAST(RegExpExec(context, iterating_regexp, iterating_string));
|
||||
var_match = RegExpExec(context, iterating_regexp, iterating_string);
|
||||
var_is_fast_regexp = Int32FalseConstant();
|
||||
Branch(IsNull(var_match.value()), &if_no_match, &if_match);
|
||||
}
|
||||
@ -2943,7 +2881,7 @@ TF_BUILTIN(RegExpStringIteratorPrototypeNext, RegExpStringIteratorAssembler) {
|
||||
|
||||
// When iterating_regexp is fast, we assume it stays fast even after
|
||||
// accessing the first match from the RegExp result.
|
||||
CSA_ASSERT(this, IsFastRegExp_Permissive(context, iterating_regexp));
|
||||
CSA_ASSERT(this, IsFastRegExpPermissive(context, iterating_regexp));
|
||||
GotoIfNot(IsEmptyString(match_str), &return_result);
|
||||
|
||||
// 1. Let thisIndex be ? ToLength(? Get(R, "lastIndex")).
|
||||
|
@ -74,7 +74,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
|
||||
TNode<String> string, Label* if_didnotmatch, const bool is_fastpath);
|
||||
TNode<RegExpMatchInfo> RegExpPrototypeExecBodyWithoutResultFast(
|
||||
TNode<Context> context, TNode<JSReceiver> maybe_regexp,
|
||||
TNode<Context> context, TNode<JSRegExp> maybe_regexp,
|
||||
TNode<String> string, Label* if_didnotmatch);
|
||||
|
||||
TNode<HeapObject> RegExpPrototypeExecBody(TNode<Context> context,
|
||||
@ -133,10 +133,6 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
Label* if_isunmodified,
|
||||
Label* if_ismodified);
|
||||
|
||||
// Analogous to BranchIfFastRegExp_Permissive, for use in asserts.
|
||||
TNode<BoolT> IsFastRegExp_Permissive(SloppyTNode<Context> context,
|
||||
SloppyTNode<Object> object);
|
||||
|
||||
// Performs fast path checks on the given object itself, but omits prototype
|
||||
// checks.
|
||||
Node* IsFastRegExpNoPrototype(Node* const context, Node* const object);
|
||||
@ -170,7 +166,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
Node* RegExpInitialize(Node* const context, Node* const regexp,
|
||||
Node* const maybe_pattern, Node* const maybe_flags);
|
||||
|
||||
Node* RegExpExec(Node* context, Node* regexp, Node* string);
|
||||
TNode<Object> RegExpExec(Node* context, Node* regexp, Node* string);
|
||||
|
||||
TNode<Number> AdvanceStringIndex(SloppyTNode<String> string,
|
||||
SloppyTNode<Number> index,
|
||||
@ -192,7 +188,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
void RegExpPrototypeSearchBodySlow(Node* const context, Node* const regexp,
|
||||
Node* const string);
|
||||
|
||||
void RegExpPrototypeSplitBody(Node* const context, Node* const regexp,
|
||||
void RegExpPrototypeSplitBody(TNode<Context> context, TNode<JSRegExp> regexp,
|
||||
TNode<String> const string,
|
||||
TNode<Smi> const limit);
|
||||
};
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
#include 'src/builtins/builtins-regexp-gen.h'
|
||||
|
||||
namespace regexp_replace {
|
||||
namespace regexp {
|
||||
|
||||
extern builtin
|
||||
StringIndexOf(implicit context: Context)(String, String, Smi): Smi;
|
||||
@ -23,10 +23,6 @@ namespace regexp_replace {
|
||||
|
||||
extern macro
|
||||
RegExpBuiltinsAssembler::AdvanceStringIndexFast(String, Smi, bool): Smi;
|
||||
extern macro
|
||||
RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast(
|
||||
implicit context: Context)(JSReceiver, String):
|
||||
RegExpMatchInfo labels IfDidNotMatch;
|
||||
|
||||
transitioning macro RegExpReplaceCallableNoExplicitCaptures(implicit context:
|
||||
Context)(
|
||||
@ -146,8 +142,9 @@ namespace regexp_replace {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const match: RegExpMatchInfo = RegExpPrototypeExecBodyWithoutResultFast(
|
||||
regexp, string) otherwise break;
|
||||
const match: RegExpMatchInfo =
|
||||
regexp::RegExpPrototypeExecBodyWithoutResultFast(regexp, string)
|
||||
otherwise break;
|
||||
const matchStart: Smi = match.GetStartOfCapture(0);
|
||||
const matchEnd: Smi = match.GetEndOfCapture(0);
|
||||
|
||||
|
26
src/builtins/regexp-test.tq
Normal file
26
src/builtins/regexp-test.tq
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 {
|
||||
|
||||
// ES#sec-regexp.prototype.test
|
||||
// RegExp.prototype.test ( S )
|
||||
transitioning javascript builtin RegExpPrototypeTest(
|
||||
js-implicit context: Context, receiver: Object)(string: String): Object {
|
||||
const methodName: constexpr string = 'RegExp.prototype.test';
|
||||
const receiver = Cast<JSReceiver>(receiver)
|
||||
otherwise ThrowTypeError(kIncompatibleMethodReceiver, methodName);
|
||||
const str: String = ToString_Inline(context, string);
|
||||
if (IsFastRegExpPermissive(receiver)) {
|
||||
RegExpPrototypeExecBodyWithoutResultFast(
|
||||
UnsafeCast<JSRegExp>(receiver), str)
|
||||
otherwise return False;
|
||||
return True;
|
||||
}
|
||||
const matchIndices = RegExpExec(context, receiver, str);
|
||||
return SelectBooleanConstant(matchIndices != Null);
|
||||
}
|
||||
}
|
@ -13,4 +13,20 @@ namespace regexp {
|
||||
BranchIfFastRegExp_Strict(o) otherwise return true, return false;
|
||||
}
|
||||
|
||||
extern macro RegExpBuiltinsAssembler::BranchIfFastRegExp_Permissive(
|
||||
implicit context: Context)(HeapObject): never labels IsFast,
|
||||
IsSlow;
|
||||
|
||||
@export
|
||||
macro IsFastRegExpPermissive(implicit context: Context)(o: HeapObject): bool {
|
||||
BranchIfFastRegExp_Permissive(o) otherwise return true, return false;
|
||||
}
|
||||
|
||||
extern macro RegExpBuiltinsAssembler::RegExpExec(Context, Object, Object):
|
||||
Object;
|
||||
|
||||
extern macro
|
||||
RegExpBuiltinsAssembler::RegExpPrototypeExecBodyWithoutResultFast(
|
||||
implicit context: Context)(JSRegExp, String):
|
||||
RegExpMatchInfo labels IfDidNotMatch;
|
||||
}
|
||||
|
9
test/mjsunit/regress/regress-crbug-994041.js
Normal file
9
test/mjsunit/regress/regress-crbug-994041.js
Normal file
@ -0,0 +1,9 @@
|
||||
// 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.
|
||||
|
||||
v0 = Array().join();
|
||||
RegExp.prototype.__defineSetter__(0, function() {
|
||||
})
|
||||
v24 = v0.search();
|
||||
assertEquals(v24, 0);
|
Loading…
Reference in New Issue
Block a user