From 118fb4ff145dfcde063d764c990a19455d4da74f Mon Sep 17 00:00:00 2001 From: cjihrig Date: Sun, 8 Sep 2019 21:25:20 -0400 Subject: [PATCH 1/2] wasi: improve JavaScript API - Add WASI.start(). - Export a valid wasi_unstable import. --- lib/wasi.js | 40 +++++++++++++++++++++++++++++++++------- src/node_wasi.cc | 43 ++++++++++++++++++------------------------- src/node_wasi.h | 2 +- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/lib/wasi.js b/lib/wasi.js index 6e95db1ab2..f7d066b1e2 100644 --- a/lib/wasi.js +++ b/lib/wasi.js @@ -1,6 +1,7 @@ // TODO(cjihrig): Put WASI behind a flag. // TODO(cjihrig): Provide a mechanism to bind to WASM modules. 'use strict'; +/* global WebAssembly */ const { Array, ArrayPrototype } = primordials; const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; const { WASI: _WASI } = internalBinding('wasi'); @@ -12,7 +13,7 @@ class WASI { throw new ERR_INVALID_ARG_TYPE('options', 'object', options); // eslint-disable-next-line prefer-const - let { args, env, preopens } = options; + let { args, env, preopens, memory } = options; if (Array.isArray(args)) args = ArrayPrototype.map(args, (arg) => { return String(arg); }); @@ -43,12 +44,37 @@ class WASI { // TODO(cjihrig): Validate preopen object schema. - // TODO(cjihrig): Temporarily expose these for development. - // eslint-disable-next-line no-undef - const memory = Buffer.alloc(200000); - this._memory = memory; - this._view = new DataView(memory.buffer); - this._wasi = new _WASI(args, envPairs, preopens, memory); + if (memory instanceof WebAssembly.Memory) { + memory = memory.buffer; + } else { + throw new ERR_INVALID_ARG_TYPE( + 'options.memory', 'WebAssembly.Memory', memory); + } + + const wrap = new _WASI(args, envPairs, preopens, memory); + + for (const prop in wrap) { + wrap[prop] = wrap[prop].bind(wrap); + } + + this.wasiImport = wrap; + } + + static start(instance) { + if (!(instance instanceof WebAssembly.Instance)) { + throw new ERR_INVALID_ARG_TYPE( + 'instance', 'WebAssembly.Instance', instance); + } + + const exports = instance.exports; + + if (exports === null || typeof exports !== 'object') + throw new ERR_INVALID_ARG_TYPE('instance.exports', 'Object', exports); + + if (exports._start) + exports._start(); + else if (exports.__wasi_unstable_reactor_start) + exports.__wasi_unstable_reactor_start(); } } diff --git a/src/node_wasi.cc b/src/node_wasi.cc index c86fbb4c79..873896e215 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -9,7 +9,7 @@ namespace node { namespace wasi { using v8::Array; -using v8::ArrayBufferView; +using v8::ArrayBuffer; using v8::Context; using v8::FunctionCallbackInfo; using v8::FunctionTemplate; @@ -26,10 +26,7 @@ WASI::WASI(Environment* env, uvwasi_options_t* options) : BaseObject(env, object) { /* uvwasi_errno_t err = */ uvwasi_init(&uvw_, options); - if (memory->IsNull()) - memory_.Reset(); - else - memory_.Reset(env->isolate(), memory.As()); + memory_.Reset(env->isolate(), memory.As()); } @@ -44,7 +41,7 @@ void WASI::New(const FunctionCallbackInfo& args) { CHECK(args[0]->IsArray()); CHECK(args[1]->IsArray()); // CHECK(args[2]->IsArray()); - CHECK(args[3]->IsArrayBufferView() || args[3]->IsNull()); + CHECK(args[3]->IsArrayBuffer()); Environment* env = Environment::GetCurrent(args); Local context = env->context(); @@ -106,17 +103,14 @@ void WASI::ArgsGet(const FunctionCallbackInfo& args) { CHECK(args[1]->IsUint32()); ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); Environment* env = wasi->env(); - Local abv = PersistentToLocal::Default(env->isolate(), - wasi->memory_); - // if (!abv->HasBuffer()) - // return UVWASI_ENOBUFS; - CHECK(abv->HasBuffer()); + Local ab = PersistentToLocal::Default(env->isolate(), + wasi->memory_); // TODO(cjihrig): Check for buffer overflows. uint32_t argv_offset = args[0].As()->Value(); uint32_t argv_buf_offset = args[1].As()->Value(); - char* buf = static_cast(abv->Buffer()->GetContents().Data()); + char* buf = static_cast(ab->GetContents().Data()); char** argv = new char*[wasi->uvw_.argc]; char* argv_buf = &buf[argv_buf_offset]; uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv, argv_buf); @@ -171,17 +165,14 @@ void WASI::EnvironGet(const FunctionCallbackInfo& args) { CHECK(args[1]->IsUint32()); ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); Environment* env = wasi->env(); - Local abv = PersistentToLocal::Default(env->isolate(), - wasi->memory_); - // if (!abv->HasBuffer()) - // return UVWASI_ENOBUFS; - CHECK(abv->HasBuffer()); + Local ab = PersistentToLocal::Default(env->isolate(), + wasi->memory_); // TODO(cjihrig): Check for buffer overflows. uint32_t environ_offset = args[0].As()->Value(); uint32_t environ_buf_offset = args[1].As()->Value(); - char* buf = static_cast(abv->Buffer()->GetContents().Data()); + char* buf = static_cast(ab->GetContents().Data()); char** environ = new char*[wasi->uvw_.envc]; char* environ_buf = &buf[environ_buf_offset]; uvwasi_errno_t err = uvwasi_environ_get(&wasi->uvw_, environ, environ_buf); @@ -380,7 +371,12 @@ void WASI::PollOneoff(const FunctionCallbackInfo& args) { void WASI::ProcExit(const FunctionCallbackInfo& args) { - args.GetReturnValue().Set(UVWASI_ENOTSUP); + WASI* wasi; + CHECK_EQ(args.Length(), 1); + CHECK(args[0]->IsUint32()); + ASSIGN_OR_RETURN_UNWRAP(&wasi, args.This()); + uint32_t code = args[0].As()->Value(); + args.GetReturnValue().Set(uvwasi_proc_exit(&wasi->uvw_, code)); } @@ -416,12 +412,9 @@ void WASI::SockShutdown(const FunctionCallbackInfo& args) { inline uvwasi_errno_t WASI::writeUInt32(uint32_t value, uint32_t offset) { Environment* env = this->env(); - Local abv = PersistentToLocal::Default(env->isolate(), - this->memory_); - if (!abv->HasBuffer()) - return UVWASI_ENOBUFS; - - uint8_t* buf = static_cast(abv->Buffer()->GetContents().Data()); + Local ab = PersistentToLocal::Default(env->isolate(), + this->memory_); + uint8_t* buf = static_cast(ab->GetContents().Data()); // Bounds check. UVWASI_EOVERFLOW buf[offset++] = value & 0xFF; diff --git a/src/node_wasi.h b/src/node_wasi.h index 46d15b8acd..c8878ace7c 100644 --- a/src/node_wasi.h +++ b/src/node_wasi.h @@ -77,7 +77,7 @@ class WASI : public BaseObject { ~WASI() override; inline uvwasi_errno_t writeUInt32(uint32_t value, uint32_t offset); uvwasi_t uvw_; - v8::Persistent memory_; + v8::Persistent memory_; }; From 2b0131c2f7e8961327ac81013927e1a659e8211a Mon Sep 17 00:00:00 2001 From: cjihrig Date: Tue, 10 Sep 2019 10:55:29 -0400 Subject: [PATCH 2/2] wasi: support SharedArrayBuffers as memory This commit adds support for SharedArrayBuffers as the WASI memory. --- lib/wasi.js | 3 ++- src/node_wasi.cc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/wasi.js b/lib/wasi.js index f7d066b1e2..16e39c43e7 100644 --- a/lib/wasi.js +++ b/lib/wasi.js @@ -5,6 +5,7 @@ const { Array, ArrayPrototype } = primordials; const { ERR_INVALID_ARG_TYPE } = require('internal/errors').codes; const { WASI: _WASI } = internalBinding('wasi'); +const { isAnyArrayBuffer } = require('internal/util/types'); class WASI { @@ -46,7 +47,7 @@ class WASI { if (memory instanceof WebAssembly.Memory) { memory = memory.buffer; - } else { + } else if (!isAnyArrayBuffer(memory)) { throw new ERR_INVALID_ARG_TYPE( 'options.memory', 'WebAssembly.Memory', memory); } diff --git a/src/node_wasi.cc b/src/node_wasi.cc index 873896e215..d7050a8e77 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -41,7 +41,7 @@ void WASI::New(const FunctionCallbackInfo& args) { CHECK(args[0]->IsArray()); CHECK(args[1]->IsArray()); // CHECK(args[2]->IsArray()); - CHECK(args[3]->IsArrayBuffer()); + CHECK(args[3]->IsArrayBuffer() || args[3]->IsSharedArrayBuffer()); Environment* env = Environment::GetCurrent(args); Local context = env->context();