pkg:wasm - more cleanup

- Added documentation to many members. Cleaned up existing documentation to
  follow standards.
- Made many more fields final.
- Hide (made private) constructors that a user cannot reasonably invoke.
  - WasmFunction, WasmInstance, WasmExportDescriptor, WasmImportDescriptor
- Added, exported, and use new WasmError class.
- Remove the fluent API in builder
  See https://dart.dev/guides/language/effective-dart/design#avoid-returning-this-from-methods-just-to-enable-a-fluent-interface

Change-Id: I4222f7a95a9b20e89728d71de56b02ec91601e25
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/202360
Reviewed-by: Liam Appelbe <liama@google.com>
Commit-Queue: Liam Appelbe <liama@google.com>
Auto-Submit: Kevin Moore <kevmoo@google.com>
This commit is contained in:
Kevin Moore 2021-06-04 19:21:51 +00:00
parent 9b56b4b858
commit 666f1ab898
25 changed files with 507 additions and 456 deletions

View file

@ -13,6 +13,7 @@ linter:
- avoid_redundant_argument_values
- avoid_renaming_method_parameters
- avoid_returning_null_for_void
- avoid_returning_this
- avoid_unused_constructor_parameters
- await_only_futures
- cancel_subscriptions
@ -25,6 +26,7 @@ linter:
- implementation_imports
- iterable_contains_unrelated_type
- join_return_with_assignment
- library_private_types_in_public_api
- lines_longer_than_80_chars
- list_remove_unrelated_type
- missing_whitespace_between_adjacent_strings

View file

@ -61,7 +61,7 @@ Uri _getSdkDir() {
Uri _getOutDir(Uri root) {
final pkgRoot = packageRootUri(root);
if (pkgRoot == null) {
throw Exception('$pkgConfigFile not found');
throw ArgumentError('Could not find "$pkgConfigFile" within "$root".');
}
return pkgRoot.resolve(wasmToolDir);
}

View file

@ -12,25 +12,42 @@ import 'dart:typed_data';
import 'package:wasm/wasm.dart';
// Brotli compression parameters.
const int kDefaultQuality = 11;
const int kDefaultWindow = 22;
const int kDefaultMode = 0;
const _kDefaultQuality = 11;
const _kDefaultWindow = 22;
const _kDefaultMode = 0;
void main(List<String> args) {
if (args.length != 1) {
print('Requires one argument: a path to the input file.');
exitCode = 64; // bad usage
return;
}
final inputFilePath = args.single;
print('Loading "$inputFilePath"...');
var inputDataFile = File(inputFilePath);
if (!inputDataFile.existsSync()) {
print('Input file "$inputFilePath" does not exist.');
exitCode = 66; // no input file
return;
}
var inputData = inputDataFile.readAsBytesSync();
print('Input size: ${inputData.length} bytes');
print('\nLoading wasm module');
var brotliPath = Platform.script.resolve('libbrotli.wasm');
var moduleData = File(brotliPath.path).readAsBytesSync();
var module = WasmModule(moduleData);
print(module.describe());
var instance = module.instantiate().enableWasi().build();
var builder = module.builder()..enableWasi();
var instance = builder.build();
var memory = instance.memory;
var compress = instance.lookupFunction('BrotliEncoderCompress');
var decompress = instance.lookupFunction('BrotliDecoderDecompress');
print('Loading ${args[0]}');
var inputData = File(args[0]).readAsBytesSync();
print('Input size: ${inputData.length} bytes');
// Grow the module's memory to get unused space to put our data.
// [initial memory][input data][output data][size][decoded data][size]
var inputPtr = memory.lengthInBytes;
@ -52,9 +69,9 @@ void main(List<String> args) {
print('\nCompressing...');
var status = compress(
kDefaultQuality,
kDefaultWindow,
kDefaultMode,
_kDefaultQuality,
_kDefaultWindow,
_kDefaultMode,
inputData.length,
inputPtr,
outSizePtr,

View file

@ -1,94 +0,0 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:ffi';
import 'package:ffi/ffi.dart';
import 'runtime.dart';
import 'wasmer_api.dart';
/// WasmFunction is a callable function from a WasmInstance.
class WasmFunction {
final String _name;
final Pointer<WasmerFunc> _func;
final List<int> _argTypes;
final int _returnType;
final Pointer<WasmerValVec> _args = calloc<WasmerValVec>();
final Pointer<WasmerValVec> _results = calloc<WasmerValVec>();
WasmFunction(this._name, this._func, this._argTypes, this._returnType) {
_args.ref.length = _argTypes.length;
_args.ref.data =
_argTypes.isEmpty ? nullptr : calloc<WasmerVal>(_argTypes.length);
_results.ref.length = _returnType == WasmerValKindVoid ? 0 : 1;
_results.ref.data =
_returnType == WasmerValKindVoid ? nullptr : calloc<WasmerVal>();
for (var i = 0; i < _argTypes.length; ++i) {
_args.ref.data[i].kind = _argTypes[i];
}
}
@override
String toString() =>
WasmRuntime.getSignatureString(_name, _argTypes, _returnType);
bool _fillArg(dynamic arg, int i) {
switch (_argTypes[i]) {
case WasmerValKindI32:
if (arg is! int) return false;
_args.ref.data[i].i32 = arg;
return true;
case WasmerValKindI64:
if (arg is! int) return false;
_args.ref.data[i].i64 = arg;
return true;
case WasmerValKindF32:
if (arg is! num) return false;
_args.ref.data[i].f32 = arg;
return true;
case WasmerValKindF64:
if (arg is! num) return false;
_args.ref.data[i].f64 = arg;
return true;
}
return false;
}
dynamic apply(List<dynamic> args) {
if (args.length != _argTypes.length) {
throw ArgumentError('Wrong number arguments for WASM function: $this');
}
for (var i = 0; i < args.length; ++i) {
if (!_fillArg(args[i], i)) {
throw ArgumentError('Bad argument type for WASM function: $this');
}
}
WasmRuntime().call(_func, _args, _results, toString());
if (_returnType == WasmerValKindVoid) {
return null;
}
var result = _results.ref.data[0];
assert(_returnType == result.kind);
switch (_returnType) {
case WasmerValKindI32:
return result.i32;
case WasmerValKindI64:
return result.i64;
case WasmerValKindF32:
return result.f32;
case WasmerValKindF64:
return result.f64;
}
}
@override
dynamic noSuchMethod(Invocation invocation) {
if (invocation.memberName == #call) {
return apply(invocation.positionalArguments);
}
return super.noSuchMethod(invocation);
}
}

View file

@ -7,14 +7,14 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'function.dart';
import 'runtime.dart';
import 'wasm_error.dart';
import 'wasmer_api.dart';
/// WasmModule is a compiled module that can be instantiated.
/// A compiled module that can be instantiated.
class WasmModule {
late Pointer<WasmerStore> _store;
late Pointer<WasmerModule> _module;
late final Pointer<WasmerStore> _store;
late final Pointer<WasmerModule> _module;
/// Compile a module.
WasmModule(Uint8List data) {
@ -23,9 +23,9 @@ class WasmModule {
_module = runtime.compile(this, _store, data);
}
/// Returns a WasmInstanceBuilder that is used to add all the imports that the
/// module needs, and then instantiate it.
WasmInstanceBuilder instantiate() => WasmInstanceBuilder(this);
/// Returns a [WasmInstanceBuilder] that is used to add all the imports that
/// the module needs before instantiating it.
WasmInstanceBuilder builder() => WasmInstanceBuilder._(this);
/// Create a new memory with the given number of initial pages, and optional
/// maximum number of pages.
@ -96,22 +96,22 @@ class _WasmFnImport extends Struct {
args.add(rawArgs.ref.data[i].toDynamic);
}
assert(
rawResult.ref.length == 1 || imp.ref.returnType == WasmerValKindVoid,
rawResult.ref.length == 1 || imp.ref.returnType == wasmerValKindVoid,
);
var result = Function.apply(fn, args);
if (imp.ref.returnType != WasmerValKindVoid) {
if (imp.ref.returnType != wasmerValKindVoid) {
rawResult.ref.data[0].kind = imp.ref.returnType;
switch (imp.ref.returnType) {
case WasmerValKindI32:
case wasmerValKindI32:
rawResult.ref.data[0].i32 = result as int;
break;
case WasmerValKindI64:
case wasmerValKindI64:
rawResult.ref.data[0].i64 = result as int;
break;
case WasmerValKindF32:
case wasmerValKindF32:
rawResult.ref.data[0].f32 = result as int;
break;
case WasmerValKindF64:
case wasmerValKindF64:
rawResult.ref.data[0].f64 = result as int;
break;
}
@ -119,20 +119,18 @@ class _WasmFnImport extends Struct {
}
}
class _WasmImportOwner {}
/// WasmInstanceBuilder is used collect all the imports that a WasmModule
/// requires before it is instantiated.
/// Used to collect all of the imports that a [WasmModule] requires before it is
/// built.
class WasmInstanceBuilder {
final _importOwner = _WasmImportOwner();
final _importIndex = <String, int>{};
final _imports = calloc<WasmerExternVec>();
final WasmModule _module;
late List<WasmImportDescriptor> _importDescs;
final Map<String, int> _importIndex;
final Pointer<WasmerExternVec> _imports = calloc<WasmerExternVec>();
late final List<WasmImportDescriptor> _importDescs;
Pointer<WasmerWasiEnv> _wasiEnv = nullptr;
final _WasmImportOwner _importOwner = _WasmImportOwner();
WasmInstanceBuilder(this._module) : _importIndex = {} {
_importDescs = WasmRuntime().importDescriptors(_module._module);
WasmInstanceBuilder._(this._module)
: _importDescs = WasmRuntime().importDescriptors(_module._module) {
_imports.ref.length = _importDescs.length;
_imports.ref.data = calloc<Pointer<WasmerExtern>>(_importDescs.length);
for (var i = 0; i < _importDescs.length; ++i) {
@ -145,39 +143,38 @@ class WasmInstanceBuilder {
int _getIndex(String moduleName, String name) {
var index = _importIndex['$moduleName::$name'];
if (index == null) {
throw Exception('Import not found: $moduleName::$name');
throw WasmError('Import not found: $moduleName::$name');
} else if (_imports.ref.data[index] != nullptr) {
throw Exception('Import already filled: $moduleName::$name');
throw WasmError('Import already filled: $moduleName::$name');
} else {
return index;
}
}
/// Add a WasmMemory to the imports.
WasmInstanceBuilder addMemory(
void addMemory(
String moduleName,
String name,
WasmMemory memory,
) {
var index = _getIndex(moduleName, name);
var imp = _importDescs[index];
if (imp.kind != WasmerExternKindMemory) {
throw Exception('Import is not a memory: $imp');
if (imp.kind != wasmerExternKindMemory) {
throw WasmError('Import is not a memory: $imp');
}
_imports.ref.data[index] = WasmRuntime().memoryToExtern(memory._mem);
return this;
}
/// Add a function to the imports.
WasmInstanceBuilder addFunction(String moduleName, String name, Function fn) {
void addFunction(String moduleName, String name, Function fn) {
var index = _getIndex(moduleName, name);
var imp = _importDescs[index];
var runtime = WasmRuntime();
if (imp.kind != WasmerExternKindFunction) {
throw Exception('Import is not a function: $imp');
if (imp.kind != wasmerExternKindFunction) {
throw WasmError('Import is not a function: $imp');
}
var runtime = WasmRuntime();
var returnType = runtime.getReturnType(imp.funcType);
var wasmFnImport = calloc<_WasmFnImport>();
wasmFnImport.ref.returnType = returnType;
@ -192,16 +189,15 @@ class WasmInstanceBuilder {
_wasmFnImportFinalizerNative,
);
_imports.ref.data[index] = runtime.functionToExtern(fnImp);
return this;
}
/// Enable WASI and add the default WASI imports.
WasmInstanceBuilder enableWasi({
void enableWasi({
bool captureStdout = false,
bool captureStderr = false,
}) {
if (_wasiEnv != nullptr) {
throw Exception('WASI is already enabled.');
throw WasmError('WASI is already enabled.');
}
var runtime = WasmRuntime();
var config = runtime.newWasiConfig();
@ -209,38 +205,51 @@ class WasmInstanceBuilder {
if (captureStderr) runtime.captureWasiStderr(config);
_wasiEnv = runtime.newWasiEnv(config);
runtime.getWasiImports(_module._store, _module._module, _wasiEnv, _imports);
return this;
}
/// Build the module instance.
WasmInstance build() {
for (var i = 0; i < _importDescs.length; ++i) {
if (_imports.ref.data[i] == nullptr) {
throw Exception('Missing import: ${_importDescs[i]}');
throw WasmError('Missing import: ${_importDescs[i]}');
}
}
return WasmInstance(_module, _imports, _wasiEnv);
return WasmInstance._(_module, _importOwner, _imports, _wasiEnv);
}
}
/// WasmInstance is an instantiated WasmModule.
// TODO: should not be required once the min supported Dart SDK includes
// github.com/dart-lang/sdk/commit/8fd81f72281d9d3aa5ef3890c947cc7305c56a50
class _WasmImportOwner {}
/// An instantiated [WasmModule].
///
/// Created by calling [WasmInstanceBuilder.build].
class WasmInstance {
final _WasmImportOwner _importOwner;
final _functions = <String, WasmFunction>{};
final WasmModule _module;
late Pointer<WasmerInstance> _instance;
Pointer<WasmerMemory>? _exportedMemory;
final Pointer<WasmerWasiEnv> _wasiEnv;
late final Pointer<WasmerInstance> _instance;
Pointer<WasmerMemory>? _exportedMemory;
Stream<List<int>>? _stdout;
Stream<List<int>>? _stderr;
final Map<String, WasmFunction> _functions = {};
WasmInstance(
WasmInstance._(
this._module,
this._importOwner,
Pointer<WasmerExternVec> imports,
this._wasiEnv,
) {
var runtime = WasmRuntime();
_instance =
runtime.instantiate(this, _module._store, _module._module, imports);
_instance = runtime.instantiate(
_importOwner,
_module._store,
_module._module,
imports,
);
var exports = runtime.exports(_instance);
var exportDescs = runtime.exportDescriptors(_module._module);
assert(exports.ref.length == exportDescs.length);
@ -248,16 +257,16 @@ class WasmInstance {
var e = exports.ref.data[i];
var kind = runtime.externKind(exports.ref.data[i]);
var name = exportDescs[i].name;
if (kind == WasmerExternKindFunction) {
if (kind == wasmerExternKindFunction) {
var f = runtime.externToFunction(e);
var ft = exportDescs[i].funcType;
_functions[name] = WasmFunction(
_functions[name] = WasmFunction._(
name,
f,
runtime.getArgTypes(ft),
runtime.getReturnType(ft),
);
} else if (kind == WasmerExternKindMemory) {
} else if (kind == wasmerExternKindMemory) {
// WASM currently allows only one memory per module.
var mem = runtime.externToMemory(e);
_exportedMemory = mem;
@ -268,40 +277,50 @@ class WasmInstance {
}
}
/// Searches the instantiated module for the given function. Returns null if
/// it is not found.
/// Searches the instantiated module for the given function.
///
/// Returns a [WasmFunction], but the return type is [dynamic] to allow
/// easy invocation as a [Function].
///
/// Returns `null` if no function exists with name [name].
dynamic lookupFunction(String name) => _functions[name];
/// Returns the memory exported from this instance.
WasmMemory get memory {
if (_exportedMemory == null) {
throw Exception('Wasm module did not export its memory.');
throw WasmError('Wasm module did not export its memory.');
}
return WasmMemory._fromExport(_exportedMemory as Pointer<WasmerMemory>);
}
/// Returns a stream that reads from stdout. To use this, you must enable WASI
/// when instantiating the module, and set captureStdout to true.
/// Returns a stream that reads from `stdout`.
///
/// To use this, you must enable WASI when instantiating the module, and set
/// `captureStdout` to `true`.
Stream<List<int>> get stdout {
if (_wasiEnv == nullptr) {
throw Exception("Can't capture stdout without WASI enabled.");
throw WasmError("Can't capture stdout without WASI enabled.");
}
return _stdout ??= WasmRuntime().getWasiStdoutStream(_wasiEnv);
}
/// Returns a stream that reads from stderr. To use this, you must enable WASI
/// when instantiating the module, and set captureStderr to true.
/// Returns a stream that reads from `stderr`.
///
/// To use this, you must enable WASI when instantiating the module, and set
/// `captureStderr` to `true`.
Stream<List<int>> get stderr {
if (_wasiEnv == nullptr) {
throw Exception("Can't capture stderr without WASI enabled.");
throw WasmError("Can't capture stderr without WASI enabled.");
}
return _stderr ??= WasmRuntime().getWasiStderrStream(_wasiEnv);
}
}
/// WasmMemory contains the memory of a WasmInstance.
/// Memory of a [WasmInstance].
///
/// Access via [WasmInstance.memory] or create via [WasmModule.createMemory].
class WasmMemory {
late Pointer<WasmerMemory> _mem;
late final Pointer<WasmerMemory> _mem;
late Uint8List _view;
WasmMemory._fromExport(this._mem) {
@ -318,13 +337,13 @@ class WasmMemory {
/// The WASM spec defines the page size as 64KiB.
static const int kPageSizeInBytes = 64 * 1024;
/// Returns the length of the memory in pages.
/// The length of the memory in pages.
int get lengthInPages => WasmRuntime().memoryLength(_mem);
/// Returns the length of the memory in bytes.
/// The length of the memory in bytes.
int get lengthInBytes => _view.lengthInBytes;
/// Returns the byte at the given index.
/// The byte at the given [index].
int operator [](int index) => _view[index];
/// Sets the byte at the given index to value.
@ -332,12 +351,99 @@ class WasmMemory {
_view[index] = value;
}
/// Returns a Uint8List view into the memory.
/// A view into the memory.
Uint8List get view => _view;
/// Grow the memory by deltaPages.
/// Grow the memory by [deltaPages] and invalidates any existing views into
/// the memory.
void grow(int deltaPages) {
var runtime = WasmRuntime()..growMemory(_mem, deltaPages);
_view = runtime.memoryView(_mem);
}
}
/// A callable function from a [WasmInstance].
///
/// Access by calling [WasmInstance.lookupFunction].
class WasmFunction {
final String _name;
final Pointer<WasmerFunc> _func;
final List<int> _argTypes;
final int _returnType;
final Pointer<WasmerValVec> _args = calloc<WasmerValVec>();
final Pointer<WasmerValVec> _results = calloc<WasmerValVec>();
WasmFunction._(this._name, this._func, this._argTypes, this._returnType) {
_args.ref.length = _argTypes.length;
_args.ref.data =
_argTypes.isEmpty ? nullptr : calloc<WasmerVal>(_argTypes.length);
_results.ref.length = _returnType == wasmerValKindVoid ? 0 : 1;
_results.ref.data =
_returnType == wasmerValKindVoid ? nullptr : calloc<WasmerVal>();
for (var i = 0; i < _argTypes.length; ++i) {
_args.ref.data[i].kind = _argTypes[i];
}
}
@override
String toString() =>
WasmRuntime.getSignatureString(_name, _argTypes, _returnType);
bool _fillArg(dynamic arg, int i) {
switch (_argTypes[i]) {
case wasmerValKindI32:
if (arg is! int) return false;
_args.ref.data[i].i32 = arg;
return true;
case wasmerValKindI64:
if (arg is! int) return false;
_args.ref.data[i].i64 = arg;
return true;
case wasmerValKindF32:
if (arg is! num) return false;
_args.ref.data[i].f32 = arg;
return true;
case wasmerValKindF64:
if (arg is! num) return false;
_args.ref.data[i].f64 = arg;
return true;
}
return false;
}
dynamic apply(List<dynamic> args) {
if (args.length != _argTypes.length) {
throw ArgumentError('Wrong number arguments for WASM function: $this');
}
for (var i = 0; i < args.length; ++i) {
if (!_fillArg(args[i], i)) {
throw ArgumentError('Bad argument type for WASM function: $this');
}
}
WasmRuntime().call(_func, _args, _results, toString());
if (_returnType == wasmerValKindVoid) {
return null;
}
var result = _results.ref.data[0];
assert(_returnType == result.kind);
switch (_returnType) {
case wasmerValKindI32:
return result.i32;
case wasmerValKindI64:
return result.i64;
case wasmerValKindF32:
return result.f32;
case wasmerValKindF64:
return result.f64;
}
}
@override
dynamic noSuchMethod(Invocation invocation) {
if (invocation.memberName == #call) {
return apply(invocation.positionalArguments);
}
return super.noSuchMethod(invocation);
}
}

View file

@ -11,22 +11,23 @@ import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'shared.dart';
import 'wasm_error.dart';
import 'wasmer_api.dart';
part 'runtime.g.dart';
class WasmImportDescriptor {
int kind;
String moduleName;
String name;
Pointer<WasmerFunctype> funcType;
final int kind;
final String moduleName;
final String name;
final Pointer<WasmerFunctype> funcType;
WasmImportDescriptor(this.kind, this.moduleName, this.name, this.funcType);
WasmImportDescriptor._(this.kind, this.moduleName, this.name, this.funcType);
@override
String toString() {
var kindName = wasmerExternKindName(kind);
if (kind == WasmerExternKindFunction) {
if (kind == wasmerExternKindFunction) {
var runtime = WasmRuntime();
var sig = WasmRuntime.getSignatureString(
'$moduleName::$name',
@ -41,16 +42,16 @@ class WasmImportDescriptor {
}
class WasmExportDescriptor {
int kind;
String name;
Pointer<WasmerFunctype> funcType;
final int kind;
final String name;
final Pointer<WasmerFunctype> funcType;
WasmExportDescriptor(this.kind, this.name, this.funcType);
WasmExportDescriptor._(this.kind, this.name, this.funcType);
@override
String toString() {
var kindName = wasmerExternKindName(kind);
if (kind == WasmerExternKindFunction) {
if (kind == wasmerExternKindFunction) {
var runtime = WasmRuntime();
var sig = WasmRuntime.getSignatureString(
name,
@ -65,16 +66,16 @@ class WasmExportDescriptor {
}
class _WasmTrapsEntry {
Object exception;
final Object exception;
_WasmTrapsEntry(this.exception);
}
class _WasiStreamIterator implements Iterator<List<int>> {
static const int _bufferLength = 1024;
final Pointer<Uint8> _buf = calloc<Uint8>(_bufferLength);
final Pointer<WasmerWasiEnv> _env;
final Function _reader;
final Pointer<Uint8> _buf = calloc<Uint8>(_bufferLength);
int _length = 0;
_WasiStreamIterator(this._env, this._reader);
@ -103,7 +104,7 @@ String _getLibName() {
if (Platform.isMacOS) return appleLib;
if (Platform.isLinux) return linuxLib;
// TODO(dartbug.com/37882): Support more platforms.
throw Exception('Wasm not currently supported on this platform');
throw WasmError('Wasm not currently supported on this platform');
}
String? _getLibPathFrom(Uri root) {
@ -117,5 +118,5 @@ String _getLibPath() {
if (path != null) return path;
path = _getLibPathFrom(Directory.current.uri);
if (path != null) return path;
throw Exception('Wasm library not found. Did you `$invocationString`?');
throw WasmError('Wasm library not found. Did you `$invocationString`?');
}

View file

@ -13,98 +13,99 @@
part of 'runtime.dart';
class WasmRuntime {
static WasmRuntime? _inst;
static final WasmRuntime _inst = WasmRuntime._init();
final DynamicLibrary _lib;
final _traps = <int, _WasmTrapsEntry>{};
late final Pointer<WasmerEngine> _engine;
DynamicLibrary _lib;
late Pointer<WasmerEngine> _engine;
Map<int, _WasmTrapsEntry> traps = {};
late final WasmerDartInitializeApiDLFn _Dart_InitializeApiDL;
late final WasmerSetFinalizerForEngineFn _set_finalizer_for_engine;
late final WasmerSetFinalizerForFuncFn _set_finalizer_for_func;
late final WasmerSetFinalizerForInstanceFn _set_finalizer_for_instance;
late final WasmerSetFinalizerForMemoryFn _set_finalizer_for_memory;
late final WasmerSetFinalizerForMemorytypeFn _set_finalizer_for_memorytype;
late final WasmerSetFinalizerForModuleFn _set_finalizer_for_module;
late final WasmerSetFinalizerForStoreFn _set_finalizer_for_store;
late final WasmerSetFinalizerForTrapFn _set_finalizer_for_trap;
late final WasmerWasiConfigInheritStderrFn _wasi_config_inherit_stderr;
late final WasmerWasiConfigInheritStdoutFn _wasi_config_inherit_stdout;
late final WasmerWasiConfigNewFn _wasi_config_new;
late final WasmerWasiEnvDeleteFn _wasi_env_delete;
late final WasmerWasiEnvNewFn _wasi_env_new;
late final WasmerWasiEnvReadStderrFn _wasi_env_read_stderr;
late final WasmerWasiEnvReadStdoutFn _wasi_env_read_stdout;
late final WasmerWasiEnvSetMemoryFn _wasi_env_set_memory;
late final WasmerWasiGetImportsFn _wasi_get_imports;
late final WasmerByteVecDeleteFn _byte_vec_delete;
late final WasmerByteVecNewFn _byte_vec_new;
late final WasmerByteVecNewEmptyFn _byte_vec_new_empty;
late final WasmerByteVecNewUninitializedFn _byte_vec_new_uninitialized;
late final WasmerEngineDeleteFn _engine_delete;
late final WasmerEngineNewFn _engine_new;
late final WasmerExporttypeNameFn _exporttype_name;
late final WasmerExporttypeTypeFn _exporttype_type;
late final WasmerExporttypeVecDeleteFn _exporttype_vec_delete;
late final WasmerExporttypeVecNewFn _exporttype_vec_new;
late final WasmerExporttypeVecNewEmptyFn _exporttype_vec_new_empty;
late final WasmerExporttypeVecNewUninitializedFn
_exporttype_vec_new_uninitialized;
late final WasmerExternAsFuncFn _extern_as_func;
late final WasmerExternAsMemoryFn _extern_as_memory;
late final WasmerExternDeleteFn _extern_delete;
late final WasmerExternKindFn _extern_kind;
late final WasmerExternVecDeleteFn _extern_vec_delete;
late final WasmerExternVecNewFn _extern_vec_new;
late final WasmerExternVecNewEmptyFn _extern_vec_new_empty;
late final WasmerExternVecNewUninitializedFn _extern_vec_new_uninitialized;
late final WasmerExterntypeAsFunctypeFn _externtype_as_functype;
late final WasmerExterntypeDeleteFn _externtype_delete;
late final WasmerExterntypeKindFn _externtype_kind;
late final WasmerFuncAsExternFn _func_as_extern;
late final WasmerFuncCallFn _func_call;
late final WasmerFuncDeleteFn _func_delete;
late final WasmerFuncNewWithEnvFn _func_new_with_env;
late final WasmerFunctypeDeleteFn _functype_delete;
late final WasmerFunctypeParamsFn _functype_params;
late final WasmerFunctypeResultsFn _functype_results;
late final WasmerImporttypeModuleFn _importtype_module;
late final WasmerImporttypeNameFn _importtype_name;
late final WasmerImporttypeTypeFn _importtype_type;
late final WasmerImporttypeVecDeleteFn _importtype_vec_delete;
late final WasmerImporttypeVecNewFn _importtype_vec_new;
late final WasmerImporttypeVecNewEmptyFn _importtype_vec_new_empty;
late final WasmerImporttypeVecNewUninitializedFn
_importtype_vec_new_uninitialized;
late final WasmerInstanceDeleteFn _instance_delete;
late final WasmerInstanceExportsFn _instance_exports;
late final WasmerInstanceNewFn _instance_new;
late final WasmerMemoryAsExternFn _memory_as_extern;
late final WasmerMemoryDataFn _memory_data;
late final WasmerMemoryDataSizeFn _memory_data_size;
late final WasmerMemoryDeleteFn _memory_delete;
late final WasmerMemoryGrowFn _memory_grow;
late final WasmerMemoryNewFn _memory_new;
late final WasmerMemorySizeFn _memory_size;
late final WasmerMemorytypeDeleteFn _memorytype_delete;
late final WasmerMemorytypeNewFn _memorytype_new;
late final WasmerModuleDeleteFn _module_delete;
late final WasmerModuleExportsFn _module_exports;
late final WasmerModuleImportsFn _module_imports;
late final WasmerModuleNewFn _module_new;
late final WasmerStoreDeleteFn _store_delete;
late final WasmerStoreNewFn _store_new;
late final WasmerTrapDeleteFn _trap_delete;
late final WasmerTrapMessageFn _trap_message;
late final WasmerTrapNewFn _trap_new;
late final WasmerValtypeDeleteFn _valtype_delete;
late final WasmerValtypeKindFn _valtype_kind;
late final WasmerValtypeVecDeleteFn _valtype_vec_delete;
late final WasmerValtypeVecNewFn _valtype_vec_new;
late final WasmerValtypeVecNewEmptyFn _valtype_vec_new_empty;
late final WasmerValtypeVecNewUninitializedFn _valtype_vec_new_uninitialized;
late final WasmerWasmerLastErrorLengthFn _wasmer_last_error_length;
late final WasmerWasmerLastErrorMessageFn _wasmer_last_error_message;
late WasmerDartInitializeApiDLFn _Dart_InitializeApiDL;
late WasmerSetFinalizerForEngineFn _set_finalizer_for_engine;
late WasmerSetFinalizerForFuncFn _set_finalizer_for_func;
late WasmerSetFinalizerForInstanceFn _set_finalizer_for_instance;
late WasmerSetFinalizerForMemoryFn _set_finalizer_for_memory;
late WasmerSetFinalizerForMemorytypeFn _set_finalizer_for_memorytype;
late WasmerSetFinalizerForModuleFn _set_finalizer_for_module;
late WasmerSetFinalizerForStoreFn _set_finalizer_for_store;
late WasmerSetFinalizerForTrapFn _set_finalizer_for_trap;
late WasmerWasiConfigInheritStderrFn _wasi_config_inherit_stderr;
late WasmerWasiConfigInheritStdoutFn _wasi_config_inherit_stdout;
late WasmerWasiConfigNewFn _wasi_config_new;
late WasmerWasiEnvDeleteFn _wasi_env_delete;
late WasmerWasiEnvNewFn _wasi_env_new;
late WasmerWasiEnvReadStderrFn _wasi_env_read_stderr;
late WasmerWasiEnvReadStdoutFn _wasi_env_read_stdout;
late WasmerWasiEnvSetMemoryFn _wasi_env_set_memory;
late WasmerWasiGetImportsFn _wasi_get_imports;
late WasmerByteVecDeleteFn _byte_vec_delete;
late WasmerByteVecNewFn _byte_vec_new;
late WasmerByteVecNewEmptyFn _byte_vec_new_empty;
late WasmerByteVecNewUninitializedFn _byte_vec_new_uninitialized;
late WasmerEngineDeleteFn _engine_delete;
late WasmerEngineNewFn _engine_new;
late WasmerExporttypeNameFn _exporttype_name;
late WasmerExporttypeTypeFn _exporttype_type;
late WasmerExporttypeVecDeleteFn _exporttype_vec_delete;
late WasmerExporttypeVecNewFn _exporttype_vec_new;
late WasmerExporttypeVecNewEmptyFn _exporttype_vec_new_empty;
late WasmerExporttypeVecNewUninitializedFn _exporttype_vec_new_uninitialized;
late WasmerExternAsFuncFn _extern_as_func;
late WasmerExternAsMemoryFn _extern_as_memory;
late WasmerExternDeleteFn _extern_delete;
late WasmerExternKindFn _extern_kind;
late WasmerExternVecDeleteFn _extern_vec_delete;
late WasmerExternVecNewFn _extern_vec_new;
late WasmerExternVecNewEmptyFn _extern_vec_new_empty;
late WasmerExternVecNewUninitializedFn _extern_vec_new_uninitialized;
late WasmerExterntypeAsFunctypeFn _externtype_as_functype;
late WasmerExterntypeDeleteFn _externtype_delete;
late WasmerExterntypeKindFn _externtype_kind;
late WasmerFuncAsExternFn _func_as_extern;
late WasmerFuncCallFn _func_call;
late WasmerFuncDeleteFn _func_delete;
late WasmerFuncNewWithEnvFn _func_new_with_env;
late WasmerFunctypeDeleteFn _functype_delete;
late WasmerFunctypeParamsFn _functype_params;
late WasmerFunctypeResultsFn _functype_results;
late WasmerImporttypeModuleFn _importtype_module;
late WasmerImporttypeNameFn _importtype_name;
late WasmerImporttypeTypeFn _importtype_type;
late WasmerImporttypeVecDeleteFn _importtype_vec_delete;
late WasmerImporttypeVecNewFn _importtype_vec_new;
late WasmerImporttypeVecNewEmptyFn _importtype_vec_new_empty;
late WasmerImporttypeVecNewUninitializedFn _importtype_vec_new_uninitialized;
late WasmerInstanceDeleteFn _instance_delete;
late WasmerInstanceExportsFn _instance_exports;
late WasmerInstanceNewFn _instance_new;
late WasmerMemoryAsExternFn _memory_as_extern;
late WasmerMemoryDataFn _memory_data;
late WasmerMemoryDataSizeFn _memory_data_size;
late WasmerMemoryDeleteFn _memory_delete;
late WasmerMemoryGrowFn _memory_grow;
late WasmerMemoryNewFn _memory_new;
late WasmerMemorySizeFn _memory_size;
late WasmerMemorytypeDeleteFn _memorytype_delete;
late WasmerMemorytypeNewFn _memorytype_new;
late WasmerModuleDeleteFn _module_delete;
late WasmerModuleExportsFn _module_exports;
late WasmerModuleImportsFn _module_imports;
late WasmerModuleNewFn _module_new;
late WasmerStoreDeleteFn _store_delete;
late WasmerStoreNewFn _store_new;
late WasmerTrapDeleteFn _trap_delete;
late WasmerTrapMessageFn _trap_message;
late WasmerTrapNewFn _trap_new;
late WasmerValtypeDeleteFn _valtype_delete;
late WasmerValtypeKindFn _valtype_kind;
late WasmerValtypeVecDeleteFn _valtype_vec_delete;
late WasmerValtypeVecNewFn _valtype_vec_new;
late WasmerValtypeVecNewEmptyFn _valtype_vec_new_empty;
late WasmerValtypeVecNewUninitializedFn _valtype_vec_new_uninitialized;
late WasmerWasmerLastErrorLengthFn _wasmer_last_error_length;
late WasmerWasmerLastErrorMessageFn _wasmer_last_error_message;
factory WasmRuntime() => _inst ??= WasmRuntime._init();
factory WasmRuntime() => _inst;
WasmRuntime._init() : _lib = DynamicLibrary.open(_getLibPath()) {
_Dart_InitializeApiDL = _lib.lookupFunction<
@ -447,7 +448,7 @@ class WasmRuntime {
);
if (_Dart_InitializeApiDL(NativeApi.initializeApiDLData) != 0) {
throw Exception('Failed to initialize Dart API');
throw WasmError('Failed to initialize Dart API');
}
_engine = _engine_new();
_checkNotEqual(_engine, nullptr, 'Failed to initialize Wasm engine.');
@ -495,11 +496,11 @@ class WasmRuntime {
var exp = exportsVec.ref.data[i];
var extern = _exporttype_type(exp);
var kind = _externtype_kind(extern);
var fnType = kind == WasmerExternKindFunction
var fnType = kind == wasmerExternKindFunction
? _externtype_as_functype(extern)
: nullptr;
exps.add(
WasmExportDescriptor(
WasmExportDescriptor._(
kind,
_exporttype_name(exp).ref.toString(),
fnType,
@ -518,11 +519,11 @@ class WasmRuntime {
var imp = importsVec.ref.data[i];
var extern = _importtype_type(imp);
var kind = _externtype_kind(extern);
var fnType = kind == WasmerExternKindFunction
var fnType = kind == wasmerExternKindFunction
? _externtype_as_functype(extern)
: nullptr;
imps.add(
WasmImportDescriptor(
WasmImportDescriptor._(
kind,
_importtype_module(imp).ref.toString(),
_importtype_name(imp).ref.toString(),
@ -541,20 +542,14 @@ class WasmRuntime {
// with a corresponding exception, and their memory is managed using a
// finalizer on the _WasmTrapsEntry. Traps can also be created by WASM
// code, and in that case we delete them in this function.
var entry = traps[trap.address];
if (entry != null) {
traps.remove(entry);
// ignore: only_throw_errors
throw entry.exception;
} else {
var trapMessage = calloc<WasmerByteVec>();
_trap_message(trap, trapMessage);
var message = 'Wasm trap when calling $source: ${trapMessage.ref}';
_byte_vec_delete(trapMessage);
calloc.free(trapMessage);
_trap_delete(trap);
throw Exception(message);
var entry = _traps.remove(trap.address);
if (entry == null) {
throw WasmError(
'This case is not (yet) supported. Please file an issue on pkg:wasm.',
);
}
// ignore: only_throw_errors
throw entry.exception;
}
}
@ -606,9 +601,9 @@ class WasmRuntime {
int getReturnType(Pointer<WasmerFunctype> funcType) {
var rets = _functype_results(funcType);
if (rets.ref.length == 0) {
return WasmerValKindVoid;
return wasmerValKindVoid;
} else if (rets.ref.length > 1) {
throw Exception('Multiple return values are not supported');
throw WasmError('Multiple return values are not supported');
}
return _valtype_kind(rets.ref.data[0]);
}
@ -636,7 +631,7 @@ class WasmRuntime {
) {
var limPtr = calloc<WasmerLimits>();
limPtr.ref.min = pages;
limPtr.ref.max = maxPages ?? wasm_limits_max_default;
limPtr.ref.max = maxPages ?? wasmLimitsMaxDefault;
var memType = _memorytype_new(limPtr);
calloc.free(limPtr);
_checkNotEqual(memType, nullptr, 'Failed to create memory type.');
@ -694,7 +689,7 @@ class WasmRuntime {
_checkNotEqual(trap, nullptr, 'Failed to create trap.');
var entry = _WasmTrapsEntry(exception);
_set_finalizer_for_trap(entry, trap);
traps[trap.address] = entry;
_traps[trap.address] = entry;
return trap;
}
@ -758,7 +753,7 @@ class WasmRuntime {
T _checkNotEqual<T>(T x, T y, String errorMessage) {
if (x == y) {
throw Exception('$errorMessage\n${_getLastError()}');
throw WasmError('$errorMessage\n${_getLastError()}'.trim());
}
return x;
}

View file

@ -0,0 +1,14 @@
// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// Error specific to unexpected behavior or incorrect usage of this package.
class WasmError extends Error {
/// Describes the nature of the error.
final String message;
WasmError(this.message) : assert(message.trim() == message);
@override
String toString() => 'WasmError:$message';
}

View file

@ -6,8 +6,6 @@
// To regenerate the file, use the following command
// "generate_ffi_boilerplate.py".
// ignore_for_file: constant_identifier_names
import 'dart:convert';
import 'dart:ffi';
import 'dart:typed_data';
@ -15,29 +13,29 @@ import 'dart:typed_data';
part 'wasmer_api.g.dart';
// wasm_valkind_enum
const int WasmerValKindI32 = 0;
const int WasmerValKindI64 = 1;
const int WasmerValKindF32 = 2;
const int WasmerValKindF64 = 3;
const int wasmerValKindI32 = 0;
const int wasmerValKindI64 = 1;
const int wasmerValKindF32 = 2;
const int wasmerValKindF64 = 3;
// The void tag is not part of the C API. It's used to represent the return type
// of a void function.
const int WasmerValKindVoid = -1;
const int wasmerValKindVoid = -1;
// wasm_externkind_enum
const int WasmerExternKindFunction = 0;
const int WasmerExternKindGlobal = 1;
const int WasmerExternKindTable = 2;
const int WasmerExternKindMemory = 3;
const int wasmerExternKindFunction = 0;
const int wasmerExternKindGlobal = 1;
const int wasmerExternKindTable = 2;
const int wasmerExternKindMemory = 3;
String wasmerExternKindName(int kind) {
switch (kind) {
case WasmerExternKindFunction:
case wasmerExternKindFunction:
return 'function';
case WasmerExternKindGlobal:
case wasmerExternKindGlobal:
return 'global';
case WasmerExternKindTable:
case wasmerExternKindTable:
return 'table';
case WasmerExternKindMemory:
case wasmerExternKindMemory:
return 'memory';
default:
return 'unknown';
@ -46,15 +44,15 @@ String wasmerExternKindName(int kind) {
String wasmerValKindName(int kind) {
switch (kind) {
case WasmerValKindI32:
case wasmerValKindI32:
return 'int32';
case WasmerValKindI64:
case wasmerValKindI64:
return 'int64';
case WasmerValKindF32:
case wasmerValKindF32:
return 'float32';
case WasmerValKindF64:
case wasmerValKindF64:
return 'float64';
case WasmerValKindVoid:
case wasmerValKindVoid:
return 'void';
default:
return 'unknown';
@ -97,23 +95,23 @@ class WasmerVal extends Struct {
set f64(num val) =>
_val = ByteData(8)..setFloat64(0, val as double, Endian.host);
bool get isI32 => kind == WasmerValKindI32;
bool get isI32 => kind == wasmerValKindI32;
bool get isI64 => kind == WasmerValKindI64;
bool get isI64 => kind == wasmerValKindI64;
bool get isF32 => kind == WasmerValKindF32;
bool get isF32 => kind == wasmerValKindF32;
bool get isF64 => kind == WasmerValKindF64;
bool get isF64 => kind == wasmerValKindF64;
dynamic get toDynamic {
switch (kind) {
case WasmerValKindI32:
case wasmerValKindI32:
return i32;
case WasmerValKindI64:
case wasmerValKindI64:
return i64;
case WasmerValKindF32:
case wasmerValKindF32:
return f32;
case WasmerValKindF64:
case wasmerValKindF64:
return f64;
}
}
@ -129,4 +127,4 @@ class WasmerLimits extends Struct {
}
// Default maximum, which indicates no upper limit.
const int wasm_limits_max_default = 0xffffffff;
const int wasmLimitsMaxDefault = 0xffffffff;

View file

@ -2,5 +2,5 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
export 'src/function.dart';
export 'src/module.dart';
export 'src/wasm_error.dart';

View file

@ -21,7 +21,7 @@ void main() {
0x7e, 0x0b,
]);
var inst = WasmModule(data).instantiate().build();
var inst = WasmModule(data).builder().build();
var fn = inst.lookupFunction('square');
var n = fn(1234) as int;

View file

@ -8,6 +8,8 @@ import 'dart:typed_data';
import 'package:test/test.dart';
import 'package:wasm/wasm.dart';
import 'test_shared.dart';
void main() {
test('corrupted module', () {
var data = Uint8List.fromList([
@ -17,6 +19,14 @@ void main() {
0x7e, 0x0b,
]);
expect(() => WasmModule(data), throwsA(isException));
expect(
() => WasmModule(data),
throwsWasmError(
allOf(
contains('Wasm module compile failed.'),
contains('Validation error: Bad magic number (at offset 0)'),
),
),
);
});
}

View file

@ -22,7 +22,7 @@ void main() {
0x7e, 0x0b,
]);
var inst = WasmModule(data).instantiate().build();
var inst = WasmModule(data).builder().build();
var fn = inst.lookupFunction('square');
expect(() => fn(), throwsA(isArgumentError));

View file

@ -31,67 +31,65 @@ void main() {
var mod = WasmModule(data);
// Valid instantiation.
mod
.instantiate()
.addFunction('env', 'someFn', (int a, int b, num c, double d) => 123)
(mod.builder()
..addFunction(
'env',
'someFn',
(int a, int b, num c, double d) => 123,
))
.build();
// Missing imports.
expect(
() => mod.instantiate().build(),
throwsExceptionWithToString(contains('Missing import')),
() => mod.builder().build(),
throwsWasmError(startsWith('Missing import')),
);
// Wrong kind of import.
expect(
() => mod.instantiate().addMemory('env', 'someFn', mod.createMemory(10)),
throwsExceptionWithToString(
contains('Import is not a memory'),
),
() => mod.builder().addMemory('env', 'someFn', mod.createMemory(10)),
throwsWasmError(startsWith('Import is not a memory:')),
);
// Wrong namespace.
expect(
() => mod
.instantiate()
.addFunction(
'foo',
'someFn',
(int a, int b, num c, double d) => 123,
)
() => (mod.builder()
..addFunction(
'foo',
'someFn',
(int a, int b, num c, double d) => 123,
))
.build(),
throwsExceptionWithToString(contains('Import not found')),
throwsWasmError(startsWith('Import not found:')),
);
// Wrong name.
expect(
() => mod
.instantiate()
.addFunction(
'env',
'otherFn',
(int a, int b, num c, double d) => 123,
)
() => (mod.builder()
..addFunction(
'env',
'otherFn',
(int a, int b, num c, double d) => 123,
))
.build(),
throwsExceptionWithToString(contains('Import not found')),
throwsWasmError(startsWith('Import not found:')),
);
// Already filled.
expect(
() => mod
.instantiate()
.addFunction(
'env',
'someFn',
(int a, int b, num c, double d) => 123,
)
.addFunction(
'env',
'someFn',
(int a, int b, num c, double d) => 456,
)
() => (mod.builder()
..addFunction(
'env',
'someFn',
(int a, int b, num c, double d) => 123,
)
..addFunction(
'env',
'someFn',
(int a, int b, num c, double d) => 456,
))
.build(),
throwsExceptionWithToString(contains('Import already filled')),
throwsWasmError(startsWith('Import already filled: env::someFn')),
);
});
}

View file

@ -25,26 +25,24 @@ void main() {
0x80, 0x80, 0x00, 0x10, 0x81, 0x80, 0x80, 0x80, 0x00, 0x0b,
]);
var calledB = false;
var thrownException = Exception('Hello exception!');
var inst = WasmModule(data).instantiate().addFunction('env', 'a', () {
throw thrownException;
}).addFunction('env', 'b', () {
calledB = true;
}).build();
var inst = (WasmModule(data).builder()
..addFunction('env', 'a', () {
throw thrownException;
})
..addFunction('env', 'b', () {
fail('should not get here!');
}))
.build();
var fn = inst.lookupFunction('fn');
expect(() => fn(), throwsA(thrownException));
expect(calledB, isFalse);
var calledA = false;
inst = WasmModule(data).instantiate().addFunction('env', 'a', () {
calledA = true;
}).addFunction('env', 'b', () {
calledB = true;
}).build();
inst = (WasmModule(data).builder()
..addFunction('env', 'a', expectAsync0(() => null))
..addFunction('env', 'b', expectAsync0(() => null)))
.build();
fn = inst.lookupFunction('fn');
fn();
expect(calledA, isTrue);
expect(calledB, isTrue);
});
}

View file

@ -26,11 +26,12 @@ void main() {
var reportX = -1;
var reportY = -1;
var inst = WasmModule(data).instantiate().addFunction('env', 'report',
(int x, int y) {
reportX = x;
reportY = y;
}).build();
var inst = (WasmModule(data).builder()
..addFunction('env', 'report', (int x, int y) {
reportX = x;
reportY = y;
}))
.build();
var fn = inst.lookupFunction('reportStuff');
fn();
expect(123, reportX);

View file

@ -172,7 +172,7 @@ void main() {
]);
var inst =
WasmModule(data).instantiate().enableWasi(captureStdout: true).build();
(WasmModule(data).builder()..enableWasi(captureStdout: true)).build();
var fn = inst.lookupFunction('_start');
fn();

View file

@ -184,23 +184,24 @@ void main() {
return n;
}
var inst = WasmModule(data)
.instantiate()
.addFunction('wasi_unstable', 'fd_write',
(int fd, int iovs, int iovsLen, int unused) {
// iovs points to an array of length iovs_len. Each element is two I32s,
// a char* and a length.
var o = StringBuffer();
for (var i = 0; i < iovsLen; ++i) {
var str = getI32(iovs + 8 * i);
var len = getI32(iovs + 4 + 8 * i);
for (var j = 0; j < len; ++j) {
o.write(String.fromCharCode(mem[str + j]));
var builder = WasmModule(data).builder()
..addFunction('wasi_unstable', 'fd_write',
(int fd, int iovs, int iovsLen, int unused) {
// iovs points to an array of length iovs_len. Each element is two I32s,
// a char* and a length.
var o = StringBuffer();
for (var i = 0; i < iovsLen; ++i) {
var str = getI32(iovs + 8 * i);
var len = getI32(iovs + 4 + 8 * i);
for (var j = 0; j < len; ++j) {
o.write(String.fromCharCode(mem[str + j]));
}
}
}
out.write(o.toString());
return o.length;
}).build();
out.write(o.toString());
return o.length;
});
var inst = builder.build();
mem = inst.memory;
var fn = inst.lookupFunction('_start');

View file

@ -8,6 +8,8 @@ import 'dart:typed_data';
import 'package:test/test.dart';
import 'package:wasm/wasm.dart';
import 'test_shared.dart';
void main() {
test('memory errors', () {
// Empty wasm module.
@ -16,10 +18,19 @@ void main() {
]);
var module = WasmModule(data);
expect(() => module.createMemory(1000000000), throwsA(isException));
expect(
() => module.createMemory(1000000000),
throwsWasmError(startsWith('Failed to create memory.')),
);
var mem = module.createMemory(100);
expect(() => mem.grow(1000000000), throwsA(isException));
expect(
() => mem.grow(1000000000),
throwsWasmError('Failed to grow memory.'),
);
mem = module.createMemory(100, 200);
expect(() => mem.grow(300), throwsA(isException));
expect(
() => mem.grow(300),
throwsWasmError('Failed to grow memory.'),
);
});
}

View file

@ -30,7 +30,7 @@ void main() {
0x01, 0x92, 0x0b,
]);
var inst = WasmModule(data).instantiate().build();
var inst = WasmModule(data).builder().build();
var addI64 = inst.lookupFunction('addI64');
var addI32 = inst.lookupFunction('addI32');
var addF64 = inst.lookupFunction('addF64');

View file

@ -3,7 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
import 'package:test/test.dart';
import 'package:wasm/src/wasm_error.dart';
Matcher throwsExceptionWithToString(Object matcher) => throwsA(
isA<Exception>().having((p0) => p0.toString(), 'toString', matcher),
Matcher throwsWasmError(Object messageMatcher) => throwsA(
isA<WasmError>().having((p0) => p0.message, 'message', messageMatcher),
);

View file

@ -26,7 +26,7 @@ void main() {
0x80, 0x08, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
]);
var inst = WasmModule(data).instantiate().build();
var inst = WasmModule(data).builder().build();
var setFn = inst.lookupFunction('set');
var getFn = inst.lookupFunction('get');
expect(setFn(123, 456), isNull);

View file

@ -19,10 +19,8 @@ void main() {
// Failed to fill WASI imports (the empty module was not built with WASI).
expect(
() => WasmModule(emptyModuleData).instantiate().enableWasi(),
throwsExceptionWithToString(
contains('Failed to fill WASI imports'),
),
() => WasmModule(emptyModuleData).builder().enableWasi(),
throwsWasmError(startsWith('Failed to fill WASI imports.')),
);
// Hello world module generated by emscripten+WASI. Exports a function like
@ -187,37 +185,32 @@ void main() {
// Trying to import WASI twice.
expect(
() => WasmModule(helloWorldData).instantiate().enableWasi().enableWasi(),
throwsExceptionWithToString(contains('WASI is already enabled')),
() => WasmModule(helloWorldData).builder()..enableWasi()..enableWasi(),
throwsWasmError(startsWith('WASI is already enabled')),
);
// Missing imports due to not enabling WASI.
expect(
() => WasmModule(helloWorldData).instantiate().build(),
throwsExceptionWithToString(contains('Missing import')),
() => WasmModule(helloWorldData).builder().build(),
throwsWasmError(startsWith('Missing import: ')),
);
// Trying to get stdout/stderr without WASI enabled (WASI function import has
// been manually filled).
var inst = WasmModule(helloWorldData)
.instantiate()
.addFunction(
'wasi_unstable',
'fd_write',
(int fd, int iovs, int iovsLen, int unused) => 0,
)
var inst = (WasmModule(helloWorldData).builder()
..addFunction(
'wasi_unstable',
'fd_write',
(int fd, int iovs, int iovsLen, int unused) => 0,
))
.build();
expect(
() => inst.stdout,
throwsExceptionWithToString(
contains("Can't capture stdout without WASI enabled"),
),
throwsWasmError("Can't capture stdout without WASI enabled."),
);
expect(
() => inst.stderr,
throwsExceptionWithToString(
contains("Can't capture stderr without WASI enabled"),
),
throwsWasmError("Can't capture stderr without WASI enabled."),
);
});
}

View file

@ -132,8 +132,8 @@ def getWasmerApi():
def getRuntimeMemb():
return '\n'.join([
" late Wasmer%sFn %s;" % (dartFnTypeName(name), dartFnMembName(name))
for name, _, _ in getFns()
" late final Wasmer%sFn %s;" %
(dartFnTypeName(name), dartFnMembName(name)) for name, _, _ in getFns()
])

View file

@ -11,21 +11,20 @@
part of 'runtime.dart';
class WasmRuntime {
static WasmRuntime? _inst;
DynamicLibrary _lib;
late Pointer<WasmerEngine> _engine;
Map<int, _WasmTrapsEntry> traps = {};
static final WasmRuntime _inst = WasmRuntime._init();
final DynamicLibrary _lib;
final _traps = <int, _WasmTrapsEntry>{};
late final Pointer<WasmerEngine> _engine;
/* <RUNTIME_MEMB> */
factory WasmRuntime() => _inst ??= WasmRuntime._init();
factory WasmRuntime() => _inst;
WasmRuntime._init() : _lib = DynamicLibrary.open(_getLibPath()) {
/* <RUNTIME_LOAD> */
if (_Dart_InitializeApiDL(NativeApi.initializeApiDLData) != 0) {
throw Exception('Failed to initialize Dart API');
throw WasmError('Failed to initialize Dart API');
}
_engine = _engine_new();
_checkNotEqual(_engine, nullptr, 'Failed to initialize Wasm engine.');
@ -73,11 +72,11 @@ class WasmRuntime {
var exp = exportsVec.ref.data[i];
var extern = _exporttype_type(exp);
var kind = _externtype_kind(extern);
var fnType = kind == WasmerExternKindFunction
var fnType = kind == wasmerExternKindFunction
? _externtype_as_functype(extern)
: nullptr;
exps.add(
WasmExportDescriptor(
WasmExportDescriptor._(
kind,
_exporttype_name(exp).ref.toString(),
fnType,
@ -96,11 +95,11 @@ class WasmRuntime {
var imp = importsVec.ref.data[i];
var extern = _importtype_type(imp);
var kind = _externtype_kind(extern);
var fnType = kind == WasmerExternKindFunction
var fnType = kind == wasmerExternKindFunction
? _externtype_as_functype(extern)
: nullptr;
imps.add(
WasmImportDescriptor(
WasmImportDescriptor._(
kind,
_importtype_module(imp).ref.toString(),
_importtype_name(imp).ref.toString(),
@ -119,19 +118,19 @@ class WasmRuntime {
// with a corresponding exception, and their memory is managed using a
// finalizer on the _WasmTrapsEntry. Traps can also be created by WASM
// code, and in that case we delete them in this function.
var entry = traps[trap.address];
var entry = _traps.remove(trap.address);
if (entry != null) {
traps.remove(entry);
// ignore: only_throw_errors
throw entry.exception;
} else {
// TODO: code path not hit in tests!
var trapMessage = calloc<WasmerByteVec>();
_trap_message(trap, trapMessage);
var message = 'Wasm trap when calling $source: ${trapMessage.ref}';
_byte_vec_delete(trapMessage);
calloc.free(trapMessage);
_trap_delete(trap);
throw Exception(message);
throw WasmError(message);
}
}
}
@ -184,9 +183,9 @@ class WasmRuntime {
int getReturnType(Pointer<WasmerFunctype> funcType) {
var rets = _functype_results(funcType);
if (rets.ref.length == 0) {
return WasmerValKindVoid;
return wasmerValKindVoid;
} else if (rets.ref.length > 1) {
throw Exception('Multiple return values are not supported');
throw WasmError('Multiple return values are not supported');
}
return _valtype_kind(rets.ref.data[0]);
}
@ -214,7 +213,7 @@ class WasmRuntime {
) {
var limPtr = calloc<WasmerLimits>();
limPtr.ref.min = pages;
limPtr.ref.max = maxPages ?? wasm_limits_max_default;
limPtr.ref.max = maxPages ?? wasmLimitsMaxDefault;
var memType = _memorytype_new(limPtr);
calloc.free(limPtr);
_checkNotEqual(memType, nullptr, 'Failed to create memory type.');
@ -272,7 +271,7 @@ class WasmRuntime {
_checkNotEqual(trap, nullptr, 'Failed to create trap.');
var entry = _WasmTrapsEntry(exception);
_set_finalizer_for_trap(entry, trap);
traps[trap.address] = entry;
_traps[trap.address] = entry;
return trap;
}
@ -336,7 +335,7 @@ class WasmRuntime {
T _checkNotEqual<T>(T x, T y, String errorMessage) {
if (x == y) {
throw Exception('$errorMessage\n${_getLastError()}');
throw WasmError('$errorMessage\n${_getLastError()}'.trim());
}
return x;
}