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