diff options
-rw-r--r-- | src/tools/wasm-interp.c | 1322 | ||||
-rw-r--r-- | src/tools/wast2wasm.c | 195 | ||||
-rw-r--r-- | src/wasm-ast-checker.c | 60 | ||||
-rw-r--r-- | src/wasm-ast.h | 11 | ||||
-rw-r--r-- | src/wasm-binary-writer-spec.c | 804 | ||||
-rw-r--r-- | src/wasm-binary-writer-spec.h | 25 | ||||
-rw-r--r-- | src/wasm-interpreter.c | 6 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 12 | ||||
-rw-r--r-- | test/interp/callimport-zero-args.txt | 2 | ||||
-rw-r--r-- | test/interp/empty.txt | 2 | ||||
-rw-r--r-- | test/interp/import.txt | 4 | ||||
-rw-r--r-- | test/interp/return-void.txt | 4 | ||||
-rwxr-xr-x | test/run-interp.py | 5 | ||||
-rwxr-xr-x | test/run-wasmdump.py | 5 | ||||
-rw-r--r-- | test/spec/address.txt | 82 | ||||
-rw-r--r-- | test/spec/float_exprs.txt | 22 | ||||
-rw-r--r-- | test/spec/float_memory.txt | 50 | ||||
-rw-r--r-- | test/spec/func_ptrs.txt | 6 | ||||
-rw-r--r-- | test/spec/memory_redundancy.txt | 6 | ||||
-rw-r--r-- | test/spec/names.txt | 8 | ||||
-rw-r--r-- | test/spec/start.txt | 14 | ||||
-rw-r--r-- | test/utils.py | 7 |
22 files changed, 1397 insertions, 1255 deletions
diff --git a/src/tools/wasm-interp.c b/src/tools/wasm-interp.c index e50772b9..e4778145 100644 --- a/src/tools/wasm-interp.c +++ b/src/tools/wasm-interp.c @@ -23,6 +23,7 @@ #include "wasm-binary-reader.h" #include "wasm-binary-reader-interpreter.h" #include "wasm-interpreter.h" +#include "wasm-literal.h" #include "wasm-option-parser.h" #include "wasm-stack-allocator.h" #include "wasm-stream.h" @@ -201,27 +202,32 @@ static WasmStringSlice get_dirname(const char* s) { return result; } -static void print_typed_value(WasmInterpreterTypedValue* tv) { +/* Not sure, but 100 chars is probably safe */ +#define MAX_TYPED_VALUE_CHARS 100 + +static void sprint_typed_value(char* buffer, + size_t size, + const WasmInterpreterTypedValue* tv) { switch (tv->type) { case WASM_TYPE_I32: - printf("i32:%u", tv->value.i32); + wasm_snprintf(buffer, size, "i32:%u", tv->value.i32); break; case WASM_TYPE_I64: - printf("i64:%" PRIu64, tv->value.i64); + wasm_snprintf(buffer, size, "i64:%" PRIu64, tv->value.i64); break; case WASM_TYPE_F32: { float value; memcpy(&value, &tv->value.f32_bits, sizeof(float)); - printf("f32:%g", value); + wasm_snprintf(buffer, size, "f32:%g", value); break; } case WASM_TYPE_F64: { double value; memcpy(&value, &tv->value.f64_bits, sizeof(double)); - printf("f64:%g", value); + wasm_snprintf(buffer, size, "f64:%g", value); break; } @@ -231,6 +237,54 @@ static void print_typed_value(WasmInterpreterTypedValue* tv) { } } + +static void print_typed_value(const WasmInterpreterTypedValue* tv) { + char buffer[MAX_TYPED_VALUE_CHARS]; + sprint_typed_value(buffer, sizeof(buffer), tv); + printf("%s", buffer); +} + +static void print_typed_values(const WasmInterpreterTypedValue* values, + size_t num_values) { + size_t i; + for (i = 0; i < num_values; ++i) { + print_typed_value(&values[i]); + if (i != num_values - 1) + printf(", "); + } +} + +static void print_typed_value_vector( + const WasmInterpreterTypedValueVector* values) { + print_typed_values(&values->data[0], values->size); +} + +static void print_interpreter_result(const char* desc, + WasmInterpreterResult iresult) { + printf("%s: %s\n", desc, s_trap_strings[iresult]); +} + +static void print_call(WasmStringSlice module_name, + WasmStringSlice func_name, + const WasmInterpreterTypedValueVector* args, + const WasmInterpreterTypedValueVector* results, + WasmInterpreterResult iresult) { + if (module_name.length) + printf(PRIstringslice ".", WASM_PRINTF_STRING_SLICE_ARG(module_name)); + printf(PRIstringslice "(", WASM_PRINTF_STRING_SLICE_ARG(func_name)); + print_typed_value_vector(args); + printf(") =>"); + if (iresult == WASM_INTERPRETER_OK) { + if (results->size > 0) { + printf(" "); + print_typed_value_vector(results); + } + printf("\n"); + } else { + print_interpreter_result(" error", iresult); + } +} + static WasmInterpreterResult run_defined_function(WasmInterpreterThread* thread, uint32_t offset) { thread->pc = offset; @@ -242,167 +296,149 @@ static WasmInterpreterResult run_defined_function(WasmInterpreterThread* thread, wasm_trace_pc(thread, s_stdout_stream); iresult = wasm_run_interpreter(thread, quantum, call_stack_return_top); } - if (iresult != WASM_INTERPRETER_RETURNED) { - if (s_trace) - printf("!!! trapped: %s\n", s_trap_strings[iresult]); + if (iresult != WASM_INTERPRETER_RETURNED) return iresult; - } /* use OK instead of RETURNED for consistency */ return WASM_INTERPRETER_OK; } -static WasmInterpreterResult run_function(WasmInterpreterThread* thread, - uint32_t func_index) { - assert(func_index < thread->env->funcs.size); - WasmInterpreterFunc* func = &thread->env->funcs.data[func_index]; - if (func->is_host) - return wasm_call_host(thread, func); - else - return run_defined_function(thread, func->defined.offset); -} +static WasmInterpreterResult push_args( + WasmInterpreterThread* thread, + const WasmInterpreterFuncSignature* sig, + const WasmInterpreterTypedValueVector* args) { + if (sig->param_types.size != args->size) + return WASM_INTERPRETER_ARGUMENT_TYPE_MISMATCH; + + size_t i; + for (i = 0; i < sig->param_types.size; ++i) { + if (sig->param_types.data[i] != args->data[i].type) + return WASM_INTERPRETER_ARGUMENT_TYPE_MISMATCH; -static WasmResult run_start_function(WasmInterpreterThread* thread) { - WasmResult result = WASM_OK; - if (thread->module->defined.start_func_index != WASM_INVALID_INDEX) { - if (s_trace) - printf(">>> running start function:\n"); WasmInterpreterResult iresult = - run_function(thread, thread->module->defined.start_func_index); + wasm_push_thread_value(thread, args->data[i].value); if (iresult != WASM_INTERPRETER_OK) { - /* trap */ - fprintf(stderr, "error: %s\n", s_trap_strings[iresult]); - result = WASM_ERROR; + thread->value_stack_top = thread->value_stack.data; + return iresult; } } - return result; + return WASM_INTERPRETER_OK; } -static WasmInterpreterFuncSignature* get_export_signature( - WasmInterpreterEnvironment* env, - WasmInterpreterExport* export) { - assert(export->kind == WASM_EXTERNAL_KIND_FUNC); - uint32_t func_index = export->index; - uint32_t sig_index = env->funcs.data[func_index].sig_index; - assert(sig_index < env->sigs.size); - return &env->sigs.data[sig_index]; +static void copy_results(WasmAllocator* allocator, + WasmInterpreterThread* thread, + const WasmInterpreterFuncSignature* sig, + WasmInterpreterTypedValueVector* out_results) { + size_t expected_results = sig->result_types.size; + size_t value_stack_depth = thread->value_stack_top - thread->value_stack.data; + WASM_USE(value_stack_depth); + assert(expected_results == value_stack_depth); + + /* Don't clear out the vector, in case it is being reused. Just reset the + * size to zero. */ + out_results->size = 0; + wasm_resize_interpreter_typed_value_vector(allocator, out_results, + expected_results); + size_t i; + for (i = 0; i < expected_results; ++i) { + out_results->data[i].type = sig->result_types.data[i]; + out_results->data[i].value = thread->value_stack.data[i]; + } } -static WasmInterpreterResult run_export( +static WasmInterpreterResult run_function( WasmAllocator* allocator, WasmInterpreterThread* thread, - WasmInterpreterExport* export, + uint32_t func_index, + const WasmInterpreterTypedValueVector* args, WasmInterpreterTypedValueVector* out_results) { - assert(export->kind == WASM_EXTERNAL_KIND_FUNC); - WasmInterpreterFuncSignature* sig = get_export_signature(thread->env, export); - - /* push all 0 values as arguments */ - assert(sig->param_types.size < thread->value_stack.size); - size_t num_args = sig->param_types.size; - thread->value_stack_top = &thread->value_stack.data[num_args]; - memset(thread->value_stack.data, 0, num_args * sizeof(WasmInterpreterValue)); - - WasmInterpreterResult result = run_function(thread, export->index); - - if (result == WASM_INTERPRETER_OK) { - size_t expected_results = sig->result_types.size; - size_t value_stack_depth = - thread->value_stack_top - thread->value_stack.data; - WASM_USE(value_stack_depth); - assert(expected_results == value_stack_depth); - - if (out_results) { - wasm_resize_interpreter_typed_value_vector(allocator, out_results, - expected_results); - size_t i; - for (i = 0; i < expected_results; ++i) { - WasmInterpreterTypedValue actual_result; - actual_result.type = sig->result_types.data[i]; - actual_result.value = thread->value_stack.data[i]; - - if (out_results) { - /* copy as many results as the caller wants */ - out_results->data[i] = actual_result; - } - } - } + assert(func_index < thread->env->funcs.size); + WasmInterpreterFunc* func = &thread->env->funcs.data[func_index]; + uint32_t sig_index = func->sig_index; + assert(sig_index < thread->env->sigs.size); + WasmInterpreterFuncSignature* sig = &thread->env->sigs.data[sig_index]; + + WasmInterpreterResult iresult = push_args(thread, sig, args); + if (iresult == WASM_INTERPRETER_OK) { + iresult = func->is_host + ? wasm_call_host(thread, func) + : run_defined_function(thread, func->defined.offset); + if (iresult == WASM_INTERPRETER_OK) + copy_results(allocator, thread, sig, out_results); } + /* Always reset the value and call stacks */ thread->value_stack_top = thread->value_stack.data; thread->call_stack_top = thread->call_stack.data; + return iresult; +} - return result; +static WasmInterpreterResult run_start_function(WasmAllocator* allocator, + WasmInterpreterThread* thread) { + if (thread->module->defined.start_func_index == WASM_INVALID_INDEX) + return WASM_INTERPRETER_OK; + + if (s_trace) + printf(">>> running start function:\n"); + WasmInterpreterTypedValueVector args; + WasmInterpreterTypedValueVector results; + WASM_ZERO_MEMORY(args); + WASM_ZERO_MEMORY(results); + + WasmInterpreterResult iresult = + run_function(allocator, thread, thread->module->defined.start_func_index, + &args, &results); + assert(results.size == 0); + return iresult; } -static WasmInterpreterResult run_export_wrapper( +static WasmInterpreterResult run_export( WasmAllocator* allocator, WasmInterpreterThread* thread, - WasmInterpreterExport* export, - WasmInterpreterTypedValueVector* out_results, - RunVerbosity verbose) { + const WasmInterpreterExport* export, + const WasmInterpreterTypedValueVector* args, + WasmInterpreterTypedValueVector* out_results) { if (s_trace) { printf(">>> running export \"" PRIstringslice "\":\n", WASM_PRINTF_STRING_SLICE_ARG(export->name)); } - WasmInterpreterResult result = - run_export(allocator, thread, export, out_results); - - if (verbose) { - WasmInterpreterFuncSignature* sig = - get_export_signature(thread->env, export); - printf(PRIstringslice "(", WASM_PRINTF_STRING_SLICE_ARG(export->name)); - size_t i; - for (i = 0; i < sig->param_types.size; ++i) { - printf("0"); - if (i != sig->param_types.size - 1) - printf(", "); - } - printf(") => "); - - if (result == WASM_INTERPRETER_OK) { - assert(out_results); - for (i = 0; i < out_results->size; ++i) { - print_typed_value(&out_results->data[i]); - if (i != out_results->size - 1) - printf(", "); - } - printf("\n"); - } else { - /* trap */ - printf("error: %s\n", s_trap_strings[result]); - } - } - return result; + assert(export->kind == WASM_EXTERNAL_KIND_FUNC); + return run_function(allocator, thread, export->index, args, out_results); } -static WasmResult run_export_by_name( +static WasmInterpreterResult run_export_by_name( WasmAllocator* allocator, WasmInterpreterThread* thread, - WasmStringSlice* name, - WasmInterpreterResult* out_iresult, + const WasmStringSlice* name, + const WasmInterpreterTypedValueVector* args, WasmInterpreterTypedValueVector* out_results, RunVerbosity verbose) { WasmInterpreterExport* export = wasm_get_interpreter_export_by_name(thread->module, name); if (!export) - return WASM_ERROR; - - *out_iresult = - run_export_wrapper(allocator, thread, export, out_results, verbose); - return WASM_OK; + return WASM_INTERPRETER_UNKNOWN_EXPORTED_FUNCTION; + return run_export(allocator, thread, export, args, out_results); } static void run_all_exports(WasmAllocator* allocator, WasmInterpreterModule* module, WasmInterpreterThread* thread, RunVerbosity verbose) { + WasmInterpreterTypedValueVector args; WasmInterpreterTypedValueVector results; + WASM_ZERO_MEMORY(args); WASM_ZERO_MEMORY(results); uint32_t i; for (i = 0; i < module->exports.size; ++i) { WasmInterpreterExport* export = &module->exports.data[i]; - run_export_wrapper(allocator, thread, export, &results, verbose); + WasmInterpreterResult iresult = + run_export(allocator, thread, export, &args, &results); + if (verbose) { + print_call(wasm_empty_string_slice(), export->name, &args, &results, + iresult); + } } + wasm_destroy_interpreter_typed_value_vector(allocator, &args); wasm_destroy_interpreter_typed_value_vector(allocator, &results); } @@ -434,16 +470,6 @@ static WasmResult read_module(WasmAllocator* allocator, return result; } -static void print_typed_values(WasmInterpreterTypedValue* values, - size_t num_values) { - uint32_t i; - for (i = 0; i < num_values; ++i) { - print_typed_value(&values[i]); - if (i != num_values - 1) - printf(", "); - } -} - static WasmResult default_host_callback(const WasmInterpreterFunc* func, const WasmInterpreterFuncSignature* sig, uint32_t num_args, @@ -456,13 +482,17 @@ static WasmResult default_host_callback(const WasmInterpreterFunc* func, for (i = 0; i < num_results; ++i) out_results[i].type = sig->result_types.data[i]; - printf("called host " PRIstringslice "." PRIstringslice "(", - WASM_PRINTF_STRING_SLICE_ARG(func->host.module_name), - WASM_PRINTF_STRING_SLICE_ARG(func->host.field_name)); - print_typed_values(args, num_args); - printf(") => ("); - print_typed_values(out_results, num_results); - printf(")\n"); + WasmInterpreterTypedValueVector vec_args; + vec_args.size = num_args; + vec_args.data = args; + + WasmInterpreterTypedValueVector vec_results; + vec_results.size = num_results; + vec_results.data = out_results; + + printf("called host "); + print_call(func->host.module_name, func->host.field_name, &vec_args, + &vec_results, WASM_INTERPRETER_OK); return WASM_OK; } @@ -513,356 +543,774 @@ static WasmResult read_and_run_module(WasmAllocator* allocator, init_environment(allocator, &env); result = read_module(allocator, module_filename, &env, &module, &thread); if (WASM_SUCCEEDED(result)) { - result = run_start_function(&thread); - if (s_run_all_exports) - run_all_exports(allocator, module, &thread, RUN_VERBOSE); + WasmInterpreterResult iresult = run_start_function(allocator, &thread); + if (iresult == WASM_INTERPRETER_OK) { + if (s_run_all_exports) + run_all_exports(allocator, module, &thread, RUN_VERBOSE); + } else { + print_interpreter_result("error running start function", iresult); + } } wasm_destroy_interpreter_thread(allocator, &thread); wasm_destroy_interpreter_environment(allocator, &env); return result; } -static WasmResult read_and_run_spec_json(WasmAllocator* allocator, - const char* spec_json_filename) { - WasmResult result = WASM_OK; +WASM_DEFINE_VECTOR(interpreter_thread, WasmInterpreterThread); + +/* An extremely simple JSON parser that only knows how to parse the expected + * format from wast2wasm. */ +typedef struct Context { + WasmAllocator* allocator; WasmInterpreterEnvironment env; - WasmInterpreterModule* module = NULL; - WasmInterpreterThread thread; - WasmStringSlice command_file; - WasmStringSlice command_name; - WasmInterpreterTypedValueVector result_values; - uint32_t command_line_no = 0; - WasmBool has_thread = WASM_FALSE; - uint32_t passed = 0; - uint32_t failed = 0; - - WASM_ZERO_MEMORY(thread); - WASM_ZERO_MEMORY(command_file); - WASM_ZERO_MEMORY(command_name); - WASM_ZERO_MEMORY(result_values); + WasmInterpreterThreadVector threads; + WasmInterpreterModule* last_module; + int thread_to_module_offset; + + /* Parsing info */ + char* json_data; + size_t json_data_size; + WasmStringSlice source_filename; + size_t json_offset; + WasmLocation loc; + WasmLocation prev_loc; + WasmBool has_prev_loc; + uint32_t command_line_number; + + /* Test info */ + int passed; + int total; +} Context; + +typedef enum ActionType { + ACTION_TYPE_INVOKE, + ACTION_TYPE_GET, +} ActionType; + +typedef struct Action { + ActionType type; + WasmStringSlice module_name; + WasmStringSlice field_name; + WasmInterpreterTypedValueVector args; +} Action; + +#define CHECK_RESULT(x) \ + do { \ + if (WASM_FAILED(x)) \ + return WASM_ERROR; \ + } while (0) + +#define EXPECT(x) CHECK_RESULT(expect(ctx, x)) +#define EXPECT_KEY(x) CHECK_RESULT(expect_key(ctx, x)) +#define PARSE_KEY_STRING_VALUE(key, value) \ + CHECK_RESULT(parse_key_string_value(ctx, key, value)) + +static void WASM_PRINTF_FORMAT(2, 3) + print_parse_error(Context* ctx, const char* format, ...) { + WASM_SNPRINTF_ALLOCA(buffer, length, format); + fprintf(stderr, "%s:%d:%d: %s\n", ctx->loc.filename, + ctx->loc.line, ctx->loc.first_column, buffer); +} - init_environment(allocator, &env); +static void WASM_PRINTF_FORMAT(2, 3) + print_command_error(Context* ctx, const char* format, ...) { + WASM_SNPRINTF_ALLOCA(buffer, length, format); + printf(PRIstringslice ":%u: %s\n", + WASM_PRINTF_STRING_SLICE_ARG(ctx->source_filename), + ctx->command_line_number, buffer); +} - void* data; - size_t size; - result = wasm_read_file(allocator, spec_json_filename, &data, &size); - if (WASM_FAILED(result)) - return WASM_ERROR; +static void putback_char(Context* ctx) { + assert(ctx->has_prev_loc); + ctx->json_offset--; + ctx->loc = ctx->prev_loc; + ctx->has_prev_loc = WASM_FALSE; +} - /* an extremely simple JSON parser that only knows how to parse the expected - * format from wast2wasm */ - enum { - INITIAL, - TOP_OBJECT, - MODULES_ARRAY, - END_MODULES_ARRAY, - MODULE_OBJECT, - END_MODULE_OBJECT, - MODULE_FILENAME, - COMMANDS_ARRAY, - END_COMMANDS_ARRAY, - COMMAND_OBJECT, - END_COMMAND_OBJECT, - COMMAND_TYPE, - COMMAND_NAME, - COMMAND_FILE, - COMMAND_LINE, - DONE, - } state = INITIAL; - - enum { - NONE, - ACTION, - ASSERT_RETURN, - ASSERT_RETURN_NAN, - ASSERT_TRAP, - } command_type = NONE; - -#define SKIP_WS() \ - while ((p < end) && (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')) \ - p++ - -#define MATCHES(c) ((p < end) && (*p == c) && (p++)) - -#define EXPECT(c) \ - if (!MATCHES(c)) \ - goto fail; - -#define STRLEN(s) (sizeof(s) - 1) - -#define MATCHES_STR(s) \ - ((p + STRLEN(s) < end) && (strncmp(p, s, STRLEN(s)) == 0) && (p += STRLEN(s))) - -#define EXPECT_STR(s) \ - if (!MATCHES_STR(s)) \ - goto fail; - -#define READ_UNTIL(c) \ - while (p < end && *p != c) \ - p++; \ - if (p == end) \ - goto fail - -#define READ_STRING(slice) \ - EXPECT('"'); \ - slice.start = p; \ - READ_UNTIL('"'); \ - slice.length = p - slice.start; \ - EXPECT('"') - -#define MAYBE_CONTINUE(name) \ - SKIP_WS(); \ - if (MATCHES(',')) \ - state = name; \ - else \ - state = END_##name - - const char* start = data; - const char* p = start; - const char* end = p + size; - while (p < end && state != DONE) { - SKIP_WS(); - - switch (state) { - case INITIAL: - EXPECT('{'); - state = TOP_OBJECT; - break; +static int read_char(Context* ctx) { + if (ctx->json_offset >= ctx->json_data_size) + return -1; + ctx->prev_loc = ctx->loc; + char c = ctx->json_data[ctx->json_offset++]; + if (c == '\n') { + ctx->loc.line++; + ctx->loc.first_column = 1; + } else { + ctx->loc.first_column++; + } + ctx->has_prev_loc = WASM_TRUE; + return c; +} - case TOP_OBJECT: - EXPECT_STR("\"modules\""); - SKIP_WS(); - EXPECT(':'); - SKIP_WS(); - EXPECT('['); - state = MODULES_ARRAY; - break; +static void skip_whitespace(Context* ctx) { + while (1) { + switch (read_char(ctx)) { + case -1: + return; - case MODULES_ARRAY: - if (MATCHES(']')) { - /* should only match with an empty module list */ - SKIP_WS(); - EXPECT('}'); - state = DONE; - } else { - EXPECT('{'); - state = MODULE_OBJECT; - } + case ' ': + case '\t': + case '\n': + case '\r': break; - case END_MODULES_ARRAY: - EXPECT(']'); - SKIP_WS(); - EXPECT('}'); - state = DONE; - break; + default: + putback_char(ctx); + return; + } + } +} - case MODULE_OBJECT: - if (MATCHES_STR("\"filename\"")) { - SKIP_WS(); - EXPECT(':'); - state = MODULE_FILENAME; - } else { - EXPECT_STR("\"commands\""); - SKIP_WS(); - EXPECT(':'); - SKIP_WS(); - EXPECT('['); - state = COMMANDS_ARRAY; - } - break; +static WasmBool match(Context* ctx, const char* s) { + skip_whitespace(ctx); + WasmLocation start_loc = ctx->loc; + size_t start_offset = ctx->json_offset; + while (*s && *s == read_char(ctx)) + s++; - case END_MODULE_OBJECT: - EXPECT('}'); - MAYBE_CONTINUE(MODULES_ARRAY); - assert(has_thread); - wasm_destroy_interpreter_thread(allocator, &thread); - has_thread = WASM_FALSE; - break; + if (*s == 0) { + return WASM_TRUE; + } else { + ctx->json_offset = start_offset; + ctx->loc = start_loc; + return WASM_FALSE; + } +} + +static WasmResult expect(Context* ctx, const char* s) { + if (match(ctx, s)) { + return WASM_OK; + } else { + print_parse_error(ctx, "expected %s", s); + return WASM_ERROR; + } +} + +static WasmResult expect_key(Context* ctx, const char* key) { + size_t keylen = strlen(key); + size_t quoted_len = keylen + 2 + 1; + char* quoted = alloca(quoted_len); + wasm_snprintf(quoted, quoted_len, "\"%s\"", key); + EXPECT(quoted); + EXPECT(":"); + return WASM_OK; +} + +static WasmResult parse_uint32(Context* ctx, uint32_t* out_int) { + uint32_t result = 0; + skip_whitespace(ctx); + while (1) { + int c = read_char(ctx); + if (c >= '0' && c <= '9') { + uint32_t last_result = result; + result = result * 10 + (uint32_t)(c - '0'); + if (result < last_result) { + print_parse_error(ctx, "uint32 overflow"); + return WASM_ERROR; + } + } else { + putback_char(ctx); + break; + } + } + *out_int = result; + return WASM_OK; +} - case MODULE_FILENAME: { - WasmStringSlice module_filename; - READ_STRING(module_filename); - WasmStringSlice dirname = get_dirname(spec_json_filename); - size_t path_len = dirname.length + 1 + module_filename.length + 1; - char* path = alloca(path_len); - if (dirname.length == 0) { - wasm_snprintf(path, path_len, PRIstringslice, - WASM_PRINTF_STRING_SLICE_ARG(module_filename)); +static WasmResult parse_string(Context* ctx, WasmStringSlice* out_string) { + skip_whitespace(ctx); + if (read_char(ctx) != '"') { + print_parse_error(ctx, "expected string"); + return WASM_ERROR; + } + /* Modify json_data in-place so we can use the WasmStringSlice directly + * without having to allocate additional memory; this is only necessary when + * the string contains an escape, but we do it always because the code is + * simpler. */ + char* start = &ctx->json_data[ctx->json_offset]; + char* p = start; + out_string->start = start; + while (1) { + int c = read_char(ctx); + if (c == '"') { + break; + } else if (c == '\\') { + /* The only escape supported is \uxxxx. */ + c = read_char(ctx); + if (c != 'u') { + print_parse_error(ctx, "expected escape: \\uxxxx"); + return WASM_ERROR; + } + int i; + uint16_t code = 0; + for (i = 0; i < 4; ++i) { + c = read_char(ctx); + int cval; + if (c >= '0' && c <= '9') { + cval = c - '0'; + } else if (c >= 'a' && c <= 'f') { + cval = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + cval = c - 'A' + 10; } else { - wasm_snprintf(path, path_len, PRIstringslice "/" PRIstringslice, - WASM_PRINTF_STRING_SLICE_ARG(dirname), - WASM_PRINTF_STRING_SLICE_ARG(module_filename)); + print_parse_error(ctx, "expected hex char"); + return WASM_ERROR; } + code = (code << 4) + cval; + } + + if (code < 256) { + *p++ = code; + } else { + print_parse_error(ctx, "only escape codes < 256 allowed, got %u\n", + code); + } + } else { + *p++ = c; + } + } + out_string->length = p - start; + return WASM_OK; +} + +static WasmResult parse_key_string_value(Context* ctx, + const char* key, + WasmStringSlice* out_string) { + EXPECT_KEY(key); + return parse_string(ctx, out_string); +} - result = read_module(allocator, path, &env, &module, &thread); - if (WASM_FAILED(result)) - goto fail; +static WasmResult parse_line(Context* ctx) { + EXPECT_KEY("line"); + CHECK_RESULT(parse_uint32(ctx, &ctx->command_line_number)); + return WASM_OK; +} - has_thread = WASM_TRUE; +static WasmBool string_slice_equals_str(const WasmStringSlice* ss, + const char* s) { + return strncmp(ss->start, s, ss->length) == 0; +} - result = run_start_function(&thread); - if (WASM_FAILED(result)) - goto fail; +static WasmResult parse_const(Context* ctx, + WasmInterpreterTypedValue* out_value) { + WasmStringSlice type_str; + WasmStringSlice value_str; + EXPECT("{"); + PARSE_KEY_STRING_VALUE("type", &type_str); + EXPECT(","); + PARSE_KEY_STRING_VALUE("value", &value_str); + EXPECT("}"); + + const char* value_start = value_str.start; + const char* value_end = value_str.start + value_str.length; + + if (string_slice_equals_str(&type_str, "i32")) { + uint32_t value; + CHECK_RESULT(wasm_parse_int32(value_start, value_end, &value, + WASM_PARSE_UNSIGNED_ONLY)); + out_value->type = WASM_TYPE_I32; + out_value->value.i32 = value; + return WASM_OK; + } else if (string_slice_equals_str(&type_str, "f32")) { + uint32_t value_bits; + CHECK_RESULT(wasm_parse_int32(value_start, value_end, &value_bits, + WASM_PARSE_UNSIGNED_ONLY)); + out_value->type = WASM_TYPE_F32; + out_value->value.f32_bits = value_bits; + return WASM_OK; + } else if (string_slice_equals_str(&type_str, "i64")) { + uint64_t value; + CHECK_RESULT(wasm_parse_int64(value_start, value_end, &value, + WASM_PARSE_UNSIGNED_ONLY)); + out_value->type = WASM_TYPE_I64; + out_value->value.i64 = value; + return WASM_OK; + } else if (string_slice_equals_str(&type_str, "f64")) { + uint64_t value_bits; + CHECK_RESULT(wasm_parse_int64(value_start, value_end, &value_bits, + WASM_PARSE_UNSIGNED_ONLY)); + out_value->type = WASM_TYPE_F64; + out_value->value.f64_bits = value_bits; + return WASM_OK; + } else { + print_parse_error(ctx, "unknown type: \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(type_str)); + return WASM_ERROR; + } +} - MAYBE_CONTINUE(MODULE_OBJECT); - break; - } +static WasmResult parse_const_vector( + Context* ctx, + WasmInterpreterTypedValueVector* out_values) { + WASM_ZERO_MEMORY(*out_values); + EXPECT("["); + WasmBool first = WASM_TRUE; + while (!match(ctx, "]")) { + if (!first) + EXPECT(","); + WasmInterpreterTypedValue value; + CHECK_RESULT(parse_const(ctx, &value)); + wasm_append_interpreter_typed_value_value(ctx->allocator, out_values, + &value); + first = WASM_FALSE; + } + return WASM_OK; +} - case COMMANDS_ARRAY: - if (MATCHES(']')) { - /* should only match with an empty command array */ - state = END_MODULE_OBJECT; - } else { - EXPECT('{'); - state = COMMAND_OBJECT; - } - break; +static WasmResult parse_action(Context* ctx, Action* out_action) { + WASM_ZERO_MEMORY(*out_action); + EXPECT_KEY("action"); + EXPECT("{"); + EXPECT_KEY("type"); + if (match(ctx, "\"invoke\"")) { + out_action->type = ACTION_TYPE_INVOKE; + } else { + EXPECT("\"get\""); + out_action->type = ACTION_TYPE_GET; + } + EXPECT(","); + if (match(ctx, "\"module\"")) { + EXPECT(":"); + CHECK_RESULT(parse_string(ctx, &out_action->module_name)); + EXPECT(","); + } + PARSE_KEY_STRING_VALUE("field", &out_action->field_name); + if (out_action->type == ACTION_TYPE_INVOKE) { + EXPECT(","); + EXPECT_KEY("args"); + CHECK_RESULT(parse_const_vector(ctx, &out_action->args)); + } + EXPECT("}"); + return WASM_OK; +} - case END_COMMANDS_ARRAY: - EXPECT(']'); - MAYBE_CONTINUE(MODULE_OBJECT); - break; +static WasmResult on_module_command(Context* ctx, + WasmStringSlice filename, + WasmStringSlice name) { + const char* spec_json_filename = ctx->loc.filename; + WasmStringSlice dirname = get_dirname(spec_json_filename); + size_t path_len = dirname.length + 1 + filename.length + 1; + char* path = alloca(path_len); + + if (dirname.length == 0) { + wasm_snprintf(path, path_len, PRIstringslice, + WASM_PRINTF_STRING_SLICE_ARG(filename)); + } else { + wasm_snprintf(path, path_len, PRIstringslice "/" PRIstringslice, + WASM_PRINTF_STRING_SLICE_ARG(dirname), + WASM_PRINTF_STRING_SLICE_ARG(filename)); + } - case COMMAND_OBJECT: - if (MATCHES_STR("\"type\"")) { - state = COMMAND_TYPE; - } else if (MATCHES_STR("\"name\"")) { - state = COMMAND_NAME; - } else if (MATCHES_STR("\"file\"")) { - state = COMMAND_FILE; - } else { - EXPECT_STR("\"line\""); - state = COMMAND_LINE; - } - SKIP_WS(); - EXPECT(':'); - break; + /* Make sure that the difference in size between the thread and module + * vectors is constant; the only modules that don't have matching threads are + * host modules, which should all be added at the beginning. */ + if (ctx->threads.size == 0) { + ctx->thread_to_module_offset = (int)ctx->env.modules.size; + } else { + assert(ctx->thread_to_module_offset == + (int)(ctx->env.modules.size - ctx->threads.size)); + } -#define FAILED(msg) \ - fprintf(stderr, "*** " PRIstringslice ":%d: " PRIstringslice " " msg "\n", \ - WASM_PRINTF_STRING_SLICE_ARG(command_file), command_line_no, \ - WASM_PRINTF_STRING_SLICE_ARG(command_name)); - - case END_COMMAND_OBJECT: { - WasmInterpreterResult iresult; - EXPECT('}'); - RunVerbosity verbose = command_type == ACTION ? RUN_VERBOSE : RUN_QUIET; - result = run_export_by_name(allocator, &thread, &command_name, &iresult, - &result_values, verbose); - if (WASM_FAILED(result)) { - FAILED("unknown export"); - failed++; - goto fail; - } + WasmInterpreterThread* thread = + wasm_append_interpreter_thread(ctx->allocator, &ctx->threads); - switch (command_type) { - case ACTION: - if (iresult != WASM_INTERPRETER_OK) { - FAILED("trapped"); - failed++; - } - break; - - case ASSERT_RETURN: - case ASSERT_RETURN_NAN: - if (iresult != WASM_INTERPRETER_OK) { - FAILED("trapped"); - failed++; - } else if (result_values.size != 1) { - FAILED("result arity mismatch"); - failed++; - } else if (result_values.data[0].type != WASM_TYPE_I32) { - FAILED("type mismatch"); - failed++; - } else if (result_values.data[0].value.i32 != 1) { - FAILED("didn't return 1"); - failed++; - } else { - passed++; - } - break; - - case ASSERT_TRAP: - if (iresult == WASM_INTERPRETER_OK) { - FAILED("didn't trap"); - failed++; - } else { - passed++; - } - break; - - default: - assert(0); - goto fail; - } - MAYBE_CONTINUE(COMMANDS_ARRAY); - break; + CHECK_RESULT( + read_module(ctx->allocator, path, &ctx->env, &ctx->last_module, thread)); + + if (ctx->last_module->name.start) { + WasmBinding* binding = wasm_insert_binding( + ctx->allocator, &ctx->env.module_bindings, &ctx->last_module->name); + binding->index = ctx->env.modules.size - 1; + } + + WasmInterpreterResult iresult = run_start_function(ctx->allocator, thread); + if (iresult != WASM_INTERPRETER_OK) { + print_interpreter_result("error running start function", iresult); + return WASM_ERROR; + } + + return WASM_OK; +} + +static WasmResult run_action(Context* ctx, + Action* action, + WasmInterpreterResult* out_iresult, + WasmInterpreterTypedValueVector* out_results, + RunVerbosity verbose) { + WASM_ZERO_MEMORY(*out_results); + + int module_index; + if (action->module_name.start) { + module_index = wasm_find_binding_index_by_name(&ctx->env.module_bindings, + &action->module_name); + } else { + module_index = (int)ctx->env.modules.size - 1; + } + + int thread_index = module_index - ctx->thread_to_module_offset; + if (thread_index < 0 || thread_index >= (int)ctx->threads.size) { + print_command_error(ctx, "invalid module in action."); + return WASM_ERROR; + } + + WasmInterpreterThread* thread = &ctx->threads.data[thread_index]; + + switch (action->type) { + case ACTION_TYPE_INVOKE: + *out_iresult = + run_export_by_name(ctx->allocator, thread, &action->field_name, + &action->args, out_results, verbose); + if (verbose) { + print_call(wasm_empty_string_slice(), action->field_name, &action->args, + out_results, *out_iresult); } + return WASM_OK; - case COMMAND_TYPE: { - if (MATCHES_STR("\"action\"")) { - command_type = ACTION; - } else if (MATCHES_STR("\"assert_return\"")) { - command_type = ASSERT_RETURN; - } else if (MATCHES_STR("\"assert_return_nan\"")) { - command_type = ASSERT_RETURN_NAN; - } else { - EXPECT_STR("\"assert_trap\""); - command_type = ASSERT_TRAP; + default: + case ACTION_TYPE_GET: + return WASM_ERROR; + } +} + +static WasmResult on_action_command(Context* ctx, Action* action) { + WasmInterpreterTypedValueVector results; + WasmInterpreterResult iresult; + + ctx->total++; + WasmResult result = run_action(ctx, action, &iresult, &results, RUN_VERBOSE); + if (WASM_SUCCEEDED(result)) { + if (iresult == WASM_INTERPRETER_OK) { + ctx->passed++; + } else { + print_command_error(ctx, "unexpected trap: %s", s_trap_strings[iresult]); + result = WASM_ERROR; + } + } + + wasm_destroy_interpreter_typed_value_vector(ctx->allocator, &results); + return result; +} + +static WasmResult on_assert_malformed_command(Context* ctx, + WasmStringSlice filename, + WasmStringSlice text) { + /* TODO */ + return WASM_OK; +} + +static WasmResult on_register_command(Context* ctx, + WasmStringSlice name, + WasmStringSlice as) { + /* TODO */ + return WASM_OK; +} + +static WasmResult on_assert_unlinkable_command(Context* ctx, + WasmStringSlice filename, + WasmStringSlice text) { + /* TODO */ + return WASM_OK; +} + +static WasmBool typed_values_are_equal(const WasmInterpreterTypedValue* tv1, + const WasmInterpreterTypedValue* tv2) { + if (tv1->type != tv2->type) + return WASM_FALSE; + + switch (tv1->type) { + case WASM_TYPE_I32: return tv1->value.i32 == tv2->value.i32; + case WASM_TYPE_F32: return tv1->value.f32_bits == tv2->value.f32_bits; + case WASM_TYPE_I64: return tv1->value.i64 == tv2->value.i64; + case WASM_TYPE_F64: return tv1->value.f64_bits == tv2->value.f64_bits; + default: assert(0); return WASM_FALSE; + } +} + +static WasmResult on_assert_return_command( + Context* ctx, + Action* action, + WasmInterpreterTypedValueVector* expected) { + WasmInterpreterTypedValueVector results; + WasmInterpreterResult iresult; + + ctx->total++; + WasmResult result = run_action(ctx, action, &iresult, &results, RUN_QUIET); + + if (WASM_SUCCEEDED(result)) { + if (iresult == WASM_INTERPRETER_OK) { + if (results.size == expected->size) { + size_t i; + for (i = 0; i < results.size; ++i) { + const WasmInterpreterTypedValue* expected_tv = &expected->data[i]; + const WasmInterpreterTypedValue* actual_tv = &results.data[i]; + if (!typed_values_are_equal(expected_tv, actual_tv)) { + char expected_str[MAX_TYPED_VALUE_CHARS]; + char actual_str[MAX_TYPED_VALUE_CHARS]; + sprint_typed_value(expected_str, sizeof(expected_str), expected_tv); + sprint_typed_value(actual_str, sizeof(actual_str), actual_tv); + print_command_error(ctx, "mismatch in result %" PRIzd + " of assert_return: expected %s, got %s", + i, expected_str, actual_str); + result = WASM_ERROR; + } } - MAYBE_CONTINUE(COMMAND_OBJECT); - break; + } else { + print_command_error( + ctx, "result length mismatch in assert_return: expected %" PRIzd + ", got %" PRIzd, + expected->size, results.size); + result = WASM_ERROR; } + } else { + print_command_error(ctx, "unexpected trap: %s", s_trap_strings[iresult]); + result = WASM_ERROR; + } + } - case COMMAND_NAME: { - READ_STRING(command_name); - MAYBE_CONTINUE(COMMAND_OBJECT); - break; - } + if (WASM_SUCCEEDED(result)) + ctx->passed++; - case COMMAND_FILE: { - READ_STRING(command_file); - MAYBE_CONTINUE(COMMAND_OBJECT); - break; + wasm_destroy_interpreter_typed_value_vector(ctx->allocator, &results); + return result; +} + +static WasmResult on_assert_return_nan_command(Context* ctx, Action* action) { + WasmInterpreterTypedValueVector results; + WasmInterpreterResult iresult; + + ctx->total++; + WasmResult result = run_action(ctx, action, &iresult, &results, RUN_QUIET); + if (WASM_SUCCEEDED(result)) { + if (iresult == WASM_INTERPRETER_OK) { + if (results.size != 1) { + print_command_error(ctx, "expected one result, got %" PRIzd, + results.size); + result = WASM_ERROR; } - case COMMAND_LINE: { - command_line_no = 0; - while (p < end && *p >= '0' && *p <= '9') { - uint32_t new_line_no = command_line_no * 10 + (*p - '0'); - if (new_line_no < command_line_no) - goto fail; - command_line_no = new_line_no; - p++; - } - MAYBE_CONTINUE(COMMAND_OBJECT); - break; + const WasmInterpreterTypedValue* actual = &results.data[0]; + switch (actual->type) { + case WASM_TYPE_F32: + if (!is_nan_f32(actual->value.f32_bits)) { + char actual_str[MAX_TYPED_VALUE_CHARS]; + sprint_typed_value(actual_str, sizeof(actual_str), actual); + print_command_error(ctx, "expected result to be nan, got %s", + actual_str); + result = WASM_ERROR; + } + break; + + case WASM_TYPE_F64: + if (!is_nan_f64(actual->value.f64_bits)) { + char actual_str[MAX_TYPED_VALUE_CHARS]; + sprint_typed_value(actual_str, sizeof(actual_str), actual); + print_command_error(ctx, "expected result to be nan, got %s", + actual_str); + result = WASM_ERROR; + } + break; + + default: + print_command_error(ctx, + "expected result type to be f32 or f64, got %s", + wasm_get_type_name(actual->type)); + result = WASM_ERROR; + break; } - default: - assert(0); - break; + } else { + print_command_error(ctx, "unexpected trap: %s", s_trap_strings[iresult]); + result = WASM_ERROR; } } - uint32_t total = passed + failed; - printf("%d/%d tests passed.\n", passed, total); - result = passed == total ? WASM_OK : WASM_ERROR; + if (WASM_SUCCEEDED(result)) + ctx->passed++; - goto done; + wasm_destroy_interpreter_typed_value_vector(ctx->allocator, &results); + return WASM_OK; +} -fail: - fprintf(stderr, "error parsing spec json file\n"); - fprintf(stderr, "got this far: %" PRIzd ":> %.*s...\n", p - start, 20, p); - result = WASM_ERROR; +static WasmResult on_assert_trap_command(Context* ctx, + Action* action, + WasmStringSlice text) { + WasmInterpreterTypedValueVector results; + WasmInterpreterResult iresult; -done: - if (has_thread) - wasm_destroy_interpreter_thread(allocator, &thread); - wasm_destroy_interpreter_environment(allocator, &env); - wasm_destroy_interpreter_typed_value_vector(allocator, &result_values); - wasm_free(allocator, data); + ctx->total++; + WasmResult result = run_action(ctx, action, &iresult, &results, RUN_QUIET); + if (WASM_SUCCEEDED(result)) { + if (iresult != WASM_INTERPRETER_OK) { + ctx->passed++; + } else { + print_command_error(ctx, "expected trap: \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(text)); + result = WASM_ERROR; + } + } + + wasm_destroy_interpreter_typed_value_vector(ctx->allocator, &results); + return result; +} + +static void destroy_action(WasmAllocator* allocator, Action* action) { + wasm_destroy_interpreter_typed_value_vector(allocator, &action->args); +} + +static WasmResult parse_command(Context* ctx) { + EXPECT("{"); + EXPECT_KEY("type"); + if (match(ctx, "\"module\"")) { + WasmStringSlice name; + WasmStringSlice filename; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + if (match(ctx, "\"name\"")) { + EXPECT(":"); + CHECK_RESULT(parse_string(ctx, &name)); + EXPECT(","); + } + PARSE_KEY_STRING_VALUE("filename", &filename); + on_module_command(ctx, filename, name); + } else if (match(ctx, "\"action\"")) { + Action action; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + CHECK_RESULT(parse_action(ctx, &action)); + on_action_command(ctx, &action); + destroy_action(ctx->allocator, &action); + } else if (match(ctx, "\"register\"")) { + WasmStringSlice as; + WasmStringSlice name; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("name", &name); + EXPECT(","); + PARSE_KEY_STRING_VALUE("as", &as); + on_register_command(ctx, name, as); + } else if (match(ctx, "\"assert_malformed\"")) { + WasmStringSlice filename; + WasmStringSlice text; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("filename", &filename); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &text); + on_assert_malformed_command(ctx, filename, text); + } else if (match(ctx, "\"assert_invalid\"")) { + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); +#if 0 + /* TODO(binji): this doesn't work currently because the spec writer can't + * write invalid modules in all cases. */ + + WasmStringSlice filename; + WasmStringSlice text; + PARSE_KEY_STRING_VALUE("filename", &filename); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &text); +#endif + } else if (match(ctx, "\"assert_unlinkable\"")) { + WasmStringSlice filename; + WasmStringSlice text; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("filename", &filename); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &text); + on_assert_unlinkable_command(ctx, filename, text); + } else if (match(ctx, "\"assert_return\"")) { + Action action; + WasmInterpreterTypedValueVector expected; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + CHECK_RESULT(parse_action(ctx, &action)); + EXPECT(","); + EXPECT_KEY("expected"); + CHECK_RESULT(parse_const_vector(ctx, &expected)); + on_assert_return_command(ctx, &action, &expected); + wasm_destroy_interpreter_typed_value_vector(ctx->allocator, &expected); + destroy_action(ctx->allocator, &action); + } else if (match(ctx, "\"assert_return_nan\"")) { + Action action; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + CHECK_RESULT(parse_action(ctx, &action)); + on_assert_return_nan_command(ctx, &action); + destroy_action(ctx->allocator, &action); + } else if (match(ctx, "\"assert_trap\"")) { + Action action; + WasmStringSlice text; + EXPECT(","); + CHECK_RESULT(parse_line(ctx)); + EXPECT(","); + CHECK_RESULT(parse_action(ctx, &action)); + EXPECT(","); + PARSE_KEY_STRING_VALUE("text", &text); + on_assert_trap_command(ctx, &action, text); + destroy_action(ctx->allocator, &action); + } else { + print_command_error(ctx, "unknown command type"); + return WASM_ERROR; + } + EXPECT("}"); + return WASM_OK; +} + +static WasmResult parse_commands(Context* ctx) { + EXPECT("{"); + PARSE_KEY_STRING_VALUE("source_filename", &ctx->source_filename); + EXPECT(","); + EXPECT_KEY("commands"); + EXPECT("["); + WasmBool first = WASM_TRUE; + while (!match(ctx, "]")) { + if (!first) + EXPECT(","); + CHECK_RESULT(parse_command(ctx)); + first = WASM_FALSE; + } + EXPECT("}"); + return WASM_OK; +} + +static void destroy_context(Context* ctx) { + WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->threads, + interpreter_thread); + wasm_destroy_interpreter_environment(ctx->allocator, &ctx->env); + wasm_free(ctx->allocator, ctx->json_data); +} + +static WasmResult read_and_run_spec_json(WasmAllocator* allocator, + const char* spec_json_filename) { + Context ctx; + WASM_ZERO_MEMORY(ctx); + ctx.allocator = allocator; + ctx.loc.filename = spec_json_filename; + ctx.loc.line = 1; + ctx.loc.first_column = 1; + init_environment(allocator, &ctx.env); + + void* data; + size_t size; + WasmResult result = + wasm_read_file(allocator, spec_json_filename, &data, &size); + if (WASM_FAILED(result)) + return WASM_ERROR; + + ctx.json_data = data; + ctx.json_data_size = size; + + result = parse_commands(&ctx); + printf("%d/%d tests passed.\n", ctx.passed, ctx.total); + destroy_context(&ctx); return result; } diff --git a/src/tools/wast2wasm.c b/src/tools/wast2wasm.c index 02ac9231..f4300dd0 100644 --- a/src/tools/wast2wasm.c +++ b/src/tools/wast2wasm.c @@ -205,181 +205,6 @@ static void write_buffer_to_file(const char* filename, } } -static WasmStringSlice strip_extension(const char* s) { - /* strip .json or .wasm, but leave other extensions, e.g.: - * - * s = "foo", => "foo" - * s = "foo.json" => "foo" - * s = "foo.wasm" => "foo" - * s = "foo.bar" => "foo.bar" - */ - if (s == NULL) { - WasmStringSlice result; - result.start = NULL; - result.length = 0; - return result; - } - - size_t slen = strlen(s); - const char* ext_start = strrchr(s, '.'); - if (ext_start == NULL) - ext_start = s + slen; - - WasmStringSlice result; - result.start = s; - - if (strcmp(ext_start, ".json") == 0 || strcmp(ext_start, ".wasm") == 0) { - result.length = ext_start - s; - } else { - result.length = slen; - } - return result; -} - -static WasmStringSlice get_basename(const char* s) { - /* strip everything up to and including the last slash, e.g.: - * - * s = "/foo/bar/baz", => "baz" - * s = "/usr/local/include/stdio.h", => "stdio.h" - * s = "foo.bar", => "foo.bar" - */ - size_t slen = strlen(s); - const char* start = s; - const char* last_slash = strrchr(s, '/'); - if (last_slash != NULL) - start = last_slash + 1; - - WasmStringSlice result; - result.start = start; - result.length = s + slen - start; - return result; -} - -static char* get_module_filename(WasmAllocator* allocator, - WasmStringSlice* filename_noext, - uint32_t index) { - size_t buflen = filename_noext->length + 20; - char* str = wasm_alloc(allocator, buflen, WASM_DEFAULT_ALIGN); - wasm_snprintf(str, buflen, PRIstringslice ".%d.wasm", - WASM_PRINTF_STRING_SLICE_ARG(*filename_noext), index); - return str; -} - -static char* convert_backslash_to_forward_slash(char* buffer, const int buf_size) { - int i = 0; - for (; i < buf_size; ++i) { - if (buffer[i] == '\\') { - buffer[i] = '/'; - } - } - return buffer; -} - -typedef struct Context { - WasmAllocator* allocator; - WasmMemoryWriter json_writer; - WasmMemoryWriter module_writer; - WasmStream json_stream; - WasmStringSlice output_filename_noext; - char* module_filename; - WasmResult result; -} Context; - -static void on_script_begin(void* user_data) { - Context* ctx = user_data; - if (WASM_FAILED(wasm_init_mem_writer(ctx->allocator, &ctx->module_writer))) - WASM_FATAL("unable to open memory writer for writing\n"); - - if (WASM_FAILED(wasm_init_mem_writer(ctx->allocator, &ctx->json_writer))) - WASM_FATAL("unable to open memory writer for writing\n"); - wasm_init_stream(&ctx->json_stream, &ctx->json_writer.base, NULL); - - wasm_writef(&ctx->json_stream, "{\"modules\": [\n"); -} - -static void on_module_begin(uint32_t index, void* user_data) { - Context* ctx = user_data; - wasm_free(ctx->allocator, ctx->module_filename); - ctx->module_filename = - get_module_filename(ctx->allocator, &ctx->output_filename_noext, index); - if (index != 0) - wasm_writef(&ctx->json_stream, ",\n"); - ctx->module_filename = - convert_backslash_to_forward_slash(ctx->module_filename, - strlen(ctx->module_filename)); - - WasmStringSlice module_basename = get_basename(ctx->module_filename); - wasm_writef(&ctx->json_stream, - " {\"filename\": \"" PRIstringslice "\", \"commands\": [\n", - WASM_PRINTF_STRING_SLICE_ARG(module_basename)); -} - -static void on_command(uint32_t index, - WasmCommandType type, - const WasmStringSlice* name, - const WasmLocation* loc, - void* user_data) { - static const char* s_command_names[] = { - "module", - "action", - "register", - "assert_malformed", - "assert_invalid", - "assert_unlinkable", - "assert_return", - "assert_return_nan", - "assert_trap", - }; - WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_command_names) == - WASM_NUM_COMMAND_TYPES); - - static const char* s_command_format = - " {" - "\"type\": \"%s\", " - "\"name\": \"" PRIstringslice - "\", " - "\"file\": \"%s\", " - "\"line\": %d}"; - - Context* ctx = user_data; - if (index != 0) - wasm_writef(&ctx->json_stream, ",\n"); - const int l = strlen(loc->filename); - char* filename = wasm_alloc(ctx->allocator, l + 1, WASM_DEFAULT_ALIGN); - memcpy(filename, loc->filename, l + 1); - filename = convert_backslash_to_forward_slash(filename, l); - wasm_writef(&ctx->json_stream, s_command_format, s_command_names[type], - WASM_PRINTF_STRING_SLICE_ARG(*name), filename, loc->line); - wasm_free(ctx->allocator, filename); -} - -static void on_module_before_write(uint32_t index, - WasmWriter** out_writer, - void* user_data) { - Context* ctx = user_data; - ctx->module_writer.buf.size = 0; - *out_writer = &ctx->module_writer.base; -} - -static void on_module_end(uint32_t index, WasmResult result, void* user_data) { - Context* ctx = user_data; - wasm_writef(&ctx->json_stream, "\n ]}"); - if (WASM_SUCCEEDED(result)) - write_buffer_to_file(ctx->module_filename, &ctx->module_writer.buf); -} - -static void on_script_end(void* user_data) { - Context* ctx = user_data; - wasm_writef(&ctx->json_stream, "\n]}\n"); - - if (WASM_SUCCEEDED(ctx->result)) - write_buffer_to_file(s_outfile, &ctx->json_writer.buf); - - wasm_free(ctx->allocator, ctx->module_filename); - wasm_close_mem_writer(&ctx->module_writer); - wasm_close_mem_writer(&ctx->json_writer); -} - int main(int argc, char** argv) { WasmStackAllocator stack_allocator; WasmAllocator* allocator; @@ -421,22 +246,10 @@ int main(int argc, char** argv) { if (WASM_SUCCEEDED(result)) { if (s_spec) { - Context ctx; - WASM_ZERO_MEMORY(ctx); - ctx.allocator = allocator; - ctx.output_filename_noext = strip_extension(s_outfile); - - s_write_binary_spec_options.on_script_begin = &on_script_begin; - s_write_binary_spec_options.on_module_begin = &on_module_begin; - s_write_binary_spec_options.on_command = &on_command, - s_write_binary_spec_options.on_module_before_write = - &on_module_before_write; - s_write_binary_spec_options.on_module_end = &on_module_end; - s_write_binary_spec_options.on_script_end = &on_script_end; - s_write_binary_spec_options.user_data = &ctx; - - result = wasm_write_binary_spec_script(allocator, &script, - &s_write_binary_options, + s_write_binary_spec_options.json_filename = s_outfile; + s_write_binary_spec_options.write_binary_options = + s_write_binary_options; + result = wasm_write_binary_spec_script(allocator, &script, s_infile, &s_write_binary_spec_options); } else { WasmMemoryWriter writer; diff --git a/src/wasm-ast-checker.c b/src/wasm-ast-checker.c index dec9c5fc..c906df2e 100644 --- a/src/wasm-ast-checker.c +++ b/src/wasm-ast-checker.c @@ -1284,9 +1284,33 @@ static const WasmTypeVector* check_invoke(Context* ctx, return &func->decl.sig.result_types; } -static WasmType check_get(Context* ctx, const WasmAction* action) { - /* TODO */ - return WASM_TYPE_ANY; +static WasmResult check_get(Context* ctx, + const WasmAction* action, + WasmType* out_type) { + const WasmActionGet* get = &action->get; + const WasmModule* module = ctx->current_module; + if (!module) { + print_error(ctx, CHECK_STYLE_FULL, &action->loc, + "get must occur after a module definition"); + return WASM_ERROR; + } + + WasmExport* export = wasm_get_export_by_name(module, &get->name); + if (!export) { + print_error(ctx, CHECK_STYLE_NAME, &action->loc, + "unknown global export \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(get->name)); + return WASM_ERROR; + } + + WasmGlobal* global = wasm_get_global_by_var(module, &export->var); + if (!global) { + /* this error will have already been reported, just skip it */ + return WASM_ERROR; + } + + *out_type = global->type; + return WASM_OK; } static ActionResult check_action(Context* ctx, const WasmAction* action) { @@ -1301,8 +1325,10 @@ static ActionResult check_action(Context* ctx, const WasmAction* action) { break; case WASM_ACTION_TYPE_GET: - result.kind = ACTION_RESULT_KIND_TYPE; - result.type = check_get(ctx, action); + if (WASM_SUCCEEDED(check_get(ctx, action, &result.type))) + result.kind = ACTION_RESULT_KIND_TYPE; + else + result.kind = ACTION_RESULT_KIND_ERROR; break; } @@ -1424,16 +1450,6 @@ WasmResult wasm_check_ast(WasmAllocator* allocator, return ctx.result; } -static WasmLocation* get_raw_module_location(WasmRawModule* raw) { - switch (raw->type) { - case WASM_RAW_MODULE_TYPE_BINARY: return &raw->binary.loc; - case WASM_RAW_MODULE_TYPE_TEXT: return &raw->text->loc; - default: - assert(0); - return NULL; - } -} - WasmResult wasm_check_assert_invalid_and_malformed( WasmAllocator* allocator, WasmAstLexer* lexer, @@ -1469,9 +1485,10 @@ WasmResult wasm_check_assert_invalid_and_malformed( check_raw_module(&ctx2, &command->assert_invalid.module); wasm_destroy_context(&ctx2); if (WASM_SUCCEEDED(ctx2.result)) { - print_error(&ctx, CHECK_STYLE_FULL, - get_raw_module_location(&command->assert_invalid.module), - "expected module to be invalid"); + print_error( + &ctx, CHECK_STYLE_FULL, + wasm_get_raw_module_location(&command->assert_invalid.module), + "expected module to be invalid"); } } else if (command->type == WASM_COMMAND_TYPE_ASSERT_MALFORMED) { ctx2.error_handler = assert_malformed_error_handler; @@ -1480,9 +1497,10 @@ WasmResult wasm_check_assert_invalid_and_malformed( destroy_read_module(ctx.allocator, &read_module); wasm_destroy_context(&ctx2); if (WASM_SUCCEEDED(ctx2.result)) { - print_error(&ctx, CHECK_STYLE_FULL, - get_raw_module_location(&command->assert_malformed.module), - "expected module to be malformed"); + print_error( + &ctx, CHECK_STYLE_FULL, + wasm_get_raw_module_location(&command->assert_malformed.module), + "expected module to be malformed"); } } } diff --git a/src/wasm-ast.h b/src/wasm-ast.h index bc30eef7..c59d29bd 100644 --- a/src/wasm-ast.h +++ b/src/wasm-ast.h @@ -567,6 +567,17 @@ wasm_get_func_type_num_results(const WasmFuncType* func_type) { return func_type->sig.result_types.size; } +static WASM_INLINE const WasmLocation* wasm_get_raw_module_location( + const WasmRawModule* raw) { + switch (raw->type) { + case WASM_RAW_MODULE_TYPE_BINARY: return &raw->binary.loc; + case WASM_RAW_MODULE_TYPE_TEXT: return &raw->text->loc; + default: + assert(0); + return NULL; + } +} + WASM_EXTERN_C_END #endif /* WASM_AST_H_ */ diff --git a/src/wasm-binary-writer-spec.c b/src/wasm-binary-writer-spec.c index cdf02ebb..2aa49e8a 100644 --- a/src/wasm-binary-writer-spec.c +++ b/src/wasm-binary-writer-spec.c @@ -17,561 +17,419 @@ #include "wasm-binary-writer-spec.h" #include <assert.h> +#include <inttypes.h> #include "wasm-ast.h" #include "wasm-binary.h" #include "wasm-binary-writer.h" #include "wasm-config.h" +#include "wasm-stream.h" #include "wasm-writer.h" -#define DUMP_OCTETS_PER_LINE 16 - -#define CALLBACK0(ctx, member) \ - do { \ - if (((ctx)->spec_options->member)) \ - (ctx)->spec_options->member((ctx)->spec_options->user_data); \ - } while (0) - -#define CALLBACK(ctx, member, ...) \ - do { \ - if (((ctx)->spec_options->member)) \ - (ctx) \ - ->spec_options->member(__VA_ARGS__, (ctx)->spec_options->user_data); \ - } while (0) - typedef struct Context { WasmAllocator* allocator; - WasmWriter* writer; - size_t writer_offset; - const WasmWriteBinaryOptions* options; + WasmMemoryWriter json_writer; + WasmStream json_stream; + const char* source_filename; + WasmStringSlice json_filename_noext; const WasmWriteBinarySpecOptions* spec_options; WasmResult result; + size_t num_modules; } Context; -typedef struct ExprList { - WasmExpr* first; - WasmExpr* last; -} ExprList; - -typedef struct ActionInfo { - const WasmAction* action; - int index; - size_t num_results; - union { - /* when action->type == INVOKE; memory is shared with the callee's sig */ - const WasmTypeVector* result_types; - /* when action->type == GET */ - WasmType result_type; - }; -} ActionInfo; - -static void append_expr(ExprList* expr_list, WasmExpr* expr) { - if (expr_list->last) - expr_list->last->next = expr; - else - expr_list->first = expr; - expr_list->last = expr; +static void convert_backslash_to_slash(char* s, size_t length) { + size_t i = 0; + for (; i < length; ++i) + if (s[i] == '\\') + s[i] = '/'; } -static void append_const_expr(WasmAllocator* allocator, - ExprList* expr_list, - const WasmConst* const_) { - WasmExpr* expr = wasm_new_const_expr(allocator); - expr->const_ = *const_; - append_expr(expr_list, expr); +static WasmStringSlice strip_extension(const char* s) { + /* strip .json or .wasm, but leave other extensions, e.g.: + * + * s = "foo", => "foo" + * s = "foo.json" => "foo" + * s = "foo.wasm" => "foo" + * s = "foo.bar" => "foo.bar" + */ + if (s == NULL) { + WasmStringSlice result; + result.start = NULL; + result.length = 0; + return result; + } + + size_t slen = strlen(s); + const char* ext_start = strrchr(s, '.'); + if (ext_start == NULL) + ext_start = s + slen; + + WasmStringSlice result; + result.start = s; + + if (strcmp(ext_start, ".json") == 0 || strcmp(ext_start, ".wasm") == 0) { + result.length = ext_start - s; + } else { + result.length = slen; + } + return result; } -static void append_i32_const_expr(WasmAllocator* allocator, - ExprList* expr_list, - uint32_t value) { - WasmConst const_; - const_.type = WASM_TYPE_I32; - const_.u32 = value; - append_const_expr(allocator, expr_list, &const_); +static WasmStringSlice get_basename(const char* s) { + /* strip everything up to and including the last slash, e.g.: + * + * s = "/foo/bar/baz", => "baz" + * s = "/usr/local/include/stdio.h", => "stdio.h" + * s = "foo.bar", => "foo.bar" + */ + size_t slen = strlen(s); + const char* start = s; + const char* last_slash = strrchr(s, '/'); + if (last_slash != NULL) + start = last_slash + 1; + + WasmStringSlice result; + result.start = start; + result.length = s + slen - start; + return result; } -static void append_invoke_expr(WasmAllocator* allocator, - ExprList* expr_list, - const WasmActionInvoke* invoke, - int func_index) { - size_t i; - for (i = 0; i < invoke->args.size; ++i) - append_const_expr(allocator, expr_list, &invoke->args.data[i]); +static char* get_module_filename(Context* ctx) { + size_t buflen = ctx->json_filename_noext.length + 20; + char* str = wasm_alloc(ctx->allocator, buflen, WASM_DEFAULT_ALIGN); + size_t length = wasm_snprintf( + str, buflen, PRIstringslice ".%" PRIzd ".wasm", + WASM_PRINTF_STRING_SLICE_ARG(ctx->json_filename_noext), ctx->num_modules); + convert_backslash_to_slash(str, length); + return str; +} - WasmExpr* expr = wasm_new_call_expr(allocator); - expr->call.var.type = WASM_VAR_TYPE_INDEX; - expr->call.var.index = func_index; - append_expr(expr_list, expr); +static void write_string(Context* ctx, const char* s) { + wasm_writef(&ctx->json_stream, "\"%s\"", s); } -static void append_get_global_expr(WasmAllocator* allocator, - ExprList* expr_list, - const WasmActionGet* get, - int global_index) { - WasmExpr* expr = wasm_new_get_global_expr(allocator); - expr->get_global.var.type = WASM_VAR_TYPE_INDEX; - expr->get_global.var.index = global_index; - append_expr(expr_list, expr); +static void write_key(Context* ctx, const char* key) { + wasm_writef(&ctx->json_stream, "\"%s\": ", key); } -static void append_eq_expr(WasmAllocator* allocator, - ExprList* expr_list, - WasmType type) { - WasmExpr* expr = wasm_new_compare_expr(allocator); - switch (type) { - case WASM_TYPE_I32: - expr->compare.opcode = WASM_OPCODE_I32_EQ; - break; - case WASM_TYPE_I64: - expr->compare.opcode = WASM_OPCODE_I64_EQ; - break; - case WASM_TYPE_F32: - expr->compare.opcode = WASM_OPCODE_F32_EQ; - break; - case WASM_TYPE_F64: - expr->compare.opcode = WASM_OPCODE_F64_EQ; - break; - default: - assert(0); - } - append_expr(expr_list, expr); +static void write_separator(Context* ctx) { + wasm_writef(&ctx->json_stream, ", "); } -static void append_ne_expr(WasmAllocator* allocator, - ExprList* expr_list, - WasmType type) { - WasmExpr* expr = wasm_new_compare_expr(allocator); - switch (type) { - case WASM_TYPE_I32: - expr->compare.opcode = WASM_OPCODE_I32_NE; - break; - case WASM_TYPE_I64: - expr->compare.opcode = WASM_OPCODE_I64_NE; - break; - case WASM_TYPE_F32: - expr->compare.opcode = WASM_OPCODE_F32_NE; - break; - case WASM_TYPE_F64: - expr->compare.opcode = WASM_OPCODE_F64_NE; - break; - default: - assert(0); +static void write_escaped_string_slice(Context* ctx, WasmStringSlice ss) { + size_t i; + wasm_write_char(&ctx->json_stream, '"'); + for (i = 0; i < ss.length; ++i) { + uint8_t c = ss.start[i]; + if (c < 0x20 || c == '\\' || c == '"') { + wasm_writef(&ctx->json_stream, "\\u%04x", c); + } else { + wasm_write_char(&ctx->json_stream, c); + } } - append_expr(expr_list, expr); + wasm_write_char(&ctx->json_stream, '"'); } -static void append_tee_local_expr(WasmAllocator* allocator, - ExprList* expr_list, - int index) { - WasmExpr* expr = wasm_new_tee_local_expr(allocator); - expr->tee_local.var.type = WASM_VAR_TYPE_INDEX; - expr->tee_local.var.index = index; - append_expr(expr_list, expr); -} +static void write_command_type(Context* ctx, const WasmCommand* command) { + static const char* s_command_names[] = { + "module", + "action", + "register", + "assert_malformed", + "assert_invalid", + "assert_unlinkable", + "assert_return", + "assert_return_nan", + "assert_trap", + }; + WASM_STATIC_ASSERT(WASM_ARRAY_SIZE(s_command_names) == + WASM_NUM_COMMAND_TYPES); -static void append_get_local_expr(WasmAllocator* allocator, - ExprList* expr_list, - int index) { - WasmExpr* expr = wasm_new_get_local_expr(allocator); - expr->get_local.var.type = WASM_VAR_TYPE_INDEX; - expr->get_local.var.index = index; - append_expr(expr_list, expr); + write_key(ctx, "type"); + write_string(ctx, s_command_names[command->type]); } -static void append_reinterpret_expr(WasmAllocator* allocator, - ExprList* expr_list, - WasmType type) { - WasmExpr* expr = wasm_new_convert_expr(allocator); - switch (type) { - case WASM_TYPE_F32: - expr->convert.opcode = WASM_OPCODE_I32_REINTERPRET_F32; - break; - case WASM_TYPE_F64: - expr->convert.opcode = WASM_OPCODE_I64_REINTERPRET_F64; - break; - default: - assert(0); - break; - } - append_expr(expr_list, expr); +static void write_location(Context* ctx, const WasmLocation* loc) { + write_key(ctx, "line"); + wasm_writef(&ctx->json_stream, "%d", loc->line); } -static void append_return_expr(WasmAllocator* allocator, ExprList* expr_list) { - WasmExpr* expr = wasm_new_return_expr(allocator); - append_expr(expr_list, expr); +static void write_var(Context* ctx, const WasmVar* var) { + if (var->type == WASM_VAR_TYPE_INDEX) + wasm_writef(&ctx->json_stream, "\"%" PRIu64 "\"", var->index); + else + write_escaped_string_slice(ctx, var->name); } -static WasmModuleField* append_module_field( - WasmAllocator* allocator, - WasmModule* module, - WasmModuleFieldType module_field_type) { - WasmModuleField* result = wasm_append_module_field(allocator, module); - result->type = module_field_type; - - switch (module_field_type) { - case WASM_MODULE_FIELD_TYPE_FUNC: { - WasmFuncPtr* func_ptr = wasm_append_func_ptr(allocator, &module->funcs); - *func_ptr = &result->func; +static void write_const(Context* ctx, const WasmConst* const_) { + wasm_writef(&ctx->json_stream, "{"); + write_key(ctx, "type"); + + /* Always write the values as strings, even though they may be representable + * as JSON numbers. This way the formatting is consistent. */ + switch (const_->type) { + case WASM_TYPE_I32: + write_string(ctx, "i32"); + write_separator(ctx); + write_key(ctx, "value"); + wasm_writef(&ctx->json_stream, "\"%u\"", const_->u32); break; - } - case WASM_MODULE_FIELD_TYPE_IMPORT: { - WasmImportPtr* import_ptr = - wasm_append_import_ptr(allocator, &module->imports); - *import_ptr = &result->import; + + case WASM_TYPE_I64: + write_string(ctx, "i64"); + write_separator(ctx); + write_key(ctx, "value"); + wasm_writef(&ctx->json_stream, "\"%" PRIu64 "\"", const_->u64); break; - } - case WASM_MODULE_FIELD_TYPE_EXPORT: { - WasmExportPtr* export_ptr = - wasm_append_export_ptr(allocator, &module->exports); - *export_ptr = &result->export_; + + case WASM_TYPE_F32: { + /* TODO(binji): write as hex float */ + write_string(ctx, "f32"); + write_separator(ctx); + write_key(ctx, "value"); + wasm_writef(&ctx->json_stream, "\"%u\"", const_->f32_bits); break; } - case WASM_MODULE_FIELD_TYPE_FUNC_TYPE: { - WasmFuncTypePtr* func_type_ptr = - wasm_append_func_type_ptr(allocator, &module->func_types); - *func_type_ptr = &result->func_type; + + case WASM_TYPE_F64: { + /* TODO(binji): write as hex float */ + write_string(ctx, "f64"); + write_separator(ctx); + write_key(ctx, "value"); + wasm_writef(&ctx->json_stream, "\"%" PRIu64 "\"", const_->f64_bits); break; } - case WASM_MODULE_FIELD_TYPE_GLOBAL: - case WASM_MODULE_FIELD_TYPE_TABLE: - case WASM_MODULE_FIELD_TYPE_MEMORY: - case WASM_MODULE_FIELD_TYPE_START: - case WASM_MODULE_FIELD_TYPE_ELEM_SEGMENT: - case WASM_MODULE_FIELD_TYPE_DATA_SEGMENT: - /* not supported */ + default: assert(0); - break; } - return result; + wasm_writef(&ctx->json_stream, "}"); } -static WasmStringSlice create_assert_func_name(WasmAllocator* allocator, - const char* format, - int format_index) { - WasmStringSlice name; - char buffer[256]; - int buffer_len = wasm_snprintf(buffer, 256, format, format_index); - name.start = wasm_strndup(allocator, buffer, buffer_len); - name.length = buffer_len; - return name; +static void write_const_vector(Context* ctx, const WasmConstVector* consts) { + wasm_writef(&ctx->json_stream, "["); + size_t i; + for (i = 0; i < consts->size; ++i) { + const WasmConst* const_ = &consts->data[i]; + write_const(ctx, const_); + if (i != consts->size - 1) + write_separator(ctx); + } + wasm_writef(&ctx->json_stream, "]"); } -static WasmFunc* append_nullary_func(WasmAllocator* allocator, - WasmModule* module, - const WasmTypeVector* result_types, - WasmStringSlice export_name) { - WasmFuncType* func_type; - WasmFuncSignature sig; - WASM_ZERO_MEMORY(sig); - /* OK to share memory w/ result_types, because we're just using it to see if - * this signature already exists */ - sig.result_types = *result_types; - int sig_index = wasm_get_func_type_index_by_sig(module, &sig); - if (sig_index == -1) { - WasmLocation loc; - WASM_ZERO_MEMORY(loc); - /* clone result_types so we don't share its memory */ - WASM_ZERO_MEMORY(sig.result_types); - wasm_extend_types(allocator, &sig.result_types, result_types); - func_type = wasm_append_implicit_func_type(allocator, &loc, module, &sig); - sig_index = module->func_types.size - 1; +static void write_action(Context* ctx, const WasmAction* action) { + write_key(ctx, "action"); + wasm_writef(&ctx->json_stream, "{"); + write_key(ctx, "type"); + if (action->type == WASM_ACTION_TYPE_INVOKE) { + write_string(ctx, "invoke"); } else { - func_type = module->func_types.data[sig_index]; + assert(action->type == WASM_ACTION_TYPE_GET); + write_string(ctx, "get"); } - - WasmModuleField* func_field = - append_module_field(allocator, module, WASM_MODULE_FIELD_TYPE_FUNC); - WasmFunc* func = &func_field->func; - func->decl.flags = WASM_FUNC_DECLARATION_FLAG_HAS_FUNC_TYPE | - WASM_FUNC_DECLARATION_FLAG_SHARED_SIGNATURE; - func->decl.type_var.type = WASM_VAR_TYPE_INDEX; - func->decl.type_var.index = sig_index; - func->decl.sig = func_type->sig; - int func_index = module->funcs.size - 1; - - WasmModuleField* export_field = - append_module_field(allocator, module, WASM_MODULE_FIELD_TYPE_EXPORT); - WasmExport* export_ = &export_field->export_; - export_->var.type = WASM_VAR_TYPE_INDEX; - export_->var.index = func_index; - export_->name = export_name; - return module->funcs.data[func_index]; -} - -static WasmFunc* append_nullary_func_0(WasmAllocator* allocator, - WasmModule* module, - WasmStringSlice export_name) { - WasmTypeVector types; - WASM_ZERO_MEMORY(types); - return append_nullary_func(allocator, module, &types, export_name); -} - -static WasmFunc* append_nullary_func_1(WasmAllocator* allocator, - WasmModule* module, - WasmType type, - WasmStringSlice export_name) { - WasmTypeVector types; - WASM_ZERO_MEMORY(types); - types.size = 1; - types.data = &type; - return append_nullary_func(allocator, module, &types, export_name); -} - -static ActionInfo get_action_info(const WasmModule* module, - const WasmAction* action) { - ActionInfo result; - WASM_ZERO_MEMORY(result); - - switch (action->type) { - case WASM_ACTION_TYPE_INVOKE: { - WasmExport* export_ = - wasm_get_export_by_name(module, &action->invoke.name); - assert(export_); - - int func_index = wasm_get_func_index_by_var(module, &export_->var); - assert(func_index >= 0 && (size_t)func_index < module->funcs.size); - WasmFunc* callee = module->funcs.data[func_index]; - result.action = action; - result.index = func_index; - result.num_results = callee->decl.sig.result_types.size; - result.result_types = &callee->decl.sig.result_types; - break; - } - - case WASM_ACTION_TYPE_GET: { - WasmExport* export_ = wasm_get_export_by_name(module, &action->get.name); - assert(export_); - - int global_index = wasm_get_global_index_by_var(module, &export_->var); - assert(global_index >= 0 && (size_t)global_index < module->globals.size); - WasmGlobal* global = module->globals.data[global_index]; - result.action = action; - result.index = global_index; - result.num_results = 1; - result.result_type = global->type; - break; - } + write_separator(ctx); + if (action->module_var.type != WASM_VAR_TYPE_INDEX) { + write_key(ctx, "module"); + write_var(ctx, &action->module_var); + write_separator(ctx); } - return result; -} - -static WasmType get_action_info_result_type(const ActionInfo* info) { - assert(info->num_results == 1); - - switch (info->action->type) { - case WASM_ACTION_TYPE_INVOKE: - return info->result_types->data[0]; - - case WASM_ACTION_TYPE_GET: - return info->result_type; + if (action->type == WASM_ACTION_TYPE_INVOKE) { + write_key(ctx, "field"); + write_escaped_string_slice(ctx, action->invoke.name); + write_separator(ctx); + write_key(ctx, "args"); + write_const_vector(ctx, &action->invoke.args); + } else { + write_key(ctx, "field"); + write_escaped_string_slice(ctx, action->get.name); } - WABT_UNREACHABLE; + wasm_writef(&ctx->json_stream, "}"); } -static void append_action_expr(WasmAllocator* allocator, - WasmModule* module, - ExprList* expr_list, - const ActionInfo* info) { - switch (info->action->type) { - case WASM_ACTION_TYPE_INVOKE: - append_invoke_expr(allocator, expr_list, &info->action->invoke, - info->index); - break; - - case WASM_ACTION_TYPE_GET: - append_get_global_expr(allocator, expr_list, &info->action->get, - info->index); - break; +static void write_module(Context* ctx, + char* filename, + const WasmModule* module) { + WasmMemoryWriter writer; + WasmResult result = wasm_init_mem_writer(ctx->allocator, &writer); + if (WASM_SUCCEEDED(result)) { + result = wasm_write_binary_module(ctx->allocator, &writer.base, module, + &ctx->spec_options->write_binary_options); + if (WASM_SUCCEEDED(result)) + result = wasm_write_output_buffer_to_file(&writer.buf, filename); + wasm_close_mem_writer(&writer); } + + ctx->result = result; } -static void write_module(Context* ctx, uint32_t index, WasmModule* module) { - WasmResult result; - WasmWriter* writer = NULL; - CALLBACK(ctx, on_module_before_write, index, &writer); - if (writer != NULL) { - result = - wasm_write_binary_module(ctx->allocator, writer, module, ctx->options); +static void write_raw_module(Context* ctx, + char* filename, + const WasmRawModule* raw_module) { + if (raw_module->type == WASM_RAW_MODULE_TYPE_TEXT) { + write_module(ctx, filename, raw_module->text); } else { - result = WASM_ERROR; - } - if (WASM_FAILED(result)) + WasmFileStream stream; + WasmResult result = wasm_init_file_writer(&stream.writer, filename); + if (WASM_SUCCEEDED(result)) { + wasm_init_stream(&stream.base, &stream.writer.base, NULL); + wasm_write_data(&stream.base, raw_module->binary.data, + raw_module->binary.size, ""); + wasm_close_file_writer(&stream.writer); + } ctx->result = result; - CALLBACK(ctx, on_module_end, index, result); + } +} + +static void write_invalid_module(Context* ctx, + const WasmRawModule* module, + WasmStringSlice text) { + char* filename = get_module_filename(ctx); + write_location(ctx, wasm_get_raw_module_location(module)); + write_separator(ctx); + write_key(ctx, "filename"); + write_escaped_string_slice(ctx, get_basename(filename)); + write_separator(ctx); + write_key(ctx, "text"); + write_escaped_string_slice(ctx, text); + write_raw_module(ctx, filename, module); + wasm_free(ctx->allocator, filename); } static void write_commands(Context* ctx, WasmScript* script) { - uint32_t i; - uint32_t num_modules = 0; - WasmAllocator* allocator = script->allocator; - WasmModule* last_module = NULL; - uint32_t num_assert_funcs = 0; + wasm_writef(&ctx->json_stream, "{\"source_filename\": \"%s\",\n", + ctx->source_filename); + wasm_writef(&ctx->json_stream, " \"commands\": [\n"); + size_t i; for (i = 0; i < script->commands.size; ++i) { WasmCommand* command = &script->commands.data[i]; - if (command->type == WASM_COMMAND_TYPE_MODULE) { - if (last_module) - write_module(ctx, num_modules, last_module); - CALLBACK(ctx, on_module_begin, num_modules); - last_module = &command->module; - num_assert_funcs = 0; - ++num_modules; - } else { - if (!last_module) - continue; - - const char* format = NULL; - WasmAction* action = NULL; - switch (command->type) { - case WASM_COMMAND_TYPE_ACTION: - format = "$action_%d"; - action = &command->action; - break; - case WASM_COMMAND_TYPE_ASSERT_RETURN: - format = "$assert_return_%d"; - action = &command->assert_return.action; - break; - case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: - format = "$assert_return_nan_%d"; - action = &command->assert_return_nan.action; - break; - case WASM_COMMAND_TYPE_ASSERT_TRAP: - format = "$assert_trap_%d"; - action = &command->assert_trap.action; - break; - default: - continue; - } - WasmStringSlice name = - create_assert_func_name(allocator, format, num_assert_funcs); - CALLBACK(ctx, on_command, num_assert_funcs, command->type, &name, - &action->loc); - - ++num_assert_funcs; - - ActionInfo info = get_action_info(last_module, action); - - switch (command->type) { - case WASM_COMMAND_TYPE_ACTION: { - WasmFunc* caller = - append_nullary_func_0(allocator, last_module, name); - ExprList expr_list; - WASM_ZERO_MEMORY(expr_list); - append_action_expr(allocator, last_module, &expr_list, &info); - append_return_expr(allocator, &expr_list); - caller->first_expr = expr_list.first; - break; + wasm_writef(&ctx->json_stream, " {"); + write_command_type(ctx, command); + write_separator(ctx); + + switch (command->type) { + case WASM_COMMAND_TYPE_MODULE: { + WasmModule* module = &command->module; + char* filename = get_module_filename(ctx); + write_location(ctx, &module->loc); + write_separator(ctx); + if (module->name.start) { + write_key(ctx, "name"); + write_escaped_string_slice(ctx, module->name); + write_separator(ctx); } - - case WASM_COMMAND_TYPE_ASSERT_RETURN: { - WasmFunc* caller = append_nullary_func_1(allocator, last_module, - WASM_TYPE_I32, name); - ExprList expr_list; - WASM_ZERO_MEMORY(expr_list); - append_action_expr(allocator, last_module, &expr_list, &info); - - if (info.num_results == 1) { - WasmType result_type = get_action_info_result_type(&info); - const WasmConstVector* expected_consts = - &command->assert_return.expected; - if (expected_consts->size == 1) { - WasmConst expected = expected_consts->data[0]; - if (expected.type == WASM_TYPE_F32) { - append_reinterpret_expr(allocator, &expr_list, WASM_TYPE_F32); - append_const_expr(allocator, &expr_list, &expected); - append_reinterpret_expr(allocator, &expr_list, WASM_TYPE_F32); - append_eq_expr(allocator, &expr_list, WASM_TYPE_I32); - } else if (expected.type == WASM_TYPE_F64) { - append_reinterpret_expr(allocator, &expr_list, WASM_TYPE_F64); - append_const_expr(allocator, &expr_list, &expected); - append_reinterpret_expr(allocator, &expr_list, WASM_TYPE_F64); - append_eq_expr(allocator, &expr_list, WASM_TYPE_I64); - } else { - append_const_expr(allocator, &expr_list, &expected); - append_eq_expr(allocator, &expr_list, result_type); - } - } else { - /* function result count mismatch; just fail */ - append_i32_const_expr(allocator, &expr_list, 0); - append_return_expr(allocator, &expr_list); - } - } else { - /* 0 results or >1 results. If there are 0 results, we just assume - * that everything was OK. We don't currenty support multiple - * results, so consider that a failure. */ - append_i32_const_expr(allocator, &expr_list, - info.num_results == 0 ? 1 : 0); - append_return_expr(allocator, &expr_list); - } - - caller->first_expr = expr_list.first; - break; - } - - case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: { - WasmFunc* caller = append_nullary_func_1(allocator, last_module, - WASM_TYPE_I32, name); - ExprList expr_list; - WASM_ZERO_MEMORY(expr_list); - - append_action_expr(allocator, last_module, &expr_list, &info); - - if (info.num_results == 1) { - WasmType result_type = get_action_info_result_type(&info); - wasm_append_type_value(allocator, &caller->local_types, - &result_type); - append_tee_local_expr(allocator, &expr_list, 0); - append_get_local_expr(allocator, &expr_list, 0); - /* x != x is true iff x is NaN */ - append_ne_expr(allocator, &expr_list, result_type); - } else { - /* assert_return_nan doesn't make sense w/ 0 or >1 results; just - * fail */ - append_i32_const_expr(allocator, &expr_list, 0); - append_return_expr(allocator, &expr_list); - } - - caller->first_expr = expr_list.first; - break; - } - - case WASM_COMMAND_TYPE_ASSERT_TRAP: { - WasmFunc* caller = - append_nullary_func_0(allocator, last_module, name); - ExprList expr_list; - WASM_ZERO_MEMORY(expr_list); - append_action_expr(allocator, last_module, &expr_list, &info); - append_return_expr(allocator, &expr_list); - caller->first_expr = expr_list.first; - break; - } - - default: - assert(0); + write_key(ctx, "filename"); + write_escaped_string_slice(ctx, get_basename(filename)); + write_module(ctx, filename, module); + wasm_free(ctx->allocator, filename); + ctx->num_modules++; + break; } + + case WASM_COMMAND_TYPE_ACTION: + write_location(ctx, &command->action.loc); + write_separator(ctx); + write_action(ctx, &command->action); + break; + + case WASM_COMMAND_TYPE_REGISTER: + write_location(ctx, &command->register_.var.loc); + write_separator(ctx); + write_key(ctx, "name"); + write_var(ctx, &command->register_.var); + write_separator(ctx); + write_key(ctx, "as"); + write_escaped_string_slice(ctx, command->register_.module_name); + break; + + case WASM_COMMAND_TYPE_ASSERT_MALFORMED: + write_invalid_module(ctx, &command->assert_malformed.module, + command->assert_malformed.text); + ctx->num_modules++; + break; + + case WASM_COMMAND_TYPE_ASSERT_INVALID: + /* TODO(binji): this doesn't currently work because of various + * assertions in wasm-binary-writer.c */ +#if 0 + write_invalid_module(ctx, &command->assert_invalid.module, + command->assert_invalid.text); + ctx->num_modules++; +#else + write_location( + ctx, wasm_get_raw_module_location(&command->assert_invalid.module)); +#endif + break; + + case WASM_COMMAND_TYPE_ASSERT_UNLINKABLE: + write_invalid_module(ctx, &command->assert_unlinkable.module, + command->assert_unlinkable.text); + ctx->num_modules++; + break; + + case WASM_COMMAND_TYPE_ASSERT_RETURN: + write_location(ctx, &command->assert_return.action.loc); + write_separator(ctx); + write_action(ctx, &command->assert_return.action); + write_separator(ctx); + write_key(ctx, "expected"); + write_const_vector(ctx, &command->assert_return.expected); + break; + + case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: + write_location(ctx, &command->assert_return_nan.action.loc); + write_separator(ctx); + write_action(ctx, &command->assert_return_nan.action); + break; + + case WASM_COMMAND_TYPE_ASSERT_TRAP: + write_location(ctx, &command->assert_trap.action.loc); + write_separator(ctx); + write_action(ctx, &command->assert_trap.action); + write_separator(ctx); + write_key(ctx, "text"); + write_escaped_string_slice(ctx, command->assert_trap.text); + break; + + case WASM_NUM_COMMAND_TYPES: + assert(0); + break; } + + wasm_writef(&ctx->json_stream, "}"); + if (i != script->commands.size - 1) + write_separator(ctx); + wasm_writef(&ctx->json_stream, "\n"); } - if (last_module) - write_module(ctx, num_modules, last_module); + wasm_writef(&ctx->json_stream, "]}\n"); } WasmResult wasm_write_binary_spec_script( WasmAllocator* allocator, WasmScript* script, - const WasmWriteBinaryOptions* options, + const char* source_filename, const WasmWriteBinarySpecOptions* spec_options) { + assert(source_filename); Context ctx; WASM_ZERO_MEMORY(ctx); ctx.allocator = allocator; - ctx.options = options; ctx.spec_options = spec_options; ctx.result = WASM_OK; - - CALLBACK0(&ctx, on_script_begin); - write_commands(&ctx, script); - CALLBACK0(&ctx, on_script_end); + ctx.source_filename = source_filename; + ctx.json_filename_noext = strip_extension(ctx.spec_options->json_filename); + + WasmResult result = wasm_init_mem_writer(ctx.allocator, &ctx.json_writer); + if (WASM_SUCCEEDED(result)) { + wasm_init_stream(&ctx.json_stream, &ctx.json_writer.base, NULL); + write_commands(&ctx, script); + if (ctx.spec_options->json_filename) { + wasm_write_output_buffer_to_file(&ctx.json_writer.buf, + ctx.spec_options->json_filename); + } + wasm_close_mem_writer(&ctx.json_writer); + } return ctx.result; } diff --git a/src/wasm-binary-writer-spec.h b/src/wasm-binary-writer-spec.h index 7991a019..1c2629ce 100644 --- a/src/wasm-binary-writer-spec.h +++ b/src/wasm-binary-writer-spec.h @@ -18,39 +18,24 @@ #define WASM_BINARY_WRITER_SPEC_H_ #include "wasm-ast.h" +#include "wasm-binary-writer.h" #include "wasm-common.h" struct WasmAllocator; -struct WasmWriteBinaryOptions; struct WasmWriter; #define WASM_WRITE_BINARY_SPEC_OPTIONS_DEFAULT \ - { NULL, NULL, NULL, NULL, NULL, NULL, NULL } + { NULL, WASM_WRITE_BINARY_OPTIONS_DEFAULT } typedef struct WasmWriteBinarySpecOptions { - /* callbacks for writing multiple modules */ - void (*on_script_begin)(void* user_data); - void (*on_module_begin)(uint32_t index, - void* user_data); - void (*on_command)(uint32_t index, - WasmCommandType type, - const WasmStringSlice* name, - const WasmLocation* loc, - void* user_data); - void (*on_module_before_write)(uint32_t index, - struct WasmWriter** out_writer, - void* user_data); - void (*on_module_end)(uint32_t index, WasmResult result, void* user_data); - void (*on_script_end)(void* user_data); - - void* user_data; + const char* json_filename; + WasmWriteBinaryOptions write_binary_options; } WasmWriteBinarySpecOptions; WASM_EXTERN_C_BEGIN -/* this function modifies the AST */ WasmResult wasm_write_binary_spec_script(struct WasmAllocator*, struct WasmScript*, - const struct WasmWriteBinaryOptions*, + const char* source_filename, const WasmWriteBinarySpecOptions*); WASM_EXTERN_C_END diff --git a/src/wasm-interpreter.c b/src/wasm-interpreter.c index 574dcc66..70b4bd1c 100644 --- a/src/wasm-interpreter.c +++ b/src/wasm-interpreter.c @@ -180,7 +180,7 @@ WasmInterpreterResult wasm_push_thread_value(WasmInterpreterThread* thread, WasmInterpreterExport* wasm_get_interpreter_export_by_name( WasmInterpreterModule* module, - WasmStringSlice* name) { + const WasmStringSlice* name) { int field_index = wasm_find_binding_index_by_name(&module->export_bindings, name); if (field_index < 0) @@ -232,7 +232,7 @@ void wasm_destroy_interpreter_thread(WasmAllocator* allocator, #define F32_SIG_MASK 0x7fffff #define F32_SIGN_MASK 0x80000000U -static WASM_INLINE WasmBool is_nan_f32(uint32_t f32_bits) { +WASM_INLINE WasmBool is_nan_f32(uint32_t f32_bits) { return (f32_bits > F32_INF && f32_bits < F32_NEG_ZERO) || (f32_bits > F32_NEG_INF); } @@ -297,7 +297,7 @@ static WASM_INLINE WasmBool is_in_range_i64_trunc_u_f32(uint32_t f32_bits) { #define F64_SIG_MASK 0xfffffffffffffULL #define F64_SIGN_MASK 0x8000000000000000ULL -static WASM_INLINE WasmBool is_nan_f64(uint64_t f64_bits) { +WASM_INLINE WasmBool is_nan_f64(uint64_t f64_bits) { return (f64_bits > F64_INF && f64_bits < F64_NEG_ZERO) || (f64_bits > F64_NEG_INF); } diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index 2b470348..4cbb1ff8 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -53,7 +53,12 @@ struct WasmStream; /* expected type */ \ V(TRAP_HOST_RESULT_TYPE_MISMATCH, "host result type mismatch") \ /* we called an import function, but it didn't complete succesfully */ \ - V(TRAP_HOST_TRAPPED, "host function trapped") + V(TRAP_HOST_TRAPPED, "host function trapped") \ + /* we attempted to call a function with the an argument list that doesn't \ + * match the function signature */ \ + V(ARGUMENT_TYPE_MISMATCH, "argument type mismatch") \ + /* we tried to call an exported function by name that doesn't exist */ \ + V(UNKNOWN_EXPORTED_FUNCTION, "unknown exported function") typedef enum WasmInterpreterResult { #define V(name, str) WASM_INTERPRETER_##name, @@ -259,6 +264,9 @@ typedef struct WasmInterpreterThreadOptions { } WasmInterpreterThreadOptions; WASM_EXTERN_C_BEGIN +WasmBool is_nan_f32(uint32_t f32_bits); +WasmBool is_nan_f64(uint64_t f64_bits); + void wasm_init_interpreter_environment(WasmAllocator* allocator, WasmInterpreterEnvironment* env); void wasm_destroy_interpreter_environment(WasmAllocator* allocator, @@ -291,7 +299,7 @@ void wasm_disassemble_module(WasmInterpreterEnvironment* env, WasmInterpreterExport* wasm_get_interpreter_export_by_name( WasmInterpreterModule* module, - WasmStringSlice* name); + const WasmStringSlice* name); WASM_EXTERN_C_END #endif /* WASM_INTERPRETER_H_ */ diff --git a/test/interp/callimport-zero-args.txt b/test/interp/callimport-zero-args.txt index 1281134b..0419d7ed 100644 --- a/test/interp/callimport-zero-args.txt +++ b/test/interp/callimport-zero-args.txt @@ -7,6 +7,6 @@ call $imported i32.add)) (;; STDOUT ;;; -called host spectest.print() => (i32:0) +called host spectest.print() => i32:0 f() => i32:13 ;;; STDOUT ;;) diff --git a/test/interp/empty.txt b/test/interp/empty.txt index b126f168..ab3807d3 100644 --- a/test/interp/empty.txt +++ b/test/interp/empty.txt @@ -2,5 +2,5 @@ (module (func (export "f"))) (;; STDOUT ;;; -f() => +f() => ;;; STDOUT ;;) diff --git a/test/interp/import.txt b/test/interp/import.txt index e84f765d..17bd8216 100644 --- a/test/interp/import.txt +++ b/test/interp/import.txt @@ -12,7 +12,7 @@ return) ) (;; STDOUT ;;; -called host spectest.print(i32:100) => () -called host spectest.print(i32:200, i32:300) => () +called host spectest.print(i32:100) => +called host spectest.print(i32:200, i32:300) => test() => i32:1 ;;; STDOUT ;;) diff --git a/test/interp/return-void.txt b/test/interp/return-void.txt index 8ce78300..728600ef 100644 --- a/test/interp/return-void.txt +++ b/test/interp/return-void.txt @@ -28,8 +28,8 @@ i32.const 0 i32.load)) (;; STDOUT ;;; -test1() => +test1() => check1() => i32:0 -test2() => +test2() => check2() => i32:1 ;;; STDOUT ;;) diff --git a/test/run-interp.py b/test/run-interp.py index caae7672..6c3dc1b8 100755 --- a/test/run-interp.py +++ b/test/run-interp.py @@ -16,7 +16,6 @@ # import argparse -import json import os import subprocess import sys @@ -83,9 +82,7 @@ def main(args): out_file = utils.ChangeDir(utils.ChangeExt(options.file, new_ext), out_dir) wast2wasm.RunWithArgs(options.file, '-o', out_file) if options.spec: - with open(out_file) as json_file: - json_data = json.load(json_file) - wasm_files = [m['filename'] for m in json_data['modules']] + wasm_files = utils.GetModuleFilenamesFromSpecJSON(out_file) wasm_files = [utils.ChangeDir(f, out_dir) for f in wasm_files] else: wasm_files = [out_file] diff --git a/test/run-wasmdump.py b/test/run-wasmdump.py index ec0628bb..7055e59a 100755 --- a/test/run-wasmdump.py +++ b/test/run-wasmdump.py @@ -16,7 +16,6 @@ # import argparse -import json import os import sys import tempfile @@ -80,9 +79,7 @@ def main(args): wast2wasm.RunWithArgs('-o', out_file, filename) if options.spec: - with open(out_file) as json_file: - json_data = json.load(json_file) - wasm_files = [m['filename'] for m in json_data['modules']] + wasm_files = utils.GetModuleFilenamesFromSpecJSON(out_file) wasm_files = [utils.ChangeDir(f, out_dir) for f in wasm_files] else: wasm_files = [out_file] diff --git a/test/spec/address.txt b/test/spec/address.txt index 1cf125c0..e93f5594 100644 --- a/test/spec/address.txt +++ b/test/spec/address.txt @@ -1,45 +1,45 @@ ;;; TOOL: run-interp-spec ;;; STDIN_FILE: third_party/testsuite/address.wast (;; STDOUT ;;; -called host spectest.print(i32:97) => () -called host spectest.print(i32:98) => () -called host spectest.print(i32:99) => () -called host spectest.print(i32:122) => () -called host spectest.print(i32:25185) => () -called host spectest.print(i32:25185) => () -called host spectest.print(i32:25442) => () -called host spectest.print(i32:25699) => () -called host spectest.print(i32:122) => () -called host spectest.print(i32:1684234849) => () -called host spectest.print(i32:1701077858) => () -called host spectest.print(i32:1717920867) => () -called host spectest.print(i32:122) => () -$action_0() => -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -$action_1() => -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -called host spectest.print(i32:0) => () -3/3 tests passed. +called host spectest.print(i32:97) => +called host spectest.print(i32:98) => +called host spectest.print(i32:99) => +called host spectest.print(i32:122) => +called host spectest.print(i32:25185) => +called host spectest.print(i32:25185) => +called host spectest.print(i32:25442) => +called host spectest.print(i32:25699) => +called host spectest.print(i32:122) => +called host spectest.print(i32:1684234849) => +called host spectest.print(i32:1701077858) => +called host spectest.print(i32:1717920867) => +called host spectest.print(i32:122) => +good(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +good(i32:65507) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +called host spectest.print(i32:0) => +5/5 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/float_exprs.txt b/test/spec/float_exprs.txt index 8ea784ad..bdf0262f 100644 --- a/test/spec/float_exprs.txt +++ b/test/spec/float_exprs.txt @@ -1,15 +1,15 @@ ;;; TOOL: run-interp-spec ;;; STDIN_FILE: third_party/testsuite/float_exprs.wast (;; STDOUT ;;; -$action_0() => -$action_1() => -$action_2() => -$action_3() => -$action_8() => -$action_0() => -$action_1() => -$action_2() => -$action_3() => -$action_8() => -694/694 tests passed. +init(i32:0, f32:15.1) => +init(i32:4, f32:15.2) => +init(i32:8, f32:15.3) => +init(i32:12, f32:15.4) => +run(i32:16, f32:3) => +init(i32:0, f64:15.1) => +init(i32:8, f64:15.2) => +init(i32:16, f64:15.3) => +init(i32:24, f64:15.4) => +run(i32:32, f64:3) => +704/704 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/float_memory.txt b/test/spec/float_memory.txt index 204b88af..21deb04b 100644 --- a/test/spec/float_memory.txt +++ b/test/spec/float_memory.txt @@ -1,29 +1,29 @@ ;;; TOOL: run-interp-spec ;;; STDIN_FILE: third_party/testsuite/float_memory.wast (;; STDOUT ;;; -$action_2() => -$action_5() => -$action_8() => -$action_11() => -$action_2() => -$action_5() => -$action_8() => -$action_11() => -$action_2() => -$action_5() => -$action_8() => -$action_11() => -$action_2() => -$action_5() => -$action_8() => -$action_11() => -$action_2() => -$action_5() => -$action_8() => -$action_11() => -$action_2() => -$action_5() => -$action_8() => -$action_11() => -60/60 tests passed. +reset() => +f32.store() => +reset() => +i32.store() => +reset() => +f64.store() => +reset() => +i64.store() => +reset() => +f32.store() => +reset() => +i32.store() => +reset() => +f64.store() => +reset() => +i64.store() => +reset() => +f32.store() => +reset() => +i32.store() => +reset() => +f64.store() => +reset() => +i64.store() => +84/84 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/func_ptrs.txt b/test/spec/func_ptrs.txt index b38faf9b..55248283 100644 --- a/test/spec/func_ptrs.txt +++ b/test/spec/func_ptrs.txt @@ -29,7 +29,7 @@ assert_invalid error: third_party/testsuite/func_ptrs.wast:48:64: function type variable out of range (max 0) ...invalid (module (import "spectest" "print" (func (type 43)))) "unknown type") ^^ -called host spectest.print(i32:83) => () -$action_3() => -25/25 tests passed. +called host spectest.print(i32:83) => +four(i32:83) => +26/26 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/memory_redundancy.txt b/test/spec/memory_redundancy.txt index a33af0d9..527ba1dc 100644 --- a/test/spec/memory_redundancy.txt +++ b/test/spec/memory_redundancy.txt @@ -1,7 +1,7 @@ ;;; TOOL: run-interp-spec ;;; STDIN_FILE: third_party/testsuite/memory_redundancy.wast (;; STDOUT ;;; -$action_1() => -$action_3() => -3/3 tests passed. +zero_everything() => +zero_everything() => +5/5 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/names.txt b/test/spec/names.txt index 157b98fc..46176bce 100644 --- a/test/spec/names.txt +++ b/test/spec/names.txt @@ -1,8 +1,8 @@ ;;; TOOL: run-interp-spec ;;; STDIN_FILE: third_party/testsuite/names.wast (;; STDOUT ;;; -called host spectest.print(i32:42) => () -called host spectest.print(i32:123) => () -$action_0() => -14/14 tests passed. +called host spectest.print(i32:42) => +called host spectest.print(i32:123) => +print32(i32:42, i32:123) => +15/15 tests passed. ;;; STDOUT ;;) diff --git a/test/spec/start.txt b/test/spec/start.txt index bd7fe7c4..9d956525 100644 --- a/test/spec/start.txt +++ b/test/spec/start.txt @@ -13,11 +13,11 @@ assert_invalid error: third_party/testsuite/start.wast:15:5: start function must be nullary (start $main) ^^^^^^^^^^^^^ -$action_1() => -$action_3() => -$action_1() => -$action_3() => -called host spectest.print(i32:1) => () -called host spectest.print(i32:2) => () -6/6 tests passed. +inc() => +inc() => +inc() => +inc() => +called host spectest.print(i32:1) => +called host spectest.print(i32:2) => +10/10 tests passed. ;;; STDOUT ;;) diff --git a/test/utils.py b/test/utils.py index 7dac9aab..254257cd 100644 --- a/test/utils.py +++ b/test/utils.py @@ -18,6 +18,7 @@ from __future__ import print_function import contextlib import os +import json import shutil import signal import subprocess @@ -163,3 +164,9 @@ def Hexdump(data): lines.append(line) return lines + + +def GetModuleFilenamesFromSpecJSON(json_filename): + with open(json_filename) as json_file: + json_data = json.load(json_file) + return [m['filename'] for m in json_data['commands'] if 'filename' in m] |