diff --git a/src/inspector.rs b/src/inspector.rs new file mode 100644 index 00000000..41d79faf --- /dev/null +++ b/src/inspector.rs @@ -0,0 +1,908 @@ +// Copyright 2019-2020 the Deno authors. All rights reserved. MIT license. + +//! Bindings to the V8 Inspector API. Documentation for the V8 inspector API is +//! very sparse, so here are a few references for the next sorry soul who has to +//! dig into it. +//! +//! https://medium.com/@hyperandroid/v8-inspector-from-an-embedder-standpoint-7f9c0472e2b7 +//! https://v8.dev/docs/inspector +//! https://chromedevtools.github.io/debugger-protocol-viewer/tot/ +//! https://cs.chromium.org/chromium/src/v8/include/v8-inspector.h +//! https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.cc +//! https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.h +//! https://github.com/nodejs/node/tree/v13.7.0/src/inspector +//! https://github.com/denoland/deno/blob/v0.38.0/cli/inspector.rs + +use crate::scope_traits::InIsolate; +use crate::support::int; +use crate::support::CxxVTable; +use crate::support::Delete; +use crate::support::FieldOffset; +use crate::support::Opaque; +use crate::support::RustVTable; +use crate::support::UniquePtr; +use crate::support::UniqueRef; +use crate::Context; +use crate::Isolate; +use crate::Local; + +extern "C" { + fn v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT( + buf: &mut std::mem::MaybeUninit, + ) -> (); + + fn v8_inspector__V8Inspector__Channel__sendResponse( + this: &mut Channel, + call_id: int, + message: UniquePtr, + ) -> (); + fn v8_inspector__V8Inspector__Channel__sendNotification( + this: &mut Channel, + message: UniquePtr, + ) -> (); + fn v8_inspector__V8Inspector__Channel__flushProtocolNotifications( + this: &mut Channel, + ) -> (); + + fn v8_inspector__V8InspectorClient__BASE__CONSTRUCT( + buf: &mut std::mem::MaybeUninit, + ) -> (); + + fn v8_inspector__V8InspectorClient__runMessageLoopOnPause( + this: &mut V8InspectorClient, + context_group_id: int, + ) -> (); + fn v8_inspector__V8InspectorClient__quitMessageLoopOnPause( + this: &mut V8InspectorClient, + ) -> (); + fn v8_inspector__V8InspectorClient__runIfWaitingForDebugger( + this: &mut V8InspectorClient, + context_group_id: int, + ) -> (); + fn v8_inspector__V8InspectorClient__consoleAPIMessage( + this: &mut V8InspectorClient, + context_group_id: int, + level: int, + message: &StringView, + url: &StringView, + line_number: u32, + column_number: u32, + stack_trace: &mut V8StackTrace, + ) -> (); + + 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: &StringView, + break_details: &StringView, + ); + + fn v8_inspector__StringBuffer__DELETE(this: &'static mut StringBuffer) -> (); + fn v8_inspector__StringBuffer__string(this: &mut StringBuffer) + -> &StringView; + fn v8_inspector__StringBuffer__create( + source: &StringView, + ) -> UniquePtr; + + 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, + ); +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendResponse( + this: &mut Channel, + call_id: int, + message: UniquePtr, +) { + ChannelBase::dispatch_mut(this).send_response(call_id, message) +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendNotification( + this: &mut Channel, + message: UniquePtr, +) { + ChannelBase::dispatch_mut(this).send_notification(message) +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__flushProtocolNotifications( + this: &mut Channel, +) { + ChannelBase::dispatch_mut(this).flush_protocol_notifications() +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runMessageLoopOnPause( + this: &mut V8InspectorClient, + context_group_id: int, +) { + 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 V8InspectorClient, +) { + V8InspectorClientBase::dispatch_mut(this).quit_message_loop_on_pause() +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runIfWaitingForDebugger( + this: &mut V8InspectorClient, + context_group_id: int, +) { + V8InspectorClientBase::dispatch_mut(this) + .run_if_waiting_for_debugger(context_group_id) +} + +#[no_mangle] +pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__consoleAPIMessage( + this: &mut V8InspectorClient, + context_group_id: int, + level: int, + message: &StringView, + url: &StringView, + line_number: u32, + column_number: u32, + stack_trace: &mut V8StackTrace, +) { + V8InspectorClientBase::dispatch_mut(this).console_api_message( + context_group_id, + level, + message, + url, + line_number, + column_number, + stack_trace, + ) +} + +#[repr(C)] +pub struct Channel { + _cxx_vtable: CxxVTable, +} + +impl Channel { + pub fn send_response( + &mut self, + call_id: i32, + message: UniquePtr, + ) { + unsafe { + v8_inspector__V8Inspector__Channel__sendResponse(self, call_id, message) + } + } + pub fn send_notification(&mut self, message: UniquePtr) { + unsafe { + v8_inspector__V8Inspector__Channel__sendNotification(self, message) + } + } + pub fn flush_protocol_notifications(&mut self) { + unsafe { + v8_inspector__V8Inspector__Channel__flushProtocolNotifications(self) + } + } +} + +pub trait AsChannel { + fn as_channel(&self) -> &Channel; + fn as_channel_mut(&mut self) -> &mut Channel; +} + +impl AsChannel for Channel { + fn as_channel(&self) -> &Channel { + self + } + fn as_channel_mut(&mut self) -> &mut Channel { + self + } +} + +impl AsChannel for T +where + T: ChannelImpl, +{ + fn as_channel(&self) -> &Channel { + &self.base().cxx_base + } + fn as_channel_mut(&mut self) -> &mut Channel { + &mut self.base_mut().cxx_base + } +} + +pub trait ChannelImpl: AsChannel { + fn base(&self) -> &ChannelBase; + fn base_mut(&mut self) -> &mut ChannelBase; + + fn send_response( + &mut self, + call_id: i32, + message: UniquePtr, + ) -> (); + fn send_notification(&mut self, message: UniquePtr) -> (); + fn flush_protocol_notifications(&mut self) -> (); +} + +pub struct ChannelBase { + cxx_base: Channel, + offset_within_embedder: FieldOffset, + rust_vtable: RustVTable<&'static dyn ChannelImpl>, +} + +impl ChannelBase { + fn construct_cxx_base() -> Channel { + unsafe { + let mut buf = std::mem::MaybeUninit::::uninit(); + v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT(&mut buf); + buf.assume_init() + } + } + + 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: ChannelImpl, + { + let buf = std::mem::MaybeUninit::::uninit(); + let embedder_ptr: *const T = buf.as_ptr(); + let self_ptr: *const Self = unsafe { (*embedder_ptr).base() }; + FieldOffset::from_ptrs(embedder_ptr, self_ptr) + } + + fn get_rust_vtable() -> RustVTable<&'static dyn ChannelImpl> + where + T: ChannelImpl, + { + let buf = std::mem::MaybeUninit::::uninit(); + let embedder_ptr = buf.as_ptr(); + let trait_object: *const dyn ChannelImpl = embedder_ptr; + let (data_ptr, vtable): (*const T, RustVTable<_>) = + unsafe { std::mem::transmute(trait_object) }; + assert_eq!(data_ptr, embedder_ptr); + vtable + } + + pub fn new() -> Self + where + T: ChannelImpl, + { + Self { + cxx_base: Self::construct_cxx_base(), + offset_within_embedder: Self::get_offset_within_embedder::(), + rust_vtable: Self::get_rust_vtable::(), + } + } + + pub unsafe fn dispatch(channel: &Channel) -> &dyn ChannelImpl { + let this = Self::get_cxx_base_offset().to_embedder::(channel); + let embedder = this.offset_within_embedder.to_embedder::(this); + std::mem::transmute((embedder, this.rust_vtable)) + } + + pub unsafe fn dispatch_mut(channel: &mut Channel) -> &mut dyn ChannelImpl { + let this = Self::get_cxx_base_offset().to_embedder_mut::(channel); + let vtable = this.rust_vtable; + let embedder = this.offset_within_embedder.to_embedder_mut::(this); + std::mem::transmute((embedder, vtable)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::support::UniquePtr; + use std::sync::atomic::AtomicUsize; + use std::sync::atomic::Ordering::SeqCst; + + static MESSAGE: &[u8] = b"Hello Pluto!"; + static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); + + // Using repr(C) to preserve field ordering and test that everything works + // when the ChannelBase field is not the first element of the struct. + #[repr(C)] + pub struct TestChannel { + field1: i32, + base: ChannelBase, + field2: u64, + } + + 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, + mut message: UniquePtr, + ) { + assert_eq!(call_id, 999); + assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len()); + self.log_call(); + } + fn send_notification(&mut self, mut message: UniquePtr) { + assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len()); + self.log_call(); + } + fn flush_protocol_notifications(&mut self) { + self.log_call() + } + } + + impl TestChannel { + pub fn new() -> Self { + Self { + base: ChannelBase::new::(), + field1: -42, + field2: 420, + } + } + + fn log_call(&self) { + assert_eq!(self.field1, -42); + assert_eq!(self.field2, 420); + CALL_COUNT.fetch_add(1, SeqCst); + } + } + + #[test] + fn test_channel() { + let mut channel = TestChannel::new(); + let msg_view = StringView::from(MESSAGE); + channel.send_response(999, StringBuffer::create(&msg_view)); + assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); + channel.send_notification(StringBuffer::create(&msg_view)); + assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); + channel.flush_protocol_notifications(); + assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); + } +} + +#[repr(C)] +pub struct V8InspectorClient { + _cxx_vtable: CxxVTable, +} + +impl V8InspectorClient { + pub fn run_message_loop_on_pause(&mut self, context_group_id: i32) { + unsafe { + v8_inspector__V8InspectorClient__runMessageLoopOnPause( + self, + context_group_id, + ) + } + } + + pub fn quit_message_loop_on_pause(&mut self) { + unsafe { v8_inspector__V8InspectorClient__quitMessageLoopOnPause(self) } + } + + pub fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) { + unsafe { + v8_inspector__V8InspectorClient__runIfWaitingForDebugger( + self, + context_group_id, + ) + } + } + + #[allow(clippy::too_many_arguments)] + pub fn console_api_message( + &mut self, + context_group_id: i32, + level: i32, + message: &StringView, + url: &StringView, + line_number: u32, + column_number: u32, + stack_trace: &mut V8StackTrace, + ) { + unsafe { + v8_inspector__V8InspectorClient__consoleAPIMessage( + self, + context_group_id, + level, + message, + url, + line_number, + column_number, + stack_trace, + ) + } + } +} + +pub trait AsV8InspectorClient { + fn as_client(&self) -> &V8InspectorClient; + fn as_client_mut(&mut self) -> &mut V8InspectorClient; +} + +impl AsV8InspectorClient for V8InspectorClient { + fn as_client(&self) -> &V8InspectorClient { + self + } + fn as_client_mut(&mut self) -> &mut V8InspectorClient { + self + } +} + +impl AsV8InspectorClient for T +where + T: V8InspectorClientImpl, +{ + fn as_client(&self) -> &V8InspectorClient { + &self.base().cxx_base + } + fn as_client_mut(&mut self) -> &mut V8InspectorClient { + &mut self.base_mut().cxx_base + } +} + +#[allow(unused_variables)] +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: i32) {} + fn quit_message_loop_on_pause(&mut self) {} + fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {} + + #[allow(clippy::too_many_arguments)] + fn console_api_message( + &mut self, + context_group_id: i32, + level: i32, + message: &StringView, + url: &StringView, + line_number: u32, + column_number: u32, + stack_trace: &mut V8StackTrace, + ) { + } +} + +pub struct V8InspectorClientBase { + cxx_base: V8InspectorClient, + offset_within_embedder: FieldOffset, + rust_vtable: RustVTable<&'static dyn V8InspectorClientImpl>, +} + +impl V8InspectorClientBase { + fn construct_cxx_base() -> V8InspectorClient { + unsafe { + let mut buf = std::mem::MaybeUninit::::uninit(); + v8_inspector__V8InspectorClient__BASE__CONSTRUCT(&mut buf); + buf.assume_init() + } + } + + 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: V8InspectorClientImpl, + { + let buf = std::mem::MaybeUninit::::uninit(); + let embedder_ptr: *const T = buf.as_ptr(); + let self_ptr: *const Self = unsafe { (*embedder_ptr).base() }; + FieldOffset::from_ptrs(embedder_ptr, self_ptr) + } + + fn get_rust_vtable() -> RustVTable<&'static dyn V8InspectorClientImpl> + where + T: V8InspectorClientImpl, + { + let buf = std::mem::MaybeUninit::::uninit(); + let embedder_ptr = buf.as_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); + vtable + } + + pub fn new() -> Self + where + T: V8InspectorClientImpl, + { + Self { + cxx_base: Self::construct_cxx_base(), + offset_within_embedder: Self::get_offset_within_embedder::(), + rust_vtable: Self::get_rust_vtable::(), + } + } + + 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 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); + std::mem::transmute((embedder, vtable)) + } +} + +#[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: &StringView, + detail: &StringView, + ) { + unsafe { + v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( + self, reason, detail, + ) + } + } +} + +impl Delete for V8InspectorSession { + fn delete(&'static mut self) { + unsafe { v8_inspector__V8InspectorSession__DELETE(self) }; + } +} + +// TODO: in C++, this class is intended to be user-extensible, just like +// like `Task`, `Client`, `Channel`. In Rust this would ideally also be the +// case, but currently to obtain a `UniquePtr` is by making a +// copy using `StringBuffer::create()`. +#[repr(C)] +pub struct StringBuffer { + _cxx_vtable: CxxVTable, +} + +// TODO: make it possible to obtain a `UniquePtr` directly from +// an owned `Vec` or `Vec`, +impl StringBuffer { + // The C++ class definition does not declare `string()` to be a const method, + // therefore we declare self as mutable here. + // TODO: figure out whether it'd be safe to assume a const receiver here. + // That would make it possible to implement `Deref`. + pub fn string(&mut self) -> &StringView { + unsafe { v8_inspector__StringBuffer__string(self) } + } + + /// This method copies contents. + pub fn create(source: &StringView) -> UniquePtr { + unsafe { v8_inspector__StringBuffer__create(source) } + } +} + +impl Delete for StringBuffer { + fn delete(&'static mut self) { + unsafe { v8_inspector__StringBuffer__DELETE(self) } + } +} + +unsafe impl Send for StringBuffer {} +use std::fmt; +use std::iter::ExactSizeIterator; +use std::iter::IntoIterator; +use std::marker::PhantomData; +use std::ops::Deref; +use std::ptr::null; +use std::ptr::NonNull; +use std::slice; +use std::string; + +// Notes: +// * This class is ported, not wrapped using bindings. +// * Since Rust `repr(bool)` is not allowed, we're assuming that `bool` and +// `u8` have the same size. This is assumption is checked in 'support.h'. +// TODO: find/open upstream issue to allow #[repr(bool)] support. + +#[repr(u8)] +pub enum StringView<'a> { + // Do not reorder! + U16(CharacterArray<'a, u16>), + U8(CharacterArray<'a, u8>), +} + +impl StringView<'static> { + pub fn empty() -> Self { + Self::U8(CharacterArray::<'static, u8>::empty()) + } +} + +impl fmt::Display for StringView<'_> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Self::U16(v) => v.fmt(f), + Self::U8(v) => v.fmt(f), + } + } +} + +impl<'a> From<&'a [u8]> for StringView<'a> { + fn from(v: &'a [u8]) -> Self { + Self::U8(CharacterArray::<'a, u8>::from(v)) + } +} + +impl<'a> From<&'a [u16]> for StringView<'a> { + fn from(v: &'a [u16]) -> Self { + Self::U16(CharacterArray::<'a, u16>::from(v)) + } +} + +impl<'a> StringView<'a> { + pub fn is_8bit(&self) -> bool { + match self { + Self::U16(..) => false, + Self::U8(..) => true, + } + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn len(&self) -> usize { + match self { + Self::U16(v) => v.len(), + Self::U8(v) => v.len(), + } + } + + pub fn characters8(&self) -> Option<&[u8]> { + match self { + Self::U16(..) => None, + Self::U8(v) => Some(v), + } + } + + pub fn characters16(&self) -> Option<&[u16]> { + match self { + Self::U16(v) => Some(v), + Self::U8(..) => None, + } + } +} + +impl<'a: 'b, 'b> IntoIterator for &'a StringView<'b> { + type IntoIter = StringViewIterator<'a, 'b>; + type Item = u16; + + fn into_iter(self) -> Self::IntoIter { + StringViewIterator { view: self, pos: 0 } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct CharacterArray<'a, T> { + m_length: usize, + m_characters: *const T, + _phantom: PhantomData<&'a T>, +} + +impl CharacterArray<'static, u8> { + pub fn empty() -> Self { + Self { + m_length: 0, + m_characters: null(), + _phantom: PhantomData, + } + } +} + +impl<'a, T> CharacterArray<'a, T> +where + T: Copy, +{ + #[inline(always)] + fn len(&self) -> usize { + self.m_length + } + + #[inline(always)] + fn get_at(&self, index: usize) -> Option { + if index < self.m_length { + Some(unsafe { *self.m_characters.add(index) }) + } else { + None + } + } +} + +unsafe impl<'a, T> Send for CharacterArray<'a, T> where T: Copy {} +unsafe impl<'a, T> Sync for CharacterArray<'a, T> where T: Sync {} + +impl fmt::Display for CharacterArray<'_, u8> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str( + self + .iter() + .cloned() + .map(char::from) + .collect::() + .as_str(), + ) + } +} + +impl fmt::Display for CharacterArray<'_, u16> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&string::String::from_utf16_lossy(&*self)) + } +} + +impl<'a, T> From<&'a [T]> for CharacterArray<'a, T> { + fn from(v: &'a [T]) -> Self { + Self { + m_length: v.len(), + m_characters: v.as_ptr(), + _phantom: PhantomData, + } + } +} + +impl<'a, T> Deref for CharacterArray<'a, T> { + type Target = [T]; + + fn deref(&self) -> &[T] { + let Self { + m_length, + mut m_characters, + .. + } = *self; + if m_characters.is_null() { + assert_eq!(m_length, 0); + m_characters = NonNull::dangling().as_ptr() + }; + unsafe { slice::from_raw_parts(m_characters, m_length) } + } +} + +#[derive(Copy, Clone)] +pub struct StringViewIterator<'a: 'b, 'b> { + view: &'a StringView<'b>, + pos: usize, +} + +impl<'a: 'b, 'b> Iterator for StringViewIterator<'a, 'b> { + type Item = u16; + + fn next(&mut self) -> Option { + let result = Some(match self.view { + StringView::U16(v) => v.get_at(self.pos)?, + StringView::U8(v) => u16::from(v.get_at(self.pos)?), + }); + self.pos += 1; + result + } +} + +impl<'a: 'b, 'b> ExactSizeIterator for StringViewIterator<'a, 'b> { + fn len(&self) -> usize { + self.view.len() + } +} + +#[test] +fn string_view_display() { + let ok: [u16; 2] = [111, 107]; + assert_eq!("ok", format!("{}", StringView::from(&ok[..]))); + assert_eq!("ok", format!("{}", StringView::from(&b"ok"[..]))); + assert_eq!("ØÞ", format!("{}", StringView::from(&[216u8, 222u8][..]))); +} + +#[repr(C)] +pub struct V8Inspector(Opaque); + +impl V8Inspector { + pub fn create( + isolate: &mut impl InIsolate, + client: &mut T, + ) -> UniqueRef + where + T: AsV8InspectorClient, + { + unsafe { + UniqueRef::from_raw(v8_inspector__V8Inspector__create( + isolate.isolate(), + client.as_client_mut(), + )) + } + } + + pub fn connect( + &mut self, + context_group_id: i32, + 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: i32, + 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) }; + } +} + +#[repr(C)] +pub struct V8StackTrace { + _cxx_vtable: CxxVTable, +} + +// TODO(bnoordhuis) This needs to be fleshed out more but that can wait +// until it's actually needed. diff --git a/src/inspector/README.md b/src/inspector/README.md deleted file mode 100644 index c3bae0f0..00000000 --- a/src/inspector/README.md +++ /dev/null @@ -1,14 +0,0 @@ -Bindings to the V8 Inspector API - -https://medium.com/@hyperandroid/v8-inspector-from-an-embedder-standpoint-7f9c0472e2b7 - -https://v8.dev/docs/inspector - -https://chromedevtools.github.io/debugger-protocol-viewer/tot/ - -https://cs.chromium.org/chromium/src/v8/include/v8-inspector.h - -https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.cc -https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.h -https://github.com/nodejs/node/tree/v13.7.0/src/inspector - diff --git a/src/inspector/channel.rs b/src/inspector/channel.rs deleted file mode 100644 index 04cf0bd1..00000000 --- a/src/inspector/channel.rs +++ /dev/null @@ -1,257 +0,0 @@ -use super::*; -use crate::support::int; -use crate::support::CxxVTable; -use crate::support::FieldOffset; -use crate::support::Opaque; -use crate::support::RustVTable; -use crate::support::UniquePtr; - -extern "C" { - fn v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT( - buf: &mut std::mem::MaybeUninit, - ) -> (); - - fn v8_inspector__V8Inspector__Channel__sendResponse( - this: &mut Channel, - call_id: int, - message: UniquePtr, - ) -> (); - fn v8_inspector__V8Inspector__Channel__sendNotification( - this: &mut Channel, - message: UniquePtr, - ) -> (); - fn v8_inspector__V8Inspector__Channel__flushProtocolNotifications( - this: &mut Channel, - ) -> (); -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendResponse( - this: &mut Channel, - call_id: int, - message: UniquePtr, -) { - ChannelBase::dispatch_mut(this).send_response(call_id, message) -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendNotification( - this: &mut Channel, - message: UniquePtr, -) { - ChannelBase::dispatch_mut(this).send_notification(message) -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__flushProtocolNotifications( - this: &mut Channel, -) { - ChannelBase::dispatch_mut(this).flush_protocol_notifications() -} - -#[repr(C)] -pub struct Channel { - _cxx_vtable: CxxVTable, -} - -impl Channel { - pub fn send_response( - &mut self, - call_id: i32, - message: UniquePtr, - ) { - unsafe { - v8_inspector__V8Inspector__Channel__sendResponse(self, call_id, message) - } - } - pub fn send_notification(&mut self, message: UniquePtr) { - unsafe { - v8_inspector__V8Inspector__Channel__sendNotification(self, message) - } - } - pub fn flush_protocol_notifications(&mut self) { - unsafe { - v8_inspector__V8Inspector__Channel__flushProtocolNotifications(self) - } - } -} - -pub trait AsChannel { - fn as_channel(&self) -> &Channel; - fn as_channel_mut(&mut self) -> &mut Channel; -} - -impl AsChannel for Channel { - fn as_channel(&self) -> &Channel { - self - } - fn as_channel_mut(&mut self) -> &mut Channel { - self - } -} - -impl AsChannel for T -where - T: ChannelImpl, -{ - fn as_channel(&self) -> &Channel { - &self.base().cxx_base - } - fn as_channel_mut(&mut self) -> &mut Channel { - &mut self.base_mut().cxx_base - } -} - -pub trait ChannelImpl: AsChannel { - fn base(&self) -> &ChannelBase; - fn base_mut(&mut self) -> &mut ChannelBase; - - fn send_response( - &mut self, - call_id: i32, - message: UniquePtr, - ) -> (); - fn send_notification(&mut self, message: UniquePtr) -> (); - fn flush_protocol_notifications(&mut self) -> (); -} - -pub struct ChannelBase { - cxx_base: Channel, - offset_within_embedder: FieldOffset, - rust_vtable: RustVTable<&'static dyn ChannelImpl>, -} - -impl ChannelBase { - fn construct_cxx_base() -> Channel { - unsafe { - let mut buf = std::mem::MaybeUninit::::uninit(); - v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT(&mut buf); - buf.assume_init() - } - } - - 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: ChannelImpl, - { - let buf = std::mem::MaybeUninit::::uninit(); - let embedder_ptr: *const T = buf.as_ptr(); - let self_ptr: *const Self = unsafe { (*embedder_ptr).base() }; - FieldOffset::from_ptrs(embedder_ptr, self_ptr) - } - - fn get_rust_vtable() -> RustVTable<&'static dyn ChannelImpl> - where - T: ChannelImpl, - { - let buf = std::mem::MaybeUninit::::uninit(); - let embedder_ptr = buf.as_ptr(); - let trait_object: *const dyn ChannelImpl = embedder_ptr; - let (data_ptr, vtable): (*const T, RustVTable<_>) = - unsafe { std::mem::transmute(trait_object) }; - assert_eq!(data_ptr, embedder_ptr); - vtable - } - - pub fn new() -> Self - where - T: ChannelImpl, - { - Self { - cxx_base: Self::construct_cxx_base(), - offset_within_embedder: Self::get_offset_within_embedder::(), - rust_vtable: Self::get_rust_vtable::(), - } - } - - pub unsafe fn dispatch(channel: &Channel) -> &dyn ChannelImpl { - let this = Self::get_cxx_base_offset().to_embedder::(channel); - let embedder = this.offset_within_embedder.to_embedder::(this); - std::mem::transmute((embedder, this.rust_vtable)) - } - - pub unsafe fn dispatch_mut(channel: &mut Channel) -> &mut dyn ChannelImpl { - let this = Self::get_cxx_base_offset().to_embedder_mut::(channel); - let vtable = this.rust_vtable; - let embedder = this.offset_within_embedder.to_embedder_mut::(this); - std::mem::transmute((embedder, vtable)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::support::UniquePtr; - use std::sync::atomic::AtomicUsize; - use std::sync::atomic::Ordering::SeqCst; - - static MESSAGE: &[u8] = b"Hello Pluto!"; - static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); - - // Using repr(C) to preserve field ordering and test that everything works - // when the ChannelBase field is not the first element of the struct. - #[repr(C)] - pub struct TestChannel { - field1: i32, - base: ChannelBase, - field2: u64, - } - - 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, - mut message: UniquePtr, - ) { - assert_eq!(call_id, 999); - assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len()); - self.log_call(); - } - fn send_notification(&mut self, mut message: UniquePtr) { - assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len()); - self.log_call(); - } - fn flush_protocol_notifications(&mut self) { - self.log_call() - } - } - - impl TestChannel { - pub fn new() -> Self { - Self { - base: ChannelBase::new::(), - field1: -42, - field2: 420, - } - } - - fn log_call(&self) { - assert_eq!(self.field1, -42); - assert_eq!(self.field2, 420); - CALL_COUNT.fetch_add(1, SeqCst); - } - } - - #[test] - fn test_channel() { - let mut channel = TestChannel::new(); - let msg_view = StringView::from(MESSAGE); - channel.send_response(999, StringBuffer::create(&msg_view)); - assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); - channel.send_notification(StringBuffer::create(&msg_view)); - assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); - channel.flush_protocol_notifications(); - assert_eq!(CALL_COUNT.swap(0, SeqCst), 1); - } -} diff --git a/src/inspector/client.rs b/src/inspector/client.rs deleted file mode 100644 index d5ed16eb..00000000 --- a/src/inspector/client.rs +++ /dev/null @@ -1,256 +0,0 @@ -use super::{StringView, V8StackTrace}; -use crate::support::int; -use crate::support::CxxVTable; -use crate::support::FieldOffset; -use crate::support::Opaque; -use crate::support::RustVTable; - -extern "C" { - fn v8_inspector__V8InspectorClient__BASE__CONSTRUCT( - buf: &mut std::mem::MaybeUninit, - ) -> (); - - fn v8_inspector__V8InspectorClient__runMessageLoopOnPause( - this: &mut V8InspectorClient, - context_group_id: int, - ) -> (); - fn v8_inspector__V8InspectorClient__quitMessageLoopOnPause( - this: &mut V8InspectorClient, - ) -> (); - fn v8_inspector__V8InspectorClient__runIfWaitingForDebugger( - this: &mut V8InspectorClient, - context_group_id: int, - ) -> (); - fn v8_inspector__V8InspectorClient__consoleAPIMessage( - this: &mut V8InspectorClient, - context_group_id: int, - level: int, - message: &StringView, - url: &StringView, - line_number: u32, - column_number: u32, - stack_trace: &mut V8StackTrace, - ) -> (); -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runMessageLoopOnPause( - this: &mut V8InspectorClient, - context_group_id: int, -) { - 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 V8InspectorClient, -) { - V8InspectorClientBase::dispatch_mut(this).quit_message_loop_on_pause() -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runIfWaitingForDebugger( - this: &mut V8InspectorClient, - context_group_id: int, -) { - V8InspectorClientBase::dispatch_mut(this) - .run_if_waiting_for_debugger(context_group_id) -} - -#[no_mangle] -pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__consoleAPIMessage( - this: &mut V8InspectorClient, - context_group_id: int, - level: int, - message: &StringView, - url: &StringView, - line_number: u32, - column_number: u32, - stack_trace: &mut V8StackTrace, -) { - V8InspectorClientBase::dispatch_mut(this).console_api_message( - context_group_id, - level, - message, - url, - line_number, - column_number, - stack_trace, - ) -} - -#[repr(C)] -pub struct V8InspectorClient { - _cxx_vtable: CxxVTable, -} - -impl V8InspectorClient { - pub fn run_message_loop_on_pause(&mut self, context_group_id: i32) { - unsafe { - v8_inspector__V8InspectorClient__runMessageLoopOnPause( - self, - context_group_id, - ) - } - } - - pub fn quit_message_loop_on_pause(&mut self) { - unsafe { v8_inspector__V8InspectorClient__quitMessageLoopOnPause(self) } - } - - pub fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) { - unsafe { - v8_inspector__V8InspectorClient__runIfWaitingForDebugger( - self, - context_group_id, - ) - } - } - - #[allow(clippy::too_many_arguments)] - pub fn console_api_message( - &mut self, - context_group_id: i32, - level: i32, - message: &StringView, - url: &StringView, - line_number: u32, - column_number: u32, - stack_trace: &mut V8StackTrace, - ) { - unsafe { - v8_inspector__V8InspectorClient__consoleAPIMessage( - self, - context_group_id, - level, - message, - url, - line_number, - column_number, - stack_trace, - ) - } - } -} - -pub trait AsV8InspectorClient { - fn as_client(&self) -> &V8InspectorClient; - fn as_client_mut(&mut self) -> &mut V8InspectorClient; -} - -impl AsV8InspectorClient for V8InspectorClient { - fn as_client(&self) -> &V8InspectorClient { - self - } - fn as_client_mut(&mut self) -> &mut V8InspectorClient { - self - } -} - -impl AsV8InspectorClient for T -where - T: V8InspectorClientImpl, -{ - fn as_client(&self) -> &V8InspectorClient { - &self.base().cxx_base - } - fn as_client_mut(&mut self) -> &mut V8InspectorClient { - &mut self.base_mut().cxx_base - } -} - -#[allow(unused_variables)] -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: i32) {} - fn quit_message_loop_on_pause(&mut self) {} - fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {} - - #[allow(clippy::too_many_arguments)] - fn console_api_message( - &mut self, - context_group_id: i32, - level: i32, - message: &StringView, - url: &StringView, - line_number: u32, - column_number: u32, - stack_trace: &mut V8StackTrace, - ) { - } -} - -pub struct V8InspectorClientBase { - cxx_base: V8InspectorClient, - offset_within_embedder: FieldOffset, - rust_vtable: RustVTable<&'static dyn V8InspectorClientImpl>, -} - -impl V8InspectorClientBase { - fn construct_cxx_base() -> V8InspectorClient { - unsafe { - let mut buf = std::mem::MaybeUninit::::uninit(); - v8_inspector__V8InspectorClient__BASE__CONSTRUCT(&mut buf); - buf.assume_init() - } - } - - 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: V8InspectorClientImpl, - { - let buf = std::mem::MaybeUninit::::uninit(); - let embedder_ptr: *const T = buf.as_ptr(); - let self_ptr: *const Self = unsafe { (*embedder_ptr).base() }; - FieldOffset::from_ptrs(embedder_ptr, self_ptr) - } - - fn get_rust_vtable() -> RustVTable<&'static dyn V8InspectorClientImpl> - where - T: V8InspectorClientImpl, - { - let buf = std::mem::MaybeUninit::::uninit(); - let embedder_ptr = buf.as_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); - vtable - } - - pub fn new() -> Self - where - T: V8InspectorClientImpl, - { - Self { - cxx_base: Self::construct_cxx_base(), - offset_within_embedder: Self::get_offset_within_embedder::(), - rust_vtable: Self::get_rust_vtable::(), - } - } - - 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 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); - std::mem::transmute((embedder, vtable)) - } -} diff --git a/src/inspector/mod.rs b/src/inspector/mod.rs deleted file mode 100644 index 817c7fae..00000000 --- a/src/inspector/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod channel; -mod client; -mod session; -mod string_buffer; -mod string_view; -mod v8_inspector; -mod v8_stack_trace; - -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; -pub use v8_stack_trace::V8StackTrace; diff --git a/src/inspector/session.rs b/src/inspector/session.rs deleted file mode 100644 index bd9de522..00000000 --- a/src/inspector/session.rs +++ /dev/null @@ -1,47 +0,0 @@ -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: &StringView, - break_details: &StringView, - ); -} - -#[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: &StringView, - detail: &StringView, - ) { - unsafe { - v8_inspector__V8InspectorSession__schedulePauseOnNextStatement( - self, reason, 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 deleted file mode 100644 index aaeb838b..00000000 --- a/src/inspector/string_buffer.rs +++ /dev/null @@ -1,48 +0,0 @@ -use super::string_view::StringView; -use crate::support::CxxVTable; -use crate::support::Delete; -use crate::support::UniquePtr; - -// TODO: in C++, this class is intended to be user-extensible, just like -// like `Task`, `Client`, `Channel`. In Rust this would ideally also be the -// case, but currently to obtain a `UniquePtr` is by making a -// copy using `StringBuffer::create()`. - -extern "C" { - fn v8_inspector__StringBuffer__DELETE(this: &'static mut StringBuffer) -> (); - fn v8_inspector__StringBuffer__string(this: &mut StringBuffer) - -> &StringView; - fn v8_inspector__StringBuffer__create( - source: &StringView, - ) -> UniquePtr; -} - -#[repr(C)] -pub struct StringBuffer { - _cxx_vtable: CxxVTable, -} - -// TODO: make it possible to obtain a `UniquePtr` directly from -// an owned `Vec` or `Vec`, -impl StringBuffer { - // The C++ class definition does not declare `string()` to be a const method, - // therefore we declare self as mutable here. - // TODO: figure out whether it'd be safe to assume a const receiver here. - // That would make it possible to implement `Deref`. - pub fn string(&mut self) -> &StringView { - unsafe { v8_inspector__StringBuffer__string(self) } - } - - /// This method copies contents. - pub fn create(source: &StringView) -> UniquePtr { - unsafe { v8_inspector__StringBuffer__create(source) } - } -} - -impl Delete for StringBuffer { - fn delete(&'static mut self) { - unsafe { v8_inspector__StringBuffer__DELETE(self) } - } -} - -unsafe impl Send for StringBuffer {} diff --git a/src/inspector/string_view.rs b/src/inspector/string_view.rs deleted file mode 100644 index 9ab7576b..00000000 --- a/src/inspector/string_view.rs +++ /dev/null @@ -1,211 +0,0 @@ -use std::fmt; -use std::iter::ExactSizeIterator; -use std::iter::IntoIterator; -use std::marker::PhantomData; -use std::ops::Deref; -use std::ptr::null; -use std::ptr::NonNull; -use std::slice; -use std::string; - -// Notes: -// * This class is ported, not wrapped using bindings. -// * Since Rust `repr(bool)` is not allowed, we're assuming that `bool` and -// `u8` have the same size. This is assumption is checked in 'support.h'. -// TODO: find/open upstream issue to allow #[repr(bool)] support. - -#[repr(u8)] -pub enum StringView<'a> { - // Do not reorder! - U16(CharacterArray<'a, u16>), - U8(CharacterArray<'a, u8>), -} - -impl StringView<'static> { - pub fn empty() -> Self { - Self::U8(CharacterArray::<'static, u8>::empty()) - } -} - -impl fmt::Display for StringView<'_> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Self::U16(v) => v.fmt(f), - Self::U8(v) => v.fmt(f), - } - } -} - -impl<'a> From<&'a [u8]> for StringView<'a> { - fn from(v: &'a [u8]) -> Self { - Self::U8(CharacterArray::<'a, u8>::from(v)) - } -} - -impl<'a> From<&'a [u16]> for StringView<'a> { - fn from(v: &'a [u16]) -> Self { - Self::U16(CharacterArray::<'a, u16>::from(v)) - } -} - -impl<'a> StringView<'a> { - pub fn is_8bit(&self) -> bool { - match self { - Self::U16(..) => false, - Self::U8(..) => true, - } - } - - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - pub fn len(&self) -> usize { - match self { - Self::U16(v) => v.len(), - Self::U8(v) => v.len(), - } - } - - pub fn characters8(&self) -> Option<&[u8]> { - match self { - Self::U16(..) => None, - Self::U8(v) => Some(v), - } - } - - pub fn characters16(&self) -> Option<&[u16]> { - match self { - Self::U16(v) => Some(v), - Self::U8(..) => None, - } - } -} - -impl<'a: 'b, 'b> IntoIterator for &'a StringView<'b> { - type IntoIter = StringViewIterator<'a, 'b>; - type Item = u16; - - fn into_iter(self) -> Self::IntoIter { - StringViewIterator { view: self, pos: 0 } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub struct CharacterArray<'a, T> { - m_length: usize, - m_characters: *const T, - _phantom: PhantomData<&'a T>, -} - -impl CharacterArray<'static, u8> { - pub fn empty() -> Self { - Self { - m_length: 0, - m_characters: null(), - _phantom: PhantomData, - } - } -} - -impl<'a, T> CharacterArray<'a, T> -where - T: Copy, -{ - #[inline(always)] - fn len(&self) -> usize { - self.m_length - } - - #[inline(always)] - fn get_at(&self, index: usize) -> Option { - if index < self.m_length { - Some(unsafe { *self.m_characters.add(index) }) - } else { - None - } - } -} - -unsafe impl<'a, T> Send for CharacterArray<'a, T> where T: Copy {} -unsafe impl<'a, T> Sync for CharacterArray<'a, T> where T: Sync {} - -impl fmt::Display for CharacterArray<'_, u8> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str( - self - .iter() - .cloned() - .map(char::from) - .collect::() - .as_str(), - ) - } -} - -impl fmt::Display for CharacterArray<'_, u16> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&string::String::from_utf16_lossy(&*self)) - } -} - -impl<'a, T> From<&'a [T]> for CharacterArray<'a, T> { - fn from(v: &'a [T]) -> Self { - Self { - m_length: v.len(), - m_characters: v.as_ptr(), - _phantom: PhantomData, - } - } -} - -impl<'a, T> Deref for CharacterArray<'a, T> { - type Target = [T]; - - fn deref(&self) -> &[T] { - let Self { - m_length, - mut m_characters, - .. - } = *self; - if m_characters.is_null() { - assert_eq!(m_length, 0); - m_characters = NonNull::dangling().as_ptr() - }; - unsafe { slice::from_raw_parts(m_characters, m_length) } - } -} - -#[derive(Copy, Clone)] -pub struct StringViewIterator<'a: 'b, 'b> { - view: &'a StringView<'b>, - pos: usize, -} - -impl<'a: 'b, 'b> Iterator for StringViewIterator<'a, 'b> { - type Item = u16; - - fn next(&mut self) -> Option { - let result = Some(match self.view { - StringView::U16(v) => v.get_at(self.pos)?, - StringView::U8(v) => u16::from(v.get_at(self.pos)?), - }); - self.pos += 1; - result - } -} - -impl<'a: 'b, 'b> ExactSizeIterator for StringViewIterator<'a, 'b> { - fn len(&self) -> usize { - self.view.len() - } -} - -#[test] -fn string_view_display() { - let ok: [u16; 2] = [111, 107]; - assert_eq!("ok", format!("{}", StringView::from(&ok[..]))); - assert_eq!("ok", format!("{}", StringView::from(&b"ok"[..]))); - assert_eq!("ØÞ", format!("{}", StringView::from(&[216u8, 222u8][..]))); -} diff --git a/src/inspector/v8_inspector.rs b/src/inspector/v8_inspector.rs deleted file mode 100644 index 88cdb7f7..00000000 --- a/src/inspector/v8_inspector.rs +++ /dev/null @@ -1,97 +0,0 @@ -use super::channel::AsChannel; -use super::client::AsV8InspectorClient; -use super::session::V8InspectorSession; -use super::Channel; -use super::StringView; -use super::V8InspectorClient; -use crate::scope_traits::InIsolate; -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 impl InIsolate, - client: &mut T, - ) -> UniqueRef - where - T: AsV8InspectorClient, - { - unsafe { - UniqueRef::from_raw(v8_inspector__V8Inspector__create( - isolate.isolate(), - client.as_client_mut(), - )) - } - } - - pub fn connect( - &mut self, - context_group_id: i32, - 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: i32, - 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/inspector/v8_stack_trace.rs b/src/inspector/v8_stack_trace.rs deleted file mode 100644 index 5fb15a48..00000000 --- a/src/inspector/v8_stack_trace.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::support::CxxVTable; - -#[repr(C)] -pub struct V8StackTrace { - _cxx_vtable: CxxVTable, -} - -// TODO(bnoordhuis) This needs to be fleshed out more but that can wait -// until it's actually needed.