summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/tools/wasm-interp.c1322
-rw-r--r--src/tools/wast2wasm.c195
-rw-r--r--src/wasm-ast-checker.c60
-rw-r--r--src/wasm-ast.h11
-rw-r--r--src/wasm-binary-writer-spec.c804
-rw-r--r--src/wasm-binary-writer-spec.h25
-rw-r--r--src/wasm-interpreter.c6
-rw-r--r--src/wasm-interpreter.h12
-rw-r--r--test/interp/callimport-zero-args.txt2
-rw-r--r--test/interp/empty.txt2
-rw-r--r--test/interp/import.txt4
-rw-r--r--test/interp/return-void.txt4
-rwxr-xr-xtest/run-interp.py5
-rwxr-xr-xtest/run-wasmdump.py5
-rw-r--r--test/spec/address.txt82
-rw-r--r--test/spec/float_exprs.txt22
-rw-r--r--test/spec/float_memory.txt50
-rw-r--r--test/spec/func_ptrs.txt6
-rw-r--r--test/spec/memory_redundancy.txt6
-rw-r--r--test/spec/names.txt8
-rw-r--r--test/spec/start.txt14
-rw-r--r--test/utils.py7
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]