Skip to content

Commit f25c50f

Browse files
liamappelbecommit-bot@chromium.org
authored andcommitted
[wasm] Use wasm traps to handle exceptions
The Dart exception is converted to a string, stored in a trap which is passed through wasm, then converted back to a Dart exception. Module instantiation can also throw a trap, because it internally invokes the initialization function, if the module defines one. So I'm handling this too. Also, update to the new version of the wasm C API, which replaces some foo_t** with foo_vec_t*. Bug: #37882 Change-Id: I1e6952a191134734a892c42b2fb8ba3506bb0844 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169784 Reviewed-by: Ryan Macnak <[email protected]> Commit-Queue: Liam Appelbe <[email protected]>
1 parent f346cb7 commit f25c50f

8 files changed

+238
-149
lines changed

pkg/wasm/lib/src/function.dart

+17-15
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,19 @@ class WasmFunction {
1313
Pointer<WasmerFunc> _func;
1414
List<int> _argTypes;
1515
int _returnType;
16-
Pointer<WasmerVal> _args;
17-
Pointer<WasmerVal> _results;
16+
Pointer<WasmerValVec> _args = allocate<WasmerValVec>();
17+
Pointer<WasmerValVec> _results = allocate<WasmerValVec>();
1818

19-
WasmFunction(this._name, this._func, this._argTypes, this._returnType)
20-
: _args = _argTypes.length == 0
21-
? nullptr
22-
: allocate<WasmerVal>(count: _argTypes.length),
23-
_results =
24-
_returnType == WasmerValKindVoid ? nullptr : allocate<WasmerVal>() {
19+
WasmFunction(this._name, this._func, this._argTypes, this._returnType) {
20+
_args.ref.length = _argTypes.length;
21+
_args.ref.data = _argTypes.length == 0
22+
? nullptr
23+
: allocate<WasmerVal>(count: _argTypes.length);
24+
_results.ref.length = _returnType == WasmerValKindVoid ? 0 : 1;
25+
_results.ref.data =
26+
_returnType == WasmerValKindVoid ? nullptr : allocate<WasmerVal>();
2527
for (var i = 0; i < _argTypes.length; ++i) {
26-
_args[i].kind = _argTypes[i];
28+
_args.ref.data[i].kind = _argTypes[i];
2729
}
2830
}
2931

@@ -35,19 +37,19 @@ class WasmFunction {
3537
switch (_argTypes[i]) {
3638
case WasmerValKindI32:
3739
if (arg is! int) return false;
38-
_args[i].i32 = arg;
40+
_args.ref.data[i].i32 = arg;
3941
return true;
4042
case WasmerValKindI64:
4143
if (arg is! int) return false;
42-
_args[i].i64 = arg;
44+
_args.ref.data[i].i64 = arg;
4345
return true;
4446
case WasmerValKindF32:
4547
if (arg is! num) return false;
46-
_args[i].f32 = arg;
48+
_args.ref.data[i].f32 = arg;
4749
return true;
4850
case WasmerValKindF64:
4951
if (arg is! num) return false;
50-
_args[i].f64 = arg;
52+
_args.ref.data[i].f64 = arg;
5153
return true;
5254
}
5355
return false;
@@ -62,12 +64,12 @@ class WasmFunction {
6264
throw ArgumentError("Bad argument type for WASM function: $this");
6365
}
6466
}
65-
WasmRuntime().call(_func, _args, _results);
67+
WasmRuntime().call(_func, _args, _results, toString());
6668

6769
if (_returnType == WasmerValKindVoid) {
6870
return null;
6971
}
70-
var result = _results[0];
72+
var result = _results.ref.data[0];
7173
assert(_returnType == result.kind);
7274
switch (_returnType) {
7375
case WasmerValKindI32:

pkg/wasm/lib/src/module.dart

+40-39
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,11 @@ class WasmModule {
4949
}
5050

5151
Pointer<WasmerTrap> _wasmFnImportTrampoline(Pointer<_WasmFnImport> imp,
52-
Pointer<WasmerVal> args, Pointer<WasmerVal> results) {
52+
Pointer<WasmerValVec> args, Pointer<WasmerValVec> results) {
5353
try {
5454
_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);
55+
} catch (exception) {
56+
return WasmRuntime().newTrap(imp.ref.store, exception);
5957
}
6058
return nullptr;
6159
}
@@ -66,43 +64,45 @@ void _wasmFnImportFinalizer(Pointer<_WasmFnImport> imp) {
6664
}
6765

6866
final _wasmFnImportTrampolineNative = Pointer.fromFunction<
69-
Pointer<WasmerTrap> Function(Pointer<_WasmFnImport>, Pointer<WasmerVal>,
70-
Pointer<WasmerVal>)>(_wasmFnImportTrampoline);
67+
Pointer<WasmerTrap> Function(Pointer<_WasmFnImport>, Pointer<WasmerValVec>,
68+
Pointer<WasmerValVec>)>(_wasmFnImportTrampoline);
7169
final _wasmFnImportToFn = <int, Function>{};
7270
final _wasmFnImportFinalizerNative =
7371
Pointer.fromFunction<Void Function(Pointer<_WasmFnImport>)>(
7472
_wasmFnImportFinalizer);
7573

7674
class _WasmFnImport extends Struct {
77-
@Int32()
78-
external int numArgs;
79-
8075
@Int32()
8176
external int returnType;
8277

83-
static void _call(Pointer<_WasmFnImport> imp, Pointer<WasmerVal> rawArgs,
84-
Pointer<WasmerVal> rawResult) {
78+
external Pointer<WasmerStore> store;
79+
80+
static void _call(Pointer<_WasmFnImport> imp, Pointer<WasmerValVec> rawArgs,
81+
Pointer<WasmerValVec> rawResult) {
8582
Function fn = _wasmFnImportToFn[imp.address] as Function;
8683
var args = [];
87-
for (var i = 0; i < imp.ref.numArgs; ++i) {
88-
args.add(rawArgs[i].toDynamic);
84+
for (var i = 0; i < rawArgs.ref.length; ++i) {
85+
args.add(rawArgs.ref.data[i].toDynamic);
8986
}
87+
assert(
88+
rawResult.ref.length == 1 || imp.ref.returnType == WasmerValKindVoid);
9089
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.
90+
if (imp.ref.returnType != WasmerValKindVoid) {
91+
rawResult.ref.data[0].kind = imp.ref.returnType;
92+
switch (imp.ref.returnType) {
93+
case WasmerValKindI32:
94+
rawResult.ref.data[0].i32 = result;
95+
break;
96+
case WasmerValKindI64:
97+
rawResult.ref.data[0].i64 = result;
98+
break;
99+
case WasmerValKindF32:
100+
rawResult.ref.data[0].f32 = result;
101+
break;
102+
case WasmerValKindF64:
103+
rawResult.ref.data[0].f64 = result;
104+
break;
105+
}
106106
}
107107
}
108108
}
@@ -113,24 +113,26 @@ class WasmInstanceBuilder {
113113
WasmModule _module;
114114
late List<WasmImportDescriptor> _importDescs;
115115
Map<String, int> _importIndex;
116-
late Pointer<Pointer<WasmerExtern>> _imports;
116+
Pointer<WasmerExternVec> _imports = allocate<WasmerExternVec>();
117117
Pointer<WasmerWasiEnv> _wasiEnv = nullptr;
118118

119119
WasmInstanceBuilder(this._module) : _importIndex = {} {
120120
_importDescs = WasmRuntime().importDescriptors(_module._module);
121-
_imports = allocate<Pointer<WasmerExtern>>(count: _importDescs.length);
121+
_imports.ref.length = _importDescs.length;
122+
_imports.ref.data =
123+
allocate<Pointer<WasmerExtern>>(count: _importDescs.length);
122124
for (var i = 0; i < _importDescs.length; ++i) {
123125
var imp = _importDescs[i];
124126
_importIndex["${imp.moduleName}::${imp.name}"] = i;
125-
_imports[i] = nullptr;
127+
_imports.ref.data[i] = nullptr;
126128
}
127129
}
128130

129131
int _getIndex(String moduleName, String name) {
130132
var index = _importIndex["${moduleName}::${name}"];
131133
if (index == null) {
132134
throw Exception("Import not found: ${moduleName}::${name}");
133-
} else if (_imports[index] != nullptr) {
135+
} else if (_imports.ref.data[index] != nullptr) {
134136
throw Exception("Import already filled: ${moduleName}::${name}");
135137
} else {
136138
return index;
@@ -145,7 +147,7 @@ class WasmInstanceBuilder {
145147
if (imp.kind != WasmerExternKindMemory) {
146148
throw Exception("Import is not a memory: $imp");
147149
}
148-
_imports[index] = WasmRuntime().memoryToExtern(memory._mem);
150+
_imports.ref.data[index] = WasmRuntime().memoryToExtern(memory._mem);
149151
return this;
150152
}
151153

@@ -162,16 +164,16 @@ class WasmInstanceBuilder {
162164
var argTypes = runtime.getArgTypes(imp.funcType);
163165
var returnType = runtime.getReturnType(imp.funcType);
164166
var wasmFnImport = allocate<_WasmFnImport>();
165-
wasmFnImport.ref.numArgs = argTypes.length;
166167
wasmFnImport.ref.returnType = returnType;
168+
wasmFnImport.ref.store = _module._store;
167169
_wasmFnImportToFn[wasmFnImport.address] = fn;
168170
var fnImp = runtime.newFunc(
169171
_module._store,
170172
imp.funcType,
171173
_wasmFnImportTrampolineNative,
172174
wasmFnImport,
173175
_wasmFnImportFinalizerNative);
174-
_imports[index] = runtime.functionToExtern(fnImp);
176+
_imports.ref.data[index] = runtime.functionToExtern(fnImp);
175177
return this;
176178
}
177179

@@ -193,7 +195,7 @@ class WasmInstanceBuilder {
193195
/// Build the module instance.
194196
WasmInstance build() {
195197
for (var i = 0; i < _importDescs.length; ++i) {
196-
if (_imports[i] == nullptr) {
198+
if (_imports.ref.data[i] == nullptr) {
197199
throw Exception("Missing import: ${_importDescs[i]}");
198200
}
199201
}
@@ -211,8 +213,7 @@ class WasmInstance {
211213
Stream<List<int>>? _stderr;
212214
Map<String, WasmFunction> _functions = {};
213215

214-
WasmInstance(
215-
this._module, Pointer<Pointer<WasmerExtern>> imports, this._wasiEnv)
216+
WasmInstance(this._module, Pointer<WasmerExternVec> imports, this._wasiEnv)
216217
: _instance = WasmRuntime()
217218
.instantiate(_module._store, _module._module, imports) {
218219
var runtime = WasmRuntime();

pkg/wasm/lib/src/runtime.dart

+48-7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ class WasmRuntime {
5959

6060
DynamicLibrary _lib;
6161
late Pointer<WasmerEngine> _engine;
62+
Map<int, dynamic> traps = {};
6263
late WasmerWasiConfigInheritStderrFn _wasi_config_inherit_stderr;
6364
late WasmerWasiConfigInheritStdoutFn _wasi_config_inherit_stdout;
6465
late WasmerWasiConfigNewFn _wasi_config_new;
@@ -124,6 +125,8 @@ class WasmRuntime {
124125
late WasmerStoreDeleteFn _store_delete;
125126
late WasmerStoreNewFn _store_new;
126127
late WasmerTrapDeleteFn _trap_delete;
128+
late WasmerTrapMessageFn _trap_message;
129+
late WasmerTrapNewFn _trap_new;
127130
late WasmerValtypeDeleteFn _valtype_delete;
128131
late WasmerValtypeKindFn _valtype_kind;
129132
late WasmerValtypeVecDeleteFn _valtype_vec_delete;
@@ -344,6 +347,11 @@ class WasmRuntime {
344347
_trap_delete =
345348
_lib.lookupFunction<NativeWasmerTrapDeleteFn, WasmerTrapDeleteFn>(
346349
'wasm_trap_delete');
350+
_trap_message =
351+
_lib.lookupFunction<NativeWasmerTrapMessageFn, WasmerTrapMessageFn>(
352+
'wasm_trap_message');
353+
_trap_new = _lib.lookupFunction<NativeWasmerTrapNewFn, WasmerTrapNewFn>(
354+
'wasm_trap_new');
347355
_valtype_delete =
348356
_lib.lookupFunction<NativeWasmerValtypeDeleteFn, WasmerValtypeDeleteFn>(
349357
'wasm_valtype_delete');
@@ -432,10 +440,31 @@ class WasmRuntime {
432440
return imps;
433441
}
434442

443+
void maybeThrowTrap(Pointer<WasmerTrap> trap, String source) {
444+
if (trap != nullptr) {
445+
var stashedException = traps[trap.address];
446+
if (stashedException != null) {
447+
traps.remove(stashedException);
448+
throw stashedException;
449+
} else {
450+
var trapMessage = allocate<WasmerByteVec>();
451+
_trap_message(trap, trapMessage);
452+
var message = "Wasm trap when calling $source: ${trapMessage.ref}";
453+
free(trapMessage.ref.data);
454+
free(trapMessage);
455+
throw Exception(message);
456+
}
457+
}
458+
}
459+
435460
Pointer<WasmerInstance> instantiate(Pointer<WasmerStore> store,
436-
Pointer<WasmerModule> module, Pointer<Pointer<WasmerExtern>> imports) {
437-
return _checkNotEqual(_instance_new(store, module, imports, nullptr),
438-
nullptr, "Wasm module instantiation failed.");
461+
Pointer<WasmerModule> module, Pointer<WasmerExternVec> imports) {
462+
var trap = allocate<Pointer<WasmerTrap>>();
463+
trap.value = nullptr;
464+
var inst = _instance_new(store, module, imports, trap);
465+
maybeThrowTrap(trap.value, "module initialization function");
466+
free(trap);
467+
return _checkNotEqual(inst, nullptr, "Wasm module instantiation failed.");
439468
}
440469

441470
Pointer<WasmerExternVec> exports(Pointer<WasmerInstance> instancePtr) {
@@ -475,9 +504,9 @@ class WasmRuntime {
475504
return _valtype_kind(rets.ref.data[0]);
476505
}
477506

478-
void call(Pointer<WasmerFunc> func, Pointer<WasmerVal> args,
479-
Pointer<WasmerVal> results) {
480-
_func_call(func, args, results);
507+
void call(Pointer<WasmerFunc> func, Pointer<WasmerValVec> args,
508+
Pointer<WasmerValVec> results, String source) {
509+
maybeThrowTrap(_func_call(func, args, results), source);
481510
}
482511

483512
Pointer<WasmerMemory> externToMemory(Pointer<WasmerExtern> extern) {
@@ -522,6 +551,18 @@ class WasmRuntime {
522551
store, funcType, func.cast(), env.cast(), finalizer.cast());
523552
}
524553

554+
Pointer<WasmerTrap> newTrap(Pointer<WasmerStore> store, dynamic exception) {
555+
var msg = allocate<WasmerByteVec>();
556+
msg.ref.data = allocate<Uint8>();
557+
msg.ref.data[0] = 0;
558+
msg.ref.length = 0;
559+
var trap = _trap_new(store, msg);
560+
traps[trap.address] = exception;
561+
free(msg.ref.data);
562+
free(msg);
563+
return _checkNotEqual(trap, nullptr, "Failed to create trap.");
564+
}
565+
525566
Pointer<WasmerWasiConfig> newWasiConfig() {
526567
var name = allocate<Uint8>();
527568
name[0] = 0;
@@ -549,7 +590,7 @@ class WasmRuntime {
549590
}
550591

551592
void getWasiImports(Pointer<WasmerStore> store, Pointer<WasmerModule> mod,
552-
Pointer<WasmerWasiEnv> env, Pointer<Pointer<WasmerExtern>> imports) {
593+
Pointer<WasmerWasiEnv> env, Pointer<WasmerExternVec> imports) {
553594
_checkNotEqual(_wasi_get_imports(store, mod, env, imports), 0,
554595
"Failed to fill WASI imports.");
555596
}

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

+6-3
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ def match(r, s):
155155
('own', ''),
156156
('WASM_API_EXTERN', ''),
157157
('wasm_name_t', 'wasm_byte_vec_t'),
158+
('wasm_message_t', 'wasm_byte_vec_t'),
158159
('wasm_memory_pages_t', 'uint32_t'),
159160
('wasm_externkind_t', 'uint8_t'),
160161
('wasm_valkind_t', 'uint8_t'),
@@ -290,7 +291,7 @@ def declareType(name, withCopy=True):
290291
WASM_API_EXTERN const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t*);
291292
WASM_API_EXTERN const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t*);
292293
WASM_API_EXTERN wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t*);
293-
WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(wasm_store_t*, const wasm_module_t*, const wasm_extern_t* const imports[], own wasm_trap_t**);
294+
WASM_API_EXTERN own wasm_instance_t* wasm_instance_new(wasm_store_t*, const wasm_module_t*, const wasm_extern_vec_t* imports, own wasm_trap_t**);
294295
WASM_API_EXTERN void wasm_instance_exports(const wasm_instance_t*, own wasm_extern_vec_t* out);
295296
WASM_API_EXTERN own wasm_memory_t* wasm_memory_new(wasm_store_t*, const wasm_memorytype_t*);
296297
WASM_API_EXTERN byte_t* wasm_memory_data(wasm_memory_t*);
@@ -305,11 +306,13 @@ def declareType(name, withCopy=True):
305306
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t*);
306307
WASM_API_EXTERN const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t*);
307308
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);
308-
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(const wasm_func_t*, const wasm_val_t args[], wasm_val_t results[]);
309+
WASM_API_EXTERN own wasm_trap_t* wasm_func_call(const wasm_func_t*, const wasm_val_vec_t* args, wasm_val_vec_t* results);
310+
WASM_API_EXTERN own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t*);
311+
WASM_API_EXTERN void wasm_trap_message(const wasm_trap_t*, own wasm_message_t* out);
309312
WASM_API_EXTERN wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t*);
310313
wasi_config_t* wasi_config_new(const uint8_t* program_name);
311314
wasi_env_t* wasi_env_new(wasi_config_t* config);
312-
bool wasi_get_imports(const wasm_store_t* store, const wasm_module_t* module, const wasi_env_t* wasi_env, wasm_extern_t** imports);
315+
bool wasi_get_imports(const wasm_store_t* store, const wasm_module_t* module, const wasi_env_t* wasi_env, wasm_extern_vec_t* imports);
313316
int wasmer_last_error_message(uint8_t* buffer, int length);
314317
int wasmer_last_error_length();
315318
void wasi_env_set_memory(wasi_env_t* env, const wasm_memory_t* memory);

0 commit comments

Comments
 (0)