Skip to content

Commit

Permalink
WASI: add args_get and args_sizes_get
Browse files Browse the repository at this point in the history
Update the test script to test passing the arguments as well

Signed-off-by: Máté Tokodi [email protected]
  • Loading branch information
matetokodi committed Dec 2, 2024
1 parent 50f6317 commit e9a6258
Show file tree
Hide file tree
Showing 5 changed files with 162 additions and 8 deletions.
19 changes: 15 additions & 4 deletions src/shell/Shell.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ struct ParseOptions {
// WASI options
std::vector<std::string> wasi_envs;
std::vector<std::pair<std::string, std::string>> wasi_dirs;
int argsIndex = -1;
};

static uint32_t s_JITFlags = 0;
Expand Down Expand Up @@ -1039,7 +1040,7 @@ static void runExports(Store* store, const std::string& filename, const std::vec
&data);
}

static void parseArguments(int argc, char* argv[], ParseOptions& options)
static void parseArguments(int argc, const char* argv[], ParseOptions& options)
{
for (int i = 1; i < argc; i++) {
if (strlen(argv[i]) >= 2 && argv[i][0] == '-') { // parse command line option
Expand Down Expand Up @@ -1083,6 +1084,15 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options)
options.wasi_dirs.push_back(std::make_pair(argv[i + 2], argv[i + 1]));
i += 2;
continue;
} else if (strcmp(argv[i], "--args") == 0) {
if (i + 1 == argc || argv[i + 1][0] == '-') {
fprintf(stderr, "error: --args requires an argument\n");
exit(1);
}
++i;
options.fileNames.emplace_back(argv[i]);
options.argsIndex = i;
break;
} else if (strcmp(argv[i], "--help") == 0) {
fprintf(stdout, "Usage: walrus [OPTIONS] <INPUT>\n\n");
fprintf(stdout, "OPTIONS:\n");
Expand All @@ -1095,6 +1105,7 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options)
#endif
fprintf(stdout, "\t--mapdirs <HOST_DIR> <VIRTUAL_DIR>\n\t\tMap real directories to virtual ones for WASI functions to use.\n\t\tExample: ./walrus test.wasm --mapdirs this/real/directory/ this/virtual/directory\n\n");
fprintf(stdout, "\t--env\n\t\tShare host environment to walrus WASI.\n\n");
fprintf(stdout, "\t--args <MODULE_FILE_NAME> [<ARG1> <ARG2> ... <ARGN>]\n\t\tRun Webassembly module with arguments: must be followed by the name of the Webassembly module file, then optionally following arguments which are passed on to the module\n\t\tExample: ./walrus --args test.wasm 'hello' 'world' 42\n\n");
exit(0);
}
}
Expand All @@ -1117,7 +1128,7 @@ static void parseArguments(int argc, char* argv[], ParseOptions& options)
}
}

int main(int argc, char* argv[])
int main(int argc, const char* argv[])
{
#ifndef NDEBUG
setbuf(stdout, NULL);
Expand Down Expand Up @@ -1164,8 +1175,8 @@ int main(int argc, char* argv[])
init_options.out = 1;
init_options.err = 2;
init_options.fd_table_size = 3;
init_options.argc = 0;
init_options.argv = nullptr;
init_options.argc = (options.argsIndex == -1 ? 0 : argc - options.argsIndex);
init_options.argv = (options.argsIndex == -1 ? nullptr : argv + options.argsIndex);
init_options.envp = envp.data();
init_options.preopenc = dirs.size();
init_options.preopens = dirs.data();
Expand Down
37 changes: 37 additions & 0 deletions src/wasi/WASI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,43 @@ WASI::WasiFuncInfo* WASI::find(const std::string& funcName)
return nullptr;
}

void WASI::args_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uvwasi_size_t argc;
uvwasi_size_t bufSize;
uvwasi_args_sizes_get(WASI::g_uvwasi, &argc, &bufSize);

uint32_t* uvArgv = reinterpret_cast<uint32_t*>(get_memory_pointer(instance, argv[0], argc * sizeof(uint32_t)));
char* uvArgBuf = reinterpret_cast<char*>(get_memory_pointer(instance, argv[1], bufSize));

if (uvArgv == nullptr || uvArgBuf == nullptr) {
result[0] = Value(WasiErrNo::inval);
return;
}

TemporaryData<void*, 8> pointers(argc);

if (pointers.data() == nullptr) {
result[0] = Value(WasiErrNo::inval);
return;
}

char** data = reinterpret_cast<char**>(pointers.data());
result[0] = Value(static_cast<uint16_t>(uvwasi_args_get(WASI::g_uvwasi, data, uvArgBuf)));

for (uvwasi_size_t i = 0; i < argc; i++) {
uvArgv[i] = data[i] - uvArgBuf;
}
}

void WASI::args_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
uvwasi_size_t* uvArgc = reinterpret_cast<uvwasi_size_t*>(get_memory_pointer(instance, argv[0], sizeof(uint32_t)));
uvwasi_size_t* uvArgvBufSize = reinterpret_cast<uvwasi_size_t*>(get_memory_pointer(instance, argv[1], sizeof(uint32_t)));

result[0] = Value(static_cast<int16_t>(uvwasi_args_sizes_get(WASI::g_uvwasi, uvArgc, uvArgvBufSize)));
}

void WASI::proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance)
{
ASSERT(argv[0].type() == Value::I32);
Expand Down
4 changes: 4 additions & 0 deletions src/wasi/WASI.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class WASI {
// https://github.com/WebAssembly/WASI/blob/main/legacy/preview1/docs.md

#define FOR_EACH_WASI_FUNC(F) \
F(args_get, I32I32_RI32) \
F(args_sizes_get, I32I32_RI32) \
F(proc_exit, I32R) \
F(proc_raise, I32_RI32) \
F(clock_res_get, I32I32_RI32) \
Expand Down Expand Up @@ -153,6 +155,8 @@ class WASI {

private:
// wasi functions
static void args_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void args_sizes_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void proc_exit(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void proc_raise(ExecutionState& state, Value* argv, Value* result, Instance* instance);
static void clock_res_get(ExecutionState& state, Value* argv, Value* result, Instance* instance);
Expand Down
75 changes: 75 additions & 0 deletions test/wasi/args.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
(module
(import "wasi_snapshot_preview1" "fd_write" (func $wasi_fd_write (param i32 i32 i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "args_get" (func $wasi_args_get (param i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_args_sizes_get (param i32 i32) (result i32)))
(memory 1)

(export "memory" (memory 0))
(export "print_args" (func $print_args))
(data (i32.const 100) "500" )

(func $print_args
(local $i i32)
i32.const 0 ;; args count
i32.const 12 ;; args overall size in characters
call $wasi_args_sizes_get
drop

i32.const 500 ;; argp
i32.const 600 ;; argp[0]
call $wasi_args_get
drop

;; Memory[100] = 600, start of output string.
i32.const 100
i32.const 600
i32.store

;; Memory[104] = size of output string.
i32.const 104
i32.const 12
i32.load
i32.store

;; Replace '\0' with '\n' for readable printing.
i32.const 0
local.set $i
(loop $loop
i32.const 600
local.get $i
i32.add
i32.load8_u

i32.eqz
(if
(then
i32.const 600
local.get $i
i32.add
i32.const 10
i32.store8
)
)

local.get $i
i32.const 1
i32.add
local.tee $i

i32.const 12
i32.load
i32.lt_u
br_if $loop
)

(call $wasi_fd_write
(i32.const 1) ;;file descriptor
(i32.const 100) ;;offset of str offset
(i32.const 1) ;;iovec length
(i32.const 200) ;;result offset
)
drop
)
)

(assert_return (invoke "print_args"))
35 changes: 31 additions & 4 deletions tools/run-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,31 @@ def __call__(self, fn):
DEFAULT_RUNNERS.append(self.suite)
return fn

def _run_wast_tests(engine, files, is_fail):
def _run_wast_tests(engine, files, is_fail, args=None, expect_stdout=None):
fails = 0
for file in files:
if jit:
filename = os.path.basename(file)
if filename in JIT_EXCLUDE_FILES:
continue

proc = Popen([engine, "--mapdirs", "./test/wasi", "/var", file], stdout=PIPE) if not jit else Popen([engine, "--mapdirs", "./test/wasi", "/var", "--jit", file], stdout=PIPE)
subprocess_args = [engine, "--mapdirs", "./test/wasi", "/var"]
if jit: subprocess_args.append("--jit")
if args: subprocess_args.append("--args")
subprocess_args.append(file)
if args: subprocess_args.extend(args)

proc = Popen(subprocess_args, stdout=PIPE)
out, _ = proc.communicate()

if is_fail and proc.returncode or not is_fail and not proc.returncode:
if ((is_fail and proc.returncode or not is_fail and not proc.returncode)
and (expect_stdout is None or out == expect_stdout)):
print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
else:
print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
print(out)
print(f"Result: {out}")
if expect_stdout is not None:
print(f"Expected: {expect_stdout}")

fails += 1

Expand Down Expand Up @@ -136,6 +145,24 @@ def run_wasi_tests(engine):
if fail_total > 0:
raise Exception("basic wasi tests failed")

@runner('wasi-args', default=True)
def run_wasi_args_tests(engine):
TEST_DIR = join(PROJECT_SOURCE_DIR, 'test', 'wasi')

print('Running wasi-args tests:')
xpass = glob(join(TEST_DIR, 'args.wast'))

expected_stdout = f"{xpass[0]}\nHello\nWorld!\n".encode('utf-8')
xpass_result = _run_wast_tests(engine, xpass, False, args=["Hello", "World!"], expect_stdout=expected_stdout)

tests_total = len(xpass)
fail_total = xpass_result
print('TOTAL: %d' % (tests_total))
print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET))
print('%sFAIL : %d%s' % (COLOR_RED, fail_total, COLOR_RESET))

if fail_total > 0:
raise Exception("basic wasi-args tests failed")

@runner('jit', default=True)
def run_jit_tests(engine):
Expand Down

0 comments on commit e9a6258

Please sign in to comment.