11// Copyright 2019-2021 the Deno authors. All rights reserved. MIT license.
22
3+ use std:: ffi:: c_void;
4+ use std:: ptr:: null;
5+ use std:: ptr:: null_mut;
6+
37use crate :: ArrayBuffer ;
8+ use crate :: Isolate ;
49use crate :: Local ;
510use crate :: PinScope ;
611use crate :: Value ;
@@ -18,8 +23,6 @@ use crate::support::Opaque;
1823use crate :: support:: ToCFn ;
1924use crate :: support:: UnitType ;
2025use crate :: support:: char;
21- use std:: ptr:: null;
22- use std:: ptr:: null_mut;
2326
2427// Type-erased std::shared_ptr<v8::WasmStreaming>. Assumes it's safe
2528// to move around (no backlinks). Not generally true for shared_ptrs
@@ -275,6 +278,209 @@ impl Drop for CompiledWasmModule {
275278 }
276279}
277280
281+ // Type-erased v8::WasmModuleCompilation allocated on the C++ heap.
282+ #[ repr( C ) ]
283+ struct InternalWasmModuleCompilation ( Opaque ) ;
284+
285+ /// An interface for asynchronous WebAssembly module compilation, to be used
286+ /// e.g. for implementing source phase imports.
287+ ///
288+ /// Note: This interface is experimental and can change or be removed without
289+ /// notice.
290+ pub struct WasmModuleCompilation ( * mut InternalWasmModuleCompilation ) ;
291+
292+ // OnBytesReceived can be called from any thread per V8 documentation.
293+ unsafe impl Send for WasmModuleCompilation { }
294+
295+ impl WasmModuleCompilation {
296+ /// Start an asynchronous module compilation. This can be called on any
297+ /// thread.
298+ #[ inline( always) ]
299+ pub fn new ( ) -> Self {
300+ unsafe { WasmModuleCompilation ( v8__WasmModuleCompilation__NEW ( ) ) }
301+ }
302+
303+ /// Pass a new chunk of bytes to WebAssembly compilation. The buffer is
304+ /// owned by the caller and will not be accessed after this call returns.
305+ /// Can be called from any thread.
306+ #[ inline( always) ]
307+ pub fn on_bytes_received ( & mut self , data : & [ u8 ] ) {
308+ unsafe {
309+ v8__WasmModuleCompilation__OnBytesReceived (
310+ self . 0 ,
311+ data. as_ptr ( ) ,
312+ data. len ( ) ,
313+ ) ;
314+ }
315+ }
316+
317+ /// Finish compilation. Must be called on the main thread after all bytes
318+ /// were passed to [`Self::on_bytes_received`].
319+ ///
320+ /// The `resolution_callback` will eventually be called with either the
321+ /// compiled module or a compilation error. The callback receives `&Isolate`
322+ /// so that [`crate::Global`] handles can be created from the [`Local`]
323+ /// handles to persist them beyond the callback.
324+ ///
325+ /// Must not be called after [`Self::abort`].
326+ #[ inline( always) ]
327+ pub fn finish (
328+ self ,
329+ scope : & mut PinScope ,
330+ caching_callback : Option < ModuleCachingCallback > ,
331+ resolution_callback : impl FnOnce (
332+ & Isolate ,
333+ Result < Local < ' _ , WasmModuleObject > , Local < ' _ , Value > > ,
334+ ) + ' static ,
335+ ) {
336+ // Capture the isolate pointer in the closure so it doesn't need to be
337+ // threaded through C++.
338+ let isolate_ptr = scope. get_isolate_ptr ( ) ;
339+ let wrapped = move |module : * const WasmModuleObject ,
340+ error : * const Value | {
341+ let isolate = unsafe { Isolate :: from_raw_ptr ( isolate_ptr) } ;
342+ if !module. is_null ( ) {
343+ resolution_callback (
344+ & isolate,
345+ Ok ( unsafe { Local :: from_raw ( module) } . unwrap ( ) ) ,
346+ ) ;
347+ } else {
348+ resolution_callback (
349+ & isolate,
350+ Err ( unsafe { Local :: from_raw ( error) } . unwrap ( ) ) ,
351+ ) ;
352+ }
353+ } ;
354+
355+ // Double-box with Option: the outer Box gives us a thin pointer suitable
356+ // for void*. The Option allows the trampoline to .take() the closure
357+ // (FnOnce semantics) without freeing the outer allocation, which is
358+ // ref-counted by shared_ptr on the C++ side.
359+ #[ allow( clippy:: type_complexity) ]
360+ let boxed: Box <
361+ Option < Box < dyn FnOnce ( * const WasmModuleObject , * const Value ) > > ,
362+ > = Box :: new ( Some ( Box :: new ( wrapped) ) ) ;
363+ let data = Box :: into_raw ( boxed) as * mut c_void ;
364+
365+ unsafe {
366+ v8__WasmModuleCompilation__Finish (
367+ self . 0 ,
368+ scope. get_isolate_ptr ( ) ,
369+ caching_callback,
370+ resolution_trampoline,
371+ data,
372+ drop_resolution_data,
373+ ) ;
374+ }
375+ }
376+
377+ /// Abort compilation. Can be called from any thread.
378+ /// Must not be called repeatedly, or after [`Self::finish`].
379+ #[ inline( always) ]
380+ pub fn abort ( self ) {
381+ unsafe { v8__WasmModuleCompilation__Abort ( self . 0 ) }
382+ }
383+
384+ /// Mark that the embedder has (potentially) cached compiled module bytes
385+ /// (i.e. a serialized [`CompiledWasmModule`]) that could match this
386+ /// compilation request. This will cause V8 to skip streaming compilation.
387+ /// The embedder should then pass a caching callback to [`Self::finish`].
388+ #[ inline( always) ]
389+ pub fn set_has_compiled_module_bytes ( & mut self ) {
390+ unsafe {
391+ v8__WasmModuleCompilation__SetHasCompiledModuleBytes ( self . 0 ) ;
392+ }
393+ }
394+
395+ /// Sets a callback which is called whenever a significant number of new
396+ /// functions are ready for serialization.
397+ #[ inline( always) ]
398+ pub fn set_more_functions_can_be_serialized_callback (
399+ & mut self ,
400+ callback : impl Fn ( CompiledWasmModule ) + Send + ' static ,
401+ ) {
402+ let boxed: Box < Box < dyn Fn ( CompiledWasmModule ) + Send > > =
403+ Box :: new ( Box :: new ( callback) ) ;
404+ let data = Box :: into_raw ( boxed) as * mut c_void ;
405+
406+ unsafe {
407+ v8__WasmModuleCompilation__SetMoreFunctionsCanBeSerializedCallback (
408+ self . 0 ,
409+ serialization_trampoline,
410+ data,
411+ drop_serialization_data,
412+ ) ;
413+ }
414+ }
415+
416+ /// Sets the UTF-8 encoded source URL for the `Script` object. This must
417+ /// be called before [`Self::finish`].
418+ #[ inline( always) ]
419+ pub fn set_url ( & mut self , url : & str ) {
420+ // V8 requires the url to be null terminated.
421+ let null_terminated_url = format ! ( "{url}\0 " ) ;
422+ unsafe {
423+ v8__WasmModuleCompilation__SetUrl (
424+ self . 0 ,
425+ null_terminated_url. as_ptr ( ) as * const char ,
426+ url. len ( ) ,
427+ ) ;
428+ }
429+ }
430+ }
431+
432+ impl Default for WasmModuleCompilation {
433+ fn default ( ) -> Self {
434+ Self :: new ( )
435+ }
436+ }
437+
438+ impl Drop for WasmModuleCompilation {
439+ fn drop ( & mut self ) {
440+ unsafe { v8__WasmModuleCompilation__DELETE ( self . 0 ) }
441+ }
442+ }
443+
444+ unsafe extern "C" fn resolution_trampoline (
445+ data : * mut c_void ,
446+ module : * const WasmModuleObject ,
447+ error : * const Value ,
448+ ) {
449+ // Take the closure out of the Option without freeing the outer Box.
450+ // The outer Box is ref-counted by shared_ptr on the C++ side and will
451+ // be freed via drop_resolution_data when the last copy is destroyed.
452+ let slot = unsafe {
453+ & mut * ( data
454+ as * mut Option < Box < dyn FnOnce ( * const WasmModuleObject , * const Value ) > > )
455+ } ;
456+ let callback = slot. take ( ) . unwrap ( ) ;
457+ callback ( module, error) ;
458+ }
459+
460+ unsafe extern "C" fn drop_resolution_data ( data : * mut c_void ) {
461+ let _ = unsafe {
462+ Box :: from_raw (
463+ data
464+ as * mut Option < Box < dyn FnOnce ( * const WasmModuleObject , * const Value ) > > ,
465+ )
466+ } ;
467+ }
468+
469+ unsafe extern "C" fn serialization_trampoline (
470+ data : * mut c_void ,
471+ compiled_module : * mut InternalCompiledWasmModule ,
472+ ) {
473+ let callback =
474+ unsafe { & * * ( data as * const Box < dyn Fn ( CompiledWasmModule ) + Send > ) } ;
475+ callback ( CompiledWasmModule ( compiled_module) ) ;
476+ }
477+
478+ unsafe extern "C" fn drop_serialization_data ( data : * mut c_void ) {
479+ let _ = unsafe {
480+ Box :: from_raw ( data as * mut Box < dyn Fn ( CompiledWasmModule ) + Send > )
481+ } ;
482+ }
483+
278484impl WasmMemoryObject {
279485 /// Returns underlying ArrayBuffer.
280486 #[ inline( always) ]
@@ -380,4 +586,44 @@ unsafe extern "C" {
380586 fn v8__WasmMemoryObject__Buffer (
381587 this : * const WasmMemoryObject ,
382588 ) -> * mut ArrayBuffer ;
589+
590+ fn v8__WasmModuleCompilation__NEW ( ) -> * mut InternalWasmModuleCompilation ;
591+ fn v8__WasmModuleCompilation__DELETE (
592+ this : * mut InternalWasmModuleCompilation ,
593+ ) ;
594+ fn v8__WasmModuleCompilation__OnBytesReceived (
595+ this : * mut InternalWasmModuleCompilation ,
596+ bytes : * const u8 ,
597+ size : usize ,
598+ ) ;
599+ fn v8__WasmModuleCompilation__Finish (
600+ this : * mut InternalWasmModuleCompilation ,
601+ isolate : * mut RealIsolate ,
602+ caching_callback : Option < ModuleCachingCallback > ,
603+ resolution_callback : unsafe extern "C" fn (
604+ * mut c_void ,
605+ * const WasmModuleObject ,
606+ * const Value ,
607+ ) ,
608+ resolution_data : * mut c_void ,
609+ drop_resolution_data : unsafe extern "C" fn ( * mut c_void ) ,
610+ ) ;
611+ fn v8__WasmModuleCompilation__Abort ( this : * mut InternalWasmModuleCompilation ) ;
612+ fn v8__WasmModuleCompilation__SetHasCompiledModuleBytes (
613+ this : * mut InternalWasmModuleCompilation ,
614+ ) ;
615+ fn v8__WasmModuleCompilation__SetMoreFunctionsCanBeSerializedCallback (
616+ this : * mut InternalWasmModuleCompilation ,
617+ callback : unsafe extern "C" fn (
618+ * mut c_void ,
619+ * mut InternalCompiledWasmModule ,
620+ ) ,
621+ data : * mut c_void ,
622+ drop_data : unsafe extern "C" fn ( * mut c_void ) ,
623+ ) ;
624+ fn v8__WasmModuleCompilation__SetUrl (
625+ this : * mut InternalWasmModuleCompilation ,
626+ url : * const char ,
627+ length : usize ,
628+ ) ;
383629}
0 commit comments