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 mate.tokodi@szteszoftver.hu
  • Loading branch information
matetokodi authored and clover2123 committed Dec 10, 2024
1 parent 866c995 commit 28d164d
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 9 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 one or more arguments\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
106 changes: 106 additions & 0 deletions test/wasi/args.wast
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
(module
(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 1)
(data (i32.const 32) "Hello\00World!\00Lorem ipsum dolor sit amet, consectetur adipiscing elit")

(func (export "check_args")(result i32)
(local $i i32)
(local $mismatched_char_count i32)
(local $argc i32)
(local $argv i32)
(local $argv_buf i32)
(local $argv_buf_size i32)
(local $value_addr i32)
(local $expected_addr i32)
i32.const 0
local.set $mismatched_char_count

i32.const 32
local.set $expected_addr

;; Memory[0] = args count
i32.const 0
local.tee $argc
;; Memory[4] = args size in characters
i32.const 4
local.tee $argv_buf_size
call $wasi_args_sizes_get
i32.const 0
i32.ne
(if
(then
i32.const -1
return
)
)
;; Memory[128] = argv[] (list of pointers to the strings)
i32.const 128
local.tee $argv
;; Memory[192] = argv_buf (the buffer the the strings themselves)
i32.const 192
local.tee $argv_buf
call $wasi_args_get
i32.const 0
i32.ne
(if
(then
i32.const -2
return
)
)

;; get start of arg string (skip argv[0], which is the program path)
local.get $argv
i32.const 4
i32.add
i32.load ;; &argv[1]
local.tee $i ;; start $i with the offset of the first char of argv[1]
local.get $argv_buf
i32.add ;; pointer to fist char in the buffer
local.set $value_addr

(loop $for_each_char
;; *($expected_addr) != *($value_addr)
local.get $expected_addr
i32.load8_u
local.get $value_addr
i32.load8_u
i32.ne
(if
(then
;; $mismatched_char_count += 1
local.get $mismatched_char_count
i32.const 1
i32.add
local.set $mismatched_char_count
)
)

local.get $value_addr
i32.const 1
i32.add
local.set $value_addr

local.get $expected_addr
i32.const 1
i32.add
local.set $expected_addr

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

;; $i < $argv_buf_size
local.get $argv_buf_size
i32.load
i32.lt_u
br_if $for_each_char
)
local.get $mismatched_char_count
)
)

(assert_return (invoke "check_args") (i32.const 0))
20 changes: 15 additions & 5 deletions tools/run-tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,23 +61,28 @@ 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):
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:
print('%sOK: %s%s' % (COLOR_GREEN, file, COLOR_RESET))
else:
print('%sFAIL(%d): %s%s' % (COLOR_RED, proc.returncode, file, COLOR_RESET))
print(out)

fails += 1

return fails
Expand Down Expand Up @@ -125,9 +130,15 @@ def run_wasi_tests(engine):

print('Running wasi tests:')
xpass = glob(join(TEST_DIR, '*.wast'))
args_tests = glob(join(TEST_DIR, 'args.wast'))
for item in args_tests:
xpass.remove(item)

xpass_result = _run_wast_tests(engine, xpass, False)
xpass_result += _run_wast_tests(engine, args_tests, False,
args=["Hello", "World!", "Lorem ipsum dolor sit amet, consectetur adipiscing elit"])

tests_total = len(xpass)
tests_total = len(xpass) + len(args_tests)
fail_total = xpass_result
print('TOTAL: %d' % (tests_total))
print('%sPASS : %d%s' % (COLOR_GREEN, tests_total - fail_total, COLOR_RESET))
Expand All @@ -136,7 +147,6 @@ def run_wasi_tests(engine):
if fail_total > 0:
raise Exception("basic wasi tests failed")


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

0 comments on commit 28d164d

Please sign in to comment.