blob: 40139663bd303b7925bf795602a9176cf403c8ae [file] [log] [blame]
// 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 'runtime.dart';
import 'function.dart';
import 'wasmer_api.dart';
import 'dart:typed_data';
import 'dart:ffi';
import 'package:ffi/ffi.dart';
/// WasmModule is a compiled module that can be instantiated.
class WasmModule {
Pointer<WasmerModule> _module;
/// Compile a module.
WasmModule(Uint8List data) : _module = WasmRuntime().compile(data) {}
/// Instantiate the module with the given imports.
WasmInstance instantiate(WasmImports imports) {
return WasmInstance(_module, imports);
}
/// Returns a description of all of the module's imports and exports, for
/// debugging.
String describe() {
var description = StringBuffer();
var runtime = WasmRuntime();
var imports = runtime.importDescriptors(_module);
for (var imp in imports) {
var kind = wasmerImpExpKindName(imp.kind);
description.write('import $kind: ${imp.moduleName}::${imp.name}\n');
}
var exports = runtime.exportDescriptors(_module);
for (var exp in exports) {
var kind = wasmerImpExpKindName(exp.kind);
description.write('export $kind: ${exp.name}\n');
}
return description.toString();
}
}
/// WasmImports holds all the imports for a WasmInstance.
class WasmImports {
Pointer<WasmerImport> _imports;
int _capacity;
int _length;
/// Create an imports object.
WasmImports([this._capacity = 4])
: _imports = allocate<WasmerImport>(count: _capacity),
_length = 0 {}
/// Returns the number of imports.
int get length => _length;
}
/// WasmInstance is an instantiated WasmModule.
class WasmInstance {
Pointer<WasmerModule> _module;
Pointer<WasmerInstance> _instance;
Pointer<WasmerMemory>? _exportedMemory;
Map<String, WasmFunction> _functions = {};
WasmInstance(this._module, WasmImports imports)
: _instance = WasmRuntime()
.instantiate(_module, imports._imports, imports.length) {
var runtime = WasmRuntime();
var exps = runtime.exports(_instance);
for (var e in exps) {
var kind = runtime.exportKind(e);
String name = runtime.exportName(e);
if (kind == WasmerImpExpKindFunction) {
var f = runtime.exportToFunction(e);
_functions[name] = WasmFunction(
name, f, runtime.getArgTypes(f), runtime.getReturnType(f));
} else if (kind == WasmerImpExpKindMemory) {
// WASM currently allows only one memory per module.
_exportedMemory = runtime.exportToMemory(e);
}
}
}
/// Searches the instantiated module for the given function. Returns null if
/// it is not found.
dynamic lookupFunction(String name) {
return _functions[name];
}
/// Returns the memory exported from this instance.
WasmMemory get memory {
if (_exportedMemory == null) {
throw Exception("Wasm module did not export its memory.");
}
return WasmMemory._fromExport(_exportedMemory as Pointer<WasmerMemory>);
}
}
/// WasmMemory contains the memory of a WasmInstance.
class WasmMemory {
Pointer<WasmerMemory> _mem;
late Uint8List _view;
WasmMemory._fromExport(this._mem) {
_view = WasmRuntime().memoryView(_mem);
}
/// Create a new memory with the given number of initial pages, and optional
/// maximum number of pages.
WasmMemory(int pages, [int? maxPages])
: _mem = WasmRuntime().newMemory(pages, maxPages) {
_view = WasmRuntime().memoryView(_mem);
}
/// The WASM spec defines the page size as 64KiB.
static const int kPageSizeInBytes = 64 * 1024;
/// Returns the length of the memory in pages.
int get lengthInPages {
return WasmRuntime().memoryLength(_mem);
}
/// Returns the length of the memory in bytes.
int get lengthInBytes => _view.lengthInBytes;
/// Returns the byte at the given index.
int operator [](int index) => _view[index];
/// Sets the byte at the given index to value.
void operator []=(int index, int value) {
_view[index] = value;
}
/// Grow the memory by deltaPages.
void grow(int deltaPages) {
var runtime = WasmRuntime();
runtime.growMemory(_mem, deltaPages);
_view = runtime.memoryView(_mem);
}
}