diff --git a/wasmer/bridge.go b/wasmer/bridge.go index 0e6da7d5..db25b885 100644 --- a/wasmer/bridge.go +++ b/wasmer/bridge.go @@ -26,6 +26,7 @@ type cWasmerInstanceT C.wasmer_instance_t type cWasmerMemoryT C.wasmer_memory_t type cWasmerModuleT C.wasmer_module_t type cWasmerResultT C.wasmer_result_t +type cWasmerSerializedModuleT C.wasmer_serialized_module_t type cWasmerValueT C.wasmer_value_t type cWasmerValueTag C.wasmer_value_tag @@ -167,6 +168,28 @@ func cWasmerModuleInstantiate(module *cWasmerModuleT, instance **cWasmerInstance return cWasmerResultT(C.wasmer_module_instantiate((*C.wasmer_module_t)(module), (**C.wasmer_instance_t)(unsafe.Pointer(instance)), (*C.wasmer_import_t)(imports), (C.int)(importsLength))) } +func cWasmerModuleSerialize(serializedModule **cWasmerSerializedModuleT, module *cWasmerModuleT) cWasmerResultT { + return cWasmerResultT(C.wasmer_module_serialize((**C.wasmer_serialized_module_t)(unsafe.Pointer(serializedModule)), (*C.wasmer_module_t)(module))) +} + +func cWasmerModuleDeserialize(module **cWasmerModuleT, serializedModule *cWasmerSerializedModuleT) cWasmerResultT { + return cWasmerResultT(C.wasmer_module_deserialize((**C.wasmer_module_t)(unsafe.Pointer(module)), (*C.wasmer_serialized_module_t)(serializedModule))) +} + +func cWasmerSerializedModuleBytes(serializedModule *cWasmerSerializedModuleT) []byte { + var byteArray = C.wasmer_serialized_module_bytes((*C.wasmer_serialized_module_t)(serializedModule)) + + return C.GoBytes(unsafe.Pointer(byteArray.bytes), (C.int)(byteArray.bytes_len)) +} + +func cWasmerSerializedModuleFromBytes(serializedModule **cWasmerSerializedModuleT, serializedModuleBytes *cUint8T, serializedModuleBytesLength cInt) cWasmerResultT { + return cWasmerResultT(C.wasmer_serialized_module_from_bytes((**C.wasmer_serialized_module_t)(unsafe.Pointer(serializedModule)), (*C.uint8_t)(serializedModuleBytes), (C.uint)(serializedModuleBytesLength))) +} + +func cWasmerSerializedModuleDestroy(serializedModule *cWasmerSerializedModuleT) { + C.wasmer_serialized_module_destroy((*C.wasmer_serialized_module_t)(serializedModule)) +} + func cGoString(string *cChar) string { return C.GoString((*C.char)(string)) } diff --git a/wasmer/example_test.go b/wasmer/example_test.go index d98fcdcf..3b3db1bb 100644 --- a/wasmer/example_test.go +++ b/wasmer/example_test.go @@ -65,6 +65,41 @@ func ExampleModule_Instantiate() { // 3 } +func ExampleModule_Serialize() { + // Compiles the bytes into a WebAssembly module. + module1, _ := wasm.Compile(GetBytes()) + defer module1.Close() + + // Serializes the module into a sequence of bytes. + serialization, _ := module1.Serialize() + + // Do something with `serialization`. + // Then later… + + // Deserializes the module. + module2, _ := wasm.DeserializeModule(serialization) + defer module2.Close() + // And enjoy! + + // Instantiates the WebAssembly module. + instance, _ := module2.Instantiate() + defer instance.Close() + + // Gets an exported function. + sum, functionExists := instance.Exports["sum"] + + fmt.Println(functionExists) + + // Calls the `sum` exported function with Go values. + result, _ := sum(1, 2) + + fmt.Println(result) + + // Output: + // true + // 3 +} + func ExampleInstance_basic() { // Instantiates a WebAssembly instance from bytes. instance, err := wasm.NewInstance(GetBytes()) diff --git a/wasmer/module.go b/wasmer/module.go index 3205a2cc..07c1a190 100644 --- a/wasmer/module.go +++ b/wasmer/module.go @@ -86,6 +86,52 @@ func (module *Module) InstantiateWithImports(imports *Imports) (Instance, error) ) } +// Serialize serializes the current module into a sequence of +// bytes. Those bytes can be deserialized into a module with +// `DeserializeModule`. +func (module *Module) Serialize() ([]byte, error) { + var serializedModule *cWasmerSerializedModuleT + var serializeResult = cWasmerModuleSerialize(&serializedModule, module.module) + defer cWasmerSerializedModuleDestroy(serializedModule) + + if serializeResult != cWasmerOk { + return nil, NewModuleError("Failed to serialize the module.") + } + + return cWasmerSerializedModuleBytes(serializedModule), nil +} + +// DeserializeModule deserializes a sequence of bytes into a +// module. Ideally, those bytes must come from `Module.Serialize`. +func DeserializeModule(serializedModuleBytes []byte) (Module, error) { + var emptyModule = Module{module: nil} + + if len(serializedModuleBytes) < 1 { + return emptyModule, NewModuleError("Serialized module bytes are empty.") + } + + var serializedModule *cWasmerSerializedModuleT + var deserializeBytesResult = cWasmerSerializedModuleFromBytes( + &serializedModule, + (*cUint8T)(unsafe.Pointer(&serializedModuleBytes[0])), + cInt(len(serializedModuleBytes)), + ) + defer cWasmerSerializedModuleDestroy(serializedModule) + + if deserializeBytesResult != cWasmerOk { + return emptyModule, NewModuleError("Failed to reconstitute the serialized module from the given bytes.") + } + + var module *cWasmerModuleT + var deserializeResult = cWasmerModuleDeserialize(&module, serializedModule) + + if deserializeResult != cWasmerOk { + return emptyModule, NewModuleError("Failed to deserialize the module.") + } + + return Module{module}, nil +} + // Close closes/frees a `Module`. func (module *Module) Close() { if module.module != nil { diff --git a/wasmer/test/module_test.go b/wasmer/test/module_test.go index 9dc2f41a..b12379b1 100644 --- a/wasmer/test/module_test.go +++ b/wasmer/test/module_test.go @@ -57,3 +57,40 @@ func TestModuleInstantiate(t *testing.T) { assert.Equal(t, wasm.I32(3), result) } + +func TestModuleSerialize(t *testing.T) { + module1, err := wasm.Compile(GetBytes()) + defer module1.Close() + + assert.NoError(t, err) + + bytes, err := module1.Serialize() + + assert.NoError(t, err) + + module2, err := wasm.DeserializeModule(bytes) + defer module2.Close() + + assert.NoError(t, err) + + instance, err := module2.Instantiate() + defer instance.Close() + + assert.NoError(t, err) + + result, _ := instance.Exports["sum"](1, 2) + + assert.Equal(t, wasm.I32(3), result) +} + +func TestModuleDeserializeModuleWithEmptyBytes(t *testing.T) { + _, err := wasm.DeserializeModule([]byte{}) + + assert.EqualError(t, err, "Serialized module bytes are empty.") +} + +func TestModuleDeserializeModuleWithRandomBytes(t *testing.T) { + _, err := wasm.DeserializeModule([]byte("random")) + + assert.EqualError(t, err, "Failed to deserialize the module.") +}