Skip to content

Commit 89dbc35

Browse files
liamappelbecommit-bot@chromium.org
authored andcommitted
[wasm] Wasm function imports.
I switched from a WasmImports object to a builder pattern: module.instantiate().addMemory(...).addFunction(...).build(); Bug: dart-lang/sdk#37882 Change-Id: I381aa0f7df1fa006ce8d051cd5b4a1bcc1835e46 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/167460 Reviewed-by: Ryan Macnak <rmacnak@google.com> Commit-Queue: Liam Appelbe <liama@google.com>
1 parent f0052ef commit 89dbc35

23 files changed

+419
-181
lines changed

DEPS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ vars = {
133133
"quiver-dart_tag": "246e754fe45cecb6aa5f3f13b4ed61037ff0d784",
134134
"resource_rev": "f8e37558a1c4f54550aa463b88a6a831e3e33cd6",
135135
"root_certificates_rev": "7e5ec82c99677a2e5b95ce296c4d68b0d3378ed8",
136-
"rust_revision": "cbe7c5ce705896d4e22bf6096590bc1f17993b78",
136+
"rust_revision": "b7856f695d65a8ebc846754f97d15814bcb1c244",
137137
"shelf_static_rev": "v0.2.8",
138138
"shelf_packages_handler_tag": "2.0.0",
139139
"shelf_proxy_tag": "0.1.0+7",

pkg/wasm/lib/src/function.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ class WasmFunction {
2828
}
2929

3030
String toString() {
31-
return "${wasmerValKindName(_returnType)} $_name" +
32-
"(${_argTypes.map(wasmerValKindName).join(", ")})";
31+
return WasmRuntime.getSignatureString(_name, _argTypes, _returnType);
3332
}
3433

3534
bool _fillArg(dynamic arg, int i) {

pkg/wasm/lib/src/module.dart

Lines changed: 143 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ class WasmModule {
1919
_module = WasmRuntime().compile(_store, data);
2020
}
2121

22-
/// Instantiate the module with the given imports.
23-
WasmInstance instantiate(WasmImports imports) {
24-
return WasmInstance(_store, _module, imports);
22+
/// Returns a WasmInstanceBuilder that is used to add all the imports that the
23+
/// module needs, and then instantiate it.
24+
WasmInstanceBuilder instantiate() {
25+
return WasmInstanceBuilder(this);
2526
}
2627

2728
/// Create a new memory with the given number of initial pages, and optional
@@ -37,47 +38,166 @@ class WasmModule {
3738
var runtime = WasmRuntime();
3839
var imports = runtime.importDescriptors(_module);
3940
for (var imp in imports) {
40-
var kind = wasmerExternKindName(imp.kind);
41-
description.write('import $kind: ${imp.moduleName}::${imp.name}\n');
41+
description.write("import $imp\n");
4242
}
4343
var exports = runtime.exportDescriptors(_module);
4444
for (var exp in exports) {
45-
var kind = wasmerExternKindName(exp.kind);
46-
description.write('export $kind: ${exp.name}\n');
45+
description.write("export $exp\n");
4746
}
4847
return description.toString();
4948
}
5049
}
5150

52-
/// WasmImports holds all the imports for a WasmInstance.
53-
class WasmImports {
54-
Pointer<Pointer<WasmerExtern>> _imports;
55-
int _capacity;
56-
int _length;
51+
Pointer<WasmerTrap> _wasmFnImportTrampoline(Pointer<_WasmFnImport> imp,
52+
Pointer<WasmerVal> args, Pointer<WasmerVal> results) {
53+
try {
54+
_WasmFnImport._call(imp, args, results);
55+
} catch (e) {
56+
// TODO: Use WasmerTrap to handle this case. For now just print the
57+
// exception (if we ignore it, FFI will silently return a default result).
58+
print(e);
59+
}
60+
return nullptr;
61+
}
62+
63+
void _wasmFnImportFinalizer(Pointer<_WasmFnImport> imp) {
64+
_wasmFnImportToFn.remove(imp.address);
65+
free(imp);
66+
}
5767

58-
/// Create an imports object.
59-
WasmImports([this._capacity = 4])
60-
: _imports = allocate<Pointer<WasmerExtern>>(count: _capacity),
61-
_length = 0 {}
68+
final _wasmFnImportTrampolineNative = Pointer.fromFunction<
69+
Pointer<WasmerTrap> Function(Pointer<_WasmFnImport>, Pointer<WasmerVal>,
70+
Pointer<WasmerVal>)>(_wasmFnImportTrampoline);
71+
final _wasmFnImportToFn = <int, Function>{};
72+
final _wasmFnImportFinalizerNative =
73+
Pointer.fromFunction<Void Function(Pointer<_WasmFnImport>)>(
74+
_wasmFnImportFinalizer);
6275

63-
/// Returns the number of imports.
64-
int get length => _length;
76+
class _WasmFnImport extends Struct {
77+
@Int32()
78+
external int numArgs;
79+
80+
@Int32()
81+
external int returnType;
82+
83+
static void _call(Pointer<_WasmFnImport> imp, Pointer<WasmerVal> rawArgs,
84+
Pointer<WasmerVal> rawResult) {
85+
Function fn = _wasmFnImportToFn[imp.address] as Function;
86+
var args = [];
87+
for (var i = 0; i < imp.ref.numArgs; ++i) {
88+
args.add(rawArgs[i].toDynamic);
89+
}
90+
var result = Function.apply(fn, args);
91+
switch (imp.ref.returnType) {
92+
case WasmerValKindI32:
93+
rawResult.ref.i32 = result;
94+
break;
95+
case WasmerValKindI64:
96+
rawResult.ref.i64 = result;
97+
break;
98+
case WasmerValKindF32:
99+
rawResult.ref.f32 = result;
100+
break;
101+
case WasmerValKindF64:
102+
rawResult.ref.f64 = result;
103+
break;
104+
case WasmerValKindVoid:
105+
// Do nothing.
106+
}
107+
}
108+
}
109+
110+
/// WasmInstanceBuilder is used collect all the imports that a WasmModule
111+
/// requires before it is instantiated.
112+
class WasmInstanceBuilder {
113+
WasmModule _module;
114+
late List<WasmImportDescriptor> _importDescs;
115+
Map<String, int> _importIndex;
116+
late Pointer<Pointer<WasmerExtern>> _imports;
117+
118+
WasmInstanceBuilder(this._module) : _importIndex = {} {
119+
_importDescs = WasmRuntime().importDescriptors(_module._module);
120+
_imports = allocate<Pointer<WasmerExtern>>(count: _importDescs.length);
121+
for (var i = 0; i < _importDescs.length; ++i) {
122+
var imp = _importDescs[i];
123+
_importIndex["${imp.moduleName}::${imp.name}"] = i;
124+
_imports[i] = nullptr;
125+
}
126+
}
127+
128+
int _getIndex(String moduleName, String name) {
129+
var index = _importIndex["${moduleName}::${name}"];
130+
if (index == null) {
131+
throw Exception("Import not found: ${moduleName}::${name}");
132+
} else if (_imports[index] != nullptr) {
133+
throw Exception("Import already filled: ${moduleName}::${name}");
134+
} else {
135+
return index;
136+
}
137+
}
138+
139+
/// Add a WasmMemory to the imports.
140+
WasmInstanceBuilder addMemory(
141+
String moduleName, String name, WasmMemory memory) {
142+
var index = _getIndex(moduleName, name);
143+
var imp = _importDescs[index];
144+
if (imp.kind != WasmerExternKindMemory) {
145+
throw Exception("Import is not a memory: $imp");
146+
}
147+
_imports[index] = WasmRuntime().memoryToExtern(memory._mem);
148+
return this;
149+
}
150+
151+
/// Add a function to the imports.
152+
WasmInstanceBuilder addFunction(String moduleName, String name, Function fn) {
153+
var index = _getIndex(moduleName, name);
154+
var imp = _importDescs[index];
155+
var runtime = WasmRuntime();
156+
157+
if (imp.kind != WasmerExternKindFunction) {
158+
throw Exception("Import is not a function: $imp");
159+
}
160+
161+
var argTypes = runtime.getArgTypes(imp.funcType);
162+
var returnType = runtime.getReturnType(imp.funcType);
163+
var wasmFnImport = allocate<_WasmFnImport>();
164+
wasmFnImport.ref.numArgs = argTypes.length;
165+
wasmFnImport.ref.returnType = returnType;
166+
_wasmFnImportToFn[wasmFnImport.address] = fn;
167+
var fnImp = runtime.newFunc(
168+
_module._store,
169+
imp.funcType,
170+
_wasmFnImportTrampolineNative,
171+
wasmFnImport,
172+
_wasmFnImportFinalizerNative);
173+
_imports[index] = runtime.functionToExtern(fnImp);
174+
return this;
175+
}
176+
177+
/// Build the module instance.
178+
WasmInstance build() {
179+
for (var i = 0; i < _importDescs.length; ++i) {
180+
if (_imports[i] == nullptr) {
181+
throw Exception("Missing import: ${_importDescs[i]}");
182+
}
183+
}
184+
return WasmInstance(_module, _imports);
185+
}
65186
}
66187

67188
/// WasmInstance is an instantiated WasmModule.
68189
class WasmInstance {
69-
Pointer<WasmerStore> _store;
70-
Pointer<WasmerModule> _module;
190+
WasmModule _module;
71191
Pointer<WasmerInstance> _instance;
72192
Pointer<WasmerMemory>? _exportedMemory;
73193
Map<String, WasmFunction> _functions = {};
74194

75-
WasmInstance(this._store, this._module, WasmImports imports)
195+
WasmInstance(this._module, Pointer<Pointer<WasmerExtern>> imports)
76196
: _instance = WasmRuntime()
77-
.instantiate(_store, _module, imports._imports, imports.length) {
197+
.instantiate(_module._store, _module._module, imports) {
78198
var runtime = WasmRuntime();
79199
var exports = runtime.exports(_instance);
80-
var exportDescs = runtime.exportDescriptors(_module);
200+
var exportDescs = runtime.exportDescriptors(_module._module);
81201
assert(exports.ref.length == exportDescs.length);
82202
for (var i = 0; i < exports.ref.length; ++i) {
83203
var e = exports.ref.data[i];

pkg/wasm/lib/src/runtime.dart

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,37 @@ class WasmImportDescriptor {
2020
String name;
2121
Pointer<WasmerFunctype> funcType;
2222
WasmImportDescriptor(this.kind, this.moduleName, this.name, this.funcType);
23+
24+
String toString() {
25+
var kindName = wasmerExternKindName(kind);
26+
if (kind == WasmerExternKindFunction) {
27+
var runtime = WasmRuntime();
28+
var sig = WasmRuntime.getSignatureString("${moduleName}::${name}",
29+
runtime.getArgTypes(funcType), runtime.getReturnType(funcType));
30+
return "$kindName: $sig";
31+
} else {
32+
return "$kindName: ${moduleName}::${name}";
33+
}
34+
}
2335
}
2436

2537
class WasmExportDescriptor {
2638
int kind;
2739
String name;
2840
Pointer<WasmerFunctype> funcType;
2941
WasmExportDescriptor(this.kind, this.name, this.funcType);
42+
43+
String toString() {
44+
var kindName = wasmerExternKindName(kind);
45+
if (kind == WasmerExternKindFunction) {
46+
var runtime = WasmRuntime();
47+
var sig = WasmRuntime.getSignatureString(
48+
name, runtime.getArgTypes(funcType), runtime.getReturnType(funcType));
49+
return "$kindName: $sig";
50+
} else {
51+
return "$kindName: ${name}";
52+
}
53+
}
3054
}
3155

3256
class WasmRuntime {
@@ -57,8 +81,10 @@ class WasmRuntime {
5781
late WasmerExterntypeAsFunctypeFn _externtype_as_functype;
5882
late WasmerExterntypeDeleteFn _externtype_delete;
5983
late WasmerExterntypeKindFn _externtype_kind;
84+
late WasmerFuncAsExternFn _func_as_extern;
6085
late WasmerFuncCallFn _func_call;
6186
late WasmerFuncDeleteFn _func_delete;
87+
late WasmerFuncNewWithEnvFn _func_new_with_env;
6288
late WasmerFunctypeDeleteFn _functype_delete;
6389
late WasmerFunctypeParamsFn _functype_params;
6490
late WasmerFunctypeResultsFn _functype_results;
@@ -72,6 +98,7 @@ class WasmRuntime {
7298
late WasmerInstanceDeleteFn _instance_delete;
7399
late WasmerInstanceExportsFn _instance_exports;
74100
late WasmerInstanceNewFn _instance_new;
101+
late WasmerMemoryAsExternFn _memory_as_extern;
75102
late WasmerMemoryDataFn _memory_data;
76103
late WasmerMemoryDataSizeFn _memory_data_size;
77104
late WasmerMemoryDeleteFn _memory_delete;
@@ -199,11 +226,16 @@ class WasmRuntime {
199226
WasmerExterntypeDeleteFn>('wasm_externtype_delete');
200227
_externtype_kind = _lib.lookupFunction<NativeWasmerExterntypeKindFn,
201228
WasmerExterntypeKindFn>('wasm_externtype_kind');
229+
_func_as_extern =
230+
_lib.lookupFunction<NativeWasmerFuncAsExternFn, WasmerFuncAsExternFn>(
231+
'wasm_func_as_extern');
202232
_func_call = _lib.lookupFunction<NativeWasmerFuncCallFn, WasmerFuncCallFn>(
203233
'wasm_func_call');
204234
_func_delete =
205235
_lib.lookupFunction<NativeWasmerFuncDeleteFn, WasmerFuncDeleteFn>(
206236
'wasm_func_delete');
237+
_func_new_with_env = _lib.lookupFunction<NativeWasmerFuncNewWithEnvFn,
238+
WasmerFuncNewWithEnvFn>('wasm_func_new_with_env');
207239
_functype_delete = _lib.lookupFunction<NativeWasmerFunctypeDeleteFn,
208240
WasmerFunctypeDeleteFn>('wasm_functype_delete');
209241
_functype_params = _lib.lookupFunction<NativeWasmerFunctypeParamsFn,
@@ -235,6 +267,8 @@ class WasmRuntime {
235267
_instance_new =
236268
_lib.lookupFunction<NativeWasmerInstanceNewFn, WasmerInstanceNewFn>(
237269
'wasm_instance_new');
270+
_memory_as_extern = _lib.lookupFunction<NativeWasmerMemoryAsExternFn,
271+
WasmerMemoryAsExternFn>('wasm_memory_as_extern');
238272
_memory_data =
239273
_lib.lookupFunction<NativeWasmerMemoryDataFn, WasmerMemoryDataFn>(
240274
'wasm_memory_data');
@@ -363,20 +397,8 @@ class WasmRuntime {
363397
return imps;
364398
}
365399

366-
Pointer<WasmerInstance> instantiate(
367-
Pointer<WasmerStore> store,
368-
Pointer<WasmerModule> module,
369-
Pointer<Pointer<WasmerExtern>> imports,
370-
int numImports) {
371-
var importsVec = allocate<WasmerImporttypeVec>();
372-
_module_imports(module, importsVec);
373-
if (importsVec.ref.length != numImports) {
374-
throw Exception(
375-
"Wrong number of imports. Expected ${importsVec.ref.length} but " +
376-
"found $numImports.");
377-
}
378-
free(importsVec);
379-
400+
Pointer<WasmerInstance> instantiate(Pointer<WasmerStore> store,
401+
Pointer<WasmerModule> module, Pointer<Pointer<WasmerExtern>> imports) {
380402
var instancePtr = _instance_new(store, module, imports, nullptr);
381403
if (instancePtr == nullptr) {
382404
throw Exception("Wasm module instantiation failed");
@@ -399,6 +421,10 @@ class WasmRuntime {
399421
return _extern_as_func(extern);
400422
}
401423

424+
Pointer<WasmerExtern> functionToExtern(Pointer<WasmerFunc> func) {
425+
return _func_as_extern(func);
426+
}
427+
402428
List<int> getArgTypes(Pointer<WasmerFunctype> funcType) {
403429
var types = <int>[];
404430
var args = _functype_params(funcType);
@@ -427,6 +453,10 @@ class WasmRuntime {
427453
return _extern_as_memory(extern);
428454
}
429455

456+
Pointer<WasmerExtern> memoryToExtern(Pointer<WasmerMemory> memory) {
457+
return _memory_as_extern(memory);
458+
}
459+
430460
Pointer<WasmerMemory> newMemory(
431461
Pointer<WasmerStore> store, int pages, int? maxPages) {
432462
var limPtr = allocate<WasmerLimits>();
@@ -456,4 +486,20 @@ class WasmRuntime {
456486
Uint8List memoryView(Pointer<WasmerMemory> memory) {
457487
return _memory_data(memory).asTypedList(_memory_data_size(memory));
458488
}
489+
490+
Pointer<WasmerFunc> newFunc(
491+
Pointer<WasmerStore> store,
492+
Pointer<WasmerFunctype> funcType,
493+
Pointer func,
494+
Pointer env,
495+
Pointer finalizer) {
496+
return _func_new_with_env(
497+
store, funcType, func.cast(), env.cast(), finalizer.cast());
498+
}
499+
500+
static String getSignatureString(
501+
String name, List<int> argTypes, int returnType) {
502+
return "${wasmerValKindName(returnType)} $name" +
503+
"(${argTypes.map(wasmerValKindName).join(", ")})";
504+
}
459505
}

pkg/wasm/lib/src/tools/generate_ffi_boilerplate.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,12 @@ def declareType(name, withCopy=True):
283283
WASM_API_EXTERN bool wasm_memory_grow(wasm_memory_t*, wasm_memory_pages_t delta);
284284
WASM_API_EXTERN wasm_externkind_t wasm_extern_kind(const wasm_extern_t*);
285285
WASM_API_EXTERN wasm_func_t* wasm_extern_as_func(wasm_extern_t*);
286+
WASM_API_EXTERN wasm_extern_t* wasm_func_as_extern(wasm_func_t*);
286287
WASM_API_EXTERN wasm_memory_t* wasm_extern_as_memory(wasm_extern_t*);
288+
WASM_API_EXTERN wasm_extern_t* wasm_memory_as_extern(wasm_memory_t*);
287289
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*);
288290
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*);
291+
WASM_API_EXTERN own wasm_func_t* wasm_func_new_with_env( wasm_store_t*, const wasm_functype_t* type, void* fn, void* env, void *finalizer);
289292
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(const wasm_func_t*, const wasm_val_t args[], wasm_val_t results[]);
290293
WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*);
291294
'''

0 commit comments

Comments
 (0)