diff options
-rw-r--r-- | src/ast.c | 25 | ||||
-rw-r--r-- | src/ast.h | 10 | ||||
-rw-r--r-- | src/binary-writer-spec.c | 23 | ||||
-rw-r--r-- | src/resolve-names.c | 87 | ||||
-rw-r--r-- | src/validator.c | 24 | ||||
-rw-r--r-- | test/parse/assert/nocheck-assertinvalid-resolve-names.txt | 9 |
6 files changed, 137 insertions, 41 deletions
@@ -177,6 +177,30 @@ void wasm_make_type_binding_reverse_mapping( } } +void wasm_find_duplicate_bindings(const WasmBindingHash* bindings, + WasmDuplicateBindingCallback callback, + void* user_data) { + 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)) + callback(a, b, user_data); + } + } + } +} + WasmModuleField* wasm_append_module_field(struct WasmAllocator* allocator, WasmModule* module) { WasmModuleField* result = @@ -539,6 +563,7 @@ void wasm_destroy_command(WasmAllocator* allocator, WasmCommand* command) { wasm_destroy_string_slice(allocator, &command->assert_malformed.text); break; case WASM_COMMAND_TYPE_ASSERT_INVALID: + case WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY: wasm_destroy_raw_module(allocator, &command->assert_invalid.module); wasm_destroy_string_slice(allocator, &command->assert_invalid.text); break; @@ -330,6 +330,9 @@ typedef enum WasmCommandType { WASM_COMMAND_TYPE_REGISTER, WASM_COMMAND_TYPE_ASSERT_MALFORMED, WASM_COMMAND_TYPE_ASSERT_INVALID, + /* This is a module that is invalid, but cannot be written as a binary module + * (e.g. it has unresolvable names.) */ + WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY, WASM_COMMAND_TYPE_ASSERT_UNLINKABLE, WASM_COMMAND_TYPE_ASSERT_UNINSTANTIABLE, WASM_COMMAND_TYPE_ASSERT_RETURN, @@ -505,6 +508,13 @@ void wasm_make_type_binding_reverse_mapping( const WasmBindingHash*, WasmStringSliceVector* out_reverse_mapping); +typedef void (*WasmDuplicateBindingCallback)(WasmBindingHashEntry* a, + WasmBindingHashEntry* b, + void* user_data); +void wasm_find_duplicate_bindings(const WasmBindingHash*, + WasmDuplicateBindingCallback callback, + void* user_data); + static WASM_INLINE WasmBool wasm_decl_has_func_type(const WasmFuncDeclaration* decl) { return (WasmBool)((decl->flags & WASM_FUNC_DECLARATION_FLAG_HAS_FUNC_TYPE) != diff --git a/src/binary-writer-spec.c b/src/binary-writer-spec.c index 909b709b..7bd0f29d 100644 --- a/src/binary-writer-spec.c +++ b/src/binary-writer-spec.c @@ -139,6 +139,7 @@ static void write_command_type(Context* ctx, const WasmCommand* command) { "register", "assert_malformed", "assert_invalid", + NULL, /* ASSERT_INVALID_NON_BINARY, this command will never be written */ "assert_unlinkable", "assert_uninstantiable", "assert_return", @@ -150,6 +151,7 @@ static void write_command_type(Context* ctx, const WasmCommand* command) { WASM_NUM_COMMAND_TYPES); write_key(ctx, "type"); + assert(s_command_names[command->type] != NULL); write_string(ctx, s_command_names[command->type]); } @@ -292,16 +294,13 @@ static void write_action_result_type(Context* ctx, static void write_module(Context* ctx, char* filename, const WasmModule* module) { - if (!ctx->write_modules) - return; - WasmMemoryWriter writer; WasmResult result = wasm_init_mem_writer(ctx->allocator, &writer); if (WASM_SUCCEEDED(result)) { WasmWriteBinaryOptions options = ctx->spec_options->write_binary_options; result = wasm_write_binary_module(ctx->allocator, &writer.base, module, &options); - if (WASM_SUCCEEDED(result)) + if (WASM_SUCCEEDED(result) && ctx->write_modules) result = wasm_write_output_buffer_to_file(&writer.buf, filename); wasm_close_mem_writer(&writer); } @@ -312,12 +311,9 @@ static void write_module(Context* ctx, static void write_raw_module(Context* ctx, char* filename, const WasmRawModule* raw_module) { - if (!ctx->write_modules) - return; - if (raw_module->type == WASM_RAW_MODULE_TYPE_TEXT) { write_module(ctx, filename, raw_module->text); - } else { + } else if (ctx->write_modules) { WasmFileStream stream; WasmResult result = wasm_init_file_writer(&stream.writer, filename); if (WASM_SUCCEEDED(result)) { @@ -354,6 +350,13 @@ static void write_commands(Context* ctx, WasmScript* script) { for (i = 0; i < script->commands.size; ++i) { WasmCommand* command = &script->commands.data[i]; + if (command->type == WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY) + continue; + + if (i != 0) + write_separator(ctx); + wasm_writef(&ctx->json_stream, "\n"); + wasm_writef(&ctx->json_stream, " {"); write_command_type(ctx, command); write_separator(ctx); @@ -459,15 +462,13 @@ static void write_commands(Context* ctx, WasmScript* script) { write_action(ctx, &command->assert_trap.action); break; + case WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY: 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"); } wasm_writef(&ctx->json_stream, "]}\n"); } diff --git a/src/resolve-names.c b/src/resolve-names.c index 4b28964c..88c1a0f2 100644 --- a/src/resolve-names.c +++ b/src/resolve-names.c @@ -56,34 +56,30 @@ static void pop_label(Context* ctx) { ctx->labels.size--; } +typedef struct FindDuplicateBindingContext { + Context* ctx; + const char* desc; +} FindDuplicateBindingContext; + +static void on_duplicate_binding(WasmBindingHashEntry* a, + WasmBindingHashEntry* b, + void* user_data) { + FindDuplicateBindingContext* fdbc = user_data; + /* 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(fdbc->ctx, loc, "redefinition of %s \"" PRIstringslice "\"", + fdbc->desc, WASM_PRINTF_STRING_SLICE_ARG(a->binding.name)); +} + 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)); - } - } - } - } + FindDuplicateBindingContext fdbc; + fdbc.ctx = ctx; + fdbc.desc = desc; + wasm_find_duplicate_bindings(bindings, on_duplicate_binding, &fdbc); } static void resolve_label_var(Context* ctx, WasmVar* var) { @@ -320,7 +316,6 @@ 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"); @@ -346,6 +341,13 @@ static void visit_raw_module(Context* ctx, WasmRawModule* raw_module) { visit_module(ctx, raw_module->text); } +void dummy_source_error_callback(const WasmLocation* loc, + const char* error, + const char* source_line, + size_t source_line_length, + size_t source_line_column_offset, + void* user_data) {} + static void visit_command(Context* ctx, WasmCommand* command) { switch (command->type) { case WASM_COMMAND_TYPE_MODULE: @@ -367,9 +369,36 @@ static void visit_command(Context* ctx, WasmCommand* command) { * 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. */ + case WASM_COMMAND_TYPE_ASSERT_INVALID: { + /* The module may be invalid because the names cannot be resolved; we + * don't want to print errors or fail if that's the case, but we still + * should try to resolve names when possible. */ + WasmSourceErrorHandler new_error_handler; + new_error_handler.on_error = dummy_source_error_callback; + new_error_handler.source_line_max_length = + ctx->error_handler->source_line_max_length; + + Context new_ctx; + WASM_ZERO_MEMORY(new_ctx); + new_ctx.allocator = ctx->allocator; + new_ctx.error_handler = &new_error_handler; + new_ctx.lexer = ctx->lexer; + new_ctx.visitor = ctx->visitor; + new_ctx.visitor.user_data = &new_ctx; + new_ctx.result = WASM_OK; + + visit_raw_module(&new_ctx, &command->assert_invalid.module); + wasm_destroy_label_ptr_vector(new_ctx.allocator, &new_ctx.labels); + if (WASM_FAILED(new_ctx.result)) { + command->type = WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY; + } + break; + } + + case WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY: + /* The only reason a module would be "non-binary" is if the names cannot + * be resolved. So we assume name resolution has already been tried and + * failed, so skip it. */ break; case WASM_COMMAND_TYPE_ASSERT_UNLINKABLE: diff --git a/src/validator.c b/src/validator.c index d56f09dc..52ca4317 100644 --- a/src/validator.c +++ b/src/validator.c @@ -1022,6 +1022,24 @@ static void check_export(Context* ctx, const WasmExport* export_) { } } +static void on_duplicate_binding(WasmBindingHashEntry* a, + WasmBindingHashEntry* b, + void* user_data) { + Context* ctx = user_data; + /* 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 export \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(a->binding.name)); +} + +static void check_duplicate_export_bindings(Context* ctx, + const WasmModule* module) { + wasm_find_duplicate_bindings(&module->export_bindings, on_duplicate_binding, + ctx); +} + static void check_module(Context* ctx, const WasmModule* module) { WasmBool seen_start = WASM_FALSE; @@ -1097,6 +1115,7 @@ static void check_module(Context* ctx, const WasmModule* module) { check_elem_segments(ctx, module); check_data_segments(ctx, module); + check_duplicate_export_bindings(ctx, module); } typedef struct BinaryErrorCallbackData { @@ -1274,6 +1293,7 @@ static void check_command(Context* ctx, const WasmCommand* command) { case WASM_COMMAND_TYPE_REGISTER: case WASM_COMMAND_TYPE_ASSERT_MALFORMED: case WASM_COMMAND_TYPE_ASSERT_INVALID: + case WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY: case WASM_COMMAND_TYPE_ASSERT_UNLINKABLE: case WASM_COMMAND_TYPE_ASSERT_UNINSTANTIABLE: /* ignore */ @@ -1378,6 +1398,7 @@ WasmResult wasm_validate_assert_invalid_and_malformed( for (i = 0; i < script->commands.size; ++i) { WasmCommand* command = &script->commands.data[i]; if (command->type != WASM_COMMAND_TYPE_ASSERT_INVALID && + command->type != WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY && command->type != WASM_COMMAND_TYPE_ASSERT_MALFORMED) { continue; } @@ -1389,7 +1410,8 @@ WasmResult wasm_validate_assert_invalid_and_malformed( ctx2.result = WASM_OK; ctx2.script = script; - if (command->type == WASM_COMMAND_TYPE_ASSERT_INVALID) { + if (command->type == WASM_COMMAND_TYPE_ASSERT_INVALID || + command->type == WASM_COMMAND_TYPE_ASSERT_INVALID_NON_BINARY) { ctx2.error_handler = assert_invalid_error_handler; check_raw_module(&ctx2, &command->assert_invalid.module); wasm_destroy_context(&ctx2); diff --git a/test/parse/assert/nocheck-assertinvalid-resolve-names.txt b/test/parse/assert/nocheck-assertinvalid-resolve-names.txt new file mode 100644 index 00000000..0414d7d2 --- /dev/null +++ b/test/parse/assert/nocheck-assertinvalid-resolve-names.txt @@ -0,0 +1,9 @@ +;;; FLAGS: --spec --no-check-assert-invalid + +(assert_invalid + (module + (func + block $l + br $g + end)) + "foo") |