use std::cell::RefCell; use std::collections::HashMap; use std::rc::Rc; use serde_json::Value; use v8::inspector::{ Channel, ChannelImpl, StringBuffer, StringView, V8Inspector, V8InspectorClient, V8InspectorClientImpl, V8InspectorClientTrustLevel, V8InspectorSession, }; pub(super) struct Inspector { pub(super) session: V8InspectorSession, pub(super) _inspector: V8Inspector, responses: Rc>, pub(super) next_id: i32, pub(super) context_id: i32, } #[derive(Default)] struct Responses { messages: HashMap, context_id: Option, } struct ReplChannel(Rc>); struct ReplInspectorClient; impl V8InspectorClientImpl for ReplInspectorClient {} impl ChannelImpl for ReplChannel { fn send_response(&self, call_id: i32, mut message: v8::UniquePtr) { if let Some(message) = message.as_mut() { self.0 .borrow_mut() .messages .insert(call_id, message.string().to_string()); } } fn send_notification(&self, mut message: v8::UniquePtr) { let Some(message) = message.as_mut() else { return; }; let Ok(value) = serde_json::from_str::(&message.string().to_string()) else { return; }; if value.get("method").and_then(Value::as_str) != Some("Runtime.executionContextCreated") { return; } let context_id = value .get("params") .and_then(|params| params.get("context")) .and_then(|context| context.get("id")) .and_then(Value::as_i64) .and_then(|id| i32::try_from(id).ok()); if let Some(context_id) = context_id { self.0.borrow_mut().context_id = Some(context_id); } } fn flush_protocol_notifications(&self) {} } impl Inspector { pub(super) fn new(isolate: &mut v8::OwnedIsolate, context: &v8::Global) -> Self { let client = V8InspectorClient::new(Box::new(ReplInspectorClient)); let inspector = V8Inspector::create(isolate, client); { v8::scope!(let scope, isolate); let context = v8::Local::new(scope, context); inspector.context_created( context, 1, StringView::from(b"avril REPL" as &[u8]), StringView::from(b"{\"isDefault\":true}" as &[u8]), ); } let responses = Rc::new(RefCell::new(Responses::default())); let channel = Channel::new(Box::new(ReplChannel(Rc::clone(&responses)))); let session = inspector.connect( 1, channel, StringView::from(b"" as &[u8]), V8InspectorClientTrustLevel::Untrusted, ); Self { session, _inspector: inspector, responses, next_id: 1, context_id: 0, } } pub(super) fn take_response(&mut self, id: i32) -> Option { let mut responses = self.responses.borrow_mut(); responses .messages .retain(|response_id, _| *response_id >= id); responses.messages.remove(&id) } pub(super) fn has_response(&self, id: i32) -> bool { self.responses.borrow().messages.contains_key(&id) } pub(super) fn context_id(&self) -> Option { self.responses.borrow().context_id } }