• 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

88.56
/wasm/lib/src/module.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:ffi';
6
import 'dart:typed_data';
7

8
import 'package:ffi/ffi.dart';
9

10
import 'runtime.dart';
11
import 'wasm_api.dart';
12
import 'wasmer_api.dart';
13
import 'wasmer_locator.dart';
14

15
/// Creates a new wasm module asynchronously.
16
Future<WasmModule> wasmModuleCompileAsync(
1✔
17
  Uint8List data,
18
) async =>
19
    wasmModuleCompileSync(data);
1✔
20

21
/// Creates a new wasm module synchronously.
22
WasmModule wasmModuleCompileSync(
16✔
23
  Uint8List data,
24
) =>
25
    _WasmModule(data, _runtime);
32✔
26

27
final WasmRuntime _runtime = wasmRuntimeFactory(
48✔
28
  loadWasmerDynamicLibrary(),
16✔
29
);
30

31
class _WasmModule implements WasmModule {
32
  late final Pointer<WasmerModule> _module;
33
  final WasmRuntime runtime;
34

35
  _WasmModule(Uint8List data, this.runtime) {
16✔
36
    _module = runtime.compile(this, data);
47✔
37
  }
38

39
  @override
13✔
40
  _WasmInstanceBuilder builder() => _WasmInstanceBuilder._(this);
13✔
41

42
  @override
3✔
43
  _WasmMemory createMemory(int pages, [int? maxPages]) =>
44
      _WasmMemory._create(pages, maxPages, runtime);
6✔
45

46
  @override
×
47
  String describe() {
48
    final description = StringBuffer();
×
49
    final imports = runtime.importDescriptors(_module);
×
50
    for (final imp in imports) {
×
51
      description.write('import $imp\n');
×
52
    }
53
    final exports = runtime.exportDescriptors(_module);
×
54
    for (final exp in exports) {
×
55
      description.write('export $exp\n');
×
56
    }
57
    return description.toString();
×
58
  }
59
}
60

61
Pointer<WasmerTrap> _wasmFnImportTrampoline(
3✔
62
  Pointer<_WasmFnImport> imp,
63
  Pointer<WasmerValVec> args,
64
  Pointer<WasmerValVec> results,
65
) {
66
  try {
67
    _WasmFnImport._call(imp, args, results);
3✔
68
  } catch (exception) {
69
    return _runtime.newTrap(exception);
2✔
70
  }
71
  return nullptr;
3✔
72
}
73

74
void _wasmFnImportFinalizer(Pointer<_WasmFnImport> imp) {
×
75
  _wasmFnImportToFn.remove(imp.address);
×
76
  calloc.free(imp);
×
77
}
78

79
final _wasmFnImportTrampolineNative = Pointer.fromFunction<
10✔
80
    Pointer<WasmerTrap> Function(
81
      Pointer<_WasmFnImport>,
82
      Pointer<WasmerValVec>,
83
      Pointer<WasmerValVec>,
84
    )>(_wasmFnImportTrampoline);
85
final _wasmFnImportToFn = <int, Function>{};
15✔
86

87
// This will be needed again once #47 is fixed.
88
// ignore: unused_element
89
final _wasmFnImportFinalizerNative =
×
90
    Pointer.fromFunction<Void Function(Pointer<_WasmFnImport>)>(
91
  _wasmFnImportFinalizer,
92
);
93

94
class _WasmFnImport extends Struct {
95
  @Int32()
96
  external int returnType;
97

98
  static void _call(
3✔
99
    Pointer<_WasmFnImport> imp,
100
    Pointer<WasmerValVec> rawArgs,
101
    Pointer<WasmerValVec> rawResult,
102
  ) {
103
    final fn = _wasmFnImportToFn[imp.address] as Function;
9✔
104
    final args = <dynamic>[];
3✔
105
    for (var i = 0; i < rawArgs.ref.length; ++i) {
8✔
106
      args.add(rawArgs.ref.data[i].toDynamic);
6✔
107
    }
108
    assert(
109
      rawResult.ref.length == 1 || imp.ref.returnType == wasmerValKindVoid,
13✔
110
    );
111
    final result = Function.apply(fn, args);
3✔
112
    if (imp.ref.returnType != wasmerValKindVoid) {
6✔
113
      rawResult.ref.data[0].fill(imp.ref.returnType, result);
3✔
114
    }
115
  }
116
}
117

118
class _WasmInstanceBuilder implements WasmInstanceBuilder {
119
  final _importOwner = _WasmImportOwner();
120
  final _importIndex = <String, int>{};
121
  final _imports = calloc<WasmerExternVec>();
122
  final _WasmModule _module;
123
  late final List<WasmImportDescriptor> _importDescs;
124
  Pointer<WasmerWasiEnv> _wasiEnv = nullptr;
125

126
  _WasmInstanceBuilder._(this._module)
13✔
127
      : _importDescs = _module.runtime.importDescriptors(_module._module) {
39✔
128
    _imports.ref.length = _importDescs.length;
52✔
129
    _imports.ref.data = calloc<Pointer<WasmerExtern>>(_importDescs.length);
52✔
130
    for (var i = 0; i < _importDescs.length; ++i) {
46✔
131
      final imp = _importDescs[i];
14✔
132
      _importIndex['${imp.moduleName}::${imp.name}'] = i;
35✔
133
      _imports.ref.data[i] = nullptr;
28✔
134
    }
135
  }
136

137
  int _getIndex(String moduleName, String name) {
6✔
138
    final index = _importIndex['$moduleName::$name'];
18✔
139
    if (index == null) {
140
      throw _WasmModuleErrorImpl('Import not found: $moduleName::$name');
2✔
141
    } else if (_imports.ref.data[index] != nullptr) {
30✔
142
      throw _WasmModuleErrorImpl('Import already filled: $moduleName::$name');
2✔
143
    } else {
144
      return index;
145
    }
146
  }
147

148
  @override
1✔
149
  void addMemory(
150
    String moduleName,
151
    String name,
152
    // As long as WasmMemory is sealed, the use of covariant will be safe here.
153
    covariant _WasmMemory memory,
154
  ) {
155
    final index = _getIndex(moduleName, name);
1✔
156
    final imp = _importDescs[index];
2✔
157
    if (imp.kind != wasmerExternKindMemory) {
2✔
158
      throw _WasmModuleErrorImpl('Import is not a memory: $imp');
2✔
159
    }
160
    _imports.ref.data[index] = _module.runtime.memoryToExtern(memory._mem);
×
161
  }
162

163
  @override
5✔
164
  void addFunction(String moduleName, String name, Function fn) {
165
    final index = _getIndex(moduleName, name);
5✔
166
    final imp = _importDescs[index];
10✔
167

168
    if (imp.kind != wasmerExternKindFunction) {
10✔
169
      throw _WasmModuleErrorImpl('Import is not a function: $imp');
×
170
    }
171

172
    final funcType = imp.type as Pointer<WasmerFunctype>;
5✔
173
    final returnType = _module.runtime.getReturnType(funcType);
15✔
174
    final wasmFnImport = calloc<_WasmFnImport>();
175
    wasmFnImport.ref.returnType = returnType;
5✔
176
    _wasmFnImportToFn[wasmFnImport.address] = fn;
15✔
177
    final fnImp = _module.runtime.newFunc(
15✔
178
      _importOwner,
5✔
179
      funcType,
180
      _wasmFnImportTrampolineNative,
5✔
181
      wasmFnImport,
182
      nullptr, // TODO(#47): Re-enable _wasmFnImportFinalizerNative.
5✔
183
    );
184
    _imports.ref.data[index] = _module.runtime.functionToExtern(fnImp);
30✔
185
  }
186

187
  @override
2✔
188
  _WasmGlobal addGlobal(String moduleName, String name, dynamic val) {
189
    final index = _getIndex(moduleName, name);
2✔
190
    final imp = _importDescs[index];
4✔
191

192
    if (imp.kind != wasmerExternKindGlobal) {
4✔
193
      throw _WasmModuleErrorImpl('Import is not a global: $imp');
2✔
194
    }
195

196
    final globalType = imp.type as Pointer<WasmerGlobaltype>;
1✔
197
    final global = _module.runtime.newGlobal(_importOwner, globalType, val);
4✔
198
    _imports.ref.data[index] = _module.runtime.globalToExtern(global);
6✔
199
    return _WasmGlobal._(
1✔
200
      '${imp.moduleName}::${imp.name}',
3✔
201
      global,
202
      _module.runtime,
2✔
203
    );
204
  }
205

206
  @override
2✔
207
  void enableWasi({
208
    bool captureStdout = false,
209
    bool captureStderr = false,
210
  }) {
211
    if (_wasiEnv != nullptr) {
6✔
212
      throw _WasmModuleErrorImpl('WASI is already enabled.');
1✔
213
    }
214
    final config = _module.runtime.newWasiConfig();
6✔
215
    if (captureStdout) _module.runtime.captureWasiStdout(config);
3✔
216
    if (captureStderr) _module.runtime.captureWasiStderr(config);
×
217
    _wasiEnv = _module.runtime.newWasiEnv(config);
8✔
218
    _module.runtime.getWasiImports(_module._module, _wasiEnv, _imports);
14✔
219
  }
220

221
  @override
13✔
222
  _WasmInstance build() {
223
    for (var i = 0; i < _importDescs.length; ++i) {
46✔
224
      if (_imports.ref.data[i] == nullptr) {
35✔
225
        throw _WasmModuleErrorImpl('Missing import: ${_importDescs[i]}');
8✔
226
      }
227
    }
228
    return _WasmInstance._(_module, _importOwner, _imports, _wasiEnv);
65✔
229
  }
230

231
  @override
1✔
232
  Future<_WasmInstance> buildAsync() async => Future<_WasmInstance>(build);
2✔
233
}
234

235
// TODO: should not be required once the min supported Dart SDK includes
236
//  github.com/dart-lang/sdk/commit/8fd81f72281d9d3aa5ef3890c947cc7305c56a50
237
class _WasmImportOwner {}
238

239
class _WasmInstance implements WasmInstance {
240
  final _WasmImportOwner _importOwner;
241
  final _functions = <String, _WasmFunction>{};
242
  final _globals = <String, _WasmGlobal>{};
243
  final _WasmModule _module;
244
  final Pointer<WasmerWasiEnv> _wasiEnv;
245

246
  late final Pointer<WasmerInstance> _instance;
247

248
  Pointer<WasmerMemory>? _exportedMemory;
249
  Stream<List<int>>? _stdout;
250
  Stream<List<int>>? _stderr;
251

252
  _WasmInstance._(
13✔
253
    this._module,
254
    this._importOwner,
255
    Pointer<WasmerExternVec> imports,
256
    this._wasiEnv,
257
  ) {
258
    _instance = _module.runtime.instantiate(
52✔
259
      _importOwner,
13✔
260
      _module._module,
26✔
261
      imports,
262
    );
263
    final exports = _module.runtime.exports(_instance);
52✔
264
    final exportDescs = _module.runtime.exportDescriptors(_module._module);
65✔
265
    assert(exports.ref.length == exportDescs.length);
52✔
266
    for (var i = 0; i < exports.ref.length; ++i) {
39✔
267
      final e = exports.ref.data[i];
26✔
268
      final kind = _module.runtime.externKind(exports.ref.data[i]);
65✔
269
      final name = exportDescs[i].name;
26✔
270
      if (kind == wasmerExternKindFunction) {
13✔
271
        final f = _module.runtime.externToFunction(e);
39✔
272
        final ft = exportDescs[i].type as Pointer<WasmerFunctype>;
26✔
273
        _functions[name] = _WasmFunction._(
39✔
274
          name,
275
          f,
276
          _module.runtime.getArgTypes(ft),
39✔
277
          _module.runtime.getReturnType(ft),
39✔
278
          _module.runtime,
26✔
279
        );
280
      } else if (kind == wasmerExternKindMemory) {
13✔
281
        // WASM currently allows only one memory per module.
282
        final mem = _module.runtime.externToMemory(e);
39✔
283
        _exportedMemory = mem;
13✔
284
      } else if (kind == wasmerExternKindGlobal) {
1✔
285
        _globals[name] = _WasmGlobal._(
3✔
286
          name,
287
          _module.runtime.externToGlobal(e),
3✔
288
          _module.runtime,
2✔
289
        );
290
      }
291
    }
292
  }
293

294
  @override
11✔
295
  dynamic lookupFunction(String name) => _functions[name];
22✔
296

297
  @override
1✔
298
  _WasmGlobal? lookupGlobal(String name) => _globals[name];
2✔
299

300
  @override
1✔
301
  _WasmMemory get memory {
302
    if (_exportedMemory == null) {
1✔
303
      throw _WasmModuleErrorImpl('Wasm module did not export its memory.');
×
304
    }
305
    return _WasmMemory._fromExport(
1✔
306
      _exportedMemory as Pointer<WasmerMemory>,
1✔
307
      _module.runtime,
2✔
308
    );
309
  }
310

311
  @override
2✔
312
  Stream<List<int>> get stdout {
313
    if (_wasiEnv == nullptr) {
6✔
314
      throw _WasmModuleErrorImpl("Can't capture stdout without WASI enabled.");
1✔
315
    }
316
    return _stdout ??= _module.runtime.getWasiStdoutStream(_wasiEnv);
5✔
317
  }
318

319
  @override
1✔
320
  Stream<List<int>> get stderr {
321
    if (_wasiEnv == nullptr) {
3✔
322
      throw _WasmModuleErrorImpl("Can't capture stderr without WASI enabled.");
1✔
323
    }
324
    return _stderr ??= _module.runtime.getWasiStderrStream(_wasiEnv);
×
325
  }
326
}
327

328
class _WasmMemory implements WasmMemory {
329
  late final Pointer<WasmerMemory> _mem;
330
  late Uint8List _view;
331
  final WasmRuntime runtime;
332

333
  _WasmMemory._fromExport(this._mem, this.runtime) {
1✔
334
    _view = runtime.memoryView(_mem);
4✔
335
  }
336

337
  _WasmMemory._create(int pages, int? maxPages, this.runtime) {
3✔
338
    _mem = runtime.newMemory(this, pages, maxPages);
9✔
339
    _view = runtime.memoryView(_mem);
12✔
340
  }
341

342
  @override
1✔
343
  int get lengthInPages => runtime.memoryLength(_mem);
3✔
344

345
  @override
1✔
346
  int get lengthInBytes => _view.lengthInBytes;
2✔
347

348
  @override
2✔
349
  int operator [](int index) => _view[index];
4✔
350

351
  @override
1✔
352
  void operator []=(int index, int value) {
353
    _view[index] = value;
2✔
354
  }
355

356
  @override
×
357
  Uint8List get view => _view;
×
358

359
  @override
2✔
360
  void grow(int deltaPages) {
361
    runtime.growMemory(_mem, deltaPages);
6✔
362
    _view = runtime.memoryView(_mem);
4✔
363
  }
364
}
365

366
class _WasmFunction implements WasmFunction {
367
  final String _name;
368
  final Pointer<WasmerFunc> _func;
369
  final List<int> _argTypes;
370
  final int _returnType;
371
  final Pointer<WasmerValVec> _args = calloc<WasmerValVec>();
372
  final Pointer<WasmerValVec> _results = calloc<WasmerValVec>();
373
  final WasmRuntime runtime;
374

375
  _WasmFunction._(
13✔
376
    this._name,
377
    this._func,
378
    this._argTypes,
379
    this._returnType,
380
    this.runtime,
381
  ) {
382
    _args.ref.length = _argTypes.length;
52✔
383
    _args.ref.data =
26✔
384
        _argTypes.isEmpty ? nullptr : calloc<WasmerVal>(_argTypes.length);
51✔
385
    _results.ref.length = _returnType == wasmerValKindVoid ? 0 : 1;
52✔
386
    _results.ref.data =
26✔
387
        _returnType == wasmerValKindVoid ? nullptr : calloc<WasmerVal>();
34✔
388
    for (var i = 0; i < _argTypes.length; ++i) {
47✔
389
      _args.ref.data[i].kind = _argTypes[i];
40✔
390
    }
391
  }
392

393
  @override
11✔
394
  String toString() => getSignatureString(_name, _argTypes, _returnType);
44✔
395

396
  @override
11✔
397
  dynamic apply(List<dynamic> args) {
398
    if (args.length != _argTypes.length) {
44✔
399
      throw ArgumentError('Wrong number arguments for WASM function: $this');
2✔
400
    }
401
    for (var i = 0; i < args.length; ++i) {
26✔
402
      if (!_args.ref.data[i].fill(_argTypes[i], args[i])) {
30✔
403
        throw ArgumentError('Bad argument type for WASM function: $this');
2✔
404
      }
405
    }
406
    runtime.call(_func, _args, _results, toString());
60✔
407

408
    if (_returnType == wasmerValKindVoid) {
18✔
409
      return null;
410
    }
411
    final result = _results.ref.data[0];
10✔
412
    assert(_returnType == result.kind);
15✔
413
    return result.toDynamic;
5✔
414
  }
415

416
  @override
11✔
417
  dynamic noSuchMethod(Invocation invocation) {
418
    if (invocation.memberName == #call) {
22✔
419
      return apply(invocation.positionalArguments);
22✔
420
    }
421
    return super.noSuchMethod(invocation);
×
422
  }
423
}
424

425
class _WasmGlobal implements WasmGlobal {
426
  final String _name;
427
  final Pointer<WasmerGlobal> _global;
428
  final int _type;
429
  final int _mut;
430
  final WasmRuntime runtime;
431

432
  _WasmGlobal._(this._name, this._global, this.runtime)
1✔
433
      : _type = runtime.getGlobalKind(runtime.getGlobalType(_global)),
2✔
434
        _mut = runtime.getGlobalMut(runtime.getGlobalType(_global));
2✔
435

436
  @override
1✔
437
  String toString() =>
438
      '${wasmerMutabilityName(_mut)} ${wasmerValKindName(_type)} $_name';
6✔
439

440
  @override
1✔
441
  dynamic get value => runtime.globalGet(_global, _type);
4✔
442

443
  @override
1✔
444
  set value(dynamic val) {
445
    if (_mut == wasmerMutabilityConst) {
2✔
446
      throw _WasmModuleErrorImpl("Can't set value of const global: $this");
2✔
447
    }
448
    runtime.globalSet(_global, _type, val);
4✔
449
  }
450
}
451

452
class _WasmModuleErrorImpl extends Error implements WasmError {
453
  @override
454
  final String message;
455

456
  _WasmModuleErrorImpl(
3✔
457
    this.message,
458
  ) : assert(message.trim() == message);
9✔
459

460
  @override
×
461
  String toString() => 'WasmModuleError: $message';
×
462
}
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