/
module.go
285 lines (223 loc) · 8.76 KB
/
module.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
package wasmer
import (
"fmt"
"io/ioutil"
"unsafe"
)
// ReadBytes reads a `.wasm` file and returns its content as an array of bytes.
func ReadBytes(filename string) ([]byte, error) {
return ioutil.ReadFile(filename)
}
// Validate validates a sequence of bytes that is supposed to represent a valid
// WebAssembly module.
func Validate(bytes []byte) bool {
return true == cWasmerValidate((*cUchar)(unsafe.Pointer(&bytes[0])), cUint(len(bytes)))
}
// ModuleError represents any kind of errors related to a WebAssembly
// module.
type ModuleError struct {
// Error message.
message string
}
// NewModuleError constructs a new `ModuleError`.
func NewModuleError(message string) *ModuleError {
return &ModuleError{message}
}
// `ModuleError` is an actual error. The `Error` function returns the
// error message.
func (error *ModuleError) Error() string {
return error.message
}
// ExportDescriptor represents an export descriptor of a WebAssembly
// module. It is different of an export of a WebAssembly instance. An
// export descriptor only has a name and a kind/type.
type ExportDescriptor struct {
// The export name.
Name string
// The export kind/type.
Kind ImportExportKind
}
// ImportExportKind represents an import/export descriptor kind/type.
type ImportExportKind int
const (
// ImportExportKindFunction represents an import/export descriptor of kind function.
ImportExportKindFunction = ImportExportKind(cWasmFunction)
// ImportExportKindGlobal represents an import/export descriptor of kind global.
ImportExportKindGlobal = ImportExportKind(cWasmGlobal)
// ImportExportKindMemory represents an import/export descriptor of kind memory.
ImportExportKindMemory = ImportExportKind(cWasmMemory)
// ImportExportKindTable represents an import/export descriptor of kind table.
ImportExportKindTable = ImportExportKind(cWasmTable)
)
// ImportDescriptor represents an import descriptor of a WebAssembly
// module. It is different of an import of a WebAssembly instance. An
// import descriptor only has a name, a namespace, and a kind/type.
type ImportDescriptor struct {
// The import name.
Name string
// The import namespace.
Namespace string
// The import kind/type.
Kind ImportExportKind
}
// Module represents a WebAssembly module.
type Module struct {
module *cWasmerModuleT
Exports []ExportDescriptor
Imports []ImportDescriptor
}
// Compile compiles a WebAssembly module from bytes.
func Compile(bytes []byte) (Module, error) {
var module *cWasmerModuleT
var compileResult = cWasmerCompile(
&module,
(*cUchar)(unsafe.Pointer(&bytes[0])),
cUint(len(bytes)),
)
var emptyModule = Module{module: nil}
if compileResult != cWasmerOk {
return emptyModule, NewModuleError("Failed to compile the module.")
}
var exports = moduleExports(module)
var imports = moduleImports(module)
return Module{module, exports, imports}, nil
}
func moduleExports(module *cWasmerModuleT) []ExportDescriptor {
var exportDescriptors *cWasmerExportDescriptorsT
cWasmerExportDescriptors(module, &exportDescriptors)
defer cWasmerExportDescriptorsDestroy(exportDescriptors)
var numberOfExportDescriptors = int(cWasmerExportDescriptorsLen(exportDescriptors))
var exports = make([]ExportDescriptor, numberOfExportDescriptors)
for nth := 0; nth < numberOfExportDescriptors; nth++ {
var exportDescriptor = cWasmerExportDescriptorsGet(exportDescriptors, cInt(nth))
var exportKind = cWasmerExportDescriptorKind(exportDescriptor)
var wasmExportName = cWasmerExportDescriptorName(exportDescriptor)
var exportName = cGoStringN((*cChar)(unsafe.Pointer(wasmExportName.bytes)), (cInt)(wasmExportName.bytes_len))
exports[nth] = ExportDescriptor{
Name: exportName,
Kind: ImportExportKind(exportKind),
}
}
return exports
}
func moduleImports(module *cWasmerModuleT) []ImportDescriptor {
var importDescriptors *cWasmerImportDescriptorsT
cWasmerImportDescriptors(module, &importDescriptors)
defer cWasmerImportDescriptorsDestroy(importDescriptors)
var numberOfImportDescriptors = int(cWasmerImportDescriptorsLen(importDescriptors))
var imports = make([]ImportDescriptor, numberOfImportDescriptors)
for nth := 0; nth < numberOfImportDescriptors; nth++ {
var importDescriptor = cWasmerImportDescriptorsGet(importDescriptors, cInt(nth))
var importKind = cWasmerImportDescriptorKind(importDescriptor)
var wasmImportName = cWasmerImportDescriptorName(importDescriptor)
var importName = cGoStringN((*cChar)(unsafe.Pointer(wasmImportName.bytes)), (cInt)(wasmImportName.bytes_len))
var wasmImportNamespace = cWasmerImportDescriptorModuleName(importDescriptor)
var importNamespace = cGoStringN((*cChar)(unsafe.Pointer(wasmImportNamespace.bytes)), (cInt)(wasmImportNamespace.bytes_len))
imports[nth] = ImportDescriptor{
Name: importName,
Namespace: importNamespace,
Kind: ImportExportKind(importKind),
}
}
return imports
}
// Instantiate creates a new instance of the WebAssembly module.
func (module *Module) Instantiate() (Instance, error) {
return module.InstantiateWithImports(NewImports())
}
// InstantiateWithImports creates a new instance with imports of the WebAssembly module.
func (module *Module) InstantiateWithImports(imports *Imports) (Instance, error) {
return newInstanceWithImports(
imports,
func(wasmImportsCPointer *cWasmerImportT, numberOfImports int) (*cWasmerInstanceT, error) {
var instance *cWasmerInstanceT
var instantiateResult = cWasmerModuleInstantiate(
module.module,
&instance,
wasmImportsCPointer,
cInt(numberOfImports),
)
if instantiateResult != cWasmerOk {
var lastError, err = GetLastError()
var errorMessage = "Failed to instantiate the module:\n %s"
if err != nil {
errorMessage = fmt.Sprintf(errorMessage, "(unknown details)")
} else {
errorMessage = fmt.Sprintf(errorMessage, lastError)
}
return nil, NewModuleError(errorMessage)
}
return instance, nil
},
)
}
// InstantiateWithImportObject creates a new instance of a WebAssembly module with an
// `ImportObject`
func (module *Module) InstantiateWithImportObject(importObject *ImportObject) (Instance, error) {
var instance *cWasmerInstanceT
var emptyInstance = Instance{instance: nil, imports: nil, Exports: nil, Memory: nil}
var instantiateResult = cWasmerModuleImportInstantiate(&instance, module.module, importObject.inner)
if instantiateResult != cWasmerOk {
var lastError, err = GetLastError()
var errorMessage = "Failed to instantiate the module:\n %s"
if err != nil {
errorMessage = fmt.Sprintf(errorMessage, "(unknown details)")
} else {
errorMessage = fmt.Sprintf(errorMessage, lastError)
}
return emptyInstance, NewModuleError(errorMessage)
}
exports, memoryPointer, err := getExportsFromInstance(instance)
if err != nil {
return emptyInstance, err
}
imports, err := importObject.Imports()
if err != nil {
return emptyInstance, NewModuleError(fmt.Sprintf("Could not get imports from ImportObject: %s", err))
}
return Instance{instance: instance, imports: imports, Exports: exports, Memory: memoryPointer}, nil
}
// 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.")
}
var exports = moduleExports(module)
var imports = moduleImports(module)
return Module{module, exports, imports}, nil
}
// Close closes/frees a `Module`.
func (module *Module) Close() {
if module.module != nil {
cWasmerModuleDestroy(module.module)
}
}