diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ast-checker.c | 308 | ||||
-rw-r--r-- | src/ast-checker.h | 17 | ||||
-rw-r--r-- | src/ast.c | 12 | ||||
-rw-r--r-- | src/ast.h | 1 | ||||
-rw-r--r-- | src/binary-reader-interpreter.c | 2 | ||||
-rw-r--r-- | src/binary-writer.c | 100 | ||||
-rw-r--r-- | src/resolve-names.c | 444 | ||||
-rw-r--r-- | src/resolve-names.h | 40 | ||||
-rw-r--r-- | src/tools/wast2wasm.c | 13 |
9 files changed, 609 insertions, 328 deletions
diff --git a/src/ast-checker.c b/src/ast-checker.c index a12b28b4..ea9835da 100644 --- a/src/ast-checker.c +++ b/src/ast-checker.c @@ -27,11 +27,7 @@ #include "ast-parser-lexer-shared.h" #include "binary-reader-ast.h" #include "binary-reader.h" - -typedef enum CheckStyle { - CHECK_STYLE_NAME, - CHECK_STYLE_FULL, -} CheckStyle; +#include "resolve-names.h" typedef enum LabelType { LABEL_TYPE_FUNC, @@ -42,7 +38,6 @@ typedef enum LabelType { typedef struct LabelNode { LabelType label_type; - const WasmLabel* label; const WasmTypeVector* sig; struct LabelNode* next; size_t type_stack_limit; @@ -63,7 +58,6 @@ typedef struct ActionResult { } ActionResult; typedef struct Context { - CheckStyle check_style; WasmSourceErrorHandler* error_handler; WasmAllocator* allocator; WasmAstLexer* lexer; @@ -79,18 +73,13 @@ typedef struct Context { WasmResult result; } Context; -static void WASM_PRINTF_FORMAT(4, 5) print_error(Context* ctx, - CheckStyle check_style, - const WasmLocation* loc, - const char* fmt, - ...) { - if (check_style <= ctx->check_style) { - ctx->result = WASM_ERROR; - va_list args; - va_start(args, fmt); - wasm_ast_format_error(ctx->error_handler, loc, ctx->lexer, fmt, args); - va_end(args); - } +static void WASM_PRINTF_FORMAT(3, 4) + print_error(Context* ctx, const WasmLocation* loc, const char* fmt, ...) { + ctx->result = WASM_ERROR; + va_list args; + va_start(args, fmt); + wasm_ast_format_error(ctx->error_handler, loc, ctx->lexer, fmt, args); + va_end(args); } static WasmBool is_power_of_two(uint32_t x) { @@ -103,57 +92,19 @@ static uint32_t get_opcode_natural_alignment(WasmOpcode opcode) { return memory_size; } -static void check_duplicate_bindings(Context* ctx, - const WasmBindingHash* bindings, - const char* desc) { - size_t i; - for (i = 0; i < bindings->entries.capacity; ++i) { - WasmBindingHashEntry* entry = &bindings->entries.data[i]; - if (wasm_hash_entry_is_free(entry)) - continue; - - /* only follow the chain if this is the first entry in the chain */ - if (entry->prev != NULL) - continue; - - WasmBindingHashEntry* a = entry; - for (; a; a = a->next) { - WasmBindingHashEntry* b = a->next; - for (; b; b = b->next) { - if (wasm_string_slices_are_equal(&a->binding.name, &b->binding.name)) { - /* choose the location that is later in the file */ - WasmLocation* a_loc = &a->binding.loc; - WasmLocation* b_loc = &b->binding.loc; - WasmLocation* loc = a_loc->line > b_loc->line ? a_loc : b_loc; - print_error(ctx, CHECK_STYLE_NAME, loc, - "redefinition of %s \"" PRIstringslice "\"", desc, - WASM_PRINTF_STRING_SLICE_ARG(a->binding.name)); - } - } - } - } -} - static WasmResult check_var(Context* ctx, - const WasmBindingHash* bindings, int max_index, const WasmVar* var, const char* desc, int* out_index) { - int index = wasm_get_index_from_var(bindings, var); - if (index >= 0 && index < max_index) { + assert(var->type == WASM_VAR_TYPE_INDEX); + if (var->index >= 0 && var->index < max_index) { if (out_index) - *out_index = index; + *out_index = var->index; return WASM_OK; } - if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, - "undefined %s variable \"" PRIstringslice "\"", desc, - WASM_PRINTF_STRING_SLICE_ARG(var->name)); - } else { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, - "%s variable out of range (max %d)", desc, max_index); - } + print_error(ctx, &var->loc, "%s variable out of range (max %d)", desc, + max_index); return WASM_ERROR; } @@ -161,9 +112,8 @@ static WasmResult check_func_var(Context* ctx, const WasmVar* var, const WasmFunc** out_func) { int index; - if (WASM_FAILED(check_var(ctx, &ctx->current_module->func_bindings, - ctx->current_module->funcs.size, var, "function", - &index))) { + if (WASM_FAILED(check_var(ctx, ctx->current_module->funcs.size, var, + "function", &index))) { return WASM_ERROR; } @@ -177,9 +127,8 @@ static WasmResult check_global_var(Context* ctx, const WasmGlobal** out_global, int* out_global_index) { int index; - if (WASM_FAILED(check_var(ctx, &ctx->current_module->global_bindings, - ctx->current_module->globals.size, var, "global", - &index))) { + if (WASM_FAILED(check_var(ctx, ctx->current_module->globals.size, var, + "global", &index))) { return WASM_ERROR; } @@ -194,8 +143,7 @@ static WasmResult check_func_type_var(Context* ctx, const WasmVar* var, const WasmFuncType** out_func_type) { int index; - if (WASM_FAILED(check_var(ctx, &ctx->current_module->func_type_bindings, - ctx->current_module->func_types.size, var, + if (WASM_FAILED(check_var(ctx, ctx->current_module->func_types.size, var, "function type", &index))) { return WASM_ERROR; } @@ -209,8 +157,7 @@ static WasmResult check_table_var(Context* ctx, const WasmVar* var, const WasmTable** out_table) { int index; - if (WASM_FAILED(check_var(ctx, &ctx->current_module->table_bindings, - ctx->current_module->tables.size, var, "table", + if (WASM_FAILED(check_var(ctx, ctx->current_module->tables.size, var, "table", &index))) { return WASM_ERROR; } @@ -224,9 +171,8 @@ static WasmResult check_memory_var(Context* ctx, const WasmVar* var, const WasmMemory** out_memory) { int index; - if (WASM_FAILED(check_var(ctx, &ctx->current_module->memory_bindings, - ctx->current_module->memories.size, var, "memory", - &index))) { + if (WASM_FAILED(check_var(ctx, ctx->current_module->memories.size, var, + "memory", &index))) { return WASM_ERROR; } @@ -254,12 +200,12 @@ static WasmResult check_local_var(Context* ctx, } if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, + print_error(ctx, &var->loc, "undefined local variable \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(var->name)); } else { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, - "local variable out of range (max %d)", max_index); + print_error(ctx, &var->loc, "local variable out of range (max %d)", + max_index); } return WASM_ERROR; } @@ -270,9 +216,9 @@ static void check_align(Context* ctx, uint32_t natural_alignment) { if (alignment != WASM_USE_NATURAL_ALIGNMENT) { if (!is_power_of_two(alignment)) - print_error(ctx, CHECK_STYLE_FULL, loc, "alignment must be power-of-two"); + print_error(ctx, loc, "alignment must be power-of-two"); if (alignment > natural_alignment) { - print_error(ctx, CHECK_STYLE_FULL, loc, + print_error(ctx, loc, "alignment must not be larger than natural alignment (%u)", natural_alignment); } @@ -283,26 +229,12 @@ static void check_offset(Context* ctx, const WasmLocation* loc, uint64_t offset) { if (offset > UINT32_MAX) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "offset must be less than or equal to 0xffffffff"); - } -} - -static LabelNode* find_label_by_name(LabelNode* top_label, - const WasmStringSlice* name) { - LabelNode* node = top_label; - while (node) { - if (wasm_string_slices_are_equal(node->label, name)) - return node; - node = node->next; + print_error(ctx, loc, "offset must be less than or equal to 0xffffffff"); } - return NULL; } static LabelNode* find_label_by_var(LabelNode* top_label, const WasmVar* var) { - if (var->type == WASM_VAR_TYPE_NAME) - return find_label_by_name(top_label, &var->name); - + assert(var->type == WASM_VAR_TYPE_INDEX); LabelNode* node = top_label; int i = 0; while (node && i != var->index) { @@ -323,15 +255,8 @@ static WasmResult check_label_var(Context* ctx, return WASM_OK; } - if (var->type == WASM_VAR_TYPE_NAME) { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, - "undefined label variable \"" PRIstringslice "\"", - WASM_PRINTF_STRING_SLICE_ARG(var->name)); - } else { - print_error(ctx, CHECK_STYLE_NAME, &var->loc, - "label variable out of range (max %d)", ctx->max_depth); - } - + print_error(ctx, &var->loc, "label variable out of range (max %d)", + ctx->max_depth); return WASM_ERROR; } @@ -339,10 +264,8 @@ static void push_label(Context* ctx, const WasmLocation* loc, LabelNode* node, LabelType label_type, - const WasmLabel* label, const WasmTypeVector* sig) { node->label_type = label_type; - node->label = label; node->next = ctx->top_label; node->sig = sig; node->type_stack_limit = ctx->type_stack.size; @@ -367,9 +290,8 @@ static WasmResult check_type_stack_limit(Context* ctx, size_t limit = type_stack_limit(ctx); size_t avail = ctx->type_stack.size - limit; if (expected > avail) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "type stack size too small at %s. got %" PRIzd - ", expected at least %" PRIzd, + print_error(ctx, loc, "type stack size too small at %s. got %" PRIzd + ", expected at least %" PRIzd, desc, avail, expected); return WASM_ERROR; } @@ -383,7 +305,7 @@ static WasmResult check_type_stack_limit_exact(Context* ctx, size_t limit = type_stack_limit(ctx); size_t avail = ctx->type_stack.size - limit; if (expected != avail) { - print_error(ctx, CHECK_STYLE_FULL, loc, + print_error(ctx, loc, "type stack at end of %s is %" PRIzd ". expected %" PRIzd, desc, avail, expected); return WASM_ERROR; @@ -437,10 +359,8 @@ static void check_type(Context* ctx, WasmType expected, const char* desc) { if (actual != expected) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "type mismatch at %s. got %s, expected %s", desc, - wasm_get_type_name(actual), - wasm_get_type_name(expected)); + print_error(ctx, loc, "type mismatch at %s. got %s, expected %s", desc, + wasm_get_type_name(actual), wasm_get_type_name(expected)); } } @@ -452,8 +372,7 @@ static void check_type_index(Context* ctx, int index, const char* index_kind) { if (actual != expected) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "type mismatch for %s %d of %s. got %s, expected %s", + print_error(ctx, loc, "type mismatch for %s %d of %s. got %s, expected %s", index_kind, index, desc, wasm_get_type_name(actual), wasm_get_type_name(expected)); } @@ -472,9 +391,8 @@ static void check_types(Context* ctx, index_kind); } } else { - print_error(ctx, CHECK_STYLE_FULL, loc, - "expected %" PRIzd " %ss, got %" PRIzd, expected->size, - index_kind, actual->size); + print_error(ctx, loc, "expected %" PRIzd " %ss, got %" PRIzd, + expected->size, index_kind, actual->size); } } @@ -490,9 +408,8 @@ static void check_const_types(Context* ctx, i, "result"); } } else { - print_error(ctx, CHECK_STYLE_FULL, loc, - "expected %" PRIzd " results, got %" PRIzd, expected->size, - actual->size); + print_error(ctx, loc, "expected %" PRIzd " results, got %" PRIzd, + expected->size, actual->size); } } @@ -571,9 +488,8 @@ static void check_assert_return_nan_type(Context* ctx, /* when using assert_return_nan, the result can be either a f32 or f64 type * so we special case it here. */ if (actual != WASM_TYPE_F32 && actual != WASM_TYPE_F64) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "type mismatch at %s. got %s, expected f32 or f64", desc, - wasm_get_type_name(actual)); + print_error(ctx, loc, "type mismatch at %s. got %s, expected f32 or f64", + desc, wasm_get_type_name(actual)); } } @@ -613,9 +529,8 @@ static void check_call(Context* ctx, } ctx->type_stack.size -= expected_args; } else { - print_error(ctx, CHECK_STYLE_FULL, loc, - "type stack size too small at %s. got %" PRIzd - ", expected at least %" PRIzd, + print_error(ctx, loc, "type stack size too small at %s. got %" PRIzd + ", expected at least %" PRIzd, desc, avail, expected_args); ctx->type_stack.size = limit; } @@ -657,8 +572,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) { case WASM_EXPR_TYPE_BLOCK: { LabelNode node; - push_label(ctx, &expr->loc, &node, LABEL_TYPE_BLOCK, &expr->block.label, - &expr->block.sig); + push_label(ctx, &expr->loc, &node, LABEL_TYPE_BLOCK, &expr->block.sig); check_block(ctx, &expr->loc, expr->block.first, "block"); pop_label(ctx); push_types(ctx, &expr->block.sig); @@ -702,7 +616,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) { case WASM_EXPR_TYPE_CALL_INDIRECT: { const WasmFuncType* func_type; if (ctx->current_module->tables.size == 0) { - print_error(ctx, CHECK_STYLE_FULL, &expr->loc, + print_error(ctx, &expr->loc, "found call_indirect operator, but no table"); } if (WASM_SUCCEEDED( @@ -754,8 +668,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) { case WASM_EXPR_TYPE_IF: { LabelNode node; pop_and_check_1_type(ctx, &expr->loc, WASM_TYPE_I32, "if condition"); - push_label(ctx, &expr->loc, &node, LABEL_TYPE_IF, &expr->if_.true_.label, - &expr->if_.true_.sig); + push_label(ctx, &expr->loc, &node, LABEL_TYPE_IF, &expr->if_.true_.sig); check_block(ctx, &expr->loc, expr->if_.true_.first, "if true branch"); check_block(ctx, &expr->loc, expr->if_.false_, "if false branch"); pop_label(ctx); @@ -772,8 +685,7 @@ static void check_expr(Context* ctx, const WasmExpr* expr) { case WASM_EXPR_TYPE_LOOP: { LabelNode node; - push_label(ctx, &expr->loc, &node, LABEL_TYPE_LOOP, &expr->loop.label, - &expr->loop.sig); + push_label(ctx, &expr->loc, &node, LABEL_TYPE_LOOP, &expr->loop.sig); check_block(ctx, &expr->loc, expr->loop.first, "loop"); pop_label(ctx); push_types(ctx, &expr->block.sig); @@ -867,8 +779,7 @@ static void check_func(Context* ctx, const WasmFunc* func) { ctx->current_func = func; if (wasm_get_num_results(func) > 1) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "multiple result values not currently supported."); + print_error(ctx, loc, "multiple result values not currently supported."); /* don't run any other checks, the won't test the result_type properly */ return; } @@ -881,14 +792,10 @@ static void check_func(Context* ctx, } } - check_duplicate_bindings(ctx, &func->param_bindings, "parameter"); - check_duplicate_bindings(ctx, &func->local_bindings, "local"); /* The function has an implicit label; branching to it is equivalent to the * returning from the function. */ LabelNode node; - WasmLabel label = wasm_empty_string_slice(); - push_label(ctx, loc, &node, LABEL_TYPE_FUNC, &label, - &func->decl.sig.result_types); + push_label(ctx, loc, &node, LABEL_TYPE_FUNC, &func->decl.sig.result_types); check_block(ctx, loc, func->first_expr, "function"); pop_label(ctx); ctx->current_func = NULL; @@ -897,7 +804,7 @@ static void check_func(Context* ctx, static void print_const_expr_error(Context* ctx, const WasmLocation* loc, const char* desc) { - print_error(ctx, CHECK_STYLE_FULL, loc, + print_error(ctx, loc, "invalid %s, must be a constant expression; either *.const or " "get_global.", desc); @@ -932,14 +839,14 @@ static void check_const_init_expr(Context* ctx, /* globals can only reference previously defined globals */ if (ref_global_index >= ctx->current_global_index) { print_error( - ctx, CHECK_STYLE_FULL, loc, + ctx, loc, "initializer expression can only be reference a previously " "defined global"); } if (ref_global->mutable_) { print_error( - ctx, CHECK_STYLE_FULL, loc, + ctx, loc, "initializer expression cannot reference a mutable global"); } break; @@ -967,20 +874,18 @@ static void check_limits(Context* ctx, uint64_t absolute_max, const char* desc) { if (limits->initial > absolute_max) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")", desc, - limits->initial, absolute_max); + print_error(ctx, loc, "initial %s (%" PRIu64 ") must be <= (%" PRIu64 ")", + desc, limits->initial, absolute_max); } if (limits->has_max) { if (limits->max > absolute_max) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")", desc, - limits->max, absolute_max); + print_error(ctx, loc, "max %s (%" PRIu64 ") must be <= (%" PRIu64 ")", + desc, limits->max, absolute_max); } if (limits->max < limits->initial) { - print_error(ctx, CHECK_STYLE_FULL, loc, + print_error(ctx, loc, "max %s (%" PRIu64 ") must be >= initial %s (%" PRIu64 ")", desc, limits->max, desc, limits->initial); } @@ -991,7 +896,7 @@ static void check_table(Context* ctx, const WasmLocation* loc, const WasmTable* table) { if (ctx->current_table_index == 1) - print_error(ctx, CHECK_STYLE_FULL, loc, "only one table allowed"); + print_error(ctx, loc, "only one table allowed"); check_limits(ctx, loc, &table->elem_limits, UINT32_MAX, "elems"); } @@ -1016,7 +921,7 @@ static void check_memory(Context* ctx, const WasmLocation* loc, const WasmMemory* memory) { if (ctx->current_memory_index == 1) - print_error(ctx, CHECK_STYLE_FULL, loc, "only one memory block allowed"); + print_error(ctx, loc, "only one memory block allowed"); check_limits(ctx, loc, &memory->page_limits, WASM_MAX_PAGES, "pages"); } @@ -1055,8 +960,7 @@ static void check_import(Context* ctx, break; case WASM_EXTERNAL_KIND_GLOBAL: if (import->global.mutable_) { - print_error(ctx, CHECK_STYLE_FULL, loc, - "mutable globals cannot be imported"); + print_error(ctx, loc, "mutable globals cannot be imported"); } ctx->current_global_index++; break; @@ -1081,7 +985,7 @@ static void check_export(Context* ctx, const WasmExport* export_) { const WasmGlobal* global; if (WASM_SUCCEEDED(check_global_var(ctx, &export_->var, &global, NULL))) { if (global->mutable_) { - print_error(ctx, CHECK_STYLE_FULL, &export_->var.loc, + print_error(ctx, &export_->var.loc, "mutable globals cannot be exported"); } } @@ -1144,20 +1048,18 @@ static void check_module(Context* ctx, const WasmModule* module) { case WASM_MODULE_FIELD_TYPE_START: { if (seen_start) { - print_error(ctx, CHECK_STYLE_FULL, &field->loc, - "only one start function allowed"); + print_error(ctx, &field->loc, "only one start function allowed"); } const WasmFunc* start_func = NULL; check_func_var(ctx, &field->start, &start_func); if (start_func) { if (wasm_get_num_params(start_func) != 0) { - print_error(ctx, CHECK_STYLE_FULL, &field->loc, - "start function must be nullary"); + print_error(ctx, &field->loc, "start function must be nullary"); } if (wasm_get_num_results(start_func) != 0) { - print_error(ctx, CHECK_STYLE_FULL, &field->loc, + print_error(ctx, &field->loc, "start function must not return anything"); } } @@ -1169,13 +1071,6 @@ static void check_module(Context* ctx, const WasmModule* module) { check_elem_segments(ctx, module); check_data_segments(ctx, module); - - check_duplicate_bindings(ctx, &module->func_bindings, "function"); - check_duplicate_bindings(ctx, &module->global_bindings, "global"); - check_duplicate_bindings(ctx, &module->export_bindings, "export"); - check_duplicate_bindings(ctx, &module->func_type_bindings, "function type"); - check_duplicate_bindings(ctx, &module->table_bindings, "table"); - check_duplicate_bindings(ctx, &module->memory_bindings, "memory"); } typedef struct BinaryErrorCallbackData { @@ -1188,11 +1083,10 @@ static void on_read_binary_error(uint32_t offset, void* user_data) { BinaryErrorCallbackData* data = user_data; if (offset == WASM_UNKNOWN_OFFSET) { - print_error(data->ctx, CHECK_STYLE_FULL, data->loc, - "error in binary module: %s", error); + print_error(data->ctx, data->loc, "error in binary module: %s", error); } else { - print_error(data->ctx, CHECK_STYLE_FULL, data->loc, - "error in binary module: @0x%08x: %s", offset, error); + print_error(data->ctx, data->loc, "error in binary module: @0x%08x: %s", + offset, error); } } @@ -1233,8 +1127,13 @@ static WasmResult read_raw_module(Context* ctx, static void check_raw_module(Context* ctx, WasmRawModule* raw) { ReadModule read_module; - if (WASM_SUCCEEDED(read_raw_module(ctx, raw, &read_module))) - check_module(ctx, &read_module.module); + if (WASM_SUCCEEDED(read_raw_module(ctx, raw, &read_module))) { + WasmModule* module = &read_module.module; + ctx->result = wasm_resolve_names_module(ctx->allocator, ctx->lexer, module, + ctx->error_handler); + if (WASM_SUCCEEDED(ctx->result)) + check_module(ctx, module); + } destroy_read_module(ctx->allocator, &read_module); } @@ -1247,13 +1146,13 @@ static const WasmTypeVector* check_invoke(Context* ctx, const WasmModule* module = wasm_get_module_by_var(ctx->script, &action->module_var); if (!module) { - print_error(ctx, CHECK_STYLE_FULL, &action->loc, "unknown module"); + print_error(ctx, &action->loc, "unknown module"); return NULL; } WasmExport* export = wasm_get_export_by_name(module, &invoke->name); if (!export) { - print_error(ctx, CHECK_STYLE_NAME, &action->loc, + print_error(ctx, &action->loc, "unknown function export \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(invoke->name)); return NULL; @@ -1268,9 +1167,8 @@ static const WasmTypeVector* check_invoke(Context* ctx, size_t actual_args = invoke->args.size; size_t expected_args = wasm_get_num_params(func); if (expected_args != actual_args) { - print_error(ctx, CHECK_STYLE_FULL, &action->loc, - "too %s parameters to function. got %" PRIzd - ", expected %" PRIzd, + print_error(ctx, &action->loc, "too %s parameters to function. got %" PRIzd + ", expected %" PRIzd, actual_args > expected_args ? "many" : "few", actual_args, expected_args); return NULL; @@ -1292,13 +1190,13 @@ static WasmResult check_get(Context* ctx, const WasmModule* module = wasm_get_module_by_var(ctx->script, &action->module_var); if (!module) { - print_error(ctx, CHECK_STYLE_FULL, &action->loc, "unknown module"); + print_error(ctx, &action->loc, "unknown module"); return WASM_ERROR; } WasmExport* export = wasm_get_export_by_name(module, &get->name); if (!export) { - print_error(ctx, CHECK_STYLE_NAME, &action->loc, + print_error(ctx, &action->loc, "unknown global export \"" PRIstringslice "\"", WASM_PRINTF_STRING_SLICE_ARG(get->name)); return WASM_ERROR; @@ -1389,8 +1287,8 @@ static void check_command(Context* ctx, const WasmCommand* command) { result.kind = ACTION_RESULT_KIND_TYPE; result.type = result.types->data[0]; } else { - print_error(ctx, CHECK_STYLE_FULL, &action->loc, - "expected 1 result, got %" PRIzd, result.types->size); + print_error(ctx, &action->loc, "expected 1 result, got %" PRIzd, + result.types->size); result.type = WASM_TYPE_ANY; } } @@ -1416,32 +1314,12 @@ static void wasm_destroy_context(Context* ctx) { wasm_destroy_type_vector(ctx->allocator, &ctx->type_stack); } -WasmResult wasm_check_names(WasmAllocator* allocator, - WasmAstLexer* lexer, - const struct WasmScript* script, - WasmSourceErrorHandler* error_handler) { +WasmResult wasm_check_script(WasmAllocator* allocator, + WasmAstLexer* lexer, + const struct WasmScript* script, + WasmSourceErrorHandler* error_handler) { Context ctx; WASM_ZERO_MEMORY(ctx); - ctx.check_style = CHECK_STYLE_NAME; - ctx.allocator = allocator; - ctx.lexer = lexer; - ctx.error_handler = error_handler; - ctx.result = WASM_OK; - ctx.script = script; - size_t i; - for (i = 0; i < script->commands.size; ++i) - check_command(&ctx, &script->commands.data[i]); - wasm_destroy_context(&ctx); - return ctx.result; -} - -WasmResult wasm_check_ast(WasmAllocator* allocator, - WasmAstLexer* lexer, - const struct WasmScript* script, - WasmSourceErrorHandler* error_handler) { - Context ctx; - WASM_ZERO_MEMORY(ctx); - ctx.check_style = CHECK_STYLE_FULL; ctx.allocator = allocator; ctx.lexer = lexer; ctx.error_handler = error_handler; @@ -1463,7 +1341,6 @@ WasmResult wasm_check_assert_invalid_and_malformed( WasmSourceErrorHandler* error_handler) { Context ctx; WASM_ZERO_MEMORY(ctx); - ctx.check_style = CHECK_STYLE_FULL; ctx.allocator = allocator; ctx.lexer = lexer; ctx.error_handler = error_handler; @@ -1480,7 +1357,6 @@ WasmResult wasm_check_assert_invalid_and_malformed( Context ctx2; WASM_ZERO_MEMORY(ctx2); - ctx2.check_style = CHECK_STYLE_FULL; ctx2.allocator = allocator; ctx2.lexer = lexer; ctx2.result = WASM_OK; @@ -1492,8 +1368,7 @@ WasmResult wasm_check_assert_invalid_and_malformed( wasm_destroy_context(&ctx2); if (WASM_SUCCEEDED(ctx2.result)) { print_error( - &ctx, CHECK_STYLE_FULL, - wasm_get_raw_module_location(&command->assert_invalid.module), + &ctx, wasm_get_raw_module_location(&command->assert_invalid.module), "expected module to be invalid"); } } else if (command->type == WASM_COMMAND_TYPE_ASSERT_MALFORMED) { @@ -1503,10 +1378,9 @@ 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, - wasm_get_raw_module_location(&command->assert_malformed.module), - "expected module to be malformed"); + print_error(&ctx, wasm_get_raw_module_location( + &command->assert_malformed.module), + "expected module to be malformed"); } } } diff --git a/src/ast-checker.h b/src/ast-checker.h index 6f0ec22e..c73386b2 100644 --- a/src/ast-checker.h +++ b/src/ast-checker.h @@ -25,24 +25,17 @@ struct WasmModule; struct WasmScript; WASM_EXTERN_C_BEGIN -/* Only check that names are valid; this is useful if you want to generate - * invalid binaries (so they can be validated by the consumer). */ -WasmResult wasm_check_names(struct WasmAllocator*, - WasmAstLexer*, - const struct WasmScript*, - WasmSourceErrorHandler*); - /* perform all checks on the AST; the module is valid if and only if this * function succeeds. */ -WasmResult wasm_check_ast(struct WasmAllocator*, - WasmAstLexer*, - const struct WasmScript*, - WasmSourceErrorHandler*); +WasmResult wasm_check_script(struct WasmAllocator*, + WasmAstLexer*, + const struct WasmScript*, + WasmSourceErrorHandler*); /* Run the assert_invalid and assert_malformed spec tests. A module is * "malformed" if it cannot be read from the binary format. A module is * "invalid" if either it is malformed, or if it does not pass the standard - * checks (as done by wasm_check_ast). This function succeeds if and only if + * checks (as done by wasm_check_script). This function succeeds if and only if * all assert_invalid and assert_malformed tests pass. */ WasmResult wasm_check_assert_invalid_and_malformed( struct WasmAllocator*, @@ -600,7 +600,7 @@ void wasm_destroy_script(WasmScript* script) { static WasmResult visit_expr(WasmExpr* expr, WasmExprVisitor* visitor); -static WasmResult visit_expr_list(WasmExpr* first, WasmExprVisitor* visitor) { +WasmResult wasm_visit_expr_list(WasmExpr* first, WasmExprVisitor* visitor) { WasmExpr* expr; for (expr = first; expr; expr = expr->next) CHECK_RESULT(visit_expr(expr, visitor)); @@ -615,7 +615,7 @@ static WasmResult visit_expr(WasmExpr* expr, WasmExprVisitor* visitor) { case WASM_EXPR_TYPE_BLOCK: CALLBACK(begin_block_expr); - CHECK_RESULT(visit_expr_list(expr->block.first, visitor)); + CHECK_RESULT(wasm_visit_expr_list(expr->block.first, visitor)); CALLBACK(end_block_expr); break; @@ -673,9 +673,9 @@ static WasmResult visit_expr(WasmExpr* expr, WasmExprVisitor* visitor) { case WASM_EXPR_TYPE_IF: CALLBACK(begin_if_expr); - CHECK_RESULT(visit_expr_list(expr->if_.true_.first, visitor)); + CHECK_RESULT(wasm_visit_expr_list(expr->if_.true_.first, visitor)); CALLBACK(after_if_true_expr); - CHECK_RESULT(visit_expr_list(expr->if_.false_, visitor)); + CHECK_RESULT(wasm_visit_expr_list(expr->if_.false_, visitor)); CALLBACK(end_if_expr); break; @@ -685,7 +685,7 @@ static WasmResult visit_expr(WasmExpr* expr, WasmExprVisitor* visitor) { case WASM_EXPR_TYPE_LOOP: CALLBACK(begin_loop_expr); - CHECK_RESULT(visit_expr_list(expr->loop.first, visitor)); + CHECK_RESULT(wasm_visit_expr_list(expr->loop.first, visitor)); CALLBACK(end_loop_expr); break; @@ -731,5 +731,5 @@ static WasmResult visit_expr(WasmExpr* expr, WasmExprVisitor* visitor) { /* TODO(binji): make the visitor non-recursive */ WasmResult wasm_visit_func(WasmFunc* func, WasmExprVisitor* visitor) { - return visit_expr_list(func->first_expr, visitor); + return wasm_visit_expr_list(func->first_expr, visitor); } @@ -462,6 +462,7 @@ void wasm_destroy_var(struct WasmAllocator*, WasmVar*); /* traversal functions */ WasmResult wasm_visit_func(WasmFunc* func, WasmExprVisitor*); +WasmResult wasm_visit_expr_list(WasmExpr* expr, WasmExprVisitor*); /* convenience functions for looking through the AST */ int wasm_get_index_from_var(const WasmBindingHash* bindings, diff --git a/src/binary-reader-interpreter.c b/src/binary-reader-interpreter.c index 32fcc6a3..bfbb9cc5 100644 --- a/src/binary-reader-interpreter.c +++ b/src/binary-reader-interpreter.c @@ -217,7 +217,7 @@ static uint32_t translate_global_index_to_env(Context* ctx, static uint32_t translate_defined_global_index_to_env(Context* ctx, uint32_t global_index) { - /* all globaltion imports are first, so skip over those */ + /* all global imports are first, so skip over those */ global_index += ctx->num_global_imports; assert(global_index < ctx->global_index_mapping.size); return ctx->global_index_mapping.data[global_index]; diff --git a/src/binary-writer.c b/src/binary-writer.c index 11a1c89b..b9d9f27f 100644 --- a/src/binary-writer.c +++ b/src/binary-writer.c @@ -40,19 +40,11 @@ static const char* s_section_name[] = {WASM_FOREACH_BINARY_SECTION(V)}; #undef V -typedef struct WasmLabelNode { - const WasmLabel* label; - int depth; - struct WasmLabelNode* next; -} WasmLabelNode; - typedef struct Context { WasmAllocator* allocator; WasmStream stream; WasmStream* log_stream; const WasmWriteBinaryOptions* options; - WasmLabelNode* top_label; - int max_depth; size_t last_section_offset; size_t last_section_leb_size_guess; @@ -267,46 +259,9 @@ static void end_section(Context* ctx) { ctx->last_section_leb_size_guess = 0; } -static WasmLabelNode* find_label_by_name(WasmLabelNode* top_label, - const WasmStringSlice* name) { - WasmLabelNode* node = top_label; - while (node) { - if (node->label && wasm_string_slices_are_equal(node->label, name)) - return node; - node = node->next; - } - return NULL; -} - -static WasmLabelNode* find_label_by_var(WasmLabelNode* top_label, - const WasmVar* var) { - if (var->type == WASM_VAR_TYPE_NAME) - return find_label_by_name(top_label, &var->name); - - WasmLabelNode* node = top_label; - int i = 0; - while (node && i != var->index) { - node = node->next; - i++; - } - return node; -} - -static void push_label(Context* ctx, - WasmLabelNode* node, - const WasmLabel* label) { - assert(label); - node->label = label; - node->next = ctx->top_label; - node->depth = ctx->max_depth; - ctx->top_label = node; - ctx->max_depth++; -} - -static void pop_label(Context* ctx, const WasmLabel* label) { - ctx->max_depth--; - if (ctx->top_label && ctx->top_label->label == label) - ctx->top_label = ctx->top_label->next; +static uint32_t get_label_var_depth(Context* ctx, const WasmVar* var) { + assert(var->type == WASM_VAR_TYPE_INDEX); + return var->index; } static void write_expr_list(Context* ctx, @@ -322,47 +277,34 @@ static void write_expr(Context* ctx, case WASM_EXPR_TYPE_BINARY: write_opcode(&ctx->stream, expr->binary.opcode); break; - case WASM_EXPR_TYPE_BLOCK: { - WasmLabelNode node; - push_label(ctx, &node, &expr->block.label); + case WASM_EXPR_TYPE_BLOCK: write_opcode(&ctx->stream, WASM_OPCODE_BLOCK); write_inline_signature_type(&ctx->stream, &expr->block.sig); write_expr_list(ctx, module, func, expr->block.first); write_opcode(&ctx->stream, WASM_OPCODE_END); - pop_label(ctx, &expr->block.label); break; - } - case WASM_EXPR_TYPE_BR: { - WasmLabelNode* node = find_label_by_var(ctx->top_label, &expr->br.var); - assert(node); + case WASM_EXPR_TYPE_BR: write_opcode(&ctx->stream, WASM_OPCODE_BR); - write_u32_leb128(&ctx->stream, ctx->max_depth - node->depth - 1, + write_u32_leb128(&ctx->stream, get_label_var_depth(ctx, &expr->br.var), "break depth"); break; - } - case WASM_EXPR_TYPE_BR_IF: { - WasmLabelNode* node = find_label_by_var(ctx->top_label, &expr->br_if.var); - assert(node); + case WASM_EXPR_TYPE_BR_IF: write_opcode(&ctx->stream, WASM_OPCODE_BR_IF); - write_u32_leb128(&ctx->stream, ctx->max_depth - node->depth - 1, + write_u32_leb128(&ctx->stream, get_label_var_depth(ctx, &expr->br_if.var), "break depth"); break; - } case WASM_EXPR_TYPE_BR_TABLE: { write_opcode(&ctx->stream, WASM_OPCODE_BR_TABLE); write_u32_leb128(&ctx->stream, expr->br_table.targets.size, "num targets"); size_t i; - WasmLabelNode* node; + uint32_t depth; for (i = 0; i < expr->br_table.targets.size; ++i) { - const WasmVar* var = &expr->br_table.targets.data[i]; - node = find_label_by_var(ctx->top_label, var); - write_u32_leb128(&ctx->stream, ctx->max_depth - node->depth - 1, - "break depth"); + depth = get_label_var_depth(ctx, &expr->br_table.targets.data[i]); + write_u32_leb128(&ctx->stream, depth, "break depth"); } - node = find_label_by_var(ctx->top_label, &expr->br_table.default_target); - write_u32_leb128(&ctx->stream, ctx->max_depth - node->depth - 1, - "break depth for default"); + depth = get_label_var_depth(ctx, &expr->br_table.default_target); + write_u32_leb128(&ctx->stream, depth, "break depth for default"); break; } case WASM_EXPR_TYPE_CALL: { @@ -435,20 +377,16 @@ static void write_expr(Context* ctx, write_opcode(&ctx->stream, WASM_OPCODE_GROW_MEMORY); write_u32_leb128(&ctx->stream, 0, "grow_memory reserved"); break; - case WASM_EXPR_TYPE_IF: { - WasmLabelNode node; + case WASM_EXPR_TYPE_IF: write_opcode(&ctx->stream, WASM_OPCODE_IF); write_inline_signature_type(&ctx->stream, &expr->if_.true_.sig); - push_label(ctx, &node, &expr->if_.true_.label); write_expr_list(ctx, module, func, expr->if_.true_.first); if (expr->if_.false_) { write_opcode(&ctx->stream, WASM_OPCODE_ELSE); write_expr_list(ctx, module, func, expr->if_.false_); } - pop_label(ctx, &expr->if_.true_.label); write_opcode(&ctx->stream, WASM_OPCODE_END); break; - } case WASM_EXPR_TYPE_LOAD: { write_opcode(&ctx->stream, expr->load.opcode); uint32_t align = @@ -458,16 +396,12 @@ static void write_expr(Context* ctx, "load offset"); break; } - case WASM_EXPR_TYPE_LOOP: { - WasmLabelNode node; - push_label(ctx, &node, &expr->loop.label); + case WASM_EXPR_TYPE_LOOP: write_opcode(&ctx->stream, WASM_OPCODE_LOOP); write_inline_signature_type(&ctx->stream, &expr->loop.sig); write_expr_list(ctx, module, func, expr->loop.first); write_opcode(&ctx->stream, WASM_OPCODE_END); - pop_label(ctx, &expr->loop.label); break; - } case WASM_EXPR_TYPE_NOP: write_opcode(&ctx->stream, WASM_OPCODE_NOP); break; @@ -578,13 +512,9 @@ static void write_func_locals(Context* ctx, static void write_func(Context* ctx, const WasmModule* module, const WasmFunc* func) { - WasmLabelNode node; - WasmLabel label = wasm_empty_string_slice(); write_func_locals(ctx, module, func, &func->local_types); - push_label(ctx, &node, &label); write_expr_list(ctx, module, func, func->first_expr); write_opcode(&ctx->stream, WASM_OPCODE_END); - pop_label(ctx, &label); } static void write_table(Context* ctx, const WasmTable* table) { diff --git a/src/resolve-names.c b/src/resolve-names.c new file mode 100644 index 00000000..ea73ed2a --- /dev/null +++ b/src/resolve-names.c @@ -0,0 +1,444 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "resolve-names.h" + +#include <assert.h> +#include <stdio.h> + +#include "allocator.h" +#include "ast.h" +#include "ast-parser-lexer-shared.h" + +typedef WasmLabel* LabelPtr; +WASM_DEFINE_VECTOR(label_ptr, LabelPtr); + +typedef struct Context { + WasmAllocator* allocator; + WasmSourceErrorHandler* error_handler; + WasmAstLexer* lexer; + WasmScript* script; + WasmModule* current_module; + WasmFunc* current_func; + WasmExprVisitor visitor; + LabelPtrVector labels; + WasmResult result; +} Context; + +static void WASM_PRINTF_FORMAT(3, 4) + print_error(Context* ctx, const WasmLocation* loc, const char* fmt, ...) { + ctx->result = WASM_ERROR; + va_list args; + va_start(args, fmt); + wasm_ast_format_error(ctx->error_handler, loc, ctx->lexer, fmt, args); + va_end(args); +} + +static void push_label(Context* ctx, WasmLabel* label) { + wasm_append_label_ptr_value(ctx->allocator, &ctx->labels, &label); +} + +static void pop_label(Context* ctx) { + assert(ctx->labels.size > 0); + ctx->labels.size--; +} + +static void check_duplicate_bindings(Context* ctx, + const WasmBindingHash* bindings, + const char* desc) { + size_t i; + for (i = 0; i < bindings->entries.capacity; ++i) { + WasmBindingHashEntry* entry = &bindings->entries.data[i]; + if (wasm_hash_entry_is_free(entry)) + continue; + + /* only follow the chain if this is the first entry in the chain */ + if (entry->prev != NULL) + continue; + + WasmBindingHashEntry* a = entry; + for (; a; a = a->next) { + WasmBindingHashEntry* b = a->next; + for (; b; b = b->next) { + if (wasm_string_slices_are_equal(&a->binding.name, &b->binding.name)) { + /* choose the location that is later in the file */ + WasmLocation* a_loc = &a->binding.loc; + WasmLocation* b_loc = &b->binding.loc; + WasmLocation* loc = a_loc->line > b_loc->line ? a_loc : b_loc; + print_error(ctx, loc, "redefinition of %s \"" PRIstringslice "\"", + desc, WASM_PRINTF_STRING_SLICE_ARG(a->binding.name)); + } + } + } + } +} + +static void resolve_label_var(Context* ctx, WasmVar* var) { + if (var->type == WASM_VAR_TYPE_NAME) { + int i; + for (i = ctx->labels.size - 1; i >= 0; --i) { + WasmLabel* label = ctx->labels.data[i]; + if (wasm_string_slices_are_equal(label, &var->name)) { + wasm_destroy_string_slice(ctx->allocator, &var->name); + var->type = WASM_VAR_TYPE_INDEX; + var->index = ctx->labels.size - i - 1; + return; + } + } + print_error(ctx, &var->loc, + "undefined label variable \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(var->name)); + } +} + +static void resolve_var(Context* ctx, + const WasmBindingHash* bindings, + WasmVar* var, + const char* desc) { + if (var->type == WASM_VAR_TYPE_NAME) { + int index = wasm_get_index_from_var(bindings, var); + if (index == -1) { + print_error(ctx, &var->loc, + "undefined %s variable \"" PRIstringslice "\"", desc, + WASM_PRINTF_STRING_SLICE_ARG(var->name)); + return; + } + + wasm_destroy_string_slice(ctx->allocator, &var->name); + var->index = index; + var->type = WASM_VAR_TYPE_INDEX; + } +} + +static void resolve_func_var(Context* ctx, WasmVar* var) { + resolve_var(ctx, &ctx->current_module->func_bindings, var, "function"); +} + +static void resolve_global_var(Context* ctx, WasmVar* var) { + resolve_var(ctx, &ctx->current_module->global_bindings, var, "global"); +} + +static void resolve_func_type_var(Context* ctx, WasmVar* var) { + resolve_var(ctx, &ctx->current_module->func_type_bindings, var, + "function type"); +} + +static void resolve_table_var(Context* ctx, WasmVar* var) { + resolve_var(ctx, &ctx->current_module->table_bindings, var, "table"); +} + +static void resolve_memory_var(Context* ctx, WasmVar* var) { + resolve_var(ctx, &ctx->current_module->memory_bindings, var, "memory"); +} + +static void resolve_local_var(Context* ctx, WasmVar* var) { + if (var->type == WASM_VAR_TYPE_NAME) { + int index = wasm_get_local_index_by_var(ctx->current_func, var); + if (index == -1) { + print_error(ctx, &var->loc, + "undefined local variable \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(var->name)); + return; + } + + wasm_destroy_string_slice(ctx->allocator, &var->name); + var->index = index; + var->type = WASM_VAR_TYPE_INDEX; + } +} + +static WasmResult begin_block_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + push_label(ctx, &expr->block.label); + return WASM_OK; +} + +static WasmResult end_block_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + pop_label(ctx); + return WASM_OK; +} + +static WasmResult begin_loop_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + push_label(ctx, &expr->loop.label); + return WASM_OK; +} + +static WasmResult end_loop_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + pop_label(ctx); + return WASM_OK; +} + +static WasmResult on_br_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_label_var(ctx, &expr->br.var); + return WASM_OK; +} + +static WasmResult on_br_if_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_label_var(ctx, &expr->br_if.var); + return WASM_OK; +} + +static WasmResult on_br_table_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + size_t i; + WasmVarVector* targets = &expr->br_table.targets; + for (i = 0; i < targets->size; ++i) { + WasmVar* target = &targets->data[i]; + resolve_label_var(ctx, target); + } + + resolve_label_var(ctx, &expr->br_table.default_target); + return WASM_OK; +} + +static WasmResult on_call_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_func_var(ctx, &expr->call.var); + return WASM_OK; +} + +static WasmResult on_call_indirect_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_func_type_var(ctx, &expr->call_indirect.var); + return WASM_OK; +} + +static WasmResult on_get_global_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_global_var(ctx, &expr->get_global.var); + return WASM_OK; +} + +static WasmResult on_get_local_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_local_var(ctx, &expr->get_local.var); + return WASM_OK; +} + +static WasmResult begin_if_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + push_label(ctx, &expr->if_.true_.label); + return WASM_OK; +} + +static WasmResult end_if_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + pop_label(ctx); + return WASM_OK; +} + +static WasmResult on_set_global_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_global_var(ctx, &expr->set_global.var); + return WASM_OK; +} + +static WasmResult on_set_local_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_local_var(ctx, &expr->set_local.var); + return WASM_OK; +} + +static WasmResult on_tee_local_expr(WasmExpr* expr, void* user_data) { + Context* ctx = user_data; + resolve_local_var(ctx, &expr->tee_local.var); + return WASM_OK; +} + +static void visit_func(Context* ctx, WasmFunc* func) { + ctx->current_func = func; + if (wasm_decl_has_func_type(&func->decl)) + resolve_func_type_var(ctx, &func->decl.type_var); + + check_duplicate_bindings(ctx, &func->param_bindings, "parameter"); + check_duplicate_bindings(ctx, &func->local_bindings, "local"); + + wasm_visit_func(func, &ctx->visitor); + ctx->current_func = NULL; +} + +static void visit_export(Context* ctx, WasmExport* export) { + switch (export->kind) { + case WASM_EXTERNAL_KIND_FUNC: + resolve_func_var(ctx, &export->var); + break; + + case WASM_EXTERNAL_KIND_TABLE: + resolve_table_var(ctx, &export->var); + break; + + case WASM_EXTERNAL_KIND_MEMORY: + resolve_memory_var(ctx, &export->var); + break; + + case WASM_EXTERNAL_KIND_GLOBAL: + resolve_global_var(ctx, &export->var); + break; + + case WASM_NUM_EXTERNAL_KINDS: + assert(0); + break; + } +} + +static void visit_global(Context* ctx, WasmGlobal* global) { + wasm_visit_expr_list(global->init_expr, &ctx->visitor); +} + +static void visit_elem_segment(Context* ctx, WasmElemSegment* segment) { + size_t i; + resolve_table_var(ctx, &segment->table_var); + wasm_visit_expr_list(segment->offset, &ctx->visitor); + for (i = 0; i < segment->vars.size; ++i) + resolve_func_var(ctx, &segment->vars.data[i]); +} + +static void visit_data_segment(Context* ctx, WasmDataSegment* segment) { + resolve_memory_var(ctx, &segment->memory_var); + wasm_visit_expr_list(segment->offset, &ctx->visitor); +} + +static void visit_module(Context* ctx, WasmModule* module) { + ctx->current_module = module; + check_duplicate_bindings(ctx, &module->func_bindings, "function"); + check_duplicate_bindings(ctx, &module->global_bindings, "global"); + check_duplicate_bindings(ctx, &module->export_bindings, "export"); + check_duplicate_bindings(ctx, &module->func_type_bindings, "function type"); + check_duplicate_bindings(ctx, &module->table_bindings, "table"); + check_duplicate_bindings(ctx, &module->memory_bindings, "memory"); + + size_t i; + for (i = 0; i < module->funcs.size; ++i) + visit_func(ctx, module->funcs.data[i]); + for (i = 0; i < module->exports.size; ++i) + visit_export(ctx, module->exports.data[i]); + for (i = 0; i < module->globals.size; ++i) + visit_global(ctx, module->globals.data[i]); + for (i = 0; i < module->elem_segments.size; ++i) + visit_elem_segment(ctx, module->elem_segments.data[i]); + for (i = 0; i < module->data_segments.size; ++i) + visit_data_segment(ctx, module->data_segments.data[i]); + if (module->start) + resolve_func_var(ctx, module->start); + ctx->current_module = NULL; +} + +static void visit_raw_module(Context* ctx, WasmRawModule* raw_module) { + if (raw_module->type == WASM_RAW_MODULE_TYPE_TEXT) + visit_module(ctx, raw_module->text); +} + +static void visit_command(Context* ctx, WasmCommand* command) { + switch (command->type) { + case WASM_COMMAND_TYPE_MODULE: + visit_module(ctx, &command->module); + break; + + case WASM_COMMAND_TYPE_ACTION: + case WASM_COMMAND_TYPE_ASSERT_RETURN: + case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN: + case WASM_COMMAND_TYPE_ASSERT_TRAP: + case WASM_COMMAND_TYPE_REGISTER: + /* Don't resolve a module_var, since it doesn't really behave like other + * vars. You can't reference a module by index. */ + break; + + case WASM_COMMAND_TYPE_ASSERT_MALFORMED: + /* Malformed modules should not be text; the whole point of this + * assertion is to test for malformed binary modules. */ + break; + + case WASM_COMMAND_TYPE_ASSERT_INVALID: + /* The module may be invalid because the names cannot be resolved (or are + * duplicates). In either case, don't resolve. */ + break; + + case WASM_COMMAND_TYPE_ASSERT_UNLINKABLE: + visit_raw_module(ctx, &command->assert_unlinkable.module); + break; + + case WASM_COMMAND_TYPE_ASSERT_UNINSTANTIABLE: + visit_raw_module(ctx, &command->assert_uninstantiable.module); + break; + + case WASM_NUM_COMMAND_TYPES: + assert(0); + break; + } +} + +static void visit_script(Context* ctx, WasmScript* script) { + size_t i; + for (i = 0; i < script->commands.size; ++i) + visit_command(ctx, &script->commands.data[i]); +} + +static void init_context(Context* ctx, + WasmAllocator* allocator, + WasmAstLexer* lexer, + WasmScript* script, + WasmSourceErrorHandler* error_handler) { + WASM_ZERO_MEMORY(*ctx); + ctx->allocator = allocator; + ctx->lexer = lexer; + ctx->error_handler = error_handler; + ctx->result = WASM_OK; + ctx->script = script; + ctx->visitor.user_data = ctx; + ctx->visitor.begin_block_expr = begin_block_expr; + ctx->visitor.end_block_expr = end_block_expr; + ctx->visitor.begin_loop_expr = begin_loop_expr; + ctx->visitor.end_loop_expr = end_loop_expr; + ctx->visitor.on_br_expr = on_br_expr; + ctx->visitor.on_br_if_expr = on_br_if_expr; + ctx->visitor.on_br_table_expr = on_br_table_expr; + ctx->visitor.on_call_expr = on_call_expr; + ctx->visitor.on_call_indirect_expr = on_call_indirect_expr; + ctx->visitor.on_get_global_expr = on_get_global_expr; + ctx->visitor.on_get_local_expr = on_get_local_expr; + ctx->visitor.begin_if_expr = begin_if_expr; + ctx->visitor.end_if_expr = end_if_expr; + ctx->visitor.on_set_global_expr = on_set_global_expr; + ctx->visitor.on_set_local_expr = on_set_local_expr; + ctx->visitor.on_tee_local_expr = on_tee_local_expr; +} + +WasmResult wasm_resolve_names_module(WasmAllocator* allocator, + WasmAstLexer* lexer, + WasmModule* module, + WasmSourceErrorHandler* error_handler) { + Context ctx; + init_context(&ctx, allocator, lexer, NULL, error_handler); + visit_module(&ctx, module); + wasm_destroy_label_ptr_vector(allocator, &ctx.labels); + return ctx.result; +} + +WasmResult wasm_resolve_names_script(WasmAllocator* allocator, + WasmAstLexer* lexer, + WasmScript* script, + WasmSourceErrorHandler* error_handler) { + Context ctx; + init_context(&ctx, allocator, lexer, script, error_handler); + visit_script(&ctx, script); + wasm_destroy_label_ptr_vector(allocator, &ctx.labels); + return ctx.result; +} diff --git a/src/resolve-names.h b/src/resolve-names.h new file mode 100644 index 00000000..f248792d --- /dev/null +++ b/src/resolve-names.h @@ -0,0 +1,40 @@ +/* + * Copyright 2016 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WASM_RESOLVE_NAMES_H_ +#define WASM_RESOLVE_NAMES_H_ + +#include "common.h" + +struct WasmAllocator; +struct WasmAstLexer; +struct WasmModule; +struct WasmScript; +struct WasmSourceErrorHandler; + +WASM_EXTERN_C_BEGIN +WasmResult wasm_resolve_names_module(struct WasmAllocator*, + struct WasmAstLexer*, + struct WasmModule*, + struct WasmSourceErrorHandler*); + +WasmResult wasm_resolve_names_script(struct WasmAllocator*, + struct WasmAstLexer*, + struct WasmScript*, + struct WasmSourceErrorHandler*); +WASM_EXTERN_C_END + +#endif /* WASM_RESOLVE_NAMES_H_ */ diff --git a/src/tools/wast2wasm.c b/src/tools/wast2wasm.c index 064806b7..6017d77f 100644 --- a/src/tools/wast2wasm.c +++ b/src/tools/wast2wasm.c @@ -28,6 +28,7 @@ #include "binary-writer-spec.h" #include "common.h" #include "option-parser.h" +#include "resolve-names.h" #include "stack-allocator.h" #include "stream.h" #include "writer.h" @@ -238,13 +239,11 @@ int main(int argc, char** argv) { WasmResult result = wasm_parse_ast(lexer, &script, &s_error_handler); if (WASM_SUCCEEDED(result)) { - if (s_check) { - /* full validation of the module */ - result = wasm_check_ast(allocator, lexer, &script, &s_error_handler); - } else { - /* minimal checks necessary to ensure we can generate a binary */ - result = wasm_check_names(allocator, lexer, &script, &s_error_handler); - } + result = + wasm_resolve_names_script(allocator, lexer, &script, &s_error_handler); + + if (WASM_SUCCEEDED(result) && s_check) + result = wasm_check_script(allocator, lexer, &script, &s_error_handler); if (WASM_SUCCEEDED(result) && s_check_assert_invalid_and_malformed) { WasmDefaultErrorHandlerInfo assert_invalid_info; |