From 2184cf5c07b2b78ebf586b032f45ce113670262d Mon Sep 17 00:00:00 2001 From: Casper Beyer Date: Wed, 30 Sep 2020 05:05:06 +0800 Subject: [PATCH] refactor(cli): extract inspector session (#7756) This extracts the inspector session specific bits from CoverageCollector into a standalone struct dubbed `InspectorSesssion` which can be used as a general purpose session to communicate with the inspector on the same thread as an isolate/inspector pair lives on. --- cli/coverage.rs | 129 +++++------------------------------------------ cli/inspector.rs | 113 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 116 deletions(-) diff --git a/cli/coverage.rs b/cli/coverage.rs index 3006bd8783..e44f025bf2 100644 --- a/cli/coverage.rs +++ b/cli/coverage.rs @@ -2,137 +2,36 @@ use crate::colors; use crate::inspector::DenoInspector; -use deno_core::error::generic_error; +use crate::inspector::InspectorSession; use deno_core::error::AnyError; -use deno_core::futures::channel::oneshot; use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::url::Url; -use deno_core::v8; use serde::Deserialize; -use std::collections::HashMap; -use std::mem::MaybeUninit; -use std::ops::Deref; -use std::ops::DerefMut; -use std::ptr; pub struct CoverageCollector { - v8_channel: v8::inspector::ChannelBase, - v8_session: v8::UniqueRef, - response_map: HashMap>, - next_message_id: i32, -} - -impl Deref for CoverageCollector { - type Target = v8::inspector::V8InspectorSession; - fn deref(&self) -> &Self::Target { - &self.v8_session - } -} - -impl DerefMut for CoverageCollector { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.v8_session - } -} - -impl v8::inspector::ChannelImpl for CoverageCollector { - fn base(&self) -> &v8::inspector::ChannelBase { - &self.v8_channel - } - - fn base_mut(&mut self) -> &mut v8::inspector::ChannelBase { - &mut self.v8_channel - } - - fn send_response( - &mut self, - call_id: i32, - message: v8::UniquePtr, - ) { - let raw_message = message.unwrap().string().to_string(); - let message = serde_json::from_str(&raw_message).unwrap(); - self - .response_map - .remove(&call_id) - .unwrap() - .send(message) - .unwrap(); - } - - fn send_notification( - &mut self, - _message: v8::UniquePtr, - ) { - } - - fn flush_protocol_notifications(&mut self) {} + session: Box, } impl CoverageCollector { - const CONTEXT_GROUP_ID: i32 = 1; - - pub fn new(inspector_ptr: *mut DenoInspector) -> Box { - new_box_with(move |self_ptr| { - let v8_channel = v8::inspector::ChannelBase::new::(); - let v8_session = unsafe { &mut *inspector_ptr }.connect( - Self::CONTEXT_GROUP_ID, - unsafe { &mut *self_ptr }, - v8::inspector::StringView::empty(), - ); - - let response_map = HashMap::new(); - let next_message_id = 0; - - Self { - v8_channel, - v8_session, - response_map, - next_message_id, - } - }) - } - - async fn post_message( - &mut self, - method: String, - params: Option, - ) -> Result { - let id = self.next_message_id; - self.next_message_id += 1; - - let (sender, receiver) = oneshot::channel::(); - self.response_map.insert(id, sender); - - let message = json!({ - "id": id, - "method": method, - "params": params, - }); - - let raw_message = serde_json::to_string(&message).unwrap(); - let raw_message = v8::inspector::StringView::from(raw_message.as_bytes()); - self.v8_session.dispatch_protocol_message(raw_message); - - let response = receiver.await.unwrap(); - if let Some(error) = response.get("error") { - return Err(generic_error(format!("{}", error))); - } - - let result = response.get("result").unwrap().clone(); - Ok(result) + pub fn new(inspector_ptr: *mut DenoInspector) -> Self { + let session = InspectorSession::new(inspector_ptr); + Self { session } } pub async fn start_collecting(&mut self) -> Result<(), AnyError> { self + .session .post_message("Debugger.enable".to_string(), None) .await?; self + .session .post_message("Profiler.enable".to_string(), None) .await?; self + .session .post_message( "Profiler.startPreciseCoverage".to_string(), Some(json!({"callCount": true, "detailed": true})), @@ -144,6 +43,7 @@ impl CoverageCollector { pub async fn collect(&mut self) -> Result, AnyError> { let result = self + .session .post_message("Profiler.takePreciseCoverage".to_string(), None) .await?; @@ -153,6 +53,7 @@ impl CoverageCollector { let mut coverages: Vec = Vec::new(); for script_coverage in take_coverage_result.result { let result = self + .session .post_message( "Debugger.getScriptSource".to_string(), Some(json!({ @@ -175,12 +76,15 @@ impl CoverageCollector { pub async fn stop_collecting(&mut self) -> Result<(), AnyError> { self + .session .post_message("Profiler.stopPreciseCoverage".to_string(), None) .await?; self + .session .post_message("Profiler.disable".to_string(), None) .await?; self + .session .post_message("Debugger.disable".to_string(), None) .await?; @@ -308,13 +212,6 @@ impl PrettyCoverageReporter { } } -fn new_box_with(new_fn: impl FnOnce(*mut T) -> T) -> Box { - let b = Box::new(MaybeUninit::::uninit()); - let p = Box::into_raw(b) as *mut T; - unsafe { ptr::write(p, new_fn(p)) }; - unsafe { Box::from_raw(p) } -} - pub fn filter_script_coverages( coverages: Vec, test_file_url: Url, diff --git a/cli/inspector.rs b/cli/inspector.rs index f68882e487..83e75bce4d 100644 --- a/cli/inspector.rs +++ b/cli/inspector.rs @@ -5,6 +5,8 @@ //! https://hyperandroid.com/2020/02/12/v8-inspector-from-an-embedder-standpoint/ use core::convert::Infallible as Never; // Alias for the future `!` type. +use deno_core::error::generic_error; +use deno_core::error::AnyError; use deno_core::futures::channel::mpsc; use deno_core::futures::channel::mpsc::UnboundedReceiver; use deno_core::futures::channel::mpsc::UnboundedSender; @@ -17,6 +19,7 @@ use deno_core::futures::stream::FuturesUnordered; use deno_core::futures::task; use deno_core::futures::task::Context; use deno_core::futures::task::Poll; +use deno_core::serde_json; use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::v8; @@ -816,6 +819,116 @@ impl Future for DenoInspectorSession { } } +/// A local inspector session that can be used to send and receive protocol messages directly on +/// the same thread as an isolate. +pub struct InspectorSession { + v8_channel: v8::inspector::ChannelBase, + v8_session: v8::UniqueRef, + response_tx_map: HashMap>, + next_message_id: i32, +} + +impl Deref for InspectorSession { + type Target = v8::inspector::V8InspectorSession; + fn deref(&self) -> &Self::Target { + &self.v8_session + } +} + +impl DerefMut for InspectorSession { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.v8_session + } +} + +impl v8::inspector::ChannelImpl for InspectorSession { + fn base(&self) -> &v8::inspector::ChannelBase { + &self.v8_channel + } + + fn base_mut(&mut self) -> &mut v8::inspector::ChannelBase { + &mut self.v8_channel + } + + fn send_response( + &mut self, + call_id: i32, + message: v8::UniquePtr, + ) { + let raw_message = message.unwrap().string().to_string(); + let message = serde_json::from_str(&raw_message).unwrap(); + self + .response_tx_map + .remove(&call_id) + .unwrap() + .send(message) + .unwrap(); + } + + fn send_notification( + &mut self, + _message: v8::UniquePtr, + ) { + } + + fn flush_protocol_notifications(&mut self) {} +} + +impl InspectorSession { + const CONTEXT_GROUP_ID: i32 = 1; + + pub fn new(inspector_ptr: *mut DenoInspector) -> Box { + new_box_with(move |self_ptr| { + let v8_channel = v8::inspector::ChannelBase::new::(); + let v8_session = unsafe { &mut *inspector_ptr }.connect( + Self::CONTEXT_GROUP_ID, + unsafe { &mut *self_ptr }, + v8::inspector::StringView::empty(), + ); + + let response_tx_map = HashMap::new(); + let next_message_id = 0; + + Self { + v8_channel, + v8_session, + response_tx_map, + next_message_id, + } + }) + } + + pub async fn post_message( + &mut self, + method: String, + params: Option, + ) -> Result { + let id = self.next_message_id; + self.next_message_id += 1; + + let (response_tx, response_rx) = oneshot::channel::(); + self.response_tx_map.insert(id, response_tx); + + let message = json!({ + "id": id, + "method": method, + "params": params, + }); + + let raw_message = serde_json::to_string(&message).unwrap(); + let raw_message = v8::inspector::StringView::from(raw_message.as_bytes()); + self.v8_session.dispatch_protocol_message(raw_message); + + let response = response_rx.await.unwrap(); + if let Some(error) = response.get("error") { + return Err(generic_error(format!("{}", error))); + } + + let result = response.get("result").unwrap().clone(); + Ok(result) + } +} + fn new_box_with(new_fn: impl FnOnce(*mut T) -> T) -> Box { let b = Box::new(MaybeUninit::::uninit()); let p = Box::into_raw(b) as *mut T;