Implement a basic WASM API using wasmer.
Currently this only supports functions that take and return numeric types. Byte arrays, and callbacks will come later, in a separate wrapper package. Bug: https://github.com/dart-lang/sdk/issues/37882 Change-Id: I7bb82be83cbbb6062736b3e958f89d021f1af4bb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/114586 Commit-Queue: Liam Appelbe <liama@google.com> Reviewed-by: Samir Jindel <sjindel@google.com> Reviewed-by: Ryan Macnak <rmacnak@google.com>
This commit is contained in:
committed by
commit-bot@chromium.org
parent
e8413cd08d
commit
ed9e89ea38
@@ -240,6 +240,10 @@ library_for_all_configs("libdart") {
|
||||
"third_party/double-conversion/src:libdouble_conversion",
|
||||
":generate_version_cc_file",
|
||||
]
|
||||
if (dart_enable_wasm) {
|
||||
extra_deps += [ "//third_party/wasmer" ]
|
||||
defines = [ "DART_ENABLE_WASM" ]
|
||||
}
|
||||
if (is_fuchsia) {
|
||||
if (using_fuchsia_sdk) {
|
||||
extra_deps += [ "$fuchsia_sdk_root/pkg:fdio" ]
|
||||
|
||||
+611
-10
@@ -2,27 +2,628 @@
|
||||
// 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.
|
||||
|
||||
#ifdef DART_ENABLE_WASM
|
||||
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
|
||||
#include "platform/unicode.h"
|
||||
#include "third_party/wasmer/wasmer.hh"
|
||||
#include "vm/bootstrap_natives.h"
|
||||
#include "vm/dart_api_state.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/exceptions.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
int callWasm(const char* name, int n) {
|
||||
return 100 * n;
|
||||
static void ThrowIfFailed(wasmer_result_t status) {
|
||||
if (status == wasmer_result_t::WASMER_OK) return;
|
||||
int len = wasmer_last_error_length();
|
||||
auto error = std::unique_ptr<char[]>(new char[len]);
|
||||
int read_len = wasmer_last_error_message(error.get(), len);
|
||||
ASSERT(read_len == len);
|
||||
TransitionNativeToVM transition(Thread::Current());
|
||||
Exceptions::ThrowArgumentError(
|
||||
String::Handle(String::NewFormatted("Wasmer error: %s", error.get())));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void Finalize(void* isolate_callback_data,
|
||||
Dart_WeakPersistentHandle handle,
|
||||
void* peer) {
|
||||
delete reinterpret_cast<T*>(peer);
|
||||
}
|
||||
|
||||
static std::unique_ptr<char[]> ToUTF8(const String& str) {
|
||||
const intptr_t str_size = Utf8::Length(str);
|
||||
auto str_raw = std::unique_ptr<char[]>(new char[str_size + 1]);
|
||||
str.ToUTF8(reinterpret_cast<uint8_t*>(str_raw.get()), str_size);
|
||||
str_raw[str_size] = '\0';
|
||||
return str_raw;
|
||||
}
|
||||
|
||||
static bool ToWasmValue(const Number& value,
|
||||
classid_t type,
|
||||
wasmer_value_t* out) {
|
||||
switch (type) {
|
||||
case kFfiInt32Cid:
|
||||
if (!value.IsInteger()) return false;
|
||||
out->tag = wasmer_value_tag::WASM_I32;
|
||||
out->value.I32 = Integer::Cast(value).AsInt64Value();
|
||||
return true;
|
||||
case kFfiInt64Cid:
|
||||
if (!value.IsInteger()) return false;
|
||||
out->tag = wasmer_value_tag::WASM_I64;
|
||||
out->value.I64 = Integer::Cast(value).AsInt64Value();
|
||||
return true;
|
||||
case kFfiFloatCid:
|
||||
if (!value.IsDouble()) return false;
|
||||
out->tag = wasmer_value_tag::WASM_F32;
|
||||
out->value.F32 = Double::Cast(value).value();
|
||||
return true;
|
||||
case kFfiDoubleCid:
|
||||
if (!value.IsDouble()) return false;
|
||||
out->tag = wasmer_value_tag::WASM_F64;
|
||||
out->value.F64 = Double::Cast(value).value();
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static RawObject* ToDartObject(wasmer_value_t ret) {
|
||||
switch (ret.tag) {
|
||||
case wasmer_value_tag::WASM_I32:
|
||||
return Integer::New(ret.value.I32);
|
||||
case wasmer_value_tag::WASM_I64:
|
||||
return Integer::New(ret.value.I64);
|
||||
case wasmer_value_tag::WASM_F32:
|
||||
return Double::New(ret.value.F32);
|
||||
case wasmer_value_tag::WASM_F64:
|
||||
return Double::New(ret.value.F64);
|
||||
default:
|
||||
FATAL("Unknown WASM type");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
class WasmModule {
|
||||
public:
|
||||
WasmModule(uint8_t* data, intptr_t len) {
|
||||
ThrowIfFailed(wasmer_compile(&_module, data, len));
|
||||
}
|
||||
|
||||
~WasmModule() { wasmer_module_destroy(_module); }
|
||||
wasmer_module_t* module() { return _module; }
|
||||
|
||||
private:
|
||||
wasmer_module_t* _module;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WasmModule);
|
||||
};
|
||||
|
||||
class WasmMemory {
|
||||
public:
|
||||
WasmMemory(uint32_t init, int64_t max) {
|
||||
wasmer_limits_t descriptor;
|
||||
descriptor.min = init;
|
||||
if (max < 0) {
|
||||
descriptor.max.has_some = false;
|
||||
} else {
|
||||
descriptor.max.has_some = true;
|
||||
descriptor.max.some = max;
|
||||
}
|
||||
ThrowIfFailed(wasmer_memory_new(&_memory, descriptor));
|
||||
}
|
||||
|
||||
~WasmMemory() { wasmer_memory_destroy(_memory); }
|
||||
wasmer_memory_t* memory() { return _memory; }
|
||||
|
||||
void Grow(intptr_t delta) {
|
||||
ThrowIfFailed(wasmer_memory_grow(_memory, delta));
|
||||
}
|
||||
|
||||
RawExternalTypedData* ToExternalTypedData() {
|
||||
uint8_t* data = wasmer_memory_data(_memory);
|
||||
uint32_t size = wasmer_memory_data_length(_memory);
|
||||
return ExternalTypedData::New(kExternalTypedDataUint8ArrayCid, data, size);
|
||||
}
|
||||
|
||||
private:
|
||||
wasmer_memory_t* _memory;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WasmMemory);
|
||||
};
|
||||
|
||||
class WasmImports {
|
||||
public:
|
||||
WasmImports(std::unique_ptr<char[]> module_name)
|
||||
: _module_name(std::move(module_name)) {}
|
||||
|
||||
~WasmImports() {
|
||||
for (wasmer_global_t* global : _globals) {
|
||||
wasmer_global_destroy(global);
|
||||
}
|
||||
for (const char* name : _import_names) {
|
||||
delete[] name;
|
||||
}
|
||||
}
|
||||
|
||||
size_t NumImports() const { return _imports.length(); }
|
||||
wasmer_import_t* RawImports() { return _imports.data(); }
|
||||
|
||||
void AddMemory(std::unique_ptr<char[]> name, WasmMemory* memory) {
|
||||
AddImport(std::move(name), wasmer_import_export_kind::WASM_MEMORY)->memory =
|
||||
memory->memory();
|
||||
}
|
||||
|
||||
void AddGlobal(std::unique_ptr<char[]> name,
|
||||
wasmer_value_t value,
|
||||
bool mutable_) {
|
||||
wasmer_global_t* global = wasmer_global_new(value, mutable_);
|
||||
_globals.Add(global);
|
||||
AddImport(std::move(name), wasmer_import_export_kind::WASM_GLOBAL)->global =
|
||||
global;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<char[]> _module_name;
|
||||
MallocGrowableArray<const char*> _import_names;
|
||||
MallocGrowableArray<wasmer_global_t*> _globals;
|
||||
MallocGrowableArray<wasmer_import_t> _imports;
|
||||
|
||||
wasmer_import_export_value* AddImport(std::unique_ptr<char[]> name,
|
||||
wasmer_import_export_kind tag) {
|
||||
wasmer_import_t import;
|
||||
import.module_name.bytes =
|
||||
reinterpret_cast<const uint8_t*>(_module_name.get());
|
||||
import.module_name.bytes_len = (uint32_t)strlen(_module_name.get());
|
||||
import.import_name.bytes = reinterpret_cast<const uint8_t*>(name.get());
|
||||
import.import_name.bytes_len = (uint32_t)strlen(name.get());
|
||||
import.tag = tag;
|
||||
_import_names.Add(name.release());
|
||||
_imports.Add(import);
|
||||
return &_imports.Last().value;
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WasmImports);
|
||||
};
|
||||
|
||||
class WasmFunction {
|
||||
public:
|
||||
WasmFunction(MallocGrowableArray<classid_t> args,
|
||||
classid_t ret,
|
||||
const wasmer_export_func_t* fn)
|
||||
: _args(std::move(args)), _ret(ret), _fn(fn) {}
|
||||
bool IsVoid() const { return _ret == kFfiVoidCid; }
|
||||
const MallocGrowableArray<classid_t>& args() const { return _args; }
|
||||
|
||||
bool SignatureMatches(const MallocGrowableArray<classid_t>& dart_args,
|
||||
classid_t dart_ret) {
|
||||
if (dart_args.length() != _args.length()) {
|
||||
return false;
|
||||
}
|
||||
for (intptr_t i = 0; i < dart_args.length(); ++i) {
|
||||
if (dart_args[i] != _args[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return dart_ret == _ret;
|
||||
}
|
||||
|
||||
wasmer_value_t Call(const wasmer_value_t* params) {
|
||||
wasmer_value_t result;
|
||||
ThrowIfFailed(wasmer_export_func_call(_fn, params, _args.length(), &result,
|
||||
IsVoid() ? 0 : 1));
|
||||
return result;
|
||||
}
|
||||
|
||||
void Print(std::ostream& o, const char* name) const {
|
||||
PrintFfiType(o, _ret);
|
||||
o << ' ' << name << '(';
|
||||
for (intptr_t i = 0; i < _args.length(); ++i) {
|
||||
if (i > 0) o << ", ";
|
||||
PrintFfiType(o, _args[i]);
|
||||
}
|
||||
o << ')';
|
||||
}
|
||||
|
||||
private:
|
||||
MallocGrowableArray<classid_t> _args;
|
||||
const classid_t _ret;
|
||||
const wasmer_export_func_t* _fn;
|
||||
|
||||
static void PrintFfiType(std::ostream& o, classid_t type) {
|
||||
switch (type) {
|
||||
case kFfiInt32Cid:
|
||||
o << "i32";
|
||||
break;
|
||||
case kFfiInt64Cid:
|
||||
o << "i64";
|
||||
break;
|
||||
case kFfiFloatCid:
|
||||
o << "f32";
|
||||
break;
|
||||
case kFfiDoubleCid:
|
||||
o << "f64";
|
||||
break;
|
||||
case kFfiVoidCid:
|
||||
o << "void";
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class WasmInstance {
|
||||
public:
|
||||
explicit WasmInstance(WasmModule* module, WasmImports* imports) {
|
||||
// Instantiate module.
|
||||
ThrowIfFailed(wasmer_module_instantiate(module->module(), &_instance,
|
||||
imports->RawImports(),
|
||||
imports->NumImports()));
|
||||
|
||||
// Load all functions.
|
||||
wasmer_instance_exports(_instance, &_exports);
|
||||
intptr_t num_exports = wasmer_exports_len(_exports);
|
||||
for (intptr_t i = 0; i < num_exports; ++i) {
|
||||
wasmer_export_t* exp = wasmer_exports_get(_exports, i);
|
||||
if (wasmer_export_kind(exp) == wasmer_import_export_kind::WASM_FUNCTION) {
|
||||
AddFunction(exp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~WasmInstance() {
|
||||
auto it = _functions.GetIterator();
|
||||
for (auto* kv = it.Next(); kv; kv = it.Next()) {
|
||||
delete[] kv->key;
|
||||
delete kv->value;
|
||||
}
|
||||
wasmer_exports_destroy(_exports);
|
||||
wasmer_instance_destroy(_instance);
|
||||
}
|
||||
|
||||
WasmFunction* GetFunction(const char* name,
|
||||
const MallocGrowableArray<classid_t>& dart_args,
|
||||
classid_t dart_ret) {
|
||||
WasmFunction* fn = _functions.LookupValue(name);
|
||||
if (fn == nullptr) {
|
||||
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
|
||||
"Couldn't find a function called %s in the WASM module's exports",
|
||||
name)));
|
||||
return nullptr;
|
||||
}
|
||||
if (!fn->SignatureMatches(dart_args, dart_ret)) {
|
||||
std::stringstream sig;
|
||||
fn->Print(sig, name);
|
||||
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
|
||||
"Function signature doesn't match: %s", sig.str().c_str())));
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
void PrintFunctions(std::ostream& o) const {
|
||||
o << '{' << std::endl;
|
||||
auto it = _functions.GetIterator();
|
||||
for (auto* kv = it.Next(); kv; kv = it.Next()) {
|
||||
kv->value->Print(o, kv->key);
|
||||
o << std::endl;
|
||||
}
|
||||
o << '}' << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
wasmer_instance_t* _instance;
|
||||
wasmer_exports_t* _exports;
|
||||
MallocDirectChainedHashMap<CStringKeyValueTrait<WasmFunction*>> _functions;
|
||||
|
||||
static classid_t ToFfiType(wasmer_value_tag wasm_type) {
|
||||
switch (wasm_type) {
|
||||
case wasmer_value_tag::WASM_I32:
|
||||
return kFfiInt32Cid;
|
||||
case wasmer_value_tag::WASM_I64:
|
||||
return kFfiInt64Cid;
|
||||
case wasmer_value_tag::WASM_F32:
|
||||
return kFfiFloatCid;
|
||||
case wasmer_value_tag::WASM_F64:
|
||||
return kFfiDoubleCid;
|
||||
}
|
||||
FATAL("Unknown WASM type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AddFunction(wasmer_export_t* exp) {
|
||||
const wasmer_export_func_t* fn = wasmer_export_to_func(exp);
|
||||
|
||||
uint32_t num_rets;
|
||||
ThrowIfFailed(wasmer_export_func_returns_arity(fn, &num_rets));
|
||||
ASSERT(num_rets <= 1);
|
||||
wasmer_value_tag wasm_ret;
|
||||
ThrowIfFailed(wasmer_export_func_returns(fn, &wasm_ret, num_rets));
|
||||
classid_t ret = num_rets == 0 ? kFfiVoidCid : ToFfiType(wasm_ret);
|
||||
|
||||
uint32_t num_args;
|
||||
ThrowIfFailed(wasmer_export_func_params_arity(fn, &num_args));
|
||||
auto wasm_args =
|
||||
std::unique_ptr<wasmer_value_tag[]>(new wasmer_value_tag[num_args]);
|
||||
ThrowIfFailed(wasmer_export_func_params(fn, wasm_args.get(), num_args));
|
||||
MallocGrowableArray<classid_t> args;
|
||||
for (intptr_t i = 0; i < num_args; ++i) {
|
||||
args.Add(ToFfiType(wasm_args[i]));
|
||||
}
|
||||
|
||||
wasmer_byte_array name_bytes = wasmer_export_name(exp);
|
||||
char* name = new char[name_bytes.bytes_len + 1];
|
||||
for (size_t i = 0; i < name_bytes.bytes_len; ++i) {
|
||||
name[i] = name_bytes.bytes[i];
|
||||
}
|
||||
name[name_bytes.bytes_len] = '\0';
|
||||
|
||||
_functions.Insert({name, new WasmFunction(std::move(args), ret, fn)});
|
||||
}
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WasmInstance);
|
||||
};
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(TypedDataBase, data, arguments->NativeArgAt(1));
|
||||
|
||||
ASSERT(mod_wrap.NumNativeFields() == 1);
|
||||
|
||||
std::unique_ptr<uint8_t[]> data_copy;
|
||||
intptr_t len;
|
||||
{
|
||||
NoSafepointScope scope(thread);
|
||||
len = data.LengthInBytes();
|
||||
data_copy = std::unique_ptr<uint8_t[]>(new uint8_t[len]);
|
||||
// The memory does not overlap.
|
||||
memcpy(data_copy.get(), data.DataAddr(0), len); // NOLINT
|
||||
}
|
||||
|
||||
WasmModule* module;
|
||||
{
|
||||
TransitionVMToNative transition(thread);
|
||||
module = new WasmModule(data_copy.get(), len);
|
||||
}
|
||||
|
||||
mod_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(module));
|
||||
FinalizablePersistentHandle::New(thread->isolate(), mod_wrap, module,
|
||||
Finalize<WasmModule>, sizeof(WasmModule));
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, module_name, arguments->NativeArgAt(1));
|
||||
|
||||
ASSERT(imp_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmImports* imports = new WasmImports(ToUTF8(module_name));
|
||||
|
||||
imp_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(imports));
|
||||
FinalizablePersistentHandle::New(thread->isolate(), imp_wrap, imports,
|
||||
Finalize<WasmImports>, sizeof(WasmImports));
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 3) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(2));
|
||||
|
||||
ASSERT(imp_wrap.NumNativeFields() == 1);
|
||||
ASSERT(mem_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmImports* imports =
|
||||
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
|
||||
WasmMemory* memory =
|
||||
reinterpret_cast<WasmMemory*>(mem_wrap.GetNativeField(0));
|
||||
|
||||
imports->AddMemory(ToUTF8(name), memory);
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 5) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(1));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Number, value, arguments->NativeArgAt(2));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Type, type, arguments->NativeArgAt(3));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Bool, mutable_, arguments->NativeArgAt(4));
|
||||
|
||||
ASSERT(imp_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmImports* imports =
|
||||
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
|
||||
wasmer_value_t wasm_value;
|
||||
if (!ToWasmValue(value, type.type_class_id(), &wasm_value)) {
|
||||
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
|
||||
"Can't convert dart value to WASM global variable")));
|
||||
}
|
||||
|
||||
imports->AddGlobal(ToUTF8(name), wasm_value, mutable_.value());
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, init, arguments->NativeArgAt(1));
|
||||
GET_NATIVE_ARGUMENT(Integer, max, arguments->NativeArgAt(2));
|
||||
|
||||
ASSERT(mem_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmMemory* memory = new WasmMemory(init.AsInt64Value(),
|
||||
max.IsNull() ? -1 : max.AsInt64Value());
|
||||
mem_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(memory));
|
||||
FinalizablePersistentHandle::New(thread->isolate(), mem_wrap, memory,
|
||||
Finalize<WasmMemory>, sizeof(WasmMemory));
|
||||
return memory->ToExternalTypedData();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mem_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, delta, arguments->NativeArgAt(1));
|
||||
|
||||
ASSERT(mem_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmMemory* memory =
|
||||
reinterpret_cast<WasmMemory*>(mem_wrap.GetNativeField(0));
|
||||
memory->Grow(delta.AsInt64Value());
|
||||
return memory->ToExternalTypedData();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, mod_wrap, arguments->NativeArgAt(1));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, imp_wrap, arguments->NativeArgAt(2));
|
||||
|
||||
ASSERT(inst_wrap.NumNativeFields() == 1);
|
||||
ASSERT(mod_wrap.NumNativeFields() == 1);
|
||||
ASSERT(imp_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmModule* module =
|
||||
reinterpret_cast<WasmModule*>(mod_wrap.GetNativeField(0));
|
||||
WasmImports* imports =
|
||||
reinterpret_cast<WasmImports*>(imp_wrap.GetNativeField(0));
|
||||
|
||||
WasmInstance* inst;
|
||||
{
|
||||
TransitionVMToNative transition(thread);
|
||||
inst = new WasmInstance(module, imports);
|
||||
}
|
||||
|
||||
inst_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(inst));
|
||||
FinalizablePersistentHandle::New(thread->isolate(), inst_wrap, inst,
|
||||
Finalize<WasmInstance>,
|
||||
sizeof(WasmInstance));
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, inst_wrap, arguments->NativeArgAt(1));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, name, arguments->NativeArgAt(2));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Type, fn_type, arguments->NativeArgAt(3));
|
||||
|
||||
ASSERT(fn_wrap.NumNativeFields() == 1);
|
||||
ASSERT(inst_wrap.NumNativeFields() == 1);
|
||||
|
||||
WasmInstance* inst =
|
||||
reinterpret_cast<WasmInstance*>(inst_wrap.GetNativeField(0));
|
||||
|
||||
Function& sig = Function::Handle(fn_type.signature());
|
||||
Array& args = Array::Handle(sig.parameter_types());
|
||||
MallocGrowableArray<classid_t> dart_args;
|
||||
for (intptr_t i = sig.NumImplicitParameters(); i < args.Length(); ++i) {
|
||||
dart_args.Add(
|
||||
AbstractType::Cast(Object::Handle(args.At(i))).type_class_id());
|
||||
}
|
||||
classid_t dart_ret = AbstractType::Handle(sig.result_type()).type_class_id();
|
||||
|
||||
std::unique_ptr<char[]> name_raw = ToUTF8(name);
|
||||
WasmFunction* fn = inst->GetFunction(name_raw.get(), dart_args, dart_ret);
|
||||
|
||||
fn_wrap.SetNativeField(0, reinterpret_cast<intptr_t>(fn));
|
||||
// Don't need a finalizer because WasmFunctions are owned their WasmInstance.
|
||||
|
||||
return Object::null();
|
||||
}
|
||||
|
||||
// This is a temporary API for prototyping.
|
||||
DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(String, fn_name, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Integer, arg, arguments->NativeArgAt(1));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Instance, fn_wrap, arguments->NativeArgAt(0));
|
||||
GET_NON_NULL_NATIVE_ARGUMENT(Array, args, arguments->NativeArgAt(1));
|
||||
|
||||
intptr_t len = Utf8::Length(fn_name);
|
||||
std::unique_ptr<char> name = std::unique_ptr<char>(new char[len + 1]);
|
||||
fn_name.ToUTF8(reinterpret_cast<uint8_t*>(name.get()), len);
|
||||
name.get()[len] = 0;
|
||||
ASSERT(fn_wrap.NumNativeFields() == 1);
|
||||
WasmFunction* fn = reinterpret_cast<WasmFunction*>(fn_wrap.GetNativeField(0));
|
||||
|
||||
return Smi::New(callWasm(name.get(), arg.AsInt64Value()));
|
||||
if (args.Length() != fn->args().length()) {
|
||||
Exceptions::ThrowArgumentError(String::Handle(String::NewFormatted(
|
||||
"Wrong number of args. Expected %" Pu " but found %" Pd ".",
|
||||
fn->args().length(), args.Length())));
|
||||
}
|
||||
intptr_t length = fn->args().length();
|
||||
if (length == 0) {
|
||||
// Wasmer requires that our params ptr is valid, even if params_len is 0.
|
||||
// TODO(liama): Remove after https://github.com/wasmerio/wasmer/issues/753
|
||||
length = 1;
|
||||
}
|
||||
auto params = std::unique_ptr<wasmer_value_t[]>(new wasmer_value_t[length]);
|
||||
for (intptr_t i = 0; i < args.Length(); ++i) {
|
||||
if (!ToWasmValue(Number::Cast(Object::Handle(args.At(i))), fn->args()[i],
|
||||
¶ms[i])) {
|
||||
Exceptions::ThrowArgumentError(String::Handle(
|
||||
String::NewFormatted("Arg %" Pd " is the wrong type.", i)));
|
||||
}
|
||||
}
|
||||
|
||||
wasmer_value_t ret;
|
||||
{
|
||||
TransitionVMToNative transition(Thread::Current());
|
||||
ret = fn->Call(params.get());
|
||||
}
|
||||
return fn->IsVoid() ? Object::null() : ToDartObject(ret);
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#else // DART_ENABLE_WASM
|
||||
|
||||
#include "vm/bootstrap_natives.h"
|
||||
#include "vm/dart_entry.h"
|
||||
#include "vm/exceptions.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initModule, 0, 2) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initImports, 0, 2) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_addMemoryImport, 0, 3) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_addGlobalImport, 0, 5) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initMemory, 0, 3) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_growMemory, 0, 3) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initInstance, 0, 3) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_initFunction, 0, 4) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DEFINE_NATIVE_ENTRY(Wasm_callFunction, 0, 2) {
|
||||
Exceptions::ThrowUnsupportedError("WASM is disabled");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // DART_ENABLE_WASM
|
||||
|
||||
+123
-2
@@ -2,7 +2,128 @@
|
||||
// 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:_internal" show patch;
|
||||
import 'dart:_internal' show patch;
|
||||
import "dart:nativewrappers" show NativeFieldWrapperClass1;
|
||||
import 'dart:typed_data';
|
||||
|
||||
@patch
|
||||
int _callWasm(String name, int arg) native "Wasm_callFunction";
|
||||
class WasmModule {
|
||||
@patch
|
||||
factory WasmModule(Uint8List data) {
|
||||
return _NativeWasmModule(data);
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class WasmMemory {
|
||||
@patch
|
||||
factory WasmMemory(int initialPages, [int maxPages]) {
|
||||
return _NativeWasmMemory(initialPages, maxPages);
|
||||
}
|
||||
}
|
||||
|
||||
@patch
|
||||
class WasmImports {
|
||||
@patch
|
||||
factory WasmImports(String moduleName) {
|
||||
return _NativeWasmImports(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
class _NativeWasmModule extends NativeFieldWrapperClass1 implements WasmModule {
|
||||
_NativeWasmModule(Uint8List data) {
|
||||
_init(data);
|
||||
}
|
||||
|
||||
WasmInstance instantiate(covariant _NativeWasmImports imports) {
|
||||
return _NativeWasmInstance(this, imports);
|
||||
}
|
||||
|
||||
void _init(Uint8List data) native 'Wasm_initModule';
|
||||
}
|
||||
|
||||
class _NativeWasmImports extends NativeFieldWrapperClass1
|
||||
implements WasmImports {
|
||||
List<WasmMemory> _memories;
|
||||
|
||||
_NativeWasmImports(String moduleName) : _memories = [] {
|
||||
_init(moduleName);
|
||||
}
|
||||
|
||||
void addMemory(String name, WasmMemory memory) {
|
||||
_memories.add(memory);
|
||||
_addMemory(name, memory);
|
||||
}
|
||||
|
||||
void addGlobal<T>(String name, num value, bool mutable) {
|
||||
_addGlobal(name, value, T, mutable);
|
||||
}
|
||||
|
||||
void _init(String moduleName) native 'Wasm_initImports';
|
||||
void _addMemory(String name, WasmMemory memory) native 'Wasm_addMemoryImport';
|
||||
void _addGlobal(String name, num value, Type type, bool mutable)
|
||||
native 'Wasm_addGlobalImport';
|
||||
}
|
||||
|
||||
class _NativeWasmMemory extends NativeFieldWrapperClass1 implements WasmMemory {
|
||||
int _pages;
|
||||
Uint8List _buffer;
|
||||
|
||||
_NativeWasmMemory(int initialPages, int maxPages) : _pages = initialPages {
|
||||
_buffer = _init(initialPages, maxPages);
|
||||
}
|
||||
|
||||
int get lengthInPages => _pages;
|
||||
int get lengthInBytes => _buffer.lengthInBytes;
|
||||
int operator [](int index) => _buffer[index];
|
||||
void operator []=(int index, int value) {
|
||||
_buffer[index] = value;
|
||||
}
|
||||
|
||||
int grow(int deltaPages) {
|
||||
int oldPages = _pages;
|
||||
_buffer = _grow(deltaPages);
|
||||
_pages += deltaPages;
|
||||
return oldPages;
|
||||
}
|
||||
|
||||
Uint8List _init(int initialPages, int maxPages) native 'Wasm_initMemory';
|
||||
Uint8List _grow(int deltaPages) native 'Wasm_growMemory';
|
||||
}
|
||||
|
||||
class _NativeWasmInstance extends NativeFieldWrapperClass1
|
||||
implements WasmInstance {
|
||||
_NativeWasmModule _module;
|
||||
_NativeWasmImports _imports;
|
||||
|
||||
_NativeWasmInstance(_NativeWasmModule module, _NativeWasmImports imports)
|
||||
: _module = module,
|
||||
_imports = imports {
|
||||
_init(module, imports);
|
||||
}
|
||||
|
||||
WasmFunction<T> lookupFunction<T extends Function>(String name) {
|
||||
return _NativeWasmFunction<T>(this, name);
|
||||
}
|
||||
|
||||
void _init(_NativeWasmModule module, _NativeWasmImports imports)
|
||||
native 'Wasm_initInstance';
|
||||
}
|
||||
|
||||
class _NativeWasmFunction<T extends Function> extends NativeFieldWrapperClass1
|
||||
implements WasmFunction<T> {
|
||||
_NativeWasmInstance _inst;
|
||||
|
||||
_NativeWasmFunction(_NativeWasmInstance inst, String name) : _inst = inst {
|
||||
_init(inst, name, T);
|
||||
}
|
||||
|
||||
num call(List<num> args) {
|
||||
var arg_copy = List<num>.from(args, growable: false);
|
||||
return _call(arg_copy);
|
||||
}
|
||||
|
||||
void _init(_NativeWasmInstance inst, String name, Type fnType)
|
||||
native 'Wasm_initFunction';
|
||||
num _call(List<num> args) native 'Wasm_callFunction';
|
||||
}
|
||||
|
||||
@@ -29,8 +29,34 @@ class BaseGrowableArray : public B {
|
||||
}
|
||||
}
|
||||
|
||||
BaseGrowableArray(BaseGrowableArray&& other)
|
||||
: length_(other.length_),
|
||||
capacity_(other.capacity_),
|
||||
data_(other.data_),
|
||||
allocator_(other.allocator_) {
|
||||
other.length_ = 0;
|
||||
other.capacity_ = 0;
|
||||
other.data_ = NULL;
|
||||
}
|
||||
|
||||
~BaseGrowableArray() { allocator_->template Free<T>(data_, capacity_); }
|
||||
|
||||
BaseGrowableArray& operator=(BaseGrowableArray&& other) {
|
||||
intptr_t temp = other.length_;
|
||||
other.length_ = length_;
|
||||
length_ = temp;
|
||||
temp = other.capacity_;
|
||||
other.capacity_ = capacity_;
|
||||
capacity_ = temp;
|
||||
T* temp_data = other.data_;
|
||||
other.data_ = data_;
|
||||
data_ = temp_data;
|
||||
Allocator* temp_allocator = other.allocator_;
|
||||
other.allocator_ = allocator_;
|
||||
allocator_ = temp_allocator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
intptr_t length() const { return length_; }
|
||||
T* data() const { return data_; }
|
||||
bool is_empty() const { return length_ == 0; }
|
||||
|
||||
@@ -89,6 +89,9 @@ declare_args() {
|
||||
|
||||
# Whether libdart should export the symbols of the Dart API.
|
||||
dart_lib_export_symbols = true
|
||||
|
||||
# Whether dart:wasm should be enabled.
|
||||
dart_enable_wasm = false
|
||||
}
|
||||
|
||||
declare_args() {
|
||||
|
||||
@@ -102,6 +102,9 @@ library_for_all_configs("libdart_lib") {
|
||||
]
|
||||
}
|
||||
}
|
||||
if (dart_enable_wasm) {
|
||||
defines = [ "DART_ENABLE_WASM" ]
|
||||
}
|
||||
include_dirs = [ ".." ]
|
||||
allsources = async_runtime_cc_files + collection_runtime_cc_files +
|
||||
core_runtime_cc_files + developer_runtime_cc_files +
|
||||
|
||||
@@ -390,6 +390,14 @@ namespace dart {
|
||||
V(Ffi_dl_executableLibrary, 0) \
|
||||
V(TransferableTypedData_factory, 2) \
|
||||
V(TransferableTypedData_materialize, 1) \
|
||||
V(Wasm_initModule, 2) \
|
||||
V(Wasm_initImports, 2) \
|
||||
V(Wasm_addMemoryImport, 3) \
|
||||
V(Wasm_addGlobalImport, 5) \
|
||||
V(Wasm_initMemory, 3) \
|
||||
V(Wasm_growMemory, 2) \
|
||||
V(Wasm_initInstance, 3) \
|
||||
V(Wasm_initFunction, 4) \
|
||||
V(Wasm_callFunction, 2)
|
||||
|
||||
// List of bootstrap native entry points used in the dart:mirror library.
|
||||
|
||||
@@ -107,4 +107,35 @@ ISOLATE_UNIT_TEST_CASE(GrowableHandlePtr) {
|
||||
EXPECT_EQ(1, test2->length());
|
||||
}
|
||||
|
||||
TEST_CASE(GrowableArrayMoveCtor) {
|
||||
GrowableArray<int> a;
|
||||
a.Add(4);
|
||||
a.Add(5);
|
||||
int* a_data = a.data();
|
||||
|
||||
GrowableArray<int> b(std::move(a));
|
||||
|
||||
EXPECT_EQ(0, a.length());
|
||||
EXPECT_EQ((int*)nullptr, a.data());
|
||||
EXPECT_EQ(2, b.length());
|
||||
EXPECT_EQ(a_data, b.data());
|
||||
}
|
||||
|
||||
TEST_CASE(GrowableArrayMoveAssign) {
|
||||
GrowableArray<int> a, b;
|
||||
a.Add(1);
|
||||
a.Add(2);
|
||||
a.Add(3);
|
||||
b.Add(7);
|
||||
int* a_data = a.data();
|
||||
int* b_data = b.data();
|
||||
|
||||
a = std::move(b);
|
||||
|
||||
EXPECT_EQ(1, a.length());
|
||||
EXPECT_EQ(b_data, a.data());
|
||||
EXPECT_EQ(3, b.length());
|
||||
EXPECT_EQ(a_data, b.data());
|
||||
}
|
||||
|
||||
} // namespace dart
|
||||
|
||||
+58
-3
@@ -6,8 +6,63 @@
|
||||
/// {@nodoc}
|
||||
library dart.wasm;
|
||||
|
||||
int callWasm(String name, int arg) {
|
||||
return _callWasm(name, arg);
|
||||
import 'dart:typed_data';
|
||||
|
||||
export 'dart:ffi' show Int32, Int64, Float, Double, Void;
|
||||
|
||||
// WasmModule is a compiled module that can be instantiated.
|
||||
abstract class WasmModule {
|
||||
// Compile a module.
|
||||
external factory WasmModule(Uint8List data);
|
||||
|
||||
// Instantiate the module with the given imports.
|
||||
WasmInstance instantiate(WasmImports imports);
|
||||
}
|
||||
|
||||
external int _callWasm(String name, int arg);
|
||||
// WasmImports holds all the imports for a WasmInstance.
|
||||
abstract class WasmImports {
|
||||
// Create an imports object.
|
||||
external factory WasmImports(String moduleName);
|
||||
|
||||
// Add a global variable to the imports.
|
||||
void addGlobal<T>(String name, num value, bool mutable);
|
||||
|
||||
// Add a memory to the imports.
|
||||
void addMemory(String name, WasmMemory memory);
|
||||
}
|
||||
|
||||
// WasmMemory is a sandbox for a WasmInstance to run in.
|
||||
abstract class WasmMemory {
|
||||
// Create a new memory with the given number of initial pages, and optional
|
||||
// maximum number of pages.
|
||||
external factory WasmMemory(int initialPages, [int maxPages]);
|
||||
|
||||
// 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;
|
||||
|
||||
// Returns the length of the memory in bytes.
|
||||
int get lengthInBytes;
|
||||
|
||||
// Returns the byte at the given index.
|
||||
int operator [](int index);
|
||||
|
||||
// Sets the byte at the iven index to value.
|
||||
void operator []=(int index, int value);
|
||||
|
||||
// Grow the memory by deltaPages. Returns the number of pages before resizing.
|
||||
int grow(int deltaPages);
|
||||
}
|
||||
|
||||
// WasmInstance is an instantiated WasmModule.
|
||||
abstract class WasmInstance {
|
||||
// Find an exported function with the given signature.
|
||||
WasmFunction<T> lookupFunction<T extends Function>(String name);
|
||||
}
|
||||
|
||||
// WasmFunction is a callable function in a WasmInstance.
|
||||
abstract class WasmFunction<T extends Function> {
|
||||
num call(List<num> args);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,63 @@
|
||||
/// {@nodoc}
|
||||
library dart.wasm;
|
||||
|
||||
int callWasm(String name, int arg) {
|
||||
return _callWasm(name, arg);
|
||||
import 'dart:typed_data';
|
||||
|
||||
export 'dart:ffi' show Int32, Int64, Float, Double, Void;
|
||||
|
||||
// WasmModule is a compiled module that can be instantiated.
|
||||
abstract class WasmModule {
|
||||
// Compile a module.
|
||||
external factory WasmModule(Uint8List data);
|
||||
|
||||
// Instantiate the module with the given imports.
|
||||
WasmInstance instantiate(WasmImports imports);
|
||||
}
|
||||
|
||||
external int _callWasm(String name, int arg);
|
||||
// WasmImports holds all the imports for a WasmInstance.
|
||||
abstract class WasmImports {
|
||||
// Create an imports object.
|
||||
external factory WasmImports(String moduleName);
|
||||
|
||||
// Add a global variable to the imports.
|
||||
void addGlobal<T>(String name, num value, bool mutable);
|
||||
|
||||
// Add a memory to the imports.
|
||||
void addMemory(String name, WasmMemory memory);
|
||||
}
|
||||
|
||||
// WasmMemory is a sandbox for a WasmInstance to run in.
|
||||
abstract class WasmMemory {
|
||||
// Create a new memory with the given number of initial pages, and optional
|
||||
// maximum number of pages.
|
||||
external factory WasmMemory(int initialPages, [int maxPages]);
|
||||
|
||||
// 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;
|
||||
|
||||
// Returns the length of the memory in bytes.
|
||||
int get lengthInBytes;
|
||||
|
||||
// Returns the byte at the given index.
|
||||
int operator [](int index);
|
||||
|
||||
// Sets the byte at the iven index to value.
|
||||
void operator []=(int index, int value);
|
||||
|
||||
// Grow the memory by deltaPages. Returns the number of pages before resizing.
|
||||
int grow(int deltaPages);
|
||||
}
|
||||
|
||||
// WasmInstance is an instantiated WasmModule.
|
||||
abstract class WasmInstance {
|
||||
// Find an exported function with the given signature.
|
||||
WasmFunction<T> lookupFunction<T extends Function>(String name);
|
||||
}
|
||||
|
||||
// WasmFunction is a callable function in a WasmInstance.
|
||||
abstract class WasmFunction<T extends Function> {
|
||||
num call(List<num> args);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test that we can load a wasm module, find a function, and call it.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:ffi';
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
// int64_t square(int64_t n) { return n * n; }
|
||||
var data = Uint8List.fromList([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
|
||||
0x01, 0x7e, 0x01, 0x7e, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70,
|
||||
0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x02, 0x06, 0x08, 0x01, 0x7f,
|
||||
0x01, 0x41, 0x80, 0x88, 0x04, 0x0b, 0x07, 0x13, 0x02, 0x06, 0x6d, 0x65,
|
||||
0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, 0x73, 0x71, 0x75, 0x61, 0x72,
|
||||
0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||
0x7e, 0x0b,
|
||||
]);
|
||||
|
||||
var inst = WasmModule(data).instantiate(WasmImports("env")
|
||||
..addMemory("memory", WasmMemory(256, 1024))
|
||||
..addGlobal<Int32>("__memory_base", 1024, false));
|
||||
var fn = inst.lookupFunction<Int64 Function(Int64)>("square");
|
||||
int n = fn.call([1234]);
|
||||
|
||||
Expect.equals(1234 * 1234, n);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test error thrown when the wasm module is corrupted.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
var data = Uint8List.fromList([
|
||||
0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, 0x01, 0x7e, 0x01, 0x7e,
|
||||
0x07, 0x13, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00,
|
||||
0x06, 0x73, 0x71, 0x75, 0x61, 0x72, 0x65, 0x00, 0x00, 0x00, 0x20, 0x00,
|
||||
0x7e, 0x0b,
|
||||
]);
|
||||
|
||||
Expect.throwsArgumentError(() => WasmModule(data));
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test error thrown when a function is called with the wrong args.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
// int64_t square(int64_t n) { return n * n; }
|
||||
var data = Uint8List.fromList([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
|
||||
0x01, 0x7e, 0x01, 0x7e, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70,
|
||||
0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x02, 0x06, 0x08, 0x01, 0x7f,
|
||||
0x01, 0x41, 0x80, 0x88, 0x04, 0x0b, 0x07, 0x13, 0x02, 0x06, 0x6d, 0x65,
|
||||
0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, 0x73, 0x71, 0x75, 0x61, 0x72,
|
||||
0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||
0x7e, 0x0b,
|
||||
]);
|
||||
|
||||
var inst = WasmModule(data).instantiate(WasmImports("env")
|
||||
..addMemory("memory", WasmMemory(256, 1024))
|
||||
..addGlobal<Int32>("__memory_base", 1024, false));
|
||||
var fn = inst.lookupFunction<Int64 Function(Int64)>("square");
|
||||
|
||||
Expect.throwsArgumentError(() => fn.call([]));
|
||||
Expect.throwsArgumentError(() => fn.call([1, 2, 3]));
|
||||
Expect.throwsArgumentError(() => fn.call([1.23]));
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test error thrown when the loaded function can't be found.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
// int64_t square(int64_t n) { return n * n; }
|
||||
var data = Uint8List.fromList([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60,
|
||||
0x01, 0x7e, 0x01, 0x7e, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70,
|
||||
0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x02, 0x06, 0x08, 0x01, 0x7f,
|
||||
0x01, 0x41, 0x80, 0x88, 0x04, 0x0b, 0x07, 0x13, 0x02, 0x06, 0x6d, 0x65,
|
||||
0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x06, 0x73, 0x71, 0x75, 0x61, 0x72,
|
||||
0x65, 0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x00,
|
||||
0x7e, 0x0b,
|
||||
]);
|
||||
|
||||
var inst = WasmModule(data).instantiate(WasmImports("env")
|
||||
..addMemory("memory", WasmMemory(256, 1024))
|
||||
..addGlobal<Int32>("__memory_base", 1024, false));
|
||||
Expect.isNotNull(inst.lookupFunction<Int64 Function(Int64)>("square"));
|
||||
Expect.throwsArgumentError(
|
||||
() => inst.lookupFunction<Int64 Function(Int64)>("blah"));
|
||||
Expect.throwsArgumentError(
|
||||
() => inst.lookupFunction<Int64 Function()>("square"));
|
||||
Expect.throwsArgumentError(
|
||||
() => inst.lookupFunction<Int64 Function(Int64, Int64)>("square"));
|
||||
Expect.throwsArgumentError(
|
||||
() => inst.lookupFunction<Void Function(Int64)>("square"));
|
||||
Expect.throwsArgumentError(
|
||||
() => inst.lookupFunction<Void Function(dynamic)>("square"));
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test numeric types.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:ffi';
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
// int64_t addI64(int64_t x, int64_t y) { return x + y; }
|
||||
// int32_t addI32(int32_t x, int32_t y) { return x + y; }
|
||||
// double addF64(double x, double y) { return x + y; }
|
||||
// float addF32(float x, float y) { return x + y; }
|
||||
var data = Uint8List.fromList([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x19, 0x04, 0x60,
|
||||
0x02, 0x7e, 0x7e, 0x01, 0x7e, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60,
|
||||
0x02, 0x7c, 0x7c, 0x01, 0x7c, 0x60, 0x02, 0x7d, 0x7d, 0x01, 0x7d, 0x03,
|
||||
0x05, 0x04, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01,
|
||||
0x01, 0x05, 0x03, 0x01, 0x00, 0x02, 0x06, 0x08, 0x01, 0x7f, 0x01, 0x41,
|
||||
0x80, 0x88, 0x04, 0x0b, 0x07, 0x2e, 0x05, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
|
||||
0x72, 0x79, 0x02, 0x00, 0x06, 0x61, 0x64, 0x64, 0x49, 0x36, 0x34, 0x00,
|
||||
0x00, 0x06, 0x61, 0x64, 0x64, 0x49, 0x33, 0x32, 0x00, 0x01, 0x06, 0x61,
|
||||
0x64, 0x64, 0x46, 0x36, 0x34, 0x00, 0x02, 0x06, 0x61, 0x64, 0x64, 0x46,
|
||||
0x33, 0x32, 0x00, 0x03, 0x0a, 0x21, 0x04, 0x07, 0x00, 0x20, 0x01, 0x20,
|
||||
0x00, 0x7c, 0x0b, 0x07, 0x00, 0x20, 0x01, 0x20, 0x00, 0x6a, 0x0b, 0x07,
|
||||
0x00, 0x20, 0x00, 0x20, 0x01, 0xa0, 0x0b, 0x07, 0x00, 0x20, 0x00, 0x20,
|
||||
0x01, 0x92, 0x0b,
|
||||
]);
|
||||
|
||||
var inst = WasmModule(data).instantiate(WasmImports("env")
|
||||
..addMemory("memory", WasmMemory(256, 1024))
|
||||
..addGlobal<Int32>("__memory_base", 1024, false));
|
||||
var addI64 = inst.lookupFunction<Int64 Function(Int64, Int64)>("addI64");
|
||||
var addI32 = inst.lookupFunction<Int32 Function(Int32, Int32)>("addI32");
|
||||
var addF64 = inst.lookupFunction<Double Function(Double, Double)>("addF64");
|
||||
var addF32 = inst.lookupFunction<Float Function(Float, Float)>("addF32");
|
||||
|
||||
int i64 = addI64.call([0x123456789ABCDEF, 0xFEDCBA987654321]);
|
||||
Expect.equals(0x1111111111111110, i64);
|
||||
|
||||
int i32 = addI32.call([0xABCDEF, 0xFEDCBA]);
|
||||
Expect.equals(0x1aaaaa9, i32);
|
||||
|
||||
double f64 = addF64.call([1234.5678, 8765.4321]);
|
||||
Expect.approxEquals(9999.9999, f64, 1e-6);
|
||||
|
||||
double f32 = addF32.call([1234.5678, 8765.4321]);
|
||||
Expect.approxEquals(9999.9999, f32, 1e-3);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Test functions with void return type, and functions that take no args.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import "dart:wasm";
|
||||
import "dart:typed_data";
|
||||
|
||||
void main() {
|
||||
// int64_t x = 0;
|
||||
// void set(int64_t a, int64_t b) { x = a + b; }
|
||||
// int64_t get() { return x; }
|
||||
var data = Uint8List.fromList([
|
||||
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x02, 0x60,
|
||||
0x02, 0x7e, 0x7e, 0x00, 0x60, 0x00, 0x01, 0x7e, 0x03, 0x03, 0x02, 0x00,
|
||||
0x01, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00,
|
||||
0x02, 0x06, 0x08, 0x01, 0x7f, 0x01, 0x41, 0x90, 0x88, 0x04, 0x0b, 0x07,
|
||||
0x16, 0x03, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x03,
|
||||
0x73, 0x65, 0x74, 0x00, 0x00, 0x03, 0x67, 0x65, 0x74, 0x00, 0x01, 0x0a,
|
||||
0x1e, 0x02, 0x10, 0x00, 0x41, 0x00, 0x20, 0x01, 0x20, 0x00, 0x7c, 0x37,
|
||||
0x03, 0x80, 0x88, 0x80, 0x80, 0x00, 0x0b, 0x0b, 0x00, 0x41, 0x00, 0x29,
|
||||
0x03, 0x80, 0x88, 0x80, 0x80, 0x00, 0x0b, 0x0b, 0x0f, 0x01, 0x00, 0x41,
|
||||
0x80, 0x08, 0x0b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
]);
|
||||
|
||||
var inst = WasmModule(data).instantiate(WasmImports("env")
|
||||
..addMemory("memory", WasmMemory(256, 1024))
|
||||
..addGlobal<Int32>("__memory_base", 1024, false));
|
||||
var setFn = inst.lookupFunction<Void Function(Int64, Int64)>("set");
|
||||
var getFn = inst.lookupFunction<Int64 Function()>("get");
|
||||
Expect.isNull(setFn.call([123, 456]));
|
||||
int n = getFn.call([]);
|
||||
Expect.equals(123 + 456, n);
|
||||
}
|
||||
Reference in New Issue
Block a user