diff --git a/deps/uvwasi/src/uvwasi.c b/deps/uvwasi/src/uvwasi.c index 51af4e5641..ac6e0cef36 100644 --- a/deps/uvwasi/src/uvwasi.c +++ b/deps/uvwasi/src/uvwasi.c @@ -348,24 +348,37 @@ uvwasi_errno_t uvwasi_args_sizes_get(uvwasi_t* uvwasi, uvwasi_errno_t uvwasi_clock_res_get(uvwasi_t* uvwasi, uvwasi_clockid_t clock_id, uvwasi_timestamp_t* resolution) { +#ifndef _WIN32 + struct timespec ts; + clockid_t clk; +#endif /* _WIN32 */ + if (uvwasi == NULL || resolution == NULL) return UVWASI_EINVAL; - /* - if (clock_id == UVWASI_CLOCK_MONOTONIC) { - - } else if (clock_id == UVWASI_CLOCK_PROCESS_CPUTIME_ID) { - - } else if (clock_id == UVWASI_CLOCK_REALTIME) { + if (clock_id == UVWASI_CLOCK_MONOTONIC || + clock_id == UVWASI_CLOCK_REALTIME) { + *resolution = 1; // Nanosecond precision. + return UVWASI_ESUCCESS; + } else if (clock_id == UVWASI_CLOCK_PROCESS_CPUTIME_ID || + clock_id == UVWASI_CLOCK_THREAD_CPUTIME_ID) { +#ifndef _WIN32 + if (clock_id == UVWASI_CLOCK_PROCESS_CPUTIME_ID) + clk = CLOCK_PROCESS_CPUTIME_ID; + else + clk = CLOCK_THREAD_CPUTIME_ID; - } else if (clock_id == UVWASI_CLOCK_THREAD_CPUTIME_ID) { + if (clock_getres(clk, &ts) < 0) + return uvwasi__translate_uv_error(uv_translate_sys_error(errno)); - } else { - return UVWASI_EINVAL; + *resolution = (ts.tv_sec * NANOS_PER_SEC) + ts.tv_nsec; + return UVWASI_ESUCCESS; +#else + return UVWASI_ENOSYS; +#endif /* _WIN32 */ } - */ - return UVWASI_ENOTSUP; + return UVWASI_EINVAL; } @@ -373,24 +386,29 @@ uvwasi_errno_t uvwasi_clock_time_get(uvwasi_t* uvwasi, uvwasi_clockid_t clock_id, uvwasi_timestamp_t precision, uvwasi_timestamp_t* time) { + uv_timeval64_t tv; + int r; + if (uvwasi == NULL || time == NULL) return UVWASI_EINVAL; - /* if (clock_id == UVWASI_CLOCK_MONOTONIC) { - - } else if (clock_id == UVWASI_CLOCK_PROCESS_CPUTIME_ID) { - + *time = uv_hrtime(); + return UVWASI_ESUCCESS; } else if (clock_id == UVWASI_CLOCK_REALTIME) { - - } else if (clock_id == UVWASI_CLOCK_THREAD_CPUTIME_ID) { - - } else { - return UVWASI_EINVAL; + r = uv_gettimeofday(&tv); + if (r != 0) + return uvwasi__translate_uv_error(r); + + *time = (tv.tv_sec * NANOS_PER_SEC) + (tv.tv_usec * 1000); + return UVWASI_ESUCCESS; + } else if (clock_id == UVWASI_CLOCK_PROCESS_CPUTIME_ID || + clock_id == UVWASI_CLOCK_THREAD_CPUTIME_ID) { + /* TODO(cjihrig): Implement these two clocks. */ + return UVWASI_ENOSYS; } - */ - return UVWASI_ENOTSUP; + return UVWASI_EINVAL; } diff --git a/src/node_wasi.cc b/src/node_wasi.cc index deb89e5d40..6f2c2ad40b 100644 --- a/src/node_wasi.cc +++ b/src/node_wasi.cc @@ -444,7 +444,23 @@ void WASI::FdFilestatGet(const FunctionCallbackInfo& args) { uvwasi_filestat_t stats; uvwasi_errno_t err = uvwasi_fd_filestat_get(&wasi->uvw_, fd, &stats); - // TODO(cjihrig): Check for buffer overflow and write result to memory. + // TODO(cjihrig): Check for buffer overflows. + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_dev, buf); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_ino, buf + 8); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt8(stats.st_filetype, buf + 16); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt32(stats.st_nlink, buf + 20); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_size, buf + 24); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_atim, buf + 32); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_mtim, buf + 40); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_ctim, buf + 48); args.GetReturnValue().Set(err); } @@ -884,7 +900,24 @@ void WASI::PathFilestatGet(const FunctionCallbackInfo& args) { &memory[path_ptr], path_len, &stats); - // TODO(cjihrig): Check for buffer overflows and write output. + // TODO(cjihrig): Check for buffer overflows. + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_dev, buf_ptr); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_ino, buf_ptr + 8); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt8(stats.st_filetype, buf_ptr + 16); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt32(stats.st_nlink, buf_ptr + 20); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_size, buf_ptr + 24); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_atim, buf_ptr + 32); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_mtim, buf_ptr + 40); + if (err == UVWASI_ESUCCESS) + err = wasi->writeUInt64(stats.st_ctim, buf_ptr + 48); + args.GetReturnValue().Set(err); } diff --git a/test/fixtures/wasi/notadir b/test/fixtures/wasi/notadir new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/wasi/c/clock_getres.c b/test/wasi/c/clock_getres.c new file mode 100644 index 0000000000..eaac02c665 --- /dev/null +++ b/test/wasi/c/clock_getres.c @@ -0,0 +1,17 @@ +#include +#include + +int main() { + struct timespec ts; + int r; + + // supported clocks + r = clock_getres(CLOCK_REALTIME, &ts); + assert(r == 0); + r = clock_getres(CLOCK_MONOTONIC, &ts); + assert(r == 0); + r = clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts); + assert(r == 0); + r = clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts); + assert(r == 0); +} diff --git a/test/wasi/c/getentropy.c b/test/wasi/c/getentropy.c new file mode 100644 index 0000000000..75547e1c47 --- /dev/null +++ b/test/wasi/c/getentropy.c @@ -0,0 +1,18 @@ +#include +#include + +int main() { + char buf[256] = {0}; + int r = getentropy(buf, 256); + assert(r == 0); + + for (int i = 0; i < 256; i++) { + if (buf[i] != 0) { + return 0; + } + } + + // if this ever is reached, we either have a bug or should buy a lottery + // ticket + return 1; +} diff --git a/test/wasi/c/getrusage.c b/test/wasi/c/getrusage.c new file mode 100644 index 0000000000..132b87deb5 --- /dev/null +++ b/test/wasi/c/getrusage.c @@ -0,0 +1,24 @@ +#include +#include + +int main() { + struct rusage ru1; + getrusage(RUSAGE_SELF, &ru1); + + for (int i = 0; i < 1000; i++) { + } + + struct rusage ru2; + getrusage(RUSAGE_SELF, &ru2); + + // assert that some time has passed + long long s1 = ru1.ru_utime.tv_sec; + long long us1 = ru1.ru_utime.tv_usec; + long long s2 = ru2.ru_utime.tv_sec; + long long us2 = ru2.ru_utime.tv_usec; + assert(s1 <= s2); + if (s1 == s2) { + // strictly less than, so the timestamps can't be equal + assert(us1 < us2); + } +} diff --git a/test/wasi/c/gettimeofday.c b/test/wasi/c/gettimeofday.c new file mode 100644 index 0000000000..20ca5b0410 --- /dev/null +++ b/test/wasi/c/gettimeofday.c @@ -0,0 +1,25 @@ +#include +#include +#include + +int main() { + struct timeval tv1; + gettimeofday(&tv1, NULL); + + for (int i = 0; i < 1000; i++) { + } + + struct timeval tv2; + gettimeofday(&tv2, NULL); + + // assert that some time has passed + long long s1 = tv1.tv_sec; + long long us1 = tv1.tv_usec; + long long s2 = tv2.tv_sec; + long long us2 = tv2.tv_usec; + assert(s1 <= s2); + if (s1 == s2) { + // strictly less than, so the timestamps can't be equal + assert(us1 < us2); + } +} diff --git a/test/wasi/c/notdir.c b/test/wasi/c/notdir.c new file mode 100644 index 0000000000..03f369ffd3 --- /dev/null +++ b/test/wasi/c/notdir.c @@ -0,0 +1,11 @@ +#include +#include +#include + +int main() { + DIR* dir = opendir("/sandbox/notadir"); + assert(dir == NULL); + assert(errno == ENOTDIR); + + return 0; +} diff --git a/test/wasi/c/preopen_populates.c b/test/wasi/c/preopen_populates.c new file mode 100644 index 0000000000..03b2213bb9 --- /dev/null +++ b/test/wasi/c/preopen_populates.c @@ -0,0 +1,3 @@ +int main(void) { + return 0; +} diff --git a/test/wasi/c/read_file_twice.c b/test/wasi/c/read_file_twice.c new file mode 100644 index 0000000000..e295e38a3b --- /dev/null +++ b/test/wasi/c/read_file_twice.c @@ -0,0 +1,16 @@ +#include +#include + +int main() { + for (int i = 0; i < 2; i++) { + FILE* file = fopen("/sandbox/input.txt", "r"); + assert(file != NULL); + + char c = fgetc(file); + while (c != EOF) { + int wrote = fputc(c, stdout); + assert(wrote != EOF); + c = fgetc(file); + } + } +} diff --git a/test/wasi/c/stat.c b/test/wasi/c/stat.c new file mode 100644 index 0000000000..fd3854937b --- /dev/null +++ b/test/wasi/c/stat.c @@ -0,0 +1,53 @@ +#include + +#include +#include +#include +#include + +#define BASE_DIR "/tmp" +#define OUTPUT_DIR BASE_DIR "/testdir" +#define PATH OUTPUT_DIR "/output.txt" +#define SIZE 500 + +int main(void) { + struct stat st; + int fd; + int ret; + off_t pos; + + (void)st; + ret = mkdir(OUTPUT_DIR, 0755); + assert(ret == 0); + + fd = open(PATH, O_CREAT | O_WRONLY, 0666); + assert(fd != -1); + + pos = lseek(fd, SIZE - 1, SEEK_SET); + assert(pos == SIZE - 1); + + ret = (int)write(fd, "", 1); + assert(ret == 1); + + ret = fstat(fd, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = close(fd); + assert(ret == 0); + + ret = access(PATH, R_OK); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == 0); + assert(st.st_size == SIZE); + + ret = unlink(PATH); + assert(ret == 0); + + ret = stat(PATH, &st); + assert(ret == -1); + + return 0; +} diff --git a/test/wasi/c/stdin.c b/test/wasi/c/stdin.c new file mode 100644 index 0000000000..5a81ea1265 --- /dev/null +++ b/test/wasi/c/stdin.c @@ -0,0 +1,13 @@ +#include + +int main(void) { + char x[32]; + + if (fgets(x, sizeof x, stdin) == NULL) { + return ferror(stdin); + } + if (fputs(x, stdout) == EOF) { + return ferror(stdout); + } + return 0; +} diff --git a/test/wasi/c/write_file.c b/test/wasi/c/write_file.c new file mode 100644 index 0000000000..c4cf30cf29 --- /dev/null +++ b/test/wasi/c/write_file.c @@ -0,0 +1,15 @@ +#include +#include +#include + +static char* message = "hello, file!"; + +int main() { + FILE* file = fopen("/tmp/output.txt", "w"); + assert(file != NULL); + + int nwritten = fprintf(file, "%s", message); + assert(nwritten == strlen(message)); + int r = fclose(file); + assert(r == 0); +} diff --git a/test/wasi/test-wasi.js b/test/wasi/test-wasi.js index 339d25cd37..2d06e9a73c 100644 --- a/test/wasi/test-wasi.js +++ b/test/wasi/test-wasi.js @@ -3,15 +3,18 @@ require('../common'); if (process.argv[2] === 'wasi-child') { const fixtures = require('../common/fixtures'); + const tmpdir = require('../../test/common/tmpdir'); const fs = require('fs'); const path = require('path'); const { WASI } = require('wasi'); + tmpdir.refresh(); const wasmDir = path.join(__dirname, 'wasm'); const wasi = new WASI({ args: [], env: process.env, preopens: { - '/sandbox': fixtures.path('wasi') + '/sandbox': fixtures.path('wasi'), + '/tmp': tmpdir.path } }); const importObject = { wasi_unstable: wasi.wasiImport }; @@ -29,12 +32,17 @@ if (process.argv[2] === 'wasi-child') { function runWASI(options) { console.log('executing', options.test); + const opts = {}; + + if (options.stdin !== undefined) + opts.input = options.stdin; + const child = cp.spawnSync(process.execPath, [ '--experimental-wasm-bigint', __filename, 'wasi-child', options.test - ]); + ], opts); assert.strictEqual(child.status, options.exitCode || 0); assert.strictEqual(child.signal, null); @@ -42,7 +50,20 @@ if (process.argv[2] === 'wasi-child') { } runWASI({ test: 'cant_dotdot' }); + runWASI({ test: 'clock_getres' }); runWASI({ test: 'exitcode', exitCode: 120 }); runWASI({ test: 'fd_prestat_get_refresh' }); + // runWASI({ test: 'getentropy' }); + runWASI({ test: 'getrusage' }); + runWASI({ test: 'gettimeofday' }); + runWASI({ test: 'notdir' }); + runWASI({ test: 'preopen_populates' }); runWASI({ test: 'read_file', stdout: 'hello from input.txt\n' }); + runWASI({ + test: 'read_file_twice', + stdout: 'hello from input.txt\nhello from input.txt\n' + }); + runWASI({ test: 'stat' }); + runWASI({ test: 'stdin', stdin: 'hello world', stdout: 'hello world' }); + runWASI({ test: 'write_file' }); } diff --git a/test/wasi/wasm/clock_getres.wasm b/test/wasi/wasm/clock_getres.wasm new file mode 100755 index 0000000000..fac14edb04 Binary files /dev/null and b/test/wasi/wasm/clock_getres.wasm differ diff --git a/test/wasi/wasm/getentropy.wasm b/test/wasi/wasm/getentropy.wasm new file mode 100755 index 0000000000..6e3e8c8a8e Binary files /dev/null and b/test/wasi/wasm/getentropy.wasm differ diff --git a/test/wasi/wasm/getrusage.wasm b/test/wasi/wasm/getrusage.wasm new file mode 100755 index 0000000000..08608c4799 Binary files /dev/null and b/test/wasi/wasm/getrusage.wasm differ diff --git a/test/wasi/wasm/gettimeofday.wasm b/test/wasi/wasm/gettimeofday.wasm new file mode 100755 index 0000000000..b385807818 Binary files /dev/null and b/test/wasi/wasm/gettimeofday.wasm differ diff --git a/test/wasi/wasm/notdir.wasm b/test/wasi/wasm/notdir.wasm new file mode 100755 index 0000000000..f83a790ddc Binary files /dev/null and b/test/wasi/wasm/notdir.wasm differ diff --git a/test/wasi/wasm/preopen_populates.wasm b/test/wasi/wasm/preopen_populates.wasm new file mode 100755 index 0000000000..e7c34bc964 Binary files /dev/null and b/test/wasi/wasm/preopen_populates.wasm differ diff --git a/test/wasi/wasm/read_file_twice.wasm b/test/wasi/wasm/read_file_twice.wasm new file mode 100755 index 0000000000..cd075a4de7 Binary files /dev/null and b/test/wasi/wasm/read_file_twice.wasm differ diff --git a/test/wasi/wasm/stat.wasm b/test/wasi/wasm/stat.wasm new file mode 100755 index 0000000000..9007334d37 Binary files /dev/null and b/test/wasi/wasm/stat.wasm differ diff --git a/test/wasi/wasm/stdin.wasm b/test/wasi/wasm/stdin.wasm new file mode 100755 index 0000000000..7264608753 Binary files /dev/null and b/test/wasi/wasm/stdin.wasm differ diff --git a/test/wasi/wasm/write_file.wasm b/test/wasi/wasm/write_file.wasm new file mode 100755 index 0000000000..500405e0fb Binary files /dev/null and b/test/wasi/wasm/write_file.wasm differ