rusty_v8 0.6.0

Rust bindings to V8
Documentation
// 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::support::int;
use crate::support::CxxVTable;
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<Channel>,
  );

  fn v8_inspector__V8Inspector__Channel__sendResponse(
    this: &mut Channel,
    call_id: int,
    message: UniquePtr<StringBuffer>,
  );
  fn v8_inspector__V8Inspector__Channel__sendNotification(
    this: &mut Channel,
    message: UniquePtr<StringBuffer>,
  );
  fn v8_inspector__V8Inspector__Channel__flushProtocolNotifications(
    this: &mut Channel,
  );

  fn v8_inspector__V8InspectorClient__BASE__CONSTRUCT(
    buf: &mut std::mem::MaybeUninit<V8InspectorClient>,
  );

  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: &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: &mut StringBuffer);
  fn v8_inspector__StringBuffer__string(this: &StringBuffer) -> StringView;
  fn v8_inspector__StringBuffer__create(
    source: StringView,
  ) -> UniquePtr<StringBuffer>;

  fn v8_inspector__V8Inspector__DELETE(this: &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: StringView,
  ) -> *mut V8InspectorSession;
  fn v8_inspector__V8Inspector__contextCreated(
    this: *mut V8Inspector,
    context: *const Context,
    contextGroupId: int,
    humanReadableName: StringView,
  );
}

#[no_mangle]
pub unsafe extern "C" fn v8_inspector__V8Inspector__Channel__BASE__sendResponse(
  this: &mut Channel,
  call_id: int,
  message: UniquePtr<StringBuffer>,
) {
  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<StringBuffer>,
) {
  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<StringBuffer>,
  ) {
    unsafe {
      v8_inspector__V8Inspector__Channel__sendResponse(self, call_id, message)
    }
  }
  pub fn send_notification(&mut self, message: UniquePtr<StringBuffer>) {
    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<T> 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<StringBuffer>);
  fn send_notification(&mut self, message: UniquePtr<StringBuffer>);
  fn flush_protocol_notifications(&mut self);
}

pub struct ChannelBase {
  cxx_base: Channel,
  offset_within_embedder: FieldOffset<Self>,
  rust_vtable: RustVTable<&'static dyn ChannelImpl>,
}

impl ChannelBase {
  fn construct_cxx_base() -> Channel {
    unsafe {
      let mut buf = std::mem::MaybeUninit::<Channel>::uninit();
      v8_inspector__V8Inspector__Channel__BASE__CONSTRUCT(&mut buf);
      buf.assume_init()
    }
  }

  fn get_cxx_base_offset() -> FieldOffset<Channel> {
    let buf = std::mem::MaybeUninit::<Self>::uninit();
    FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_base })
  }

  fn get_offset_within_embedder<T>() -> FieldOffset<Self>
  where
    T: ChannelImpl,
  {
    let buf = std::mem::MaybeUninit::<T>::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<T>() -> RustVTable<&'static dyn ChannelImpl>
  where
    T: ChannelImpl,
  {
    let buf = std::mem::MaybeUninit::<T>::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<T>() -> Self
  where
    T: ChannelImpl,
  {
    Self {
      cxx_base: Self::construct_cxx_base(),
      offset_within_embedder: Self::get_offset_within_embedder::<T>(),
      rust_vtable: Self::get_rust_vtable::<T>(),
    }
  }

  pub unsafe fn dispatch(channel: &Channel) -> &dyn ChannelImpl {
    let this = Self::get_cxx_base_offset().to_embedder::<Self>(channel);
    let embedder = this.offset_within_embedder.to_embedder::<Opaque>(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::<Self>(channel);
    let vtable = this.rust_vtable;
    let embedder = this.offset_within_embedder.to_embedder_mut::<Opaque>(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<StringBuffer>,
    ) {
      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<StringBuffer>) {
      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::<Self>(),
        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<T> 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<Self>,
  rust_vtable: RustVTable<&'static dyn V8InspectorClientImpl>,
}

impl V8InspectorClientBase {
  fn construct_cxx_base() -> V8InspectorClient {
    unsafe {
      let mut buf = std::mem::MaybeUninit::<V8InspectorClient>::uninit();
      v8_inspector__V8InspectorClient__BASE__CONSTRUCT(&mut buf);
      buf.assume_init()
    }
  }

  fn get_cxx_base_offset() -> FieldOffset<V8InspectorClient> {
    let buf = std::mem::MaybeUninit::<Self>::uninit();
    FieldOffset::from_ptrs(buf.as_ptr(), unsafe { &(*buf.as_ptr()).cxx_base })
  }

  fn get_offset_within_embedder<T>() -> FieldOffset<Self>
  where
    T: V8InspectorClientImpl,
  {
    let buf = std::mem::MaybeUninit::<T>::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<T>() -> RustVTable<&'static dyn V8InspectorClientImpl>
  where
    T: V8InspectorClientImpl,
  {
    let buf = std::mem::MaybeUninit::<T>::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<T>() -> Self
  where
    T: V8InspectorClientImpl,
  {
    Self {
      cxx_base: Self::construct_cxx_base(),
      offset_within_embedder: Self::get_offset_within_embedder::<T>(),
      rust_vtable: Self::get_rust_vtable::<T>(),
    }
  }

  pub unsafe fn dispatch(
    client: &V8InspectorClient,
  ) -> &dyn V8InspectorClientImpl {
    let this = Self::get_cxx_base_offset().to_embedder::<Self>(client);
    let embedder = this.offset_within_embedder.to_embedder::<Opaque>(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::<Self>(client);
    let vtable = this.rust_vtable;
    let embedder = this.offset_within_embedder.to_embedder_mut::<Opaque>(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 Drop for V8InspectorSession {
  fn drop(&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<StringBuffer>` is by making a
// copy using `StringBuffer::create()`.
#[repr(C)]
pub struct StringBuffer {
  _cxx_vtable: CxxVTable,
}

// TODO: make it possible to obtain a `UniquePtr<StringBuffer>` directly from
// an owned `Vec<u8>` or `Vec<u16>`,
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<Target = StringBuffer>`.
  pub fn string(&self) -> StringView {
    unsafe { v8_inspector__StringBuffer__string(self) }
  }

  /// This method copies contents.
  pub fn create(source: StringView) -> UniquePtr<StringBuffer> {
    unsafe { v8_inspector__StringBuffer__create(source) }
  }
}

impl Drop for StringBuffer {
  fn drop(&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.

#[derive(Clone, Copy)]
#[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> IntoIterator for StringView<'a> {
  type IntoIter = StringViewIterator<'a>;
  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<T> {
    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::<string::String>()
        .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> {
  view: StringView<'a>,
  pos: usize,
}

impl<'a> Iterator for StringViewIterator<'a> {
  type Item = u16;

  fn next(&mut self) -> Option<Self::Item> {
    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> ExactSizeIterator for StringViewIterator<'a> {
  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<T>(
    isolate: &mut Isolate,
    client: &mut T,
  ) -> UniqueRef<V8Inspector>
  where
    T: AsV8InspectorClient,
  {
    unsafe {
      UniqueRef::from_raw(v8_inspector__V8Inspector__create(
        isolate,
        client.as_client_mut(),
      ))
    }
  }

  pub fn connect<T>(
    &mut self,
    context_group_id: i32,
    channel: &mut T,
    state: StringView,
  ) -> UniqueRef<V8InspectorSession>
  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,
    context: Local<Context>,
    context_group_id: i32,
    human_readable_name: StringView,
  ) {
    unsafe {
      v8_inspector__V8Inspector__contextCreated(
        self,
        &*context,
        context_group_id,
        human_readable_name,
      )
    }
  }
}

impl Drop for V8Inspector {
  fn drop(&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.