rusty_v8/
wasm.rs

1// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
2
3use crate::function::FunctionCallbackArguments;
4use crate::function::FunctionCallbackInfo;
5use crate::scope::CallbackScope;
6use crate::scope::HandleScope;
7use crate::support::char;
8use crate::support::Opaque;
9use crate::support::UnitType;
10use crate::Isolate;
11use crate::Local;
12use crate::Value;
13use crate::WasmModuleObject;
14use std::ptr::null;
15use std::ptr::null_mut;
16
17// Type-erased std::shared_ptr<v8::WasmStreaming>. Assumes it's safe
18// to move around (no backlinks). Not generally true for shared_ptrs
19// but it is in this case - other shared_ptrs that point to the same
20// v8::WasmStreaming exist but are managed by V8 and don't leak out.
21//
22// We don't use crate::support::SharedPtr because it only allows
23// immutable borrows and derefs to avoid aliasing but that's not
24// a problem here, only a single instance outside V8 exists.
25//
26// Note: uses *mut u8 rather than e.g. usize to enforce !Send and !Sync.
27#[repr(C)]
28struct WasmStreamingSharedPtr([*mut u8; 2]);
29
30/// The V8 interface for WebAssembly streaming compilation.
31/// When streaming compilation is initiated, V8 passes a [Self]
32/// object to the embedder such that the embedder can pass the
33/// input bytes for streaming compilation to V8.
34#[repr(C)]
35pub struct WasmStreaming(WasmStreamingSharedPtr);
36
37impl WasmStreaming {
38  /// Pass a new chunk of bytes to WebAssembly streaming compilation.
39  pub fn on_bytes_received(&mut self, data: &[u8]) {
40    unsafe {
41      v8__WasmStreaming__OnBytesReceived(&mut self.0, data.as_ptr(), data.len())
42    }
43  }
44
45  /// Should be called after all received bytes where passed to
46  /// [`Self::on_bytes_received()`] to tell V8 that there will be no
47  /// more bytes. Does not have to be called after [`Self::abort()`]
48  /// has been called already.
49  pub fn finish(mut self) {
50    unsafe { v8__WasmStreaming__Finish(&mut self.0) }
51  }
52
53  /// Abort streaming compilation. If {exception} has a value, then the promise
54  /// associated with streaming compilation is rejected with that value. If
55  /// {exception} does not have value, the promise does not get rejected.
56  pub fn abort(mut self, exception: Option<Local<Value>>) {
57    let exception = exception.map(|v| &*v as *const Value).unwrap_or(null());
58    unsafe { v8__WasmStreaming__Abort(&mut self.0, exception) }
59  }
60
61  /// Sets the UTF-8 encoded source URL for the `Script` object. This must be
62  /// called before [`Self::finish()`].
63  pub fn set_url(&mut self, url: &str) {
64    unsafe {
65      v8__WasmStreaming__SetUrl(
66        &mut self.0,
67        url.as_ptr() as *const char,
68        url.len(),
69      )
70    }
71  }
72}
73
74impl Drop for WasmStreaming {
75  fn drop(&mut self) {
76    unsafe { v8__WasmStreaming__shared_ptr_DESTRUCT(&mut self.0) }
77  }
78}
79
80impl WasmModuleObject {
81  /// Efficiently re-create a WasmModuleObject, without recompiling, from
82  /// a CompiledWasmModule.
83  pub fn from_compiled_module<'s>(
84    scope: &mut HandleScope<'s>,
85    compiled_module: &CompiledWasmModule,
86  ) -> Option<Local<'s, WasmModuleObject>> {
87    unsafe {
88      scope.cast_local(|sd| {
89        v8__WasmModuleObject__FromCompiledModule(
90          sd.get_isolate_ptr(),
91          compiled_module.0,
92        )
93      })
94    }
95  }
96
97  /// Get the compiled module for this module object. The compiled module can be
98  /// shared by several module objects.
99  pub fn get_compiled_module(&self) -> CompiledWasmModule {
100    let ptr = unsafe { v8__WasmModuleObject__GetCompiledModule(self) };
101    CompiledWasmModule(ptr)
102  }
103}
104
105// Type-erased v8::CompiledWasmModule. We need this because the C++
106// v8::CompiledWasmModule must be destructed because its private fields hold
107// pointers that must be freed, but v8::CompiledWasmModule itself doesn't have
108// a destructor. Therefore, in order to avoid memory leaks, the Rust-side
109// CompiledWasmModule must be a pointer to a C++ allocation of
110// v8::CompiledWasmModule.
111#[repr(C)]
112struct InternalCompiledWasmModule(Opaque);
113
114/// Wrapper around a compiled WebAssembly module, which is potentially shared by
115/// different WasmModuleObjects.
116pub struct CompiledWasmModule(*mut InternalCompiledWasmModule);
117
118impl CompiledWasmModule {
119  /// Get the (wasm-encoded) wire bytes that were used to compile this module.
120  pub fn get_wire_bytes_ref(&self) -> &[u8] {
121    use std::convert::TryInto;
122    let mut len = 0isize;
123    unsafe {
124      let ptr = v8__CompiledWasmModule__GetWireBytesRef(self.0, &mut len);
125      std::slice::from_raw_parts(ptr, len.try_into().unwrap())
126    }
127  }
128
129  pub fn source_url(&self) -> &str {
130    let mut len = 0;
131    unsafe {
132      let ptr = v8__CompiledWasmModule__SourceUrl(self.0, &mut len);
133      let bytes = std::slice::from_raw_parts(ptr as *const u8, len);
134      std::str::from_utf8_unchecked(bytes)
135    }
136  }
137}
138
139// TODO(andreubotella): Safety???
140unsafe impl Send for CompiledWasmModule {}
141unsafe impl Sync for CompiledWasmModule {}
142
143impl Drop for CompiledWasmModule {
144  fn drop(&mut self) {
145    unsafe { v8__CompiledWasmModule__DELETE(self.0) }
146  }
147}
148
149pub(crate) fn trampoline<F>() -> extern "C" fn(*const FunctionCallbackInfo)
150where
151  F: UnitType + Fn(&mut HandleScope, Local<Value>, WasmStreaming),
152{
153  extern "C" fn c_fn<F>(info: *const FunctionCallbackInfo)
154  where
155    F: UnitType + Fn(&mut HandleScope, Local<Value>, WasmStreaming),
156  {
157    let scope = &mut unsafe { CallbackScope::new(&*info) };
158    let args = FunctionCallbackArguments::from_function_callback_info(info);
159    let data = args.data().unwrap(); // Always present.
160    let data = &*data as *const Value;
161    let zero = null_mut();
162    let mut that = WasmStreamingSharedPtr([zero, zero]);
163    unsafe {
164      v8__WasmStreaming__Unpack(scope.get_isolate_ptr(), data, &mut that)
165    };
166    let source = args.get(0);
167    (F::get())(scope, source, WasmStreaming(that));
168  }
169  c_fn::<F>
170}
171
172extern "C" {
173  fn v8__WasmStreaming__Unpack(
174    isolate: *mut Isolate,
175    value: *const Value,
176    that: *mut WasmStreamingSharedPtr, // Out parameter.
177  );
178  fn v8__WasmStreaming__shared_ptr_DESTRUCT(this: *mut WasmStreamingSharedPtr);
179  fn v8__WasmStreaming__OnBytesReceived(
180    this: *mut WasmStreamingSharedPtr,
181    data: *const u8,
182    len: usize,
183  );
184  fn v8__WasmStreaming__Finish(this: *mut WasmStreamingSharedPtr);
185  fn v8__WasmStreaming__Abort(
186    this: *mut WasmStreamingSharedPtr,
187    exception: *const Value,
188  );
189  fn v8__WasmStreaming__SetUrl(
190    this: *mut WasmStreamingSharedPtr,
191    url: *const char,
192    len: usize,
193  );
194
195  fn v8__WasmModuleObject__FromCompiledModule(
196    isolate: *mut Isolate,
197    compiled_module: *const InternalCompiledWasmModule,
198  ) -> *const WasmModuleObject;
199  fn v8__WasmModuleObject__GetCompiledModule(
200    this: *const WasmModuleObject,
201  ) -> *mut InternalCompiledWasmModule;
202
203  fn v8__CompiledWasmModule__GetWireBytesRef(
204    this: *mut InternalCompiledWasmModule,
205    length: *mut isize,
206  ) -> *const u8;
207  fn v8__CompiledWasmModule__SourceUrl(
208    this: *mut InternalCompiledWasmModule,
209    length: *mut usize,
210  ) -> *const char;
211  fn v8__CompiledWasmModule__DELETE(this: *mut InternalCompiledWasmModule);
212}