Bug 1896505 - Track Type III Subclassing in RegExp r=jandem

The challenge of getting this right is truly comical.

Differential Revision: https://phabricator.services.mozilla.com/D211457
This commit is contained in:
Matthew Gaudet 2024-05-29 19:14:14 +00:00
Родитель ca21ea9219
Коммит b40d7d5990
8 изменённых файлов: 178 добавлений и 24 удалений

Просмотреть файл

@ -78,6 +78,7 @@ custom JS_subclassing_typedarray_type_2 TypedArray is Type II subclassed
custom JS_subclassing_typedarray_type_3 TypedArray is Type III subclassed
custom JS_subclassing_arraybuffer_type_3 ArrayBuffer is Type III subclassed
custom JS_subclassing_sharedarraybuffer_type_3 SharedArrayBuffer is Type III subclassed
custom JS_subclassing_regexp_type_3 RegExp is Type III subclassed
// Console API
method console.assert

Просмотреть файл

@ -107,8 +107,8 @@ use.counter:
send_in_pings:
- use-counters
# Total of 2325 use counter metrics (excludes denominators).
# Total of 362 'page' use counters.
# Total of 2327 use counter metrics (excludes denominators).
# Total of 363 'page' use counters.
use.counter.page:
svgsvgelement_getelementbyid:
type: counter
@ -671,6 +671,23 @@ use.counter.page:
send_in_pings:
- use-counters
js_subclassing_regexp_type_3:
type: counter
description: >
Whether a page RegExp is Type III subclassed.
Compare against `use.counter.top_level_content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
console_assert:
type: counter
description: >
@ -6264,7 +6281,7 @@ use.counter.page:
send_in_pings:
- use-counters
# Total of 362 'document' use counters.
# Total of 363 'document' use counters.
use.counter.doc:
svgsvgelement_getelementbyid:
type: counter
@ -6827,6 +6844,23 @@ use.counter.doc:
send_in_pings:
- use-counters
js_subclassing_regexp_type_3:
type: counter
description: >
Whether a document RegExp is Type III subclassed.
Compare against `use.counter.content_documents_destroyed`
to calculate the rate.
bugs:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
data_reviews:
- https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
notification_emails:
- dom-core@mozilla.com
- emilio@mozilla.com
expires: never
send_in_pings:
- use-counters
console_assert:
type: counter
description: >

Просмотреть файл

@ -95,7 +95,9 @@ extern JS_PUBLIC_API void JS_SetAccumulateTelemetryCallback(
_(SUBCLASSING_TYPEDARRAY_TYPE_II, SubclassingTypedArrayTypeII) \
_(SUBCLASSING_TYPEDARRAY_TYPE_III, SubclassingTypedArrayTypeIII) \
_(SUBCLASSING_ARRAYBUFFER_TYPE_III, SubclassingArrayBufferTypeIII) \
_(SUBCLASSING_SHAREDARRAYBUFFER_TYPE_III, SubclassingSharedArrayBufferTypeIII)
_(SUBCLASSING_SHAREDARRAYBUFFER_TYPE_III, \
SubclassingSharedArrayBufferTypeIII) \
_(SUBCLASSING_REGEXP_TYPE_III, SubclassingRegExpTypeIII)
/*
* Use counter names passed to the accumulate use counter callback.

Просмотреть файл

@ -135,7 +135,8 @@
#define SUBCLASSING_TYPEDARRAY 3
#define SUBCLASSING_ARRAYBUFFER 4
#define SUBCLASSING_SHAREDARRAYBUFFER 5
#define SUBCLASSING_LAST_BUILTIN 6
#define SUBCLASSING_REGEXP 6
#define SUBCLASSING_LAST_BUILTIN 7
#define SUBCLASSING_TYPE_II 2
#define SUBCLASSING_TYPE_III 3
@ -158,4 +159,7 @@
#define SUBCLASSING_DETERMINE_THROUGH_CONSTRUCTOR_TYPE_III SUBCLASSING_TYPE_III
#define SUBCLASS_REGEXP_TYPE_III \
((SUBCLASSING_REGEXP << SUBCLASSING_BUILTIN_SHIFT) | SUBCLASSING_TYPE_III)
#endif

Просмотреть файл

@ -0,0 +1,69 @@
function test_function_for_use_counter_integration(fn, counter, expected_growth = true) {
let before = getUseCounterResults();
assertEq(counter in before, true);
fn();
let after = getUseCounterResults();
if (expected_growth) {
console.log("Yes Increase: Before ", before[counter], " After", after[counter]);
assertEq(after[counter] > before[counter], true);
} else {
console.log("No Increase: Before ", before[counter], " After", after[counter]);
assertEq(after[counter] == before[counter], true);
}
}
function R() { }
Object.setPrototypeOf(R, RegExp);
Object.setPrototypeOf(R.prototype, RegExp.prototype);
// Define a bunch of new getters since the builtin getters throw on
// non-RegExp-branded `this`
Object.defineProperty(R.prototype, "hasIndices", { value: false });
Object.defineProperty(R.prototype, "global", { value: true });
Object.defineProperty(R.prototype, "ignoreCase", { value: false });
Object.defineProperty(R.prototype, "multiline", { value: false });
Object.defineProperty(R.prototype, "dotAll", { value: false });
Object.defineProperty(R.prototype, "unicode", { value: false });
Object.defineProperty(R.prototype, "unicodeSets", { value: false });
Object.defineProperty(R.prototype, "sticky", { value: false });
function test_regexp_matchall() {
"some".matchAll(/foo/g);
}
function test_regexp_matchall_type_iii() {
"some string".matchAll(new R("foo"))
}
test_function_for_use_counter_integration(test_regexp_matchall, "SubclassingRegExpTypeIII", false)
test_function_for_use_counter_integration(test_regexp_matchall_type_iii, "SubclassingRegExpTypeIII", true)
function test_regexp_split() {
let r = /p/;
let split = r[Symbol.split];
split.call(r, "split");
}
function test_regexp_split_type_iii() {
let r = /r/;
class B extends RegExp {
x = "this";
}
let b = new B("R");
let split = r[Symbol.split];
// This reciever needs to be a class-subclass RegExp,
// as RegExp[@@split] will call RegExp.exec, which
// typechecks this.
split.call(b, "split");
}
test_function_for_use_counter_integration(test_regexp_split, "SubclassingRegExpTypeIII", false)
test_function_for_use_counter_integration(test_regexp_split_type_iii, "SubclassingRegExpTypeIII", true)

Просмотреть файл

@ -605,6 +605,14 @@ class GlobalObject : public NativeObject {
return &global->getPrototype(JSProto_RegExp);
}
static JSObject* getOrCreateRegExpConstructor(JSContext* cx,
Handle<GlobalObject*> global) {
if (!ensureConstructor(cx, global, JSProto_RegExp)) {
return nullptr;
}
return &global->getConstructor(JSProto_RegExp);
}
JSObject* maybeGetRegExpPrototype() {
if (classIsInitialized(JSProto_RegExp)) {
return &getPrototype(JSProto_RegExp);

Просмотреть файл

@ -2970,17 +2970,25 @@ bool js::ReportUsageCounter(JSContext* cx, HandleObject constructorArg,
// Do this here rather than as part of the self-hosted code.
if (builtin == SUBCLASSING_DETERMINE_THROUGH_CONSTRUCTOR) {
MOZ_ASSERT(constructor);
if (IsPromiseConstructor(constructor)) {
builtin = SUBCLASSING_PROMISE;
} else if (IsTypedArrayConstructor(constructor)) {
builtin = SUBCLASSING_TYPEDARRAY;
} else if (IsArrayConstructor(constructor)) {
builtin = SUBCLASSING_ARRAY;
} else {
do {
if (IsPromiseConstructor(constructor)) {
builtin = SUBCLASSING_PROMISE;
break;
}
if (IsTypedArrayConstructor(constructor)) {
builtin = SUBCLASSING_TYPEDARRAY;
break;
}
if (IsArrayConstructor(constructor)) {
builtin = SUBCLASSING_ARRAY;
break;
}
if (IsCrossCompartmentWrapper(constructor)) {
// Bail on reporting CCWs.
return true;
}
Rooted<GlobalObject*> global(cx, &constructor->nonCCWGlobal());
RootedObject abConstructor(
cx, GlobalObject::getOrCreateArrayBufferConstructor(cx, global));
@ -2989,21 +2997,35 @@ bool js::ReportUsageCounter(JSContext* cx, HandleObject constructorArg,
}
if (constructor == abConstructor) {
builtin = SUBCLASSING_ARRAYBUFFER;
} else {
RootedObject sabConstructor(
cx,
GlobalObject::getOrCreateSharedArrayBufferConstructor(cx, global));
if (!sabConstructor) {
return false;
}
if (constructor == sabConstructor) {
builtin = SUBCLASSING_SHAREDARRAYBUFFER;
}
break;
}
}
// While we still have builtins to process, return true here.
RootedObject sabConstructor(
cx,
GlobalObject::getOrCreateSharedArrayBufferConstructor(cx, global));
if (!sabConstructor) {
return false;
}
if (constructor == sabConstructor) {
builtin = SUBCLASSING_SHAREDARRAYBUFFER;
break;
}
RootedObject regExpConstructor(
cx, GlobalObject::getOrCreateRegExpConstructor(cx, global));
if (!regExpConstructor) {
return false;
}
if (constructor == regExpConstructor) {
builtin = SUBCLASSING_REGEXP;
break;
}
} while (false);
// We should have determined the constructor here.
if (builtin == SUBCLASSING_DETERMINE_THROUGH_CONSTRUCTOR) {
MOZ_CRASH("Unable to determine constructor where expected.");
return true;
}
// We -do- want to report here, because we've determined exterior to this
@ -3083,6 +3105,16 @@ bool js::ReportUsageCounter(JSContext* cx, HandleObject constructorArg,
: JSUseCounter::SUBCLASSING_SHAREDARRAYBUFFER_TYPE_III);
return true;
}
case SUBCLASSING_REGEXP: {
switch (type) {
case SUBCLASSING_TYPE_III:
cx->runtime()->setUseCounter(
cx->global(), JSUseCounter::SUBCLASSING_REGEXP_TYPE_III);
return true;
default:
MOZ_CRASH("Unexpected RegExp Subclassing Type");
}
}
default:
MOZ_CRASH("Unexpected builtin");
};

Просмотреть файл

@ -2668,6 +2668,10 @@ static void SetUseCounterCallback(JSObject* obj, JSUseCounter counter) {
obj,
mozilla::eUseCounter_custom_JS_subclassing_sharedarraybuffer_type_3);
return;
case JSUseCounter::SUBCLASSING_REGEXP_TYPE_III:
SetUseCounter(obj,
mozilla::eUseCounter_custom_JS_subclassing_regexp_type_3);
return;
case JSUseCounter::COUNT:
break;
}