Skip to content

Commit

Permalink
feat: Add v8::CompiledWasmModule
Browse files Browse the repository at this point in the history
`v8::CompiledWasmModule` is a representation of a compiled WebAssembly
module, which can be shared by multiple `v8::WasmModuleObject`s.

Closes #759.
  • Loading branch information
Andreu Botella committed Sep 13, 2021
1 parent 674f444 commit 291ff09
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 0 deletions.
25 changes: 25 additions & 0 deletions src/binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2747,3 +2747,28 @@ bool v8__ValueDeserializer__ReadRawBytes(v8::ValueDeserializer* self,
return self->ReadRawBytes(length, data);
}
} // extern "C"

// v8::CompiledWasmModule

extern "C" {
const v8::WasmModuleObject* v8__WasmModuleObject__FromCompiledModule(v8::Isolate* isolate,
const v8::CompiledWasmModule* compiled_module) {
return maybe_local_to_ptr(v8::WasmModuleObject::FromCompiledModule(isolate, *compiled_module));
}

v8::CompiledWasmModule* v8__WasmModuleObject__GetCompiledModule(const v8::WasmModuleObject* self) {
v8::CompiledWasmModule cwm = ptr_to_local(self)->GetCompiledModule();
return new v8::CompiledWasmModule(std::move(cwm));
}

const uint8_t* v8__CompiledWasmModule__GetWireBytesRef(v8::CompiledWasmModule* self,
size_t* length) {
v8::MemorySpan<const uint8_t> span = self->GetWireBytesRef();
*length = span.size();
return span.data();
}

void v8__CompiledWasmModule__DELETE(v8::CompiledWasmModule* self) {
delete self;
}
} // extern "C"
1 change: 1 addition & 0 deletions src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,6 +1381,7 @@ impl_partial_eq! { Value for SymbolObject use identity }
impl_partial_eq! { Object for SymbolObject use identity }
impl_partial_eq! { SymbolObject for SymbolObject use identity }

/// An instance of WebAssembly.Module.
#[repr(C)]
#[derive(Debug)]
pub struct WasmModuleObject(Opaque);
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ pub use value_deserializer::ValueDeserializerImpl;
pub use value_serializer::ValueSerializer;
pub use value_serializer::ValueSerializerHelper;
pub use value_serializer::ValueSerializerImpl;
pub use wasm::CompiledWasmModule;
pub use wasm::WasmStreaming;

// TODO(piscisaureus): Ideally this trait would not be exported.
Expand Down
78 changes: 78 additions & 0 deletions src/wasm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ use crate::function::FunctionCallbackArguments;
use crate::function::FunctionCallbackInfo;
use crate::scope::CallbackScope;
use crate::scope::HandleScope;
use crate::support::Opaque;
use crate::support::UnitType;
use crate::Isolate;
use crate::Local;
use crate::Value;
use crate::WasmModuleObject;
use std::ptr::null;
use std::ptr::null_mut;

Expand Down Expand Up @@ -62,6 +64,68 @@ impl Drop for WasmStreaming {
}
}

impl WasmModuleObject {
/**
* Efficiently re-create a WasmModuleObject, without recompiling, from
* a CompiledWasmModule.
*/
pub fn from_compiled_module<'s>(
scope: &mut HandleScope<'s>,
compiled_module: &CompiledWasmModule,
) -> Option<Local<'s, WasmModuleObject>> {
unsafe {
scope.cast_local(|sd| {
v8__WasmModuleObject__FromCompiledModule(
sd.get_isolate_ptr(),
compiled_module.0,
)
})
}
}

/**
* Get the compiled module for this module object. The compiled module can be
* shared by several module objects.
*/
pub fn get_compiled_module(&self) -> CompiledWasmModule {
let ptr = unsafe { v8__WasmModuleObject__GetCompiledModule(self) };
CompiledWasmModule(ptr)
}
}

// Type-erased v8::CompiledWasmModule. We need this because the C++
// v8::CompiledWasmModule must be destructed because its private fields hold
// pointers that must be freed, but v8::CompiledWasmModule itself doesn't have
// a destructor. Therefore, in order to avoid memory leaks, the Rust-side
// CompiledWasmModule must be a pointer to a C++ allocation of
// v8::CompiledWasmModule.
#[repr(C)]
struct InternalCompiledWasmModule(Opaque);

/// Wrapper around a compiled WebAssembly module, which is potentially shared by
/// different WasmModuleObjects.
pub struct CompiledWasmModule(*mut InternalCompiledWasmModule);

impl CompiledWasmModule {
/**
* Get the (wasm-encoded) wire bytes that were used to compile this module.
*/
pub fn get_wire_bytes_ref(&self) -> &[u8] {
use std::convert::TryInto;
let mut len = 0isize;
unsafe {
let ptr = v8__CompiledWasmModule__GetWireBytesRef(self.0, &mut len);
std::slice::from_raw_parts(ptr, len.try_into().unwrap())
}
}
}

impl Drop for CompiledWasmModule {
fn drop(&mut self) {
unsafe { v8__CompiledWasmModule__DELETE(self.0) }
}
}

pub(crate) fn trampoline<F>() -> extern "C" fn(*const FunctionCallbackInfo)
where
F: UnitType + Fn(&mut HandleScope, Local<Value>, WasmStreaming),
Expand Down Expand Up @@ -102,4 +166,18 @@ extern "C" {
this: *mut WasmStreamingSharedPtr,
exception: *const Value,
);

fn v8__WasmModuleObject__FromCompiledModule(
isolate: *mut Isolate,
compiled_module: *const InternalCompiledWasmModule,
) -> *const WasmModuleObject;
fn v8__WasmModuleObject__GetCompiledModule(
this: *const WasmModuleObject,
) -> *mut InternalCompiledWasmModule;

fn v8__CompiledWasmModule__GetWireBytesRef(
this: *mut InternalCompiledWasmModule,
length: *mut isize,
) -> *const u8;
fn v8__CompiledWasmModule__DELETE(this: *mut InternalCompiledWasmModule);
}
62 changes: 62 additions & 0 deletions tests/test_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5408,3 +5408,65 @@ fn counter_lookup_callback() {

assert_ne!(count, 0);
}

#[test]
fn compiled_wasm_module() {
let _setup_guard = setup();

let compiled_module = {
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let module: v8::Local<v8::WasmModuleObject> = eval(
scope,
r#"
new WebAssembly.Module(Uint8Array.from([
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00,
0x00, 0x07, 0x03, 0x66, 0x6F, 0x6F, 0x62, 0x61, 0x72
]));
"#,
)
.unwrap()
.try_into()
.unwrap();

module.get_compiled_module()
};

assert_eq!(
compiled_module.get_wire_bytes_ref(),
&[
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x66,
0x6F, 0x6F, 0x62, 0x61, 0x72
]
);

{
let isolate = &mut v8::Isolate::new(Default::default());
let scope = &mut v8::HandleScope::new(isolate);
let context = v8::Context::new(scope);
let scope = &mut v8::ContextScope::new(scope, context);

let global = context.global(scope);

let module =
v8::WasmModuleObject::from_compiled_module(scope, &compiled_module)
.unwrap();

let key = v8::String::new(scope, "module").unwrap().into();
global.set(scope, key, module.into());

let foo_ab: v8::Local<v8::ArrayBuffer> =
eval(scope, "WebAssembly.Module.customSections(module, 'foo')[0]")
.unwrap()
.try_into()
.unwrap();
let foo_bs = foo_ab.get_backing_store();
let foo_section = unsafe {
std::slice::from_raw_parts(foo_bs.data() as *mut u8, foo_bs.byte_length())
};
assert_eq!(foo_section, b"bar");
}
}

0 comments on commit 291ff09

Please sign in to comment.