deno/ext/node/ops/inspector.rs
Bartek Iwańczuk 9399f7b894
Some checks are pending
ci / test debug linux-aarch64 (push) Blocked by required conditions
ci / test release linux-aarch64 (push) Blocked by required conditions
ci / build libs (push) Blocked by required conditions
ci / pre-build (push) Waiting to run
ci / test debug macos-aarch64 (push) Blocked by required conditions
ci / test release macos-aarch64 (push) Blocked by required conditions
ci / lint debug linux-x86_64 (push) Blocked by required conditions
ci / lint debug macos-x86_64 (push) Blocked by required conditions
ci / lint debug windows-x86_64 (push) Blocked by required conditions
ci / test debug macos-x86_64 (push) Blocked by required conditions
ci / test release macos-x86_64 (push) Blocked by required conditions
ci / bench release linux-x86_64 (push) Blocked by required conditions
ci / test debug linux-x86_64 (push) Blocked by required conditions
ci / test release linux-x86_64 (push) Blocked by required conditions
ci / test debug windows-x86_64 (push) Blocked by required conditions
ci / test release windows-x86_64 (push) Blocked by required conditions
ci / publish canary (push) Blocked by required conditions
refactor: Rewrite usages of V8 inspector to the new API (#30743)
Based on https://github.com/denoland/deno_core/pull/1193.

This commit rewrites 3 parts of the system to use a new "sync"
V8 inspector API exposed by `deno_core`:
- REPL
- coverage collection
- hot module replacement

Turns out the async abstraction over V8 inspector was unnecessary
and actually greatly complicated usage of the inspector.

Towards https://github.com/denoland/deno/issues/13572
Towards https://github.com/denoland/deno/issues/13206
2025-09-17 16:11:03 +02:00

184 lines
4.7 KiB
Rust

// Copyright 2018-2025 the Deno authors. MIT license.
use std::cell::RefCell;
use std::rc::Rc;
use deno_core::GarbageCollected;
use deno_core::InspectorMsg;
use deno_core::InspectorSessionKind;
use deno_core::InspectorSessionOptions;
use deno_core::JsRuntimeInspector;
use deno_core::OpState;
use deno_core::op2;
use deno_core::v8;
use deno_error::JsErrorBox;
use crate::NodePermissions;
#[op2(fast)]
pub fn op_inspector_enabled() -> bool {
// TODO: hook up to InspectorServer
false
}
#[op2(stack_trace)]
pub fn op_inspector_open<P>(
_state: &mut OpState,
_port: Option<u16>,
#[string] _host: Option<String>,
) -> Result<(), JsErrorBox>
where
P: NodePermissions + 'static,
{
// TODO: hook up to InspectorServer
/*
let server = state.borrow_mut::<InspectorServer>();
if let Some(host) = host {
server.set_host(host);
}
if let Some(port) = port {
server.set_port(port);
}
state
.borrow_mut::<P>()
.check_net((server.host(), Some(server.port())), "inspector.open")?;
*/
Ok(())
}
#[op2(fast)]
pub fn op_inspector_close() {
// TODO: hook up to InspectorServer
}
#[op2]
#[string]
pub fn op_inspector_url() -> Option<String> {
// TODO: hook up to InspectorServer
None
}
#[op2(fast)]
pub fn op_inspector_wait(state: &OpState) -> bool {
match state.try_borrow::<Rc<RefCell<JsRuntimeInspector>>>() {
Some(inspector) => {
inspector
.borrow_mut()
.wait_for_session_and_break_on_next_statement();
true
}
None => false,
}
}
#[op2(fast)]
pub fn op_inspector_emit_protocol_event(
#[string] _event_name: String,
#[string] _params: String,
) {
// TODO: inspector channel & protocol notifications
}
struct JSInspectorSession {
session: RefCell<Option<deno_core::LocalInspectorSession>>,
}
// SAFETY: we're sure this can be GCed
unsafe impl GarbageCollected for JSInspectorSession {
fn trace(&self, _visitor: &mut deno_core::v8::cppgc::Visitor) {}
fn get_name(&self) -> &'static std::ffi::CStr {
c"JSInspectorSession"
}
}
#[derive(Debug, thiserror::Error, deno_error::JsError)]
pub enum InspectorConnectError {
#[class(inherit)]
#[error(transparent)]
Permission(
#[from]
#[inherit]
deno_permissions::PermissionCheckError,
),
#[class(generic)]
#[error("connectToMainThread not supported")]
ConnectToMainThreadUnsupported,
}
#[op2(stack_trace)]
#[cppgc]
pub fn op_inspector_connect<'s, P>(
isolate: *mut v8::Isolate,
scope: &mut v8::HandleScope<'s>,
state: &mut OpState,
connect_to_main_thread: bool,
callback: v8::Local<'s, v8::Function>,
) -> Result<JSInspectorSession, InspectorConnectError>
where
P: NodePermissions + 'static,
{
state
.borrow_mut::<P>()
.check_sys("inspector", "inspector.Session.connect")?;
if connect_to_main_thread {
return Err(InspectorConnectError::ConnectToMainThreadUnsupported);
}
let context = scope.get_current_context();
let context = v8::Global::new(scope, context);
let callback = v8::Global::new(scope, callback);
let inspector = state.borrow::<Rc<RefCell<JsRuntimeInspector>>>().clone();
// The inspector connection does not keep the event loop alive but
// when the inspector sends a message to the frontend, the JS that
// that runs may keep the event loop alive so we have to call back
// synchronously, instead of using the usual LocalInspectorSession
// UnboundedReceiver<InspectorMsg> API.
let callback = Box::new(move |message: InspectorMsg| {
// SAFETY: This function is called directly by the inspector, so
// 1) The isolate is still valid
// 2) We are on the same thread as the Isolate
let scope = unsafe { &mut v8::CallbackScope::new(&mut *isolate) };
let context = v8::Local::new(scope, context.clone());
let scope = &mut v8::ContextScope::new(scope, context);
let scope = &mut v8::TryCatch::new(scope);
let recv = v8::undefined(scope);
if let Some(message) = v8::String::new(scope, &message.content) {
let callback = v8::Local::new(scope, callback.clone());
callback.call(scope, recv.into(), &[message.into()]);
}
});
let session = JsRuntimeInspector::create_local_session(
inspector,
callback,
InspectorSessionOptions {
kind: InspectorSessionKind::NonBlocking {
wait_for_disconnect: false,
},
},
);
Ok(JSInspectorSession {
session: RefCell::new(Some(session)),
})
}
#[op2(fast)]
pub fn op_inspector_dispatch(
#[cppgc] session: &JSInspectorSession,
#[string] message: String,
) {
if let Some(session) = &mut *session.session.borrow_mut() {
session.dispatch(message);
}
}
#[op2(fast)]
pub fn op_inspector_disconnect(#[cppgc] session: &JSInspectorSession) {
drop(session.session.borrow_mut().take());
}