Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions changelog.d/9281-perf-hooks-prototype-dispatch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
`PerformanceObserver.observe()` no longer recurses through native method
dispatch until the process exhausts its stack. Perry now records the built-in
`perf_hooks` class hierarchy as class-default prototype wiring instead of
misclassifying it as a user `Object.setPrototypeOf` override. This also keeps
`PerformanceMark`, `PerformanceMeasure`, `PerformanceResourceTiming`, and
observer entry-list instances on the ordinary built-in dispatch path.
26 changes: 14 additions & 12 deletions crates/perry-runtime/src/perf_hooks/prototypes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,23 @@ fn perf_constructor_prototype(class_name: &str) -> f64 {
crate::closure::closure_get_dynamic_prop(ptr, "prototype")
}

/// Link runtime-created perf objects through their built-in class hierarchy.
///
/// This is class-default wiring, not a user `Object.setPrototypeOf` override.
/// The loud setter marks the receiver with `OBJECT_META_FLAG_PROTO_OVERRIDE`;
/// method dispatch then treats the chain as user-replaced and re-dispatches an
/// already bound prototype method back through `js_native_call_method`,
/// recursing until the process exhausts its stack (#9281).
fn link_perf_class_default_prototype(obj: usize, proto_bits: u64) {
crate::object::prototype_chain::object_link_class_default_prototype(obj, proto_bits);
}

pub(super) fn link_perf_prototype(value: f64, class_name: &str) -> f64 {
let scope = crate::gc::RuntimeHandleScope::new();
let value = scope.root_nanbox_f64(value);
let prototype = scope.root_nanbox_f64(perf_constructor_prototype(class_name));
let obj = crate::value::js_nanbox_get_pointer(value.get_nanbox_f64()) as usize;
crate::object::prototype_chain::object_set_static_prototype(
obj,
prototype.get_nanbox_f64().to_bits(),
);
link_perf_class_default_prototype(obj, prototype.get_nanbox_f64().to_bits());
value.get_nanbox_f64()
}

Expand Down Expand Up @@ -290,10 +298,7 @@ pub(crate) unsafe fn attach_perf_hooks_constructor(
}
"PerformanceMark" | "PerformanceMeasure" => {
let base = perf_constructor_prototype("PerformanceEntry");
crate::object::prototype_chain::object_set_static_prototype(
proto as usize,
base.to_bits(),
);
link_perf_class_default_prototype(proto as usize, base.to_bits());
let getter = perf_field_getter("detail");
install_perf_getter(proto, "detail", getter, true);
let to_json = perf_method_value(perf_entry_to_json_thunk as *const u8, "toJSON", 0);
Expand Down Expand Up @@ -356,10 +361,7 @@ pub(crate) unsafe fn attach_perf_hooks_constructor(
}
"PerformanceResourceTiming" => {
let base = perf_constructor_prototype("PerformanceEntry");
crate::object::prototype_chain::object_set_static_prototype(
proto as usize,
base.to_bits(),
);
link_perf_class_default_prototype(proto as usize, base.to_bits());
for field in [
"initiatorType",
"workerStart",
Expand Down
Loading