summaryrefslogtreecommitdiff
path: root/src/tools/wasm-interp.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/wasm-interp.c')
-rw-r--r--src/tools/wasm-interp.c1322
1 files changed, 885 insertions, 437 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;
}