[regexp] Add use counters for IsRegExp
A spec change to simplify IsRegExp has been proposed: https://github.com/tc39/ecma262/pull/1318 This CL adds use counters for cases in which the spec change would alter behavior: 1. o[@@match] is trueish but o is not a JSRegExp 2. o[@@match] is falseish (but not undefined) and o is a JSRegExp This is the V8 side of required changes. The Chromium-side CL: https://crrev.com/c/1360730 Drive-by: TNodeify IsRegExp. Tbr: yangguo@chromium.org Bug: v8:8522 Change-Id: I3766e02977f256a80d0e59472d3bafa9c692af9e Reviewed-on: https://chromium-review.googlesource.com/c/1360630 Reviewed-by: Jakob Gruber <jgruber@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org> Commit-Queue: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#58064}
This commit is contained in:
parent
8c7a29b201
commit
a0858cf0ca
@ -7320,6 +7320,8 @@ class V8_EXPORT Isolate {
|
||||
kAttemptOverrideReadOnlyOnPrototypeSloppy = 69,
|
||||
kAttemptOverrideReadOnlyOnPrototypeStrict = 70,
|
||||
kOptimizedFunctionWithOneShotBytecode = 71,
|
||||
kRegExpMatchIsTrueishOnNonJSRegExp = 72,
|
||||
kRegExpMatchIsFalseishOnJSRegExp = 73,
|
||||
|
||||
// If you add new values here, you'll also need to update Chromium's:
|
||||
// web_feature.mojom, UseCounterCallback.cpp, and enums.xml. V8 changes to
|
||||
|
@ -1202,20 +1202,20 @@ Node* RegExpBuiltinsAssembler::FlagsGetter(Node* const context,
|
||||
}
|
||||
|
||||
// ES#sec-isregexp IsRegExp ( argument )
|
||||
Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
|
||||
Node* const maybe_receiver) {
|
||||
TNode<BoolT> RegExpBuiltinsAssembler::IsRegExp(TNode<Context> context,
|
||||
TNode<Object> maybe_receiver) {
|
||||
Label out(this), if_isregexp(this);
|
||||
|
||||
VARIABLE(var_result, MachineRepresentation::kWord32, Int32Constant(0));
|
||||
TVARIABLE(BoolT, var_result, Int32FalseConstant());
|
||||
|
||||
GotoIf(TaggedIsSmi(maybe_receiver), &out);
|
||||
GotoIfNot(IsJSReceiver(maybe_receiver), &out);
|
||||
GotoIfNot(IsJSReceiver(CAST(maybe_receiver)), &out);
|
||||
|
||||
Node* const receiver = maybe_receiver;
|
||||
TNode<JSReceiver> receiver = CAST(maybe_receiver);
|
||||
|
||||
// Check @@match.
|
||||
{
|
||||
Node* const value =
|
||||
TNode<Object> value =
|
||||
GetProperty(context, receiver, isolate()->factory()->match_symbol());
|
||||
|
||||
Label match_isundefined(this), match_isnotundefined(this);
|
||||
@ -1225,11 +1225,26 @@ Node* RegExpBuiltinsAssembler::IsRegExp(Node* const context,
|
||||
Branch(IsJSRegExp(receiver), &if_isregexp, &out);
|
||||
|
||||
BIND(&match_isnotundefined);
|
||||
BranchIfToBooleanIsTrue(value, &if_isregexp, &out);
|
||||
Label match_istrueish(this), match_isfalseish(this);
|
||||
BranchIfToBooleanIsTrue(value, &match_istrueish, &match_isfalseish);
|
||||
|
||||
// The common path. Symbol.match exists, equals the RegExpPrototypeMatch
|
||||
// function (and is thus trueish), and the receiver is a JSRegExp.
|
||||
BIND(&match_istrueish);
|
||||
GotoIf(IsJSRegExp(receiver), &if_isregexp);
|
||||
CallRuntime(Runtime::kIncrementUseCounter, context,
|
||||
SmiConstant(v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp));
|
||||
Goto(&if_isregexp);
|
||||
|
||||
BIND(&match_isfalseish);
|
||||
GotoIfNot(IsJSRegExp(receiver), &out);
|
||||
CallRuntime(Runtime::kIncrementUseCounter, context,
|
||||
SmiConstant(v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp));
|
||||
Goto(&out);
|
||||
}
|
||||
|
||||
BIND(&if_isregexp);
|
||||
var_result.Bind(Int32Constant(1));
|
||||
var_result = Int32TrueConstant();
|
||||
Goto(&out);
|
||||
|
||||
BIND(&out);
|
||||
@ -1299,7 +1314,7 @@ TF_BUILTIN(RegExpConstructor, RegExpBuiltinsAssembler) {
|
||||
Node* const regexp_function =
|
||||
LoadContextElement(native_context, Context::REGEXP_FUNCTION_INDEX);
|
||||
|
||||
Node* const pattern_is_regexp = IsRegExp(context, pattern);
|
||||
TNode<BoolT> pattern_is_regexp = IsRegExp(context, pattern);
|
||||
|
||||
{
|
||||
Label next(this);
|
||||
|
@ -112,7 +112,7 @@ class RegExpBuiltinsAssembler : public CodeStubAssembler {
|
||||
void FlagGetter(Node* context, Node* receiver, JSRegExp::Flag flag,
|
||||
int counter, const char* method_name);
|
||||
|
||||
Node* IsRegExp(Node* const context, Node* const maybe_receiver);
|
||||
TNode<BoolT> IsRegExp(TNode<Context> context, TNode<Object> maybe_receiver);
|
||||
|
||||
Node* RegExpInitialize(Node* const context, Node* const regexp,
|
||||
Node* const maybe_pattern, Node* const maybe_flags);
|
||||
|
@ -131,7 +131,18 @@ Maybe<bool> RegExpUtils::IsRegExp(Isolate* isolate, Handle<Object> object) {
|
||||
isolate->factory()->match_symbol()),
|
||||
Nothing<bool>());
|
||||
|
||||
if (!match->IsUndefined(isolate)) return Just(match->BooleanValue(isolate));
|
||||
if (!match->IsUndefined(isolate)) {
|
||||
const bool match_as_boolean = match->BooleanValue(isolate);
|
||||
|
||||
if (match_as_boolean && !object->IsJSRegExp()) {
|
||||
isolate->CountUsage(v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp);
|
||||
} else if (!match_as_boolean && object->IsJSRegExp()) {
|
||||
isolate->CountUsage(v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp);
|
||||
}
|
||||
|
||||
return Just(match_as_boolean);
|
||||
}
|
||||
|
||||
return Just(object->IsJSRegExp());
|
||||
}
|
||||
|
||||
|
@ -165,6 +165,40 @@ TEST(OverrideReadOnlyPropertyOnPrototype) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(RegExpMatchIsTrueishOnNonJSRegExp) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
|
||||
global_use_counts = use_counts;
|
||||
CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
|
||||
|
||||
CompileRun("new RegExp(/./); new RegExp('');");
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp]);
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp]);
|
||||
|
||||
CompileRun("let p = { [Symbol.match]: true }; new RegExp(p);");
|
||||
CHECK_EQ(1, use_counts[v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp]);
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp]);
|
||||
}
|
||||
|
||||
TEST(RegExpMatchIsFalseishOnJSRegExp) {
|
||||
v8::Isolate* isolate = CcTest::isolate();
|
||||
v8::HandleScope scope(isolate);
|
||||
LocalContext env;
|
||||
int use_counts[v8::Isolate::kUseCounterFeatureCount] = {};
|
||||
global_use_counts = use_counts;
|
||||
CcTest::isolate()->SetUseCounterCallback(MockUseCounterCallback);
|
||||
|
||||
CompileRun("new RegExp(/./); new RegExp('');");
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp]);
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp]);
|
||||
|
||||
CompileRun("let p = /./; p[Symbol.match] = false; new RegExp(p);");
|
||||
CHECK_EQ(0, use_counts[v8::Isolate::kRegExpMatchIsTrueishOnNonJSRegExp]);
|
||||
CHECK_EQ(1, use_counts[v8::Isolate::kRegExpMatchIsFalseishOnJSRegExp]);
|
||||
}
|
||||
|
||||
} // namespace test_usecounters
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
Loading…
Reference in New Issue
Block a user