Skip to content

Commit 922d211

Browse files
committed
wasi: fast calls
1 parent 7233b18 commit 922d211

28 files changed

+184
-88
lines changed

β€Žsrc/node_wasi.cc

+174-79
Original file line numberDiff line numberDiff line change
@@ -72,18 +72,28 @@ inline void Debug(WASI* wasi, Args&&... args) {
7272
} \
7373
} while (0)
7474

75+
#define CHECK_BOUNDS_OR_RETURN2(mem_size, offset, buf_size) \
76+
do { \
77+
if (!uvwasi_serdes_check_bounds((offset), (mem_size), (buf_size))) { \
78+
return UVWASI_EOVERFLOW; \
79+
} \
80+
} while (0)
81+
7582
using v8::Array;
7683
using v8::BackingStore;
7784
using v8::BigInt;
85+
using v8::CFunction;
7886
using v8::Context;
7987
using v8::Exception;
88+
using v8::FastApiCallbackOptions;
8089
using v8::FunctionCallbackInfo;
8190
using v8::FunctionTemplate;
8291
using v8::Integer;
8392
using v8::Isolate;
8493
using v8::Local;
8594
using v8::MaybeLocal;
8695
using v8::Object;
96+
using v8::Signature;
8797
using v8::String;
8898
using v8::Uint32;
8999
using v8::Value;
@@ -248,46 +258,155 @@ void WASI::New(const FunctionCallbackInfo<Value>& args) {
248258
}
249259
}
250260

261+
struct WasmMemory {
262+
char* data;
263+
size_t size;
264+
};
265+
266+
template <auto F, typename R, typename... Args>
267+
class WasiFunction {
268+
public:
269+
static void SetFunction(Environment* env,
270+
const char* name,
271+
Local<FunctionTemplate> tmpl) {
272+
auto c_function = CFunction::Make(FastCallback);
273+
Local<FunctionTemplate> t =
274+
v8::FunctionTemplate::New(env->isolate(),
275+
SlowCallback,
276+
Local<Value>(),
277+
Local<Signature>(),
278+
sizeof...(Args),
279+
v8::ConstructorBehavior::kThrow,
280+
v8::SideEffectType::kHasSideEffect,
281+
&c_function);
282+
const v8::NewStringType type = v8::NewStringType::kInternalized;
283+
v8::Local<v8::String> name_string =
284+
v8::String::NewFromUtf8(env->isolate(), name, type).ToLocalChecked();
285+
tmpl->PrototypeTemplate()->Set(name_string, t);
286+
t->SetClassName(name_string);
287+
}
251288

252-
void WASI::ArgsGet(const FunctionCallbackInfo<Value>& args) {
253-
WASI* wasi;
254-
uint32_t argv_offset;
255-
uint32_t argv_buf_offset;
256-
char* memory;
257-
size_t mem_size;
258-
RETURN_IF_BAD_ARG_COUNT(args, 2);
259-
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, argv_offset);
260-
CHECK_TO_TYPE_OR_RETURN(args, args[1], Uint32, argv_buf_offset);
261-
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
262-
Debug(wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset);
263-
GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size);
264-
CHECK_BOUNDS_OR_RETURN(args,
265-
mem_size,
266-
argv_buf_offset,
267-
wasi->uvw_.argv_buf_size);
268-
CHECK_BOUNDS_OR_RETURN(args,
269-
mem_size,
270-
argv_offset,
271-
wasi->uvw_.argc * UVWASI_SERDES_SIZE_uint32_t);
272-
std::vector<char*> argv(wasi->uvw_.argc);
273-
char* argv_buf = &memory[argv_buf_offset];
274-
uvwasi_errno_t err = uvwasi_args_get(&wasi->uvw_, argv.data(), argv_buf);
289+
private:
290+
static R FastCallback(Local<Object> receiver,
291+
Args... args,
292+
FastApiCallbackOptions& options) {
293+
WASI* wasi = reinterpret_cast<WASI*>(BaseObject::FromJSObject(receiver));
294+
if (UNLIKELY(wasi == nullptr)) return UVWASI_EINVAL;
295+
296+
if (UNLIKELY(options.wasm_memory == nullptr)) return UVWASI_EINVAL;
297+
uint8_t* memory = nullptr;
298+
CHECK(LIKELY(options.wasm_memory->getStorageIfAligned(&memory)));
299+
300+
return F(*wasi, {(char*)memory, options.wasm_memory->length()}, args...);
301+
}
302+
303+
template <typename VT>
304+
static uvwasi_errno_t CheckType(Local<Value> v);
305+
306+
template <typename VT>
307+
static VT ConvertType(Local<Value> V);
308+
309+
template <>
310+
static uvwasi_errno_t CheckType<uint32_t>(Local<Value> value) {
311+
if (!value->IsUint32()) return UVWASI_EINVAL;
312+
return UVWASI_ESUCCESS;
313+
}
314+
315+
template <>
316+
static uint32_t ConvertType(Local<Value> value) {
317+
return value.As<Uint32>()->Value();
318+
}
319+
320+
template <>
321+
static uvwasi_errno_t CheckType<uint64_t>(Local<Value> value) {
322+
if (!value->IsBigInt()) return UVWASI_EINVAL;
323+
return UVWASI_ESUCCESS;
324+
}
325+
326+
template <>
327+
static uint64_t ConvertType(Local<Value> value) {
328+
Local<BigInt> js_value = value.As<BigInt>();
329+
bool lossless;
330+
return js_value->Uint64Value(&lossless);
331+
}
332+
333+
template <>
334+
static uvwasi_errno_t CheckType<int64_t>(Local<Value> value) {
335+
if (!value->IsBigInt()) return UVWASI_EINVAL;
336+
return UVWASI_ESUCCESS;
337+
}
338+
339+
template <>
340+
static int64_t ConvertType(Local<Value> value) {
341+
Local<BigInt> js_value = value.As<BigInt>();
342+
bool lossless;
343+
return js_value->Int64Value(&lossless);
344+
}
345+
346+
#pragma clang diagnostic push
347+
#pragma clang diagnostic ignored "-Wunsequenced"
348+
static void SlowCallback(const FunctionCallbackInfo<Value>& args) {
349+
WASI* wasi;
350+
char* memory;
351+
size_t mem_size;
352+
RETURN_IF_BAD_ARG_COUNT(args, sizeof...(Args));
353+
{
354+
int counter = 0;
355+
uvwasi_errno_t results[sizeof...(Args)] = {
356+
(CheckType<Args>(args[counter++]))...};
357+
for (size_t i = 0; i < sizeof...(Args); i += 1) {
358+
if (results[i] != UVWASI_ESUCCESS) {
359+
args.GetReturnValue().Set(results[i]);
360+
return;
361+
}
362+
}
363+
}
364+
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
365+
GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size);
366+
367+
{
368+
int counter = 0;
369+
args.GetReturnValue().Set(
370+
F(*wasi, {memory, mem_size}, ConvertType<Args>(args[counter++])...));
371+
}
372+
}
373+
#pragma clang diagnostic pop
374+
};
375+
376+
template <auto F, typename R, typename... Args>
377+
static void SetFunction(R (*)(WASI&, WasmMemory, Args...),
378+
Environment* env,
379+
const char* name,
380+
Local<FunctionTemplate> tmpl) {
381+
WasiFunction<F, R, Args...>::SetFunction(env, name, tmpl);
382+
}
383+
384+
static uint32_t ArgsGet(WASI& wasi,
385+
WasmMemory memory,
386+
uint32_t argv_offset,
387+
uint32_t argv_buf_offset) {
388+
Debug(&wasi, "args_get(%d, %d)\n", argv_offset, argv_buf_offset);
389+
390+
CHECK_BOUNDS_OR_RETURN2(
391+
memory.size, argv_buf_offset, wasi.uvw_.argv_buf_size);
392+
CHECK_BOUNDS_OR_RETURN2(
393+
memory.size, argv_offset, wasi.uvw_.argc * UVWASI_SERDES_SIZE_uint32_t);
394+
std::vector<char*> argv(wasi.uvw_.argc);
395+
char* argv_buf = &memory.data[argv_buf_offset];
396+
uvwasi_errno_t err = uvwasi_args_get(&wasi.uvw_, argv.data(), argv_buf);
275397

276398
if (err == UVWASI_ESUCCESS) {
277-
for (size_t i = 0; i < wasi->uvw_.argc; i++) {
399+
for (size_t i = 0; i < wasi.uvw_.argc; i++) {
278400
uint32_t offset =
279401
static_cast<uint32_t>(argv_buf_offset + (argv[i] - argv[0]));
280-
uvwasi_serdes_write_uint32_t(memory,
281-
argv_offset +
282-
(i * UVWASI_SERDES_SIZE_uint32_t),
283-
offset);
402+
uvwasi_serdes_write_uint32_t(
403+
memory.data, argv_offset + (i * UVWASI_SERDES_SIZE_uint32_t), offset);
284404
}
285405
}
286406

287-
args.GetReturnValue().Set(err);
407+
return err;
288408
}
289409

290-
291410
void WASI::ArgsSizesGet(const FunctionCallbackInfo<Value>& args) {
292411
WASI* wasi;
293412
uint32_t argc_offset;
@@ -454,25 +573,16 @@ void WASI::EnvironSizesGet(const FunctionCallbackInfo<Value>& args) {
454573
args.GetReturnValue().Set(err);
455574
}
456575

457-
458-
void WASI::FdAdvise(const FunctionCallbackInfo<Value>& args) {
459-
WASI* wasi;
460-
uint32_t fd;
461-
uint64_t offset;
462-
uint64_t len;
463-
uint8_t advice;
464-
RETURN_IF_BAD_ARG_COUNT(args, 4);
465-
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd);
466-
UNWRAP_BIGINT_OR_RETURN(args, args[1], Uint64, offset);
467-
UNWRAP_BIGINT_OR_RETURN(args, args[2], Uint64, len);
468-
CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, advice);
469-
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
470-
Debug(wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice);
471-
uvwasi_errno_t err = uvwasi_fd_advise(&wasi->uvw_, fd, offset, len, advice);
472-
args.GetReturnValue().Set(err);
576+
static uint32_t FdAdvise(WASI& wasi,
577+
WasmMemory,
578+
uint32_t fd,
579+
uint64_t offset,
580+
uint64_t len,
581+
uint32_t advice) {
582+
Debug(&wasi, "fd_advise(%d, %d, %d, %d)\n", fd, offset, len, advice);
583+
return uvwasi_fd_advise(&wasi.uvw_, fd, offset, len, advice);
473584
}
474585

475-
476586
void WASI::FdAllocate(const FunctionCallbackInfo<Value>& args) {
477587
WASI* wasi;
478588
uint32_t fd;
@@ -892,40 +1002,24 @@ void WASI::FdRenumber(const FunctionCallbackInfo<Value>& args) {
8921002
args.GetReturnValue().Set(err);
8931003
}
8941004

895-
896-
void WASI::FdSeek(const FunctionCallbackInfo<Value>& args) {
897-
WASI* wasi;
898-
uint32_t fd;
899-
int64_t offset;
900-
uint8_t whence;
901-
uint32_t newoffset_ptr;
902-
char* memory;
903-
size_t mem_size;
904-
RETURN_IF_BAD_ARG_COUNT(args, 4);
905-
CHECK_TO_TYPE_OR_RETURN(args, args[0], Uint32, fd);
906-
UNWRAP_BIGINT_OR_RETURN(args, args[1], Int64, offset);
907-
CHECK_TO_TYPE_OR_RETURN(args, args[2], Uint32, whence);
908-
CHECK_TO_TYPE_OR_RETURN(args, args[3], Uint32, newoffset_ptr);
909-
ASSIGN_INITIALIZED_OR_RETURN_UNWRAP(&wasi, args.This());
910-
Debug(wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr);
911-
GET_BACKING_STORE_OR_RETURN(wasi, args, &memory, &mem_size);
912-
CHECK_BOUNDS_OR_RETURN(args,
913-
mem_size,
914-
newoffset_ptr,
915-
UVWASI_SERDES_SIZE_filesize_t);
1005+
uint32_t FdSeek(WASI& wasi,
1006+
WasmMemory memory,
1007+
uint32_t fd,
1008+
int64_t offset,
1009+
uint32_t whence,
1010+
uint32_t newoffset_ptr) {
1011+
Debug(&wasi, "fd_seek(%d, %d, %d, %d)\n", fd, offset, whence, newoffset_ptr);
1012+
CHECK_BOUNDS_OR_RETURN2(
1013+
memory.size, newoffset_ptr, UVWASI_SERDES_SIZE_filesize_t);
9161014
uvwasi_filesize_t newoffset;
917-
uvwasi_errno_t err = uvwasi_fd_seek(&wasi->uvw_,
918-
fd,
919-
offset,
920-
whence,
921-
&newoffset);
1015+
uvwasi_errno_t err =
1016+
uvwasi_fd_seek(&wasi.uvw_, fd, offset, whence, &newoffset);
9221017
if (err == UVWASI_ESUCCESS)
923-
uvwasi_serdes_write_filesize_t(memory, newoffset_ptr, newoffset);
1018+
uvwasi_serdes_write_filesize_t(memory.data, newoffset_ptr, newoffset);
9241019

925-
args.GetReturnValue().Set(err);
1020+
return err;
9261021
}
9271022

928-
9291023
void WASI::FdSync(const FunctionCallbackInfo<Value>& args) {
9301024
WASI* wasi;
9311025
uint32_t fd;
@@ -1673,13 +1767,14 @@ static void Initialize(Local<Object> target,
16731767
tmpl->InstanceTemplate()->SetInternalFieldCount(WASI::kInternalFieldCount);
16741768
tmpl->Inherit(BaseObject::GetConstructorTemplate(env));
16751769

1676-
env->SetProtoMethod(tmpl, "args_get", WASI::ArgsGet);
1770+
SetFunction<ArgsGet>(ArgsGet, env, "args_get", tmpl);
1771+
16771772
env->SetProtoMethod(tmpl, "args_sizes_get", WASI::ArgsSizesGet);
16781773
env->SetProtoMethod(tmpl, "clock_res_get", WASI::ClockResGet);
16791774
env->SetProtoMethod(tmpl, "clock_time_get", WASI::ClockTimeGet);
16801775
env->SetProtoMethod(tmpl, "environ_get", WASI::EnvironGet);
16811776
env->SetProtoMethod(tmpl, "environ_sizes_get", WASI::EnvironSizesGet);
1682-
env->SetProtoMethod(tmpl, "fd_advise", WASI::FdAdvise);
1777+
SetFunction<FdAdvise>(FdAdvise, env, "fd_advise", tmpl);
16831778
env->SetProtoMethod(tmpl, "fd_allocate", WASI::FdAllocate);
16841779
env->SetProtoMethod(tmpl, "fd_close", WASI::FdClose);
16851780
env->SetProtoMethod(tmpl, "fd_datasync", WASI::FdDatasync);
@@ -1696,7 +1791,7 @@ static void Initialize(Local<Object> target,
16961791
env->SetProtoMethod(tmpl, "fd_read", WASI::FdRead);
16971792
env->SetProtoMethod(tmpl, "fd_readdir", WASI::FdReaddir);
16981793
env->SetProtoMethod(tmpl, "fd_renumber", WASI::FdRenumber);
1699-
env->SetProtoMethod(tmpl, "fd_seek", WASI::FdSeek);
1794+
SetFunction<FdSeek>(FdSeek, env, "fd_seek", tmpl);
17001795
env->SetProtoMethod(tmpl, "fd_sync", WASI::FdSync);
17011796
env->SetProtoMethod(tmpl, "fd_tell", WASI::FdTell);
17021797
env->SetProtoMethod(tmpl, "fd_write", WASI::FdWrite);

β€Žsrc/node_wasi.h

+6-7
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,11 @@ class WASI : public BaseObject,
2323
SET_MEMORY_INFO_NAME(WASI)
2424
SET_SELF_SIZE(WASI)
2525

26-
static void ArgsGet(const v8::FunctionCallbackInfo<v8::Value>& args);
2726
static void ArgsSizesGet(const v8::FunctionCallbackInfo<v8::Value>& args);
2827
static void ClockResGet(const v8::FunctionCallbackInfo<v8::Value>& args);
2928
static void ClockTimeGet(const v8::FunctionCallbackInfo<v8::Value>& args);
3029
static void EnvironGet(const v8::FunctionCallbackInfo<v8::Value>& args);
3130
static void EnvironSizesGet(const v8::FunctionCallbackInfo<v8::Value>& args);
32-
static void FdAdvise(const v8::FunctionCallbackInfo<v8::Value>& args);
3331
static void FdAllocate(const v8::FunctionCallbackInfo<v8::Value>& args);
3432
static void FdClose(const v8::FunctionCallbackInfo<v8::Value>& args);
3533
static void FdDatasync(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -82,6 +80,12 @@ class WASI : public BaseObject,
8280
void IncreaseAllocatedSize(size_t size);
8381
void DecreaseAllocatedSize(size_t size);
8482

83+
uvwasi_errno_t backingStore(char** store, size_t* byte_length);
84+
uvwasi_t uvw_;
85+
v8::Global<v8::WasmMemoryObject> memory_;
86+
uvwasi_mem_t alloc_info_;
87+
size_t current_uvwasi_memory_ = 0;
88+
8589
private:
8690
~WASI() override;
8791
inline void readUInt8(char* memory, uint8_t* value, uint32_t offset);
@@ -92,11 +96,6 @@ class WASI : public BaseObject,
9296
inline void writeUInt16(char* memory, uint16_t value, uint32_t offset);
9397
inline void writeUInt32(char* memory, uint32_t value, uint32_t offset);
9498
inline void writeUInt64(char* memory, uint64_t value, uint32_t offset);
95-
uvwasi_errno_t backingStore(char** store, size_t* byte_length);
96-
uvwasi_t uvw_;
97-
v8::Global<v8::WasmMemoryObject> memory_;
98-
uvwasi_mem_t alloc_info_;
99-
size_t current_uvwasi_memory_ = 0;
10099
};
101100

102101

β€Žtest/wasi/Makefile

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
CC = /opt/wasi-sdk/bin/clang
2-
TARGET = wasm32-unknown-wasi
2+
TARGET = wasm32-wasi
33
SYSROOT =
4+
CFLAGS = -D_WASI_EMULATED_PROCESS_CLOCKS -lwasi-emulated-process-clocks
45

56
OBJ = $(patsubst c/%.c, wasm/%.wasm, $(wildcard c/*.c))
67
all: $(OBJ)
78

89
wasm/%.wasm : c/%.c
9-
$(CC) $< --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@
10+
$(CC) $< $(CFLAGS) --target=$(TARGET) --sysroot=$(SYSROOT) -s -o $@
1011

1112
.PHONY clean:
1213
rm -f $(OBJ)

β€Žtest/wasi/test-wasi.js

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ if (process.argv[2] === 'wasi-child') {
5050
opts.input = options.stdin;
5151

5252
const child = cp.spawnSync(process.execPath, [
53+
...process.argv.slice(1, -1),
5354
'--experimental-wasi-unstable-preview1',
5455
__filename,
5556
'wasi-child',

β€Žtest/wasi/wasm/cant_dotdot.wasm

-1.83 KB
Binary file not shown.

β€Žtest/wasi/wasm/clock_getres.wasm

-11.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/create_symlink.wasm

-2.72 KB
Binary file not shown.

β€Žtest/wasi/wasm/exitcode.wasm

-11.6 KB
Binary file not shown.
-11.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/follow_symlink.wasm

-2.9 KB
Binary file not shown.

β€Žtest/wasi/wasm/freopen.wasm

-2.53 KB
Binary file not shown.

β€Žtest/wasi/wasm/ftruncate.wasm

-1.84 KB
Binary file not shown.

β€Žtest/wasi/wasm/getentropy.wasm

-11.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/getrusage.wasm

-11.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/gettimeofday.wasm

-11.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/link.wasm

-1.86 KB
Binary file not shown.

β€Žtest/wasi/wasm/main_args.wasm

-2.67 KB
Binary file not shown.

β€Žtest/wasi/wasm/notdir.wasm

-1.96 KB
Binary file not shown.

β€Žtest/wasi/wasm/poll.wasm

-1.76 KB
Binary file not shown.
-11.6 KB
Binary file not shown.

β€Žtest/wasi/wasm/read_file.wasm

-2.9 KB
Binary file not shown.
-2.9 KB
Binary file not shown.

β€Žtest/wasi/wasm/readdir.wasm

-1.45 KB
Binary file not shown.

β€Žtest/wasi/wasm/stat.wasm

-1.62 KB
Binary file not shown.

β€Žtest/wasi/wasm/stdin.wasm

-12.5 KB
Binary file not shown.

β€Žtest/wasi/wasm/symlink_escape.wasm

-1.85 KB
Binary file not shown.

β€Žtest/wasi/wasm/symlink_loop.wasm

-1.85 KB
Binary file not shown.

β€Žtest/wasi/wasm/write_file.wasm

-1.48 KB
Binary file not shown.

0 commit comments

Comments
Β (0)