diff --git a/src/binding.cc b/src/binding.cc index c0238c2d..a5266d62 100644 --- a/src/binding.cc +++ b/src/binding.cc @@ -1224,6 +1224,50 @@ void v8_inspector__V8Inspector__Channel__BASE__sendNotification( v8_inspector::StringBuffer* message); void v8_inspector__V8Inspector__Channel__BASE__flushProtocolNotifications( v8_inspector::V8Inspector::Channel& self); + +void v8_inspector__V8Inspector__DELETE(v8_inspector::V8Inspector& self) { + delete &self; +} + +v8_inspector::V8Inspector* v8_inspector__V8Inspector__create( + v8::Isolate* isolate, v8_inspector::V8InspectorClient* client) { + std::unique_ptr u = + v8_inspector::V8Inspector::create(isolate, client); + return u.release(); +} + +v8_inspector::V8InspectorSession* v8_inspector__V8Inspector__connect( + v8_inspector::V8Inspector& self, int context_group_id, + v8_inspector::V8Inspector::Channel* channel, + v8_inspector::StringView& state) { + std::unique_ptr u = + self.connect(context_group_id, channel, state); + return u.release(); +} + +void v8_inspector__V8Inspector__contextCreated( + v8_inspector::V8Inspector& self, v8::Context* context, int contextGroupId, + v8_inspector::StringView& humanReadableName) { + self.contextCreated(v8_inspector::V8ContextInfo( + ptr_to_local(context), contextGroupId, humanReadableName)); +} + +void v8_inspector__V8InspectorSession__DELETE( + v8_inspector::V8InspectorSession& self) { + delete &self; +} + +void v8_inspector__V8InspectorSession__dispatchProtocolMessage( + v8_inspector::V8InspectorSession& self, + v8_inspector::StringView& message) { + self.dispatchProtocolMessage(message); +} + +void v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( + v8_inspector::V8InspectorSession& self, v8_inspector::StringBuffer& reason, + v8_inspector::StringBuffer& detail) { + self.schedulePauseOnNextStatement(reason.string(), detail.string()); +} } // extern "C" struct v8_inspector__V8Inspector__Channel__BASE diff --git a/src/inspector/client.rs b/src/inspector/client.rs index 545a18e2..6ac7e507 100644 --- a/src/inspector/client.rs +++ b/src/inspector/client.rs @@ -4,115 +4,55 @@ use crate::support::FieldOffset; use crate::support::Opaque; use crate::support::RustVTable; -// class V8InspectorClient { -// public: -// virtual ~V8InspectorClient() = default; -// -// virtual void runMessageLoopOnPause(int contextGroupId) {} -// virtual void quitMessageLoopOnPause() {} -// virtual void runIfWaitingForDebugger(int contextGroupId) {} -// -// virtual void muteMetrics(int contextGroupId) {} -// virtual void unmuteMetrics(int contextGroupId) {} -// -// virtual void beginUserGesture() {} -// virtual void endUserGesture() {} -// -// virtual std::unique_ptr valueSubtype(v8::Local) { -// return nullptr; -// } -// virtual bool formatAccessorsAsProperties(v8::Local) { -// return false; -// } -// virtual bool isInspectableHeapObject(v8::Local) { -// return true; -// } -// -// virtual v8::Local ensureDefaultContextInGroup( -// int contextGroupId) { -// return v8::Local(); -// } -// virtual void beginEnsureAllContextsInGroup(int contextGroupId) {} -// virtual void endEnsureAllContextsInGroup(int contextGroupId) {} -// -// virtual void installAdditionalCommandLineAPI(v8::Local, -// v8::Local) {} -// virtual void consoleAPIMessage(int contextGroupId, -// v8::Isolate::MessageErrorLevel level, -// const StringView& message, -// const StringView& url, unsigned lineNumber, -// unsigned columnNumber, V8StackTrace*) {} -// virtual v8::MaybeLocal memoryInfo(v8::Isolate*, -// v8::Local) { -// return v8::MaybeLocal(); -// } -// -// virtual void consoleTime(const StringView& title) {} -// virtual void consoleTimeEnd(const StringView& title) {} -// virtual void consoleTimeStamp(const StringView& title) {} -// virtual void consoleClear(int contextGroupId) {} -// virtual double currentTimeMS() { return 0; } -// typedef void (*TimerCallback)(void*); -// virtual void startRepeatingTimer(double, TimerCallback, void* data) {} -// virtual void cancelTimer(void* data) {} -// -// virtual bool canExecuteScripts(int contextGroupId) { return true; } -// -// virtual void maxAsyncCallStackDepthChanged(int depth) {} -// -// virtual std::unique_ptr resourceNameToUrl( -// const StringView& resourceName) { -// return nullptr; -// } -// }; - extern "C" { fn v8_inspector__V8InspectorClient__BASE__CONSTRUCT( - buf: &mut std::mem::MaybeUninit, + buf: &mut std::mem::MaybeUninit, ) -> (); fn v8_inspector__V8InspectorClient__runMessageLoopOnPause( - this: &mut Client, + this: &mut V8InspectorClient, context_group_id: int, ) -> (); fn v8_inspector__V8InspectorClient__quitMessageLoopOnPause( - this: &mut Client, + this: &mut V8InspectorClient, ) -> (); fn v8_inspector__V8InspectorClient__runIfWaitingForDebugger( - this: &mut Client, + this: &mut V8InspectorClient, context_group_id: int, ) -> (); } #[no_mangle] pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runMessageLoopOnPause( - this: &mut Client, + this: &mut V8InspectorClient, context_group_id: int, ) { - ClientBase::dispatch_mut(this).run_message_loop_on_pause(context_group_id) + V8InspectorClientBase::dispatch_mut(this) + .run_message_loop_on_pause(context_group_id) } #[no_mangle] pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__quitMessageLoopOnPause( - this: &mut Client, + this: &mut V8InspectorClient, ) { - ClientBase::dispatch_mut(this).quit_message_loop_on_pause() + V8InspectorClientBase::dispatch_mut(this).quit_message_loop_on_pause() } #[no_mangle] pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runIfWaitingForDebugger( - this: &mut Client, + this: &mut V8InspectorClient, context_group_id: int, ) { - ClientBase::dispatch_mut(this).run_if_waiting_for_debugger(context_group_id) + V8InspectorClientBase::dispatch_mut(this) + .run_if_waiting_for_debugger(context_group_id) } #[repr(C)] -pub struct Client { +pub struct V8InspectorClient { _cxx_vtable: CxxVTable, } -impl Client { +impl V8InspectorClient { pub fn run_message_loop_on_pause(&mut self, context_group_id: int) { unsafe { v8_inspector__V8InspectorClient__runMessageLoopOnPause( @@ -134,65 +74,65 @@ impl Client { } } -pub trait AsClient { - fn as_client(&self) -> &Client; - fn as_client_mut(&mut self) -> &mut Client; +pub trait AsV8InspectorClient { + fn as_client(&self) -> &V8InspectorClient; + fn as_client_mut(&mut self) -> &mut V8InspectorClient; } -impl AsClient for Client { - fn as_client(&self) -> &Client { +impl AsV8InspectorClient for V8InspectorClient { + fn as_client(&self) -> &V8InspectorClient { self } - fn as_client_mut(&mut self) -> &mut Client { + fn as_client_mut(&mut self) -> &mut V8InspectorClient { self } } -impl AsClient for T +impl AsV8InspectorClient for T where - T: ClientImpl, + T: V8InspectorClientImpl, { - fn as_client(&self) -> &Client { + fn as_client(&self) -> &V8InspectorClient { &self.base().cxx_base } - fn as_client_mut(&mut self) -> &mut Client { + fn as_client_mut(&mut self) -> &mut V8InspectorClient { &mut self.base_mut().cxx_base } } #[allow(unused_variables)] -pub trait ClientImpl: AsClient { - fn base(&self) -> &ClientBase; - fn base_mut(&mut self) -> &mut ClientBase; +pub trait V8InspectorClientImpl: AsV8InspectorClient { + fn base(&self) -> &V8InspectorClientBase; + fn base_mut(&mut self) -> &mut V8InspectorClientBase; fn run_message_loop_on_pause(&mut self, context_group_id: int) {} fn quit_message_loop_on_pause(&mut self) {} fn run_if_waiting_for_debugger(&mut self, context_group_id: int) {} } -pub struct ClientBase { - cxx_base: Client, +pub struct V8InspectorClientBase { + cxx_base: V8InspectorClient, offset_within_embedder: FieldOffset, - rust_vtable: RustVTable<&'static dyn ClientImpl>, + rust_vtable: RustVTable<&'static dyn V8InspectorClientImpl>, } -impl ClientBase { - fn construct_cxx_base() -> Client { +impl V8InspectorClientBase { + fn construct_cxx_base() -> V8InspectorClient { unsafe { - let mut buf = std::mem::MaybeUninit::::uninit(); + let mut buf = std::mem::MaybeUninit::::uninit(); v8_inspector__V8InspectorClient__BASE__CONSTRUCT(&mut buf); buf.assume_init() } } - fn get_cxx_base_offset() -> FieldOffset { + fn get_cxx_base_offset() -> FieldOffset { let buf = std::mem::MaybeUninit::::uninit(); FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_base }) } fn get_offset_within_embedder() -> FieldOffset where - T: ClientImpl, + T: V8InspectorClientImpl, { let buf = std::mem::MaybeUninit::::uninit(); let embedder_ptr: *const T = buf.as_ptr(); @@ -200,13 +140,13 @@ impl ClientBase { FieldOffset::from_ptrs(embedder_ptr, self_ptr) } - fn get_rust_vtable() -> RustVTable<&'static dyn ClientImpl> + fn get_rust_vtable() -> RustVTable<&'static dyn V8InspectorClientImpl> where - T: ClientImpl, + T: V8InspectorClientImpl, { let buf = std::mem::MaybeUninit::::uninit(); let embedder_ptr = buf.as_ptr(); - let trait_object: *const dyn ClientImpl = embedder_ptr; + let trait_object: *const dyn V8InspectorClientImpl = embedder_ptr; let (data_ptr, vtable): (*const T, RustVTable<_>) = unsafe { std::mem::transmute(trait_object) }; assert_eq!(data_ptr, embedder_ptr); @@ -215,7 +155,7 @@ impl ClientBase { pub fn new() -> Self where - T: ClientImpl, + T: V8InspectorClientImpl, { Self { cxx_base: Self::construct_cxx_base(), @@ -224,13 +164,17 @@ impl ClientBase { } } - pub unsafe fn dispatch(client: &Client) -> &dyn ClientImpl { + pub unsafe fn dispatch( + client: &V8InspectorClient, + ) -> &dyn V8InspectorClientImpl { let this = Self::get_cxx_base_offset().to_embedder::(client); let embedder = this.offset_within_embedder.to_embedder::(this); std::mem::transmute((embedder, this.rust_vtable)) } - pub unsafe fn dispatch_mut(client: &mut Client) -> &mut dyn ClientImpl { + pub unsafe fn dispatch_mut( + client: &mut V8InspectorClient, + ) -> &mut dyn V8InspectorClientImpl { let this = Self::get_cxx_base_offset().to_embedder_mut::(client); let vtable = this.rust_vtable; let embedder = this.offset_within_embedder.to_embedder_mut::(this); diff --git a/src/inspector/mod.rs b/src/inspector/mod.rs index bb70184c..c4e8b3b6 100644 --- a/src/inspector/mod.rs +++ b/src/inspector/mod.rs @@ -1,9 +1,16 @@ mod channel; mod client; +mod session; mod string_buffer; mod string_view; +mod v8_inspector; -pub use channel::Channel; -pub use client::Client; +pub use channel::{AsChannel, Channel, ChannelBase, ChannelImpl}; +pub use client::AsV8InspectorClient; +pub use client::V8InspectorClient; +pub use client::V8InspectorClientBase; +pub use client::V8InspectorClientImpl; +pub use session::V8InspectorSession; pub use string_buffer::StringBuffer; pub use string_view::StringView; +pub use v8_inspector::V8Inspector; diff --git a/src/inspector/session.rs b/src/inspector/session.rs new file mode 100644 index 00000000..97dbd8fa --- /dev/null +++ b/src/inspector/session.rs @@ -0,0 +1,50 @@ +use super::StringBuffer; +use super::StringView; +use crate::support::Delete; +use crate::support::Opaque; + +extern "C" { + fn v8_inspector__V8InspectorSession__DELETE( + this: &'static mut V8InspectorSession, + ); + fn v8_inspector__V8InspectorSession__dispatchProtocolMessage( + session: *mut V8InspectorSession, + message: &StringView, + ); + fn v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( + session: *mut V8InspectorSession, + break_reason: *mut StringBuffer, + break_details: *mut StringBuffer, + ); +} + +#[repr(C)] +pub struct V8InspectorSession(Opaque); + +impl V8InspectorSession { + pub fn dispatch_protocol_message(&mut self, message: &StringView) { + unsafe { + v8_inspector__V8InspectorSession__dispatchProtocolMessage(self, message) + } + } + + pub fn schedule_pause_on_next_statement( + &mut self, + reason: &mut StringBuffer, + detail: &mut StringBuffer, + ) { + unsafe { + v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( + self, + &mut *reason, + &mut *detail, + ) + } + } +} + +impl Delete for V8InspectorSession { + fn delete(&'static mut self) { + unsafe { v8_inspector__V8InspectorSession__DELETE(self) }; + } +} diff --git a/src/inspector/string_buffer.rs b/src/inspector/string_buffer.rs index 37c7153e..2f734627 100644 --- a/src/inspector/string_buffer.rs +++ b/src/inspector/string_buffer.rs @@ -52,3 +52,5 @@ impl Delete for StringBuffer { unsafe { v8_inspector__StringBuffer__DELETE(self) } } } + +unsafe impl Send for StringBuffer {} diff --git a/src/inspector/v8_inspector.rs b/src/inspector/v8_inspector.rs new file mode 100644 index 00000000..2a971ac7 --- /dev/null +++ b/src/inspector/v8_inspector.rs @@ -0,0 +1,96 @@ +use super::channel::AsChannel; +use super::client::AsV8InspectorClient; +use super::session::V8InspectorSession; +use super::Channel; +use super::StringView; +use super::V8InspectorClient; +use crate::support::int; +use crate::support::Delete; +use crate::support::Opaque; +use crate::support::UniqueRef; +use crate::Context; +use crate::Isolate; +use crate::Local; + +extern "C" { + fn v8_inspector__V8Inspector__DELETE(this: &'static mut V8Inspector); + fn v8_inspector__V8Inspector__create( + isolate: *mut Isolate, + client: *mut V8InspectorClient, + ) -> *mut V8Inspector; + fn v8_inspector__V8Inspector__connect( + inspector: *mut V8Inspector, + context_group_id: int, + channel: *mut Channel, + state: *const StringView, + ) -> *mut V8InspectorSession; + fn v8_inspector__V8Inspector__contextCreated( + inspector: *mut V8Inspector, + context: *mut Context, + context_group_id: int, + human_readable_name: *const StringView, + ); +} + +#[repr(C)] +pub struct V8Inspector(Opaque); + +impl V8Inspector { + pub fn create( + isolate: &mut Isolate, + client: &mut T, + ) -> UniqueRef + where + T: AsV8InspectorClient, + { + unsafe { + UniqueRef::from_raw(v8_inspector__V8Inspector__create( + isolate, + client.as_client_mut(), + )) + } + } + + pub fn connect( + &mut self, + context_group_id: int, + channel: &mut T, + state: &StringView, + ) -> UniqueRef + where + T: AsChannel, + { + unsafe { + UniqueRef::from_raw(v8_inspector__V8Inspector__connect( + self, + context_group_id, + channel.as_channel_mut(), + state, + )) + } + } + + /// Note: this method deviates from the C++ API here because it's a lot of + /// work to bind the V8ContextInfo, which is not used elsewhere. + pub fn context_created( + &mut self, + mut context: Local, + context_group_id: int, + human_readable_name: &StringView, + ) { + unsafe { + v8_inspector__V8Inspector__contextCreated( + self, + &mut *context, + context_group_id, + human_readable_name, + ) + } + } +} + +impl Delete for V8Inspector { + fn delete(&'static mut self) { + unsafe { v8_inspector__V8Inspector__DELETE(self) }; + } +} diff --git a/src/lib.rs b/src/lib.rs index 0a75f6ee..f1ce7fa4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -115,8 +115,10 @@ pub use snapshot::OwnedStartupData; pub use snapshot::SnapshotCreator; pub use snapshot::StartupData; pub use string::NewStringType; +pub use support::int; pub use support::MaybeBool; pub use support::SharedRef; +pub use support::UniquePtr; pub use support::UniqueRef; pub use try_catch::{TryCatch, TryCatchScope}; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 57a3ce86..a36ac876 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -1,10 +1,11 @@ pub mod task; -pub use task::Task; +pub use task::{Task, TaskBase, TaskImpl}; use crate::support::Delete; use crate::support::Opaque; use crate::support::UniquePtr; +use crate::Isolate; extern "C" { // TODO: move this to libplatform.rs? @@ -26,3 +27,15 @@ impl Delete for Platform { unsafe { v8__Platform__DELETE(self) } } } + +impl Platform { + /// Pumps the message loop for the given isolate. + /// + /// The caller has to make sure that this is called from the right thread. + /// Returns true if a task was executed, and false otherwise. Unless requested + /// through the |behavior| parameter, this call does not block if no task is + /// pending. The |platform| has to be created using |NewDefaultPlatform|. + pub fn pump_message_loop(_platform: &Self, _isolate: &Isolate) -> bool { + todo!() + } +} diff --git a/tests/test_api.rs b/tests/test_api.rs index 68030d3c..b7f1d0ff 100644 --- a/tests/test_api.rs +++ b/tests/test_api.rs @@ -2127,3 +2127,101 @@ fn try_from_local() { context.exit(); } } + +#[test] +fn inspector_dispatch_protocol_message() { + let _g = setup(); + let mut params = v8::Isolate::create_params(); + params.set_array_buffer_allocator(v8::new_default_allocator()); + let mut isolate = v8::Isolate::new(params); + let mut locker = v8::Locker::new(&isolate); + + use v8::inspector::*; + + struct Client { + base: V8InspectorClientBase, + } + + impl Client { + fn new() -> Self { + Self { + base: V8InspectorClientBase::new::(), + } + } + } + + impl V8InspectorClientImpl for Client { + fn base(&self) -> &V8InspectorClientBase { + &self.base + } + fn base_mut(&mut self) -> &mut V8InspectorClientBase { + &mut self.base + } + } + + struct TestChannel { + base: ChannelBase, + send_response_count: usize, + send_notification_count: usize, + flush_protocol_notifications_count: usize, + } + + impl TestChannel { + pub fn new() -> Self { + Self { + base: ChannelBase::new::(), + send_response_count: 0, + send_notification_count: 0, + flush_protocol_notifications_count: 0, + } + } + } + + impl ChannelImpl for TestChannel { + fn base(&self) -> &ChannelBase { + &self.base + } + fn base_mut(&mut self) -> &mut ChannelBase { + &mut self.base + } + fn send_response( + &mut self, + _call_id: i32, + _message: v8::UniquePtr, + ) { + self.send_response_count += 1; + } + fn send_notification(&mut self, _message: v8::UniquePtr) { + self.send_notification_count += 1; + } + fn flush_protocol_notifications(&mut self) { + self.flush_protocol_notifications_count += 1; + } + } + + let mut hs = v8::HandleScope::new(&mut locker); + let scope = hs.enter(); + let mut context = v8::Context::new(scope); + context.enter(); + + let mut default_client = Client::new(); + let mut inspector = V8Inspector::create(&mut isolate, &mut default_client); + let name = b""; + let name_view = StringView::from(&name[..]); + inspector.context_created(context, 1, &name_view); + let mut channel = TestChannel::new(); + let state = b"{}"; + let state_view = StringView::from(&state[..]); + let mut session = inspector.connect(1, &mut channel, &state_view); + let message = String::from( + r#"{"id":1,"method":"Network.enable","params":{"maxPostDataSize":65536}}"#, + ); + let message = &message.into_bytes()[..]; + let string_view = StringView::from(message); + session.dispatch_protocol_message(&string_view); + assert_eq!(channel.send_response_count, 1); + assert_eq!(channel.send_notification_count, 0); + assert_eq!(channel.flush_protocol_notifications_count, 0); + + context.exit(); +}