Skip to main content

v8/
wasm.rs

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