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
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ pub(crate) const NODE_CORE_INSPECTOR_VM_ROWS: &[NativeModSig] = &[
args: &[],
ret: NR_F64,
},
NativeModSig {
module: "inspector/promises",
has_receiver: false,
method: "SessionCall",
class_filter: None,
runtime: "js_node_inspector_session_call_without_new",
args: &[],
ret: NR_F64,
},
NativeModSig {
module: "inspector",
has_receiver: true,
Expand Down Expand Up @@ -224,7 +233,7 @@ pub(crate) const NODE_CORE_INSPECTOR_VM_ROWS: &[NativeModSig] = &[
has_receiver: true,
method: "post",
class_filter: Some("Session"),
runtime: "js_node_inspector_session_post",
runtime: "js_node_inspector_promises_session_post",
args: &[NA_F64, NA_F64, NA_F64],
ret: NR_F64,
},
Expand Down
9 changes: 9 additions & 0 deletions crates/perry-hir/src/lower/expr_call/globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,15 @@ pub(super) fn try_global_builtins(
// Check if this is a direct call on an aliased named import
// e.g., uuid() where import { v4 as uuid } from 'uuid'
if let Some((module_name, Some(method_name))) = ctx.lookup_native_module(func_name) {
if module_name == "inspector/promises" && method_name == "Session" {
return Ok(Ok(Expr::NativeMethodCall {
module: module_name.to_string(),
class_name: None,
object: None,
method: "SessionCall".to_string(),
args,
}));
}
if module_name == "os" || module_name == "node:os" {
match method_name {
"availableParallelism" => return Ok(Ok(Expr::OsAvailableParallelism)),
Expand Down
13 changes: 13 additions & 0 deletions crates/perry-hir/src/lower/expr_call/native_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,19 @@ pub(super) fn try_native_module_methods(
expr: &ast::Expr,
args: Vec<Expr>,
) -> Result<Result<Expr, Vec<Expr>>> {
// `Session` is a class export. A direct call is not construction, even
// though the native constructor fast path handles `new Session()`.
if let ast::Expr::Ident(ident) = expr {
if matches!(ctx.lookup_native_module(ident.sym.as_ref()), Some(("inspector/promises", Some("Session")))) {
return Ok(Ok(Expr::NativeMethodCall {
module: "inspector/promises".to_string(),
class_name: None,
object: None,
method: "SessionCall".to_string(),
args,
}));
}
}
// Check for native module method calls (e.g., mysql.createConnection())
if let ast::Expr::Member(member) = expr {
// Bundled mysql2 (webpack/turbopack): when a bundler inlines mysql2
Expand Down
270 changes: 164 additions & 106 deletions crates/perry-runtime/src/node_inspector.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ pub(crate) fn is_native_module_callable_export_reference(module: &str, prop: &st
"createInterface" | "Interface" | "Readline",
)
| (
"inspector",
"inspector" | "inspector/promises",
"open" | "close" | "url" | "waitForDebugger" | "Session",
)
| (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ pub(crate) fn bound_native_callable_export_value(module_name: &str, property_nam
let module_name = cjs_default_base_module(module_name).unwrap_or(module_name);
let module_name = assert_instance_base_module(module_name).unwrap_or(module_name);
let property_name = canonical_native_callable_property(module_name, property_name);
// node:inspector/promises is the callback namespace with Session replaced.
if module_name == "inspector/promises" && property_name != "Session" {
return bound_native_callable_export_value("inspector", property_name);
}
let export_module_name = if property_name == "Assert" && module_name == "assert/strict" {
"assert"
} else {
Expand Down Expand Up @@ -325,7 +329,7 @@ fn native_callable_export_arity_reference(module: &str, prop: &str) -> Option<u3
| "registerStorage",
) => Some(1),
("inspector.Session", "connect" | "connectToMainThread" | "disconnect") => Some(0),
("inspector.Session", "post") => Some(3),
("inspector.Session" | "inspector/promises.Session", "post") => Some(3),
(
"process",
"setUncaughtExceptionCaptureCallback" | "addUncaughtExceptionCaptureCallback",
Expand Down Expand Up @@ -2003,6 +2007,7 @@ static CALLABLE_EXPORT_ARITY_TABLE: &[(&str, &[(&str, u32)])] = &[
("post", 3),
],
),
("inspector/promises.Session", &[("post", 3)]),
(
"module",
&[
Expand Down
29 changes: 24 additions & 5 deletions crates/perry-runtime/src/object/native_module/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ fn dns_lookup_flag_constant(property: &str) -> Option<f64> {
}
}

fn cached_inspector_object(property: &str, create: impl FnOnce() -> f64) -> f64 {
let key = format!("inspector\0object\0{property}");
if let Some(bits) = NATIVE_CALLABLE_EXPORTS.with(|cache| cache.borrow().get(&key).copied()) {
return f64::from_bits(bits);
}
let value = create();
NATIVE_CALLABLE_EXPORTS.with(|cache| {
cache.borrow_mut().insert(key, value.to_bits());
crate::gc::runtime_write_barrier_root_nanbox(value.to_bits());
});
value
}

fn dns_error_alias(property: &str) -> Option<&'static str> {
match property {
"NODATA" => Some("ENODATA"),
Expand Down Expand Up @@ -451,10 +464,10 @@ pub(crate) unsafe fn get_native_module_constant(
},
"inspector" => match property {
"default" if !is_cjs_default_object => cjs_default_export_value("inspector"),
"console" => Some(crate::node_inspector::js_node_inspector_console_object()),
"Network" => Some(create_sub_namespace("inspector.Network")),
"NetworkResources" => Some(create_sub_namespace("inspector.NetworkResources")),
"DOMStorage" => Some(create_sub_namespace("inspector.DOMStorage")),
"console" => Some(cached_inspector_object("console", || crate::node_inspector::js_node_inspector_console_object())),
"Network" => Some(cached_inspector_object("Network", || create_sub_namespace("inspector.Network"))),
"NetworkResources" => Some(cached_inspector_object("NetworkResources", || create_sub_namespace("inspector.NetworkResources"))),
"DOMStorage" => Some(cached_inspector_object("DOMStorage", || create_sub_namespace("inspector.DOMStorage"))),
"Session" => {
let value = bound_native_callable_export_value("inspector", "Session");
crate::node_inspector::install_session_prototype(value, false);
Expand All @@ -469,7 +482,13 @@ pub(crate) unsafe fn get_native_module_constant(
crate::node_inspector::install_session_prototype(value, true);
Some(value)
}
_ => None,
// Node's promise entry point spreads the callback namespace and
// replaces only Session, so read the callback export itself.
_ => cjs_default_export_value("inspector").map(|callback| {
let raw = (callback.to_bits() & crate::value::POINTER_MASK) as *const crate::ObjectHeader;
let name = crate::string::js_string_from_bytes(property.as_ptr(), property.len() as u32);
crate::object::js_object_get_field_by_name_f64(raw, name)
}),
},
"process" => crate::process::process_metadata_property(property),
"dns" => match property {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1669,7 +1669,7 @@ pub(crate) fn native_module_enumerable_keys(module_name: &str) -> Option<&'stati
"punycode" => Some(PUNYCODE_NAMESPACE_KEYS),
"punycode.default" => Some(PUNYCODE_DEFAULT_KEYS),
"punycode.ucs2" => Some(PUNYCODE_UCS2_KEYS),
"inspector" | "inspector.default" => Some(INSPECTOR_NAMESPACE_KEYS),
"inspector" | "inspector.default" | "inspector/promises" | "inspector/promises.default" => Some(INSPECTOR_NAMESPACE_KEYS),
"inspector.Network" => Some(INSPECTOR_NETWORK_KEYS),
"inspector.NetworkResources" => Some(INSPECTOR_NETWORK_RESOURCES_KEYS),
"inspector.DOMStorage" => Some(INSPECTOR_DOM_STORAGE_KEYS),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,12 @@ pub(crate) unsafe fn nm_dispatch_inspector(
("inspector.Session", "disconnect") | ("inspector/promises.Session", "disconnect") => {
crate::node_inspector::js_node_inspector_session_disconnect(obj as usize as i64)
}
("inspector.Session", "post") | ("inspector/promises.Session", "post") => {
("inspector.Session", "post") => {
crate::node_inspector::js_node_inspector_session_post(obj as usize as i64, arg(0), arg(1), arg(2))
}
("inspector/promises.Session", "post") => {
crate::node_inspector::js_node_inspector_promises_session_post(obj as usize as i64, arg(0), arg(1), arg(2))
}
("inspector.Network", "requestWillBeSent")
| ("inspector.Network", "responseReceived")
| ("inspector.Network", "loadingFinished")
Expand Down
Loading