• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

dart-lang / wasm / 4775430471

23 Apr 2023 12:22AM UTC coverage: 90.827%. Remained the same
4775430471

push

github

GitHub
Fix formatting (#142)

604 of 665 relevant lines covered (90.83%)

13.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.13
/wasm/lib/src/runtime.dart
1
// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
2
// for details. All rights reserved. Use of this source code is governed by a
3
// BSD-style license that can be found in the LICENSE file.
4

5
import 'dart:async';
6
import 'dart:convert';
7
import 'dart:ffi';
8
import 'dart:typed_data';
9

10
import 'package:ffi/ffi.dart';
11

12
import 'wasm_api.dart';
13
import 'wasmer_api.dart';
14

15
part 'runtime.g.dart';
16

17
WasmRuntime wasmRuntimeFactory(
16✔
18
  DynamicLibrary lib,
19
) =>
20
    WasmRuntime._(lib);
16✔
21

22
class WasmRuntime with _WasmRuntimeGeneratedMixin {
23
  @override
24
  final DynamicLibrary _lib;
25
  final _traps = <String, _WasmTrapsEntry>{};
26
  late final Pointer<WasmerEngine> _engine;
27
  late final Pointer<WasmerStore> _store;
28

29
  WasmRuntime._(this._lib) {
16✔
30
    initBindings();
16✔
31
    _engine = _engine_new();
48✔
32
    _checkNotEqual(_engine, nullptr, 'Failed to initialize Wasm engine.');
48✔
33
    _set_finalizer_for_engine(this, _engine);
48✔
34
    _store = _store_new(_engine);
64✔
35
    _checkNotEqual(_store, nullptr, 'Failed to create Wasm store.');
48✔
36
    _set_finalizer_for_store(this, _store);
48✔
37
  }
38

39
  Pointer<WasmerModule> compile(Object owner, Uint8List data) {
16✔
40
    final dataPtr = calloc<Uint8>(data.length);
16✔
41
    for (var i = 0; i < data.length; ++i) {
48✔
42
      dataPtr[i] = data[i];
32✔
43
    }
44
    final dataVec = calloc<WasmerByteVec>();
45
    dataVec.ref.data = dataPtr;
16✔
46
    dataVec.ref.length = data.length;
32✔
47

48
    final modulePtr = _module_new(_store, dataVec);
48✔
49

50
    calloc
51
      ..free(dataPtr)
16✔
52
      ..free(dataVec);
16✔
53

54
    _checkNotEqual(modulePtr, nullptr, 'Wasm module compilation failed.');
32✔
55
    _set_finalizer_for_module(owner, modulePtr);
30✔
56
    return modulePtr;
57
  }
58

59
  Pointer _externTypeToFuncOrGlobalType(
13✔
60
    int kind,
61
    Pointer<WasmerExterntype> extern,
62
  ) {
63
    if (kind == wasmerExternKindFunction) {
13✔
64
      return _externtype_as_functype(extern);
26✔
65
    } else if (kind == wasmerExternKindGlobal) {
13✔
66
      return _externtype_as_globaltype(extern);
2✔
67
    }
68
    return nullptr;
13✔
69
  }
70

71
  List<WasmExportDescriptor> exportDescriptors(Pointer<WasmerModule> module) {
13✔
72
    var exportsVec = calloc<WasmerExporttypeVec>();
73
    _module_exports(module, exportsVec);
26✔
74
    var exps = <WasmExportDescriptor>[];
13✔
75
    for (var i = 0; i < exportsVec.ref.length; ++i) {
39✔
76
      final exp = exportsVec.ref.data[i];
26✔
77
      final extern = _exporttype_type(exp);
26✔
78
      final kind = _externtype_kind(extern);
26✔
79
      final name = _exporttype_name(exp).ref.toString();
39✔
80
      final type = _externTypeToFuncOrGlobalType(kind, extern);
13✔
81
      exps.add(
13✔
82
        WasmExportDescriptor._(
13✔
83
          kind,
84
          name,
85
          type,
86
          _getImportExportString(kind, name, type),
13✔
87
        ),
88
      );
89
    }
90
    calloc.free(exportsVec);
13✔
91
    return exps;
92
  }
93

94
  List<WasmImportDescriptor> importDescriptors(Pointer<WasmerModule> module) {
13✔
95
    var importsVec = calloc<WasmerImporttypeVec>();
96
    _module_imports(module, importsVec);
26✔
97
    var imps = <WasmImportDescriptor>[];
13✔
98
    for (var i = 0; i < importsVec.ref.length; ++i) {
33✔
99
      final imp = importsVec.ref.data[i];
14✔
100
      final extern = _importtype_type(imp);
14✔
101
      final kind = _externtype_kind(extern);
14✔
102
      final moduleName = _importtype_module(imp).ref.toString();
21✔
103
      final name = _importtype_name(imp).ref.toString();
21✔
104
      final type = _externTypeToFuncOrGlobalType(kind, extern);
7✔
105
      imps.add(
7✔
106
        WasmImportDescriptor._(
7✔
107
          kind,
108
          moduleName,
109
          name,
110
          type,
111
          _getImportExportString(kind, '$moduleName::$name', type),
14✔
112
        ),
113
      );
114
    }
115
    calloc.free(importsVec);
13✔
116
    return imps;
117
  }
118

119
  void maybeThrowTrap(Pointer<WasmerTrap> trap, String source) {
13✔
120
    if (trap != nullptr) {
26✔
121
      // There are 2 kinds of trap, and their memory is managed differently.
122
      // Traps created in the newTrap method below are stored in the traps map
123
      // with a corresponding exception, and their memory is managed using a
124
      // finalizer on the _WasmTrapsEntry. Traps can also be created by WASM
125
      // code, and in that case we delete them in this function.
126
      final trapMessage = calloc<WasmerByteVec>();
127
      _trap_message(trap, trapMessage);
4✔
128
      final message = trapMessage.ref.toString();
2✔
129
      _byte_vec_delete(trapMessage);
4✔
130
      calloc.free(trapMessage);
2✔
131
      final entry = _traps.remove(message);
4✔
132
      if (entry == null) {
133
        // TODO(#87): Report a full stack trace to the user.
134
        throw _WasmRuntimeExceptionImpl(message);
1✔
135
      }
136
      // ignore: only_throw_errors
137
      throw entry.exception;
1✔
138
    }
139
  }
140

141
  Pointer<WasmerInstance> instantiate(
13✔
142
    Object owner,
143
    Pointer<WasmerModule> module,
144
    Pointer<WasmerExternVec> imports,
145
  ) {
146
    var trap = calloc<Pointer<WasmerTrap>>()..value = nullptr;
26✔
147
    var inst = _instance_new(_store, module, imports, trap);
39✔
148
    maybeThrowTrap(trap.value, 'module initialization function');
26✔
149
    calloc.free(trap);
13✔
150
    _checkNotEqual(inst, nullptr, 'Wasm module instantiation failed.');
26✔
151
    _set_finalizer_for_instance(owner, inst);
26✔
152
    return inst;
153
  }
154

155
  // Clean up the exports after use, with deleteExports.
156
  Pointer<WasmerExternVec> exports(Pointer<WasmerInstance> instancePtr) {
13✔
157
    var exports = calloc<WasmerExternVec>();
158
    _instance_exports(instancePtr, exports);
26✔
159
    return exports;
160
  }
161

162
  void deleteExports(Pointer<WasmerExternVec> exports) {
×
163
    _extern_vec_delete(exports);
×
164
    calloc.free(exports);
×
165
  }
166

167
  int externKind(Pointer<WasmerExtern> extern) => _extern_kind(extern);
39✔
168

169
  Pointer<WasmerFunc> externToFunction(Pointer<WasmerExtern> extern) =>
13✔
170
      _extern_as_func(extern);
26✔
171

172
  Pointer<WasmerExtern> functionToExtern(Pointer<WasmerFunc> func) =>
5✔
173
      _func_as_extern(func);
10✔
174

175
  List<int> getArgTypes(Pointer<WasmerFunctype> funcType) {
13✔
176
    var types = <int>[];
13✔
177
    var args = _functype_params(funcType);
26✔
178
    for (var i = 0; i < args.ref.length; ++i) {
36✔
179
      types.add(_valtype_kind(args.ref.data[i]));
50✔
180
    }
181
    return types;
182
  }
183

184
  int getReturnType(Pointer<WasmerFunctype> funcType) {
13✔
185
    var rets = _functype_results(funcType);
26✔
186
    if (rets.ref.length == 0) {
26✔
187
      return wasmerValKindVoid;
188
    } else if (rets.ref.length > 1) {
20✔
189
      throw _WasmRuntimeErrorImpl('Multiple return values are not supported');
×
190
    }
191
    return _valtype_kind(rets.ref.data[0]);
40✔
192
  }
193

194
  void call(
10✔
195
    Pointer<WasmerFunc> func,
196
    Pointer<WasmerValVec> args,
197
    Pointer<WasmerValVec> results,
198
    String source,
199
  ) {
200
    maybeThrowTrap(_func_call(func, args, results), source);
30✔
201
  }
202

203
  Pointer<WasmerMemory> externToMemory(Pointer<WasmerExtern> extern) =>
13✔
204
      _extern_as_memory(extern);
26✔
205

206
  Pointer<WasmerExtern> memoryToExtern(Pointer<WasmerMemory> memory) =>
×
207
      _memory_as_extern(memory);
×
208

209
  Pointer<WasmerMemory> newMemory(
3✔
210
    Object owner,
211
    int pages,
212
    int? maxPages,
213
  ) {
214
    var limPtr = calloc<WasmerLimits>();
215
    limPtr.ref.min = pages;
3✔
216
    limPtr.ref.max = maxPages ?? wasmLimitsMaxDefault;
3✔
217
    var memType = _memorytype_new(limPtr);
6✔
218
    calloc.free(limPtr);
3✔
219
    _checkNotEqual(memType, nullptr, 'Failed to create memory type.');
6✔
220
    _set_finalizer_for_memorytype(owner, memType);
6✔
221
    var memory = _checkNotEqual(
3✔
222
      _memory_new(_store, memType),
9✔
223
      nullptr,
3✔
224
      'Failed to create memory.',
225
    );
226
    _set_finalizer_for_memory(owner, memory);
6✔
227
    return memory;
228
  }
229

230
  void growMemory(Pointer<WasmerMemory> memory, int deltaPages) {
2✔
231
    _checkNotEqual(
2✔
232
      _memory_grow(memory, deltaPages),
4✔
233
      0,
234
      'Failed to grow memory.',
235
    );
236
  }
237

238
  int memoryLength(Pointer<WasmerMemory> memory) => _memory_size(memory);
3✔
239

240
  Uint8List memoryView(Pointer<WasmerMemory> memory) =>
4✔
241
      _memory_data(memory).asTypedList(_memory_data_size(memory));
20✔
242

243
  Pointer<WasmerFunc> newFunc(
5✔
244
    Object owner,
245
    Pointer<WasmerFunctype> funcType,
246
    Pointer func,
247
    Pointer env,
248
    Pointer finalizer,
249
  ) {
250
    var f = _func_new_with_env(
10✔
251
      _store,
5✔
252
      funcType,
253
      func.cast(),
5✔
254
      env.cast(),
5✔
255
      finalizer.cast(),
5✔
256
    );
257
    _checkNotEqual(f, nullptr, 'Failed to create function.');
10✔
258
    _set_finalizer_for_func(owner, f);
10✔
259
    return f;
260
  }
261

262
  Pointer<WasmerVal> newValue(int type, dynamic val) {
1✔
263
    final wasmerVal = calloc<WasmerVal>();
264
    if (!wasmerVal.ref.fill(type, val)) {
1✔
265
      throw _WasmRuntimeErrorImpl(
1✔
266
        'Bad value for WASM type: ${wasmerValKindName(type)}',
2✔
267
      );
268
    }
269
    return wasmerVal;
270
  }
271

272
  Pointer<WasmerGlobal> newGlobal(
1✔
273
    Object owner,
274
    Pointer<WasmerGlobaltype> globalType,
275
    dynamic val,
276
  ) {
277
    final wasmerVal = newValue(getGlobalKind(globalType), val);
2✔
278
    final global = _global_new(_store, globalType, wasmerVal);
3✔
279
    _set_finalizer_for_global(owner, global);
2✔
280
    calloc.free(wasmerVal);
1✔
281
    return global;
282
  }
283

284
  Pointer<WasmerGlobaltype> getGlobalType(Pointer<WasmerGlobal> global) =>
1✔
285
      _global_type(global);
2✔
286

287
  int getGlobalKind(Pointer<WasmerGlobaltype> globalType) =>
1✔
288
      _valtype_kind(_globaltype_content(globalType));
4✔
289

290
  int getGlobalMut(Pointer<WasmerGlobaltype> globalType) =>
1✔
291
      _globaltype_mutability(globalType);
2✔
292

293
  Pointer<WasmerGlobal> externToGlobal(Pointer<WasmerExtern> extern) =>
1✔
294
      _extern_as_global(extern);
2✔
295

296
  Pointer<WasmerExtern> globalToExtern(Pointer<WasmerGlobal> global) =>
1✔
297
      _global_as_extern(global);
2✔
298

299
  dynamic globalGet(Pointer<WasmerGlobal> global, int type) {
1✔
300
    final wasmerVal = newValue(type, 0);
1✔
301
    _global_get(global, wasmerVal);
2✔
302
    final result = wasmerVal.ref.toDynamic;
1✔
303
    calloc.free(wasmerVal);
1✔
304
    return result;
305
  }
306

307
  void globalSet(Pointer<WasmerGlobal> global, int type, dynamic val) {
1✔
308
    final wasmerVal = newValue(type, val);
1✔
309
    _global_set(global, wasmerVal);
2✔
310
    calloc.free(wasmerVal);
1✔
311
  }
312

313
  Pointer<WasmerByteVec> _allocateString(String str) {
1✔
314
    // Allocates both the WasmerByteVec and its internal byte list using calloc.
315
    // The caller is responsible for freeing both.
316
    final strList = utf8.encode(str);
1✔
317
    final bytes = calloc<WasmerByteVec>();
318
    bytes.ref.data = calloc<Uint8>(strList.length);
2✔
319
    for (var i = 0; i < strList.length; ++i) {
3✔
320
      bytes.ref.data[i] = strList[i];
3✔
321
    }
322
    bytes.ref.length = strList.length;
2✔
323
    return bytes;
324
  }
325

326
  Pointer<WasmerTrap> newTrap(Object exception) {
1✔
327
    final msg = 'dart:${exception.hashCode.toRadixString(36)}';
3✔
328
    final bytes = _allocateString(msg);
1✔
329
    var trap = _trap_new(_store, bytes);
3✔
330
    calloc
331
      ..free(bytes.ref.data)
2✔
332
      ..free(bytes);
1✔
333
    _checkNotEqual(trap, nullptr, 'Failed to create trap.');
2✔
334
    var entry = _WasmTrapsEntry(exception);
1✔
335
    _set_finalizer_for_trap(entry, trap);
2✔
336
    _traps[msg] = entry;
2✔
337
    return trap;
338
  }
339

340
  Pointer<WasmerWasiConfig> newWasiConfig() {
2✔
341
    var name = calloc<Uint8>();
342
    name[0] = 0;
2✔
343
    var config = _wasi_config_new(name);
4✔
344
    calloc.free(name);
2✔
345
    return _checkNotEqual(config, nullptr, 'Failed to create WASI config.');
4✔
346
  }
347

348
  void captureWasiStdout(Pointer<WasmerWasiConfig> config) {
1✔
349
    _wasi_config_capture_stdout(config);
2✔
350
  }
351

352
  void captureWasiStderr(Pointer<WasmerWasiConfig> config) {
×
353
    _wasi_config_capture_stderr(config);
×
354
  }
355

356
  Pointer<WasmerWasiEnv> newWasiEnv(Pointer<WasmerWasiConfig> config) =>
2✔
357
      _checkNotEqual(
2✔
358
        _wasi_env_new(config),
4✔
359
        nullptr,
2✔
360
        'Failed to create WASI environment.',
361
      );
362

363
  void getWasiImports(
2✔
364
    Pointer<WasmerModule> mod,
365
    Pointer<WasmerWasiEnv> env,
366
    Pointer<WasmerExternVec> imports,
367
  ) {
368
    _checkNotEqual(
2✔
369
      _wasi_get_imports(_store, mod, env, imports),
6✔
370
      0,
371
      'Failed to fill WASI imports.',
372
    );
373
  }
374

375
  Stream<List<int>> getWasiStdoutStream(Pointer<WasmerWasiEnv> env) =>
1✔
376
      Stream.fromIterable(_WasiStreamIterable(env, _wasi_env_read_stdout));
3✔
377

378
  Stream<List<int>> getWasiStderrStream(Pointer<WasmerWasiEnv> env) =>
×
379
      Stream.fromIterable(_WasiStreamIterable(env, _wasi_env_read_stderr));
×
380

381
  String _getLastError() {
3✔
382
    var length = _wasmer_last_error_length();
6✔
383
    var buf = calloc<Uint8>(length);
384
    _wasmer_last_error_message(buf, length);
6✔
385
    var message = utf8.decode(buf.asTypedList(length));
6✔
386
    calloc.free(buf);
3✔
387
    return message;
388
  }
389

390
  T _checkNotEqual<T>(T x, T y, String errorMessage) {
16✔
391
    if (x == y) {
16✔
392
      throw _WasmRuntimeErrorImpl('$errorMessage\n${_getLastError()}'.trim());
12✔
393
    }
394
    return x;
395
  }
396

397
  String _getImportExportString(
13✔
398
    int kind,
399
    String name,
400
    Pointer type,
401
  ) {
402
    final kindName = wasmerExternKindName(kind);
13✔
403
    if (kind == wasmerExternKindFunction) {
13✔
404
      final funcType = type as Pointer<WasmerFunctype>;
405
      final sig = getSignatureString(
13✔
406
        name,
407
        getArgTypes(funcType),
13✔
408
        getReturnType(funcType),
13✔
409
      );
410
      return '$kindName: $sig';
13✔
411
    } else if (kind == wasmerExternKindGlobal) {
13✔
412
      final globalType = type as Pointer<WasmerGlobaltype>;
413
      final typeName = wasmerValKindName(getGlobalKind(globalType));
2✔
414
      final mutName = wasmerMutabilityName(getGlobalMut(globalType));
2✔
415
      return '$kindName: $mutName $typeName $name';
1✔
416
    } else {
417
      return '$kindName: $name';
13✔
418
    }
419
  }
420
}
421

422
class WasmImportDescriptor {
423
  final int kind;
424
  final String moduleName;
425
  final String name;
426
  final Pointer type;
427
  final String description;
428

429
  const WasmImportDescriptor._(
7✔
430
    this.kind,
431
    this.moduleName,
432
    this.name,
433
    this.type,
434
    this.description,
435
  );
436

437
  @override
2✔
438
  String toString() => description;
2✔
439
}
440

441
class WasmExportDescriptor {
442
  final int kind;
443
  final String name;
444
  final Pointer type;
445
  final String description;
446

447
  const WasmExportDescriptor._(
13✔
448
    this.kind,
449
    this.name,
450
    this.type,
451
    this.description,
452
  );
453

454
  @override
×
455
  String toString() => description;
×
456
}
457

458
class _WasmTrapsEntry {
459
  final Object exception;
460

461
  _WasmTrapsEntry(this.exception);
1✔
462
}
463

464
class _WasiStreamIterator implements Iterator<List<int>> {
465
  static const int _bufferLength = 1024;
466
  final Pointer<Uint8> _buf = calloc<Uint8>(_bufferLength);
467
  final Pointer<WasmerWasiEnv> _env;
468
  final Function _reader;
469
  int _length = 0;
470

471
  _WasiStreamIterator(this._env, this._reader);
1✔
472

473
  @override
1✔
474
  bool moveNext() {
475
    _length = _reader(_env, _buf, _bufferLength) as int;
5✔
476
    return _length >= 0;
2✔
477
  }
478

479
  @override
1✔
480
  List<int> get current => _buf.asTypedList(_length);
3✔
481
}
482

483
class _WasiStreamIterable extends Iterable<List<int>> {
484
  final Pointer<WasmerWasiEnv> _env;
485
  final Function _reader;
486

487
  _WasiStreamIterable(this._env, this._reader);
1✔
488

489
  @override
1✔
490
  Iterator<List<int>> get iterator => _WasiStreamIterator(_env, _reader);
3✔
491
}
492

493
String getSignatureString(
13✔
494
  String name,
495
  List<int> argTypes,
496
  int returnType,
497
) =>
13✔
498
    '${wasmerValKindName(returnType)} '
13✔
499
    '$name(${argTypes.map(wasmerValKindName).join(', ')})';
26✔
500

501
class _WasmRuntimeErrorImpl extends Error implements WasmError {
502
  @override
503
  final String message;
504

505
  _WasmRuntimeErrorImpl(
4✔
506
    this.message,
507
  ) : assert(message.trim() == message);
12✔
508

509
  @override
×
510
  String toString() => 'WasmRuntimeError: $message';
×
511
}
512

513
class _WasmRuntimeExceptionImpl implements WasmException {
514
  @override
515
  final String message;
516

517
  const _WasmRuntimeExceptionImpl(
1✔
518
    this.message,
519
  );
520

521
  @override
×
522
  String toString() => 'WasmRuntimeException: $message';
×
523
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc