rusty_v8/
inspector.rs

1// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
2
3//! Bindings to the V8 Inspector API. Documentation for the V8 inspector API is
4//! very sparse, so here are a few references for the next sorry soul who has to
5//! dig into it.
6//!
7//! https://medium.com/@hyperandroid/v8-inspector-from-an-embedder-standpoint-7f9c0472e2b7
8//! https://v8.dev/docs/inspector
9//! https://chromedevtools.github.io/debugger-protocol-viewer/tot/
10//! https://cs.chromium.org/chromium/src/v8/include/v8-inspector.h
11//! https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.cc
12//! https://github.com/nodejs/node/blob/v13.7.0/src/inspector_agent.h
13//! https://github.com/nodejs/node/tree/v13.7.0/src/inspector
14//! https://github.com/denoland/deno/blob/v0.38.0/cli/inspector.rs
15
16use crate::support::int;
17use crate::support::CxxVTable;
18use crate::support::FieldOffset;
19use crate::support::Opaque;
20use crate::support::RustVTable;
21use crate::support::UniquePtr;
22use crate::support::UniqueRef;
23use crate::Context;
24use crate::Isolate;
25use crate::Local;
26use std::fmt::{self, Debug, Formatter};
27
28extern "C" {
29  fn v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT(
30    buf: &mut std::mem::MaybeUninit<Channel>,
31  );
32
33  fn v8_inspector__V8Inspector__Channel__sendResponse(
34    this: &mut Channel,
35    call_id: int,
36    message: UniquePtr<StringBuffer>,
37  );
38  fn v8_inspector__V8Inspector__Channel__sendNotification(
39    this: &mut Channel,
40    message: UniquePtr<StringBuffer>,
41  );
42  fn v8_inspector__V8Inspector__Channel__flushProtocolNotifications(
43    this: &mut Channel,
44  );
45
46  fn v8_inspector__V8InspectorClient__BASE__CONSTRUCT(
47    buf: &mut std::mem::MaybeUninit<V8InspectorClient>,
48  );
49
50  fn v8_inspector__V8InspectorClient__generateUniqueId(
51    this: &mut V8InspectorClient,
52  ) -> i64;
53  fn v8_inspector__V8InspectorClient__runMessageLoopOnPause(
54    this: &mut V8InspectorClient,
55    context_group_id: int,
56  );
57  fn v8_inspector__V8InspectorClient__quitMessageLoopOnPause(
58    this: &mut V8InspectorClient,
59  );
60  fn v8_inspector__V8InspectorClient__runIfWaitingForDebugger(
61    this: &mut V8InspectorClient,
62    context_group_id: int,
63  );
64  fn v8_inspector__V8InspectorClient__consoleAPIMessage(
65    this: &mut V8InspectorClient,
66    context_group_id: int,
67    level: int,
68    message: &StringView,
69    url: &StringView,
70    line_number: u32,
71    column_number: u32,
72    stack_trace: &mut V8StackTrace,
73  );
74
75  fn v8_inspector__V8InspectorSession__DELETE(this: &mut V8InspectorSession);
76  fn v8_inspector__V8InspectorSession__dispatchProtocolMessage(
77    session: *mut V8InspectorSession,
78    message: StringView,
79  );
80  fn v8_inspector__V8InspectorSession__schedulePauseOnNextStatement(
81    session: *mut V8InspectorSession,
82    break_reason: StringView,
83    break_details: StringView,
84  );
85  fn v8_inspector__V8InspectorSession__canDispatchMethod(
86    method: StringView,
87  ) -> bool;
88
89  fn v8_inspector__StringBuffer__DELETE(this: &mut StringBuffer);
90  fn v8_inspector__StringBuffer__string(this: &StringBuffer) -> StringView;
91  fn v8_inspector__StringBuffer__create(
92    source: StringView,
93  ) -> UniquePtr<StringBuffer>;
94
95  fn v8_inspector__V8Inspector__DELETE(this: &mut V8Inspector);
96  fn v8_inspector__V8Inspector__create(
97    isolate: *mut Isolate,
98    client: *mut V8InspectorClient,
99  ) -> *mut V8Inspector;
100  fn v8_inspector__V8Inspector__connect(
101    inspector: *mut V8Inspector,
102    context_group_id: int,
103    channel: *mut Channel,
104    state: StringView,
105  ) -> *mut V8InspectorSession;
106  fn v8_inspector__V8Inspector__contextCreated(
107    this: *mut V8Inspector,
108    context: *const Context,
109    contextGroupId: int,
110    humanReadableName: StringView,
111  );
112}
113
114#[no_mangle]
115pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendResponse(
116  this: &mut Channel,
117  call_id: int,
118  message: UniquePtr<StringBuffer>,
119) {
120  ChannelBase::dispatch_mut(this).send_response(call_id, message)
121}
122
123#[no_mangle]
124pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendNotification(
125  this: &mut Channel,
126  message: UniquePtr<StringBuffer>,
127) {
128  ChannelBase::dispatch_mut(this).send_notification(message)
129}
130
131#[no_mangle]
132pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__flushProtocolNotifications(
133  this: &mut Channel,
134) {
135  ChannelBase::dispatch_mut(this).flush_protocol_notifications()
136}
137
138#[no_mangle]
139pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__generateUniqueId(
140  this: &mut V8InspectorClient,
141) -> i64 {
142  V8InspectorClientBase::dispatch_mut(this).generate_unique_id()
143}
144
145#[no_mangle]
146pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runMessageLoopOnPause(
147  this: &mut V8InspectorClient,
148  context_group_id: int,
149) {
150  V8InspectorClientBase::dispatch_mut(this)
151    .run_message_loop_on_pause(context_group_id)
152}
153
154#[no_mangle]
155pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__quitMessageLoopOnPause(
156  this: &mut V8InspectorClient,
157) {
158  V8InspectorClientBase::dispatch_mut(this).quit_message_loop_on_pause()
159}
160
161#[no_mangle]
162pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__runIfWaitingForDebugger(
163  this: &mut V8InspectorClient,
164  context_group_id: int,
165) {
166  V8InspectorClientBase::dispatch_mut(this)
167    .run_if_waiting_for_debugger(context_group_id)
168}
169
170#[no_mangle]
171pub unsafe extern "C" fn v8_inspector__V8InspectorClient__BASE__consoleAPIMessage(
172  this: &mut V8InspectorClient,
173  context_group_id: int,
174  level: int,
175  message: &StringView,
176  url: &StringView,
177  line_number: u32,
178  column_number: u32,
179  stack_trace: &mut V8StackTrace,
180) {
181  V8InspectorClientBase::dispatch_mut(this).console_api_message(
182    context_group_id,
183    level,
184    message,
185    url,
186    line_number,
187    column_number,
188    stack_trace,
189  )
190}
191
192#[repr(C)]
193#[derive(Debug)]
194pub struct Channel {
195  _cxx_vtable: CxxVTable,
196}
197
198impl Channel {
199  pub fn send_response(
200    &mut self,
201    call_id: i32,
202    message: UniquePtr<StringBuffer>,
203  ) {
204    unsafe {
205      v8_inspector__V8Inspector__Channel__sendResponse(self, call_id, message)
206    }
207  }
208  pub fn send_notification(&mut self, message: UniquePtr<StringBuffer>) {
209    unsafe {
210      v8_inspector__V8Inspector__Channel__sendNotification(self, message)
211    }
212  }
213  pub fn flush_protocol_notifications(&mut self) {
214    unsafe {
215      v8_inspector__V8Inspector__Channel__flushProtocolNotifications(self)
216    }
217  }
218}
219
220pub trait AsChannel {
221  fn as_channel(&self) -> &Channel;
222  fn as_channel_mut(&mut self) -> &mut Channel;
223}
224
225impl AsChannel for Channel {
226  fn as_channel(&self) -> &Channel {
227    self
228  }
229  fn as_channel_mut(&mut self) -> &mut Channel {
230    self
231  }
232}
233
234impl<T> AsChannel for T
235where
236  T: ChannelImpl,
237{
238  fn as_channel(&self) -> &Channel {
239    &self.base().cxx_base
240  }
241  fn as_channel_mut(&mut self) -> &mut Channel {
242    &mut self.base_mut().cxx_base
243  }
244}
245
246pub trait ChannelImpl: AsChannel {
247  fn base(&self) -> &ChannelBase;
248  fn base_mut(&mut self) -> &mut ChannelBase;
249
250  fn send_response(&mut self, call_id: i32, message: UniquePtr<StringBuffer>);
251  fn send_notification(&mut self, message: UniquePtr<StringBuffer>);
252  fn flush_protocol_notifications(&mut self);
253}
254
255pub struct ChannelBase {
256  cxx_base: Channel,
257  offset_within_embedder: FieldOffset<Self>,
258  rust_vtable: RustVTable<&'static dyn ChannelImpl>,
259}
260
261impl ChannelBase {
262  fn construct_cxx_base() -> Channel {
263    unsafe {
264      let mut buf = std::mem::MaybeUninit::<Channel>::uninit();
265      v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT(&mut buf);
266      buf.assume_init()
267    }
268  }
269
270  fn get_cxx_base_offset() -> FieldOffset<Channel> {
271    let buf = std::mem::MaybeUninit::<Self>::uninit();
272    FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_base })
273  }
274
275  fn get_offset_within_embedder<T>() -> FieldOffset<Self>
276  where
277    T: ChannelImpl,
278  {
279    let buf = std::mem::MaybeUninit::<T>::uninit();
280    let embedder_ptr: *const T = buf.as_ptr();
281    let self_ptr: *const Self = unsafe { (*embedder_ptr).base() };
282    FieldOffset::from_ptrs(embedder_ptr, self_ptr)
283  }
284
285  fn get_rust_vtable<T>() -> RustVTable<&'static dyn ChannelImpl>
286  where
287    T: ChannelImpl,
288  {
289    let buf = std::mem::MaybeUninit::<T>::uninit();
290    let embedder_ptr = buf.as_ptr();
291    let trait_object: *const dyn ChannelImpl = embedder_ptr;
292    let (data_ptr, vtable): (*const T, RustVTable<_>) =
293      unsafe { std::mem::transmute(trait_object) };
294    assert_eq!(data_ptr, embedder_ptr);
295    vtable
296  }
297
298  pub fn new<T>() -> Self
299  where
300    T: ChannelImpl,
301  {
302    Self {
303      cxx_base: Self::construct_cxx_base(),
304      offset_within_embedder: Self::get_offset_within_embedder::<T>(),
305      rust_vtable: Self::get_rust_vtable::<T>(),
306    }
307  }
308
309  pub unsafe fn dispatch(channel: &Channel) -> &dyn ChannelImpl {
310    let this = Self::get_cxx_base_offset().to_embedder::<Self>(channel);
311    let embedder = this.offset_within_embedder.to_embedder::<Opaque>(this);
312    std::mem::transmute((embedder, this.rust_vtable))
313  }
314
315  pub unsafe fn dispatch_mut(channel: &mut Channel) -> &mut dyn ChannelImpl {
316    let this = Self::get_cxx_base_offset().to_embedder_mut::<Self>(channel);
317    let vtable = this.rust_vtable;
318    let embedder = this.offset_within_embedder.to_embedder_mut::<Opaque>(this);
319    std::mem::transmute((embedder, vtable))
320  }
321}
322
323impl Debug for ChannelBase {
324  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
325    f.debug_struct("ChannelBase")
326      .field("cxx_base", &self.cxx_base)
327      .field("offset_within_embedder", &self.offset_within_embedder)
328      .finish()
329  }
330}
331
332#[cfg(test)]
333mod tests {
334  use super::*;
335  use crate::support::UniquePtr;
336  use std::sync::atomic::AtomicUsize;
337  use std::sync::atomic::Ordering::SeqCst;
338
339  static MESSAGE: &[u8] = b"Hello Pluto!";
340  static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
341
342  // Using repr(C) to preserve field ordering and test that everything works
343  // when the ChannelBase field is not the first element of the struct.
344  #[repr(C)]
345  #[derive(Debug)]
346  pub struct TestChannel {
347    field1: i32,
348    base: ChannelBase,
349    field2: u64,
350  }
351
352  impl ChannelImpl for TestChannel {
353    fn base(&self) -> &ChannelBase {
354      &self.base
355    }
356    fn base_mut(&mut self) -> &mut ChannelBase {
357      &mut self.base
358    }
359    fn send_response(
360      &mut self,
361      call_id: i32,
362      mut message: UniquePtr<StringBuffer>,
363    ) {
364      assert_eq!(call_id, 999);
365      assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len());
366      self.log_call();
367    }
368    fn send_notification(&mut self, mut message: UniquePtr<StringBuffer>) {
369      assert_eq!(message.as_mut().unwrap().string().len(), MESSAGE.len());
370      self.log_call();
371    }
372    fn flush_protocol_notifications(&mut self) {
373      self.log_call()
374    }
375  }
376
377  impl TestChannel {
378    pub fn new() -> Self {
379      Self {
380        base: ChannelBase::new::<Self>(),
381        field1: -42,
382        field2: 420,
383      }
384    }
385
386    fn log_call(&self) {
387      assert_eq!(self.field1, -42);
388      assert_eq!(self.field2, 420);
389      CALL_COUNT.fetch_add(1, SeqCst);
390    }
391  }
392
393  #[test]
394  fn test_channel() {
395    let mut channel = TestChannel::new();
396    let msg_view = StringView::from(MESSAGE);
397    channel.send_response(999, StringBuffer::create(msg_view));
398    assert_eq!(CALL_COUNT.swap(0, SeqCst), 1);
399    channel.send_notification(StringBuffer::create(msg_view));
400    assert_eq!(CALL_COUNT.swap(0, SeqCst), 1);
401    channel.flush_protocol_notifications();
402    assert_eq!(CALL_COUNT.swap(0, SeqCst), 1);
403  }
404}
405
406#[repr(C)]
407#[derive(Debug)]
408pub struct V8InspectorClient {
409  _cxx_vtable: CxxVTable,
410}
411
412impl V8InspectorClient {
413  pub fn run_message_loop_on_pause(&mut self, context_group_id: i32) {
414    unsafe {
415      v8_inspector__V8InspectorClient__runMessageLoopOnPause(
416        self,
417        context_group_id,
418      )
419    }
420  }
421
422  pub fn quit_message_loop_on_pause(&mut self) {
423    unsafe { v8_inspector__V8InspectorClient__quitMessageLoopOnPause(self) }
424  }
425
426  pub fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {
427    unsafe {
428      v8_inspector__V8InspectorClient__runIfWaitingForDebugger(
429        self,
430        context_group_id,
431      )
432    }
433  }
434
435  #[allow(clippy::too_many_arguments)]
436  pub fn console_api_message(
437    &mut self,
438    context_group_id: i32,
439    level: i32,
440    message: &StringView,
441    url: &StringView,
442    line_number: u32,
443    column_number: u32,
444    stack_trace: &mut V8StackTrace,
445  ) {
446    unsafe {
447      v8_inspector__V8InspectorClient__consoleAPIMessage(
448        self,
449        context_group_id,
450        level,
451        message,
452        url,
453        line_number,
454        column_number,
455        stack_trace,
456      )
457    }
458  }
459
460  pub fn generate_unique_id(&mut self) -> i64 {
461    unsafe { v8_inspector__V8InspectorClient__generateUniqueId(self) }
462  }
463}
464
465pub trait AsV8InspectorClient {
466  fn as_client(&self) -> &V8InspectorClient;
467  fn as_client_mut(&mut self) -> &mut V8InspectorClient;
468}
469
470impl AsV8InspectorClient for V8InspectorClient {
471  fn as_client(&self) -> &V8InspectorClient {
472    self
473  }
474  fn as_client_mut(&mut self) -> &mut V8InspectorClient {
475    self
476  }
477}
478
479impl<T> AsV8InspectorClient for T
480where
481  T: V8InspectorClientImpl,
482{
483  fn as_client(&self) -> &V8InspectorClient {
484    &self.base().cxx_base
485  }
486  fn as_client_mut(&mut self) -> &mut V8InspectorClient {
487    &mut self.base_mut().cxx_base
488  }
489}
490
491#[allow(unused_variables)]
492pub trait V8InspectorClientImpl: AsV8InspectorClient {
493  fn base(&self) -> &V8InspectorClientBase;
494  fn base_mut(&mut self) -> &mut V8InspectorClientBase;
495
496  fn run_message_loop_on_pause(&mut self, context_group_id: i32) {}
497  fn quit_message_loop_on_pause(&mut self) {}
498  fn run_if_waiting_for_debugger(&mut self, context_group_id: i32) {}
499
500  fn generate_unique_id(&mut self) -> i64 {
501    0 // 0 = let V8 pick a unique id itself
502  }
503
504  #[allow(clippy::too_many_arguments)]
505  fn console_api_message(
506    &mut self,
507    context_group_id: i32,
508    level: i32,
509    message: &StringView,
510    url: &StringView,
511    line_number: u32,
512    column_number: u32,
513    stack_trace: &mut V8StackTrace,
514  ) {
515  }
516}
517
518pub struct V8InspectorClientBase {
519  cxx_base: V8InspectorClient,
520  offset_within_embedder: FieldOffset<Self>,
521  rust_vtable: RustVTable<&'static dyn V8InspectorClientImpl>,
522}
523
524impl V8InspectorClientBase {
525  fn construct_cxx_base() -> V8InspectorClient {
526    unsafe {
527      let mut buf = std::mem::MaybeUninit::<V8InspectorClient>::uninit();
528      v8_inspector__V8InspectorClient__BASE__CONSTRUCT(&mut buf);
529      buf.assume_init()
530    }
531  }
532
533  fn get_cxx_base_offset() -> FieldOffset<V8InspectorClient> {
534    let buf = std::mem::MaybeUninit::<Self>::uninit();
535    FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_base })
536  }
537
538  fn get_offset_within_embedder<T>() -> FieldOffset<Self>
539  where
540    T: V8InspectorClientImpl,
541  {
542    let buf = std::mem::MaybeUninit::<T>::uninit();
543    let embedder_ptr: *const T = buf.as_ptr();
544    let self_ptr: *const Self = unsafe { (*embedder_ptr).base() };
545    FieldOffset::from_ptrs(embedder_ptr, self_ptr)
546  }
547
548  fn get_rust_vtable<T>() -> RustVTable<&'static dyn V8InspectorClientImpl>
549  where
550    T: V8InspectorClientImpl,
551  {
552    let buf = std::mem::MaybeUninit::<T>::uninit();
553    let embedder_ptr = buf.as_ptr();
554    let trait_object: *const dyn V8InspectorClientImpl = embedder_ptr;
555    let (data_ptr, vtable): (*const T, RustVTable<_>) =
556      unsafe { std::mem::transmute(trait_object) };
557    assert_eq!(data_ptr, embedder_ptr);
558    vtable
559  }
560
561  pub fn new<T>() -> Self
562  where
563    T: V8InspectorClientImpl,
564  {
565    Self {
566      cxx_base: Self::construct_cxx_base(),
567      offset_within_embedder: Self::get_offset_within_embedder::<T>(),
568      rust_vtable: Self::get_rust_vtable::<T>(),
569    }
570  }
571
572  pub unsafe fn dispatch(
573    client: &V8InspectorClient,
574  ) -> &dyn V8InspectorClientImpl {
575    let this = Self::get_cxx_base_offset().to_embedder::<Self>(client);
576    let embedder = this.offset_within_embedder.to_embedder::<Opaque>(this);
577    std::mem::transmute((embedder, this.rust_vtable))
578  }
579
580  pub unsafe fn dispatch_mut(
581    client: &mut V8InspectorClient,
582  ) -> &mut dyn V8InspectorClientImpl {
583    let this = Self::get_cxx_base_offset().to_embedder_mut::<Self>(client);
584    let vtable = this.rust_vtable;
585    let embedder = this.offset_within_embedder.to_embedder_mut::<Opaque>(this);
586    std::mem::transmute((embedder, vtable))
587  }
588}
589
590impl Debug for V8InspectorClientBase {
591  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
592    f.debug_struct("V8InspectorClientBase")
593      .field("cxx_base", &self.cxx_base)
594      .field("offset_within_embedder", &self.offset_within_embedder)
595      .finish()
596  }
597}
598
599#[repr(C)]
600#[derive(Debug)]
601pub struct V8InspectorSession(Opaque);
602
603impl V8InspectorSession {
604  pub fn can_dispatch_method(method: StringView) -> bool {
605    unsafe { v8_inspector__V8InspectorSession__canDispatchMethod(method) }
606  }
607
608  pub fn dispatch_protocol_message(&mut self, message: StringView) {
609    unsafe {
610      v8_inspector__V8InspectorSession__dispatchProtocolMessage(self, message)
611    }
612  }
613
614  pub fn schedule_pause_on_next_statement(
615    &mut self,
616    reason: StringView,
617    detail: StringView,
618  ) {
619    unsafe {
620      v8_inspector__V8InspectorSession__schedulePauseOnNextStatement(
621        self, reason, detail,
622      )
623    }
624  }
625}
626
627impl Drop for V8InspectorSession {
628  fn drop(&mut self) {
629    unsafe { v8_inspector__V8InspectorSession__DELETE(self) };
630  }
631}
632
633// TODO: in C++, this class is intended to be user-extensible, just like
634// like `Task`, `Client`, `Channel`. In Rust this would ideally also be the
635// case, but currently to obtain a `UniquePtr<StringBuffer>` is by making a
636// copy using `StringBuffer::create()`.
637#[repr(C)]
638#[derive(Debug)]
639pub struct StringBuffer {
640  _cxx_vtable: CxxVTable,
641}
642
643// TODO: make it possible to obtain a `UniquePtr<StringBuffer>` directly from
644// an owned `Vec<u8>` or `Vec<u16>`,
645impl StringBuffer {
646  // The C++ class definition does not declare `string()` to be a const method,
647  // therefore we declare self as mutable here.
648  // TODO: figure out whether it'd be safe to assume a const receiver here.
649  // That would make it possible to implement `Deref<Target = StringBuffer>`.
650  pub fn string(&self) -> StringView {
651    unsafe { v8_inspector__StringBuffer__string(self) }
652  }
653
654  /// This method copies contents.
655  pub fn create(source: StringView) -> UniquePtr<StringBuffer> {
656    unsafe { v8_inspector__StringBuffer__create(source) }
657  }
658}
659
660impl Drop for StringBuffer {
661  fn drop(&mut self) {
662    unsafe { v8_inspector__StringBuffer__DELETE(self) }
663  }
664}
665
666unsafe impl Send for StringBuffer {}
667use std::iter::ExactSizeIterator;
668use std::iter::IntoIterator;
669use std::marker::PhantomData;
670use std::ops::Deref;
671use std::ptr::null;
672use std::ptr::NonNull;
673use std::slice;
674use std::string;
675
676// Notes:
677//  * This class is ported, not wrapped using bindings.
678//  * Since Rust `repr(bool)` is not allowed, we're assuming that `bool` and
679//    `u8` have the same size. This is assumption is checked in 'support.h'.
680//    TODO: find/open upstream issue to allow #[repr(bool)] support.
681
682#[derive(Clone, Debug, Copy)]
683#[repr(u8)]
684pub enum StringView<'a> {
685  // Do not reorder!
686  U16(CharacterArray<'a, u16>),
687  U8(CharacterArray<'a, u8>),
688}
689
690impl StringView<'static> {
691  pub fn empty() -> Self {
692    Self::U8(CharacterArray::<'static, u8>::empty())
693  }
694}
695
696impl fmt::Display for StringView<'_> {
697  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
698    match self {
699      Self::U16(v) => write!(f, "{}", v),
700      Self::U8(v) => write!(f, "{}", v),
701    }
702  }
703}
704
705impl<'a> From<&'a [u8]> for StringView<'a> {
706  fn from(v: &'a [u8]) -> Self {
707    Self::U8(CharacterArray::<'a, u8>::from(v))
708  }
709}
710
711impl<'a> From<&'a [u16]> for StringView<'a> {
712  fn from(v: &'a [u16]) -> Self {
713    Self::U16(CharacterArray::<'a, u16>::from(v))
714  }
715}
716
717impl<'a> StringView<'a> {
718  pub fn is_8bit(&self) -> bool {
719    match self {
720      Self::U16(..) => false,
721      Self::U8(..) => true,
722    }
723  }
724
725  pub fn is_empty(&self) -> bool {
726    self.len() == 0
727  }
728
729  pub fn len(&self) -> usize {
730    match self {
731      Self::U16(v) => v.len(),
732      Self::U8(v) => v.len(),
733    }
734  }
735
736  pub fn characters8(&self) -> Option<&[u8]> {
737    match self {
738      Self::U16(..) => None,
739      Self::U8(v) => Some(v),
740    }
741  }
742
743  pub fn characters16(&self) -> Option<&[u16]> {
744    match self {
745      Self::U16(v) => Some(v),
746      Self::U8(..) => None,
747    }
748  }
749}
750
751impl<'a> IntoIterator for StringView<'a> {
752  type IntoIter = StringViewIterator<'a>;
753  type Item = u16;
754
755  fn into_iter(self) -> Self::IntoIter {
756    StringViewIterator { view: self, pos: 0 }
757  }
758}
759
760#[repr(C)]
761#[derive(Copy, Clone, Debug)]
762pub struct CharacterArray<'a, T> {
763  m_length: usize,
764  m_characters: *const T,
765  _phantom: PhantomData<&'a T>,
766}
767
768impl CharacterArray<'static, u8> {
769  pub fn empty() -> Self {
770    Self {
771      m_length: 0,
772      m_characters: null(),
773      _phantom: PhantomData,
774    }
775  }
776}
777
778impl<'a, T> CharacterArray<'a, T>
779where
780  T: Copy,
781{
782  #[inline(always)]
783  fn len(&self) -> usize {
784    self.m_length
785  }
786
787  #[inline(always)]
788  fn get_at(&self, index: usize) -> Option<T> {
789    if index < self.m_length {
790      Some(unsafe { *self.m_characters.add(index) })
791    } else {
792      None
793    }
794  }
795}
796
797unsafe impl<'a, T> Send for CharacterArray<'a, T> where T: Copy {}
798unsafe impl<'a, T> Sync for CharacterArray<'a, T> where T: Sync {}
799
800impl fmt::Display for CharacterArray<'_, u8> {
801  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
802    f.write_str(
803      self
804        .iter()
805        .cloned()
806        .map(char::from)
807        .collect::<string::String>()
808        .as_str(),
809    )
810  }
811}
812
813impl fmt::Display for CharacterArray<'_, u16> {
814  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
815    f.write_str(&string::String::from_utf16_lossy(&*self))
816  }
817}
818
819impl<'a, T> From<&'a [T]> for CharacterArray<'a, T> {
820  fn from(v: &'a [T]) -> Self {
821    Self {
822      m_length: v.len(),
823      m_characters: v.as_ptr(),
824      _phantom: PhantomData,
825    }
826  }
827}
828
829impl<'a, T> Deref for CharacterArray<'a, T> {
830  type Target = [T];
831
832  fn deref(&self) -> &[T] {
833    let Self {
834      m_length,
835      mut m_characters,
836      ..
837    } = *self;
838    if m_characters.is_null() {
839      assert_eq!(m_length, 0);
840      m_characters = NonNull::dangling().as_ptr()
841    };
842    unsafe { slice::from_raw_parts(m_characters, m_length) }
843  }
844}
845
846#[derive(Copy, Clone, Debug)]
847pub struct StringViewIterator<'a> {
848  view: StringView<'a>,
849  pos: usize,
850}
851
852impl<'a> Iterator for StringViewIterator<'a> {
853  type Item = u16;
854
855  fn next(&mut self) -> Option<Self::Item> {
856    let result = Some(match self.view {
857      StringView::U16(v) => v.get_at(self.pos)?,
858      StringView::U8(v) => u16::from(v.get_at(self.pos)?),
859    });
860    self.pos += 1;
861    result
862  }
863}
864
865impl<'a> ExactSizeIterator for StringViewIterator<'a> {
866  fn len(&self) -> usize {
867    self.view.len()
868  }
869}
870
871#[test]
872fn string_view_display() {
873  let ok: [u16; 2] = [111, 107];
874  assert_eq!("ok", format!("{}", StringView::from(&ok[..])));
875  assert_eq!("ok", format!("{}", StringView::from(&b"ok"[..])));
876  assert_eq!("ØÞ", format!("{}", StringView::from(&[216u8, 222u8][..])));
877}
878
879#[repr(C)]
880#[derive(Debug)]
881pub struct V8Inspector(Opaque);
882
883impl V8Inspector {
884  pub fn create<T>(
885    isolate: &mut Isolate,
886    client: &mut T,
887  ) -> UniqueRef<V8Inspector>
888  where
889    T: AsV8InspectorClient,
890  {
891    unsafe {
892      UniqueRef::from_raw(v8_inspector__V8Inspector__create(
893        isolate,
894        client.as_client_mut(),
895      ))
896    }
897  }
898
899  pub fn connect<T>(
900    &mut self,
901    context_group_id: i32,
902    channel: &mut T,
903    state: StringView,
904  ) -> UniqueRef<V8InspectorSession>
905  where
906    T: AsChannel,
907  {
908    unsafe {
909      UniqueRef::from_raw(v8_inspector__V8Inspector__connect(
910        self,
911        context_group_id,
912        channel.as_channel_mut(),
913        state,
914      ))
915    }
916  }
917
918  /// Note: this method deviates from the C++ API here because it's a lot of
919  /// work to bind the V8ContextInfo, which is not used elsewhere.
920  pub fn context_created(
921    &mut self,
922    context: Local<Context>,
923    context_group_id: i32,
924    human_readable_name: StringView,
925  ) {
926    unsafe {
927      v8_inspector__V8Inspector__contextCreated(
928        self,
929        &*context,
930        context_group_id,
931        human_readable_name,
932      )
933    }
934  }
935}
936
937impl Drop for V8Inspector {
938  fn drop(&mut self) {
939    unsafe { v8_inspector__V8Inspector__DELETE(self) };
940  }
941}
942
943#[repr(C)]
944#[derive(Debug)]
945pub struct V8StackTrace {
946  _cxx_vtable: CxxVTable,
947}
948
949// TODO(bnoordhuis) This needs to be fleshed out more but that can wait
950// until it's actually needed.