diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | src/tools/wasm-interp.c | 236 | ||||
-rw-r--r-- | src/wasm-ast.c | 171 | ||||
-rw-r--r-- | src/wasm-ast.h | 25 | ||||
-rw-r--r-- | src/wasm-binary-reader-interpreter.c | 559 | ||||
-rw-r--r-- | src/wasm-binary-reader-interpreter.h | 4 | ||||
-rw-r--r-- | src/wasm-binary-reader.c | 2 | ||||
-rw-r--r-- | src/wasm-binding-hash.c | 175 | ||||
-rw-r--r-- | src/wasm-binding-hash.h | 54 | ||||
-rw-r--r-- | src/wasm-common.c | 7 | ||||
-rw-r--r-- | src/wasm-common.h | 17 | ||||
-rw-r--r-- | src/wasm-interpreter.c | 358 | ||||
-rw-r--r-- | src/wasm-interpreter.h | 175 | ||||
-rw-r--r-- | src/wasm-writer.c | 23 | ||||
-rw-r--r-- | src/wasm-writer.h | 6 | ||||
-rw-r--r-- | test/binary/bad-data-size.txt | 1 | ||||
-rw-r--r-- | test/interp/callimport-zero-args.txt | 4 | ||||
-rw-r--r-- | test/interp/import.txt | 8 |
18 files changed, 1146 insertions, 682 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index f416911a..a160ec6e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,9 +205,10 @@ add_library(libwasm STATIC src/wasm-binary-writer.c src/wasm-binary-writer-spec.c src/wasm-binary-reader-ast.c + src/wasm-binding-hash.c src/wasm-ast-writer.c - src/wasm-binary-reader-interpreter.c src/wasm-interpreter.c + src/wasm-binary-reader-interpreter.c src/wasm-apply-names.c src/wasm-generate-names.c diff --git a/src/tools/wasm-interp.c b/src/tools/wasm-interp.c index 85abc7e4..5e99a345 100644 --- a/src/tools/wasm-interp.c +++ b/src/tools/wasm-interp.c @@ -231,41 +231,7 @@ static void print_typed_value(WasmInterpreterTypedValue* tv) { } } -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 WasmInterpreterFuncSignature* sig, - const WasmStringSlice* module_name, - const WasmStringSlice* field_name, - uint32_t num_args, - WasmInterpreterTypedValue* args, - uint32_t num_results, - WasmInterpreterTypedValue* out_results, - void* user_data) { - memset(out_results, 0, sizeof(WasmInterpreterTypedValue) * num_results); - uint32_t i; - 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(*module_name), - WASM_PRINTF_STRING_SLICE_ARG(*field_name)); - print_typed_values(args, num_args); - printf(") => ("); - print_typed_values(out_results, num_results); - printf(")\n"); - return WASM_OK; -} - -static WasmInterpreterResult run_defined_function(WasmInterpreterModule* module, - WasmInterpreterThread* thread, +static WasmInterpreterResult run_defined_function(WasmInterpreterThread* thread, uint32_t offset) { thread->pc = offset; WasmInterpreterResult iresult = WASM_INTERPRETER_OK; @@ -273,9 +239,8 @@ static WasmInterpreterResult run_defined_function(WasmInterpreterModule* module, uint32_t* call_stack_return_top = thread->call_stack_top; while (iresult == WASM_INTERPRETER_OK) { if (s_trace) - wasm_trace_pc(module, thread, s_stdout_stream); - iresult = - wasm_run_interpreter(module, thread, quantum, call_stack_return_top); + 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) @@ -286,27 +251,23 @@ static WasmInterpreterResult run_defined_function(WasmInterpreterModule* module, return WASM_INTERPRETER_OK; } -static WasmInterpreterResult run_function(WasmInterpreterModule* module, - WasmInterpreterThread* thread, +static WasmInterpreterResult run_function(WasmInterpreterThread* thread, uint32_t func_index) { - assert(func_index < module->funcs.size); - WasmInterpreterFunc* func = &module->funcs.data[func_index]; - if (func->is_host) { - WasmInterpreterImport* import = &module->imports.data[func->import_index]; - return wasm_call_host(module, thread, import); - } else { - return run_defined_function(module, thread, func->offset); - } + 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 WasmResult run_start_function(WasmInterpreterModule* module, - WasmInterpreterThread* thread) { +static WasmResult run_start_function(WasmInterpreterThread* thread) { WasmResult result = WASM_OK; - if (module->start_func_index != WASM_INVALID_FUNC_INDEX) { + if (thread->module->defined.start_func_index != WASM_INVALID_INDEX) { if (s_trace) printf(">>> running start function:\n"); WasmInterpreterResult iresult = - run_function(module, thread, module->start_func_index); + run_function(thread, thread->module->defined.start_func_index); if (iresult != WASM_INTERPRETER_OK) { /* trap */ fprintf(stderr, "error: %s\n", s_trap_strings[iresult]); @@ -317,22 +278,22 @@ static WasmResult run_start_function(WasmInterpreterModule* module, } static WasmInterpreterFuncSignature* get_export_signature( - WasmInterpreterModule* module, + WasmInterpreterEnvironment* env, WasmInterpreterExport* export) { + assert(export->kind == WASM_EXTERNAL_KIND_FUNC); uint32_t func_index = export->index; - uint32_t sig_index = module->funcs.data[func_index].sig_index; - assert(sig_index < module->sigs.size); - return &module->sigs.data[sig_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 WasmInterpreterResult run_export( WasmAllocator* allocator, - WasmInterpreterModule* module, WasmInterpreterThread* thread, WasmInterpreterExport* export, - WasmInterpreterFuncSignature* sig, 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); @@ -340,7 +301,7 @@ static WasmInterpreterResult run_export( thread->value_stack_top = &thread->value_stack.data[num_args]; memset(thread->value_stack.data, 0, num_args * sizeof(WasmInterpreterValue)); - WasmInterpreterResult result = run_function(module, thread, export->index); + WasmInterpreterResult result = run_function(thread, export->index); if (result == WASM_INTERPRETER_OK) { size_t expected_results = sig->result_types.size; @@ -374,7 +335,6 @@ static WasmInterpreterResult run_export( static WasmInterpreterResult run_export_wrapper( WasmAllocator* allocator, - WasmInterpreterModule* module, WasmInterpreterThread* thread, WasmInterpreterExport* export, WasmInterpreterTypedValueVector* out_results, @@ -384,11 +344,12 @@ static WasmInterpreterResult run_export_wrapper( WASM_PRINTF_STRING_SLICE_ARG(export->name)); } - WasmInterpreterFuncSignature* sig = get_export_signature(module, export); WasmInterpreterResult result = - run_export(allocator, module, thread, export, sig, out_results); + 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) { @@ -416,19 +377,18 @@ static WasmInterpreterResult run_export_wrapper( static WasmResult run_export_by_name( WasmAllocator* allocator, - WasmInterpreterModule* module, WasmInterpreterThread* thread, WasmStringSlice* name, WasmInterpreterResult* out_iresult, WasmInterpreterTypedValueVector* out_results, RunVerbosity verbose) { WasmInterpreterExport* export = - wasm_get_interpreter_export_by_name(module, name); + wasm_get_interpreter_export_by_name(thread->module, name); if (!export) return WASM_ERROR; - *out_iresult = run_export_wrapper(allocator, module, thread, export, - out_results, verbose); + *out_iresult = + run_export_wrapper(allocator, thread, export, out_results, verbose); return WASM_OK; } @@ -441,86 +401,148 @@ static void run_all_exports(WasmAllocator* allocator, uint32_t i; for (i = 0; i < module->exports.size; ++i) { WasmInterpreterExport* export = &module->exports.data[i]; - run_export_wrapper(allocator, module, thread, export, &results, verbose); + run_export_wrapper(allocator, thread, export, &results, verbose); } wasm_destroy_interpreter_typed_value_vector(allocator, &results); } static WasmResult read_module(WasmAllocator* allocator, const char* module_filename, - WasmInterpreterModule* out_module, + WasmInterpreterEnvironment* env, + WasmInterpreterModule** out_module, WasmInterpreterThread* out_thread) { WasmResult result; void* data; size_t size; - WASM_ZERO_MEMORY(*out_module); WASM_ZERO_MEMORY(*out_thread); result = wasm_read_file(allocator, module_filename, &data, &size); if (WASM_SUCCEEDED(result)) { WasmAllocator* memory_allocator = &g_wasm_libc_allocator; - result = wasm_read_binary_interpreter(allocator, memory_allocator, data, - size, &s_read_binary_options, + result = wasm_read_binary_interpreter(allocator, memory_allocator, env, + data, size, &s_read_binary_options, &s_error_handler, out_module); if (WASM_SUCCEEDED(result)) { - if (s_verbose) { - wasm_disassemble_module(out_module, s_stdout_stream, 0, - out_module->istream.size); - } + if (s_verbose) + wasm_disassemble_module(env, s_stdout_stream, *out_module); - result = wasm_init_interpreter_thread(allocator, out_module, out_thread, - &s_thread_options); - out_thread->host_func.callback = default_host_callback; - out_thread->host_func.user_data = NULL; + result = wasm_init_interpreter_thread(allocator, env, *out_module, + out_thread, &s_thread_options); } wasm_free(allocator, data); } return result; } -static void destroy_module_and_thread(WasmAllocator* allocator, - WasmInterpreterModule* module, - WasmInterpreterThread* thread) { - wasm_destroy_interpreter_thread(allocator, thread); - wasm_destroy_interpreter_module(allocator, module); +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, + WasmInterpreterTypedValue* args, + uint32_t num_results, + WasmInterpreterTypedValue* out_results, + void* user_data) { + memset(out_results, 0, sizeof(WasmInterpreterTypedValue) * num_results); + uint32_t i; + 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"); + return WASM_OK; +} + +static WasmResult spectest_import_func(WasmInterpreterImport* import, + WasmInterpreterFunc* func, + WasmInterpreterFuncSignature* sig, + void* user_data) { + func->host.callback = default_host_callback; + return WASM_OK; +} + +static WasmResult spectest_import_table(WasmInterpreterImport* import, + WasmInterpreterTable* table, + void* user_data) { + return WASM_ERROR; +} + +static WasmResult spectest_import_memory(WasmInterpreterImport* import, + WasmInterpreterMemory* memory, + void* user_data) { + return WASM_ERROR; +} + +static WasmResult spectest_import_global(WasmInterpreterImport* import, + WasmInterpreterGlobal* global, + void* user_data) { + return WASM_ERROR; +} + +static void init_environment(WasmAllocator* allocator, + WasmInterpreterEnvironment* env) { + wasm_init_interpreter_environment(allocator, env); + WasmInterpreterModule* host_module = wasm_append_host_module( + allocator, env, wasm_string_slice_from_cstr("spectest")); + host_module->host.import_delegate.import_func = spectest_import_func; + host_module->host.import_delegate.import_table = spectest_import_table; + host_module->host.import_delegate.import_memory = spectest_import_memory; + host_module->host.import_delegate.import_global = spectest_import_global; } static WasmResult read_and_run_module(WasmAllocator* allocator, const char* module_filename) { WasmResult result; - WasmInterpreterModule module; + WasmInterpreterEnvironment env; + WasmInterpreterModule* module = NULL; WasmInterpreterThread thread; - result = read_module(allocator, module_filename, &module, &thread); - if (WASM_SUCCEEDED(result)) - result = run_start_function(&module, &thread); - if (WASM_SUCCEEDED(result) && s_run_all_exports) - run_all_exports(allocator, &module, &thread, RUN_VERBOSE); - destroy_module_and_thread(allocator, &module, &thread); + 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); + } + 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; - WasmInterpreterModule module; + WasmInterpreterEnvironment env; + WasmInterpreterModule* module = NULL; WasmInterpreterThread thread; WasmStringSlice command_file; WasmStringSlice command_name; - WasmAllocatorMark module_mark; WasmInterpreterTypedValueVector result_values; uint32_t command_line_no; - WasmBool has_module = WASM_FALSE; + WasmBool has_thread = WASM_FALSE; uint32_t passed = 0; uint32_t failed = 0; - WASM_ZERO_MEMORY(module); WASM_ZERO_MEMORY(thread); WASM_ZERO_MEMORY(command_file); WASM_ZERO_MEMORY(command_name); - WASM_ZERO_MEMORY(module_mark); WASM_ZERO_MEMORY(result_values); + init_environment(allocator, &env); + void* data; size_t size; result = wasm_read_file(allocator, spec_json_filename, &data, &size); @@ -653,9 +675,9 @@ static WasmResult read_and_run_spec_json(WasmAllocator* allocator, case END_MODULE_OBJECT: EXPECT('}'); MAYBE_CONTINUE(MODULES_ARRAY); - destroy_module_and_thread(allocator, &module, &thread); - wasm_reset_to_mark(allocator, module_mark); - has_module = WASM_FALSE; + assert(has_thread); + wasm_destroy_interpreter_thread(allocator, &thread); + has_thread = WASM_FALSE; break; case MODULE_FILENAME: { @@ -673,14 +695,13 @@ static WasmResult read_and_run_spec_json(WasmAllocator* allocator, WASM_PRINTF_STRING_SLICE_ARG(module_filename)); } - module_mark = wasm_mark(allocator); - result = read_module(allocator, path, &module, &thread); + result = read_module(allocator, path, &env, &module, &thread); if (WASM_FAILED(result)) goto fail; - has_module = WASM_TRUE; + has_thread = WASM_TRUE; - result = run_start_function(&module, &thread); + result = run_start_function(&thread); if (WASM_FAILED(result)) goto fail; @@ -727,8 +748,8 @@ static WasmResult read_and_run_spec_json(WasmAllocator* allocator, WasmInterpreterResult iresult; EXPECT('}'); RunVerbosity verbose = command_type == ACTION ? RUN_VERBOSE : RUN_QUIET; - result = run_export_by_name(allocator, &module, &thread, &command_name, - &iresult, &result_values, verbose); + result = run_export_by_name(allocator, &thread, &command_name, &iresult, + &result_values, verbose); if (WASM_FAILED(result)) { FAILED("unknown export"); failed++; @@ -837,8 +858,9 @@ fail: result = WASM_ERROR; done: - if (has_module) - destroy_module_and_thread(allocator, &module, &thread); + 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); return result; diff --git a/src/wasm-ast.c b/src/wasm-ast.c index a6c4fe8a..780f595f 100644 --- a/src/wasm-ast.c +++ b/src/wasm-ast.c @@ -21,158 +21,15 @@ #include "wasm-allocator.h" -#define INITIAL_HASH_CAPACITY 8 - -static size_t hash_name(const WasmStringSlice* name) { - // FNV-1a hash - const uint32_t fnv_prime = 0x01000193; - const uint8_t* bp = (const uint8_t*)name->start; - const uint8_t* be = bp + name->length; - uint32_t hval = 0x811c9dc5; - while (bp < be) { - hval ^= (uint32_t)*bp++; - hval *= fnv_prime; - } - return hval; -} - -static WasmBindingHashEntry* hash_main_entry(const WasmBindingHash* hash, - const WasmStringSlice* name) { - return &hash->entries.data[hash_name(name) % hash->entries.capacity]; -} - -WasmBool wasm_hash_entry_is_free(const WasmBindingHashEntry* entry) { - return !entry->binding.name.start; -} - -static WasmBindingHashEntry* hash_new_entry(WasmBindingHash* hash, - const WasmStringSlice* name) { - WasmBindingHashEntry* entry = hash_main_entry(hash, name); - if (!wasm_hash_entry_is_free(entry)) { - assert(hash->free_head); - WasmBindingHashEntry* free_entry = hash->free_head; - hash->free_head = free_entry->next; - if (free_entry->next) - free_entry->next->prev = NULL; - - /* our main position is already claimed. Check to see if the entry in that - * position is in its main position */ - WasmBindingHashEntry* other_entry = - hash_main_entry(hash, &entry->binding.name); - if (other_entry == entry) { - /* yes, so add this new entry to the chain, even if it is already there */ - /* add as the second entry in the chain */ - free_entry->next = entry->next; - entry->next = free_entry; - entry = free_entry; - } else { - /* no, move the entry to the free entry */ - assert(!wasm_hash_entry_is_free(other_entry)); - while (other_entry->next != entry) - other_entry = other_entry->next; - - other_entry->next = free_entry; - *free_entry = *entry; - entry->next = NULL; - } - } else { - /* remove from the free list */ - if (entry->next) - entry->next->prev = entry->prev; - if (entry->prev) - entry->prev->next = entry->next; - else - hash->free_head = entry->next; - entry->next = NULL; - } - - WASM_ZERO_MEMORY(entry->binding); - entry->binding.name = *name; - entry->prev = NULL; - /* entry->next is set above */ - return entry; -} - -static void hash_resize(WasmAllocator* allocator, - WasmBindingHash* hash, - size_t desired_capacity) { - WasmBindingHash new_hash; - WASM_ZERO_MEMORY(new_hash); - /* TODO(binji): better plural */ - wasm_reserve_binding_hash_entrys(allocator, &new_hash.entries, - desired_capacity); - - /* update the free list */ - size_t i; - for (i = 0; i < new_hash.entries.capacity; ++i) { - WasmBindingHashEntry* entry = &new_hash.entries.data[i]; - if (new_hash.free_head) - new_hash.free_head->prev = entry; - - WASM_ZERO_MEMORY(entry->binding.name); - entry->next = new_hash.free_head; - new_hash.free_head = entry; - } - new_hash.free_head->prev = NULL; - - /* copy from the old hash to the new hash */ - for (i = 0; i < hash->entries.capacity; ++i) { - WasmBindingHashEntry* old_entry = &hash->entries.data[i]; - if (wasm_hash_entry_is_free(old_entry)) - continue; - - WasmStringSlice* name = &old_entry->binding.name; - WasmBindingHashEntry* new_entry = hash_new_entry(&new_hash, name); - new_entry->binding = old_entry->binding; - } - - /* we are sharing the WasmStringSlices, so we only need to destroy the old - * binding vector */ - wasm_destroy_binding_hash_entry_vector(allocator, &hash->entries); - *hash = new_hash; -} - -WasmBinding* wasm_insert_binding(WasmAllocator* allocator, - WasmBindingHash* hash, - const WasmStringSlice* name) { - if (hash->entries.size == 0) - hash_resize(allocator, hash, INITIAL_HASH_CAPACITY); - - if (!hash->free_head) { - /* no more free space, allocate more */ - hash_resize(allocator, hash, hash->entries.capacity * 2); - } - - WasmBindingHashEntry* entry = hash_new_entry(hash, name); - assert(entry); - hash->entries.size++; - return &entry->binding; -} - -static int find_binding_index_by_name(const WasmBindingHash* hash, - const WasmStringSlice* name) { - if (hash->entries.capacity == 0) - return -1; - - WasmBindingHashEntry* entry = hash_main_entry(hash, name); - do { - if (wasm_string_slices_are_equal(&entry->binding.name, name)) - return entry->binding.index; - - entry = entry->next; - } while (entry && !wasm_hash_entry_is_free(entry)); - return -1; -} - int wasm_get_index_from_var(const WasmBindingHash* hash, const WasmVar* var) { if (var->type == WASM_VAR_TYPE_NAME) - return find_binding_index_by_name(hash, &var->name); + return wasm_find_binding_index_by_name(hash, &var->name); return (int)var->index; } WasmExportPtr wasm_get_export_by_name(const WasmModule* module, const WasmStringSlice* name) { - int index = find_binding_index_by_name(&module->export_bindings, name); + int index = wasm_find_binding_index_by_name(&module->export_bindings, name); if (index == -1) return NULL; return module->exports.data[index]; @@ -203,11 +60,12 @@ int wasm_get_local_index_by_var(const WasmFunc* func, const WasmVar* var) { if (var->type == WASM_VAR_TYPE_INDEX) return (int)var->index; - int result = find_binding_index_by_name(&func->param_bindings, &var->name); + int result = + wasm_find_binding_index_by_name(&func->param_bindings, &var->name); if (result != -1) return result; - result = find_binding_index_by_name(&func->local_bindings, &var->name); + result = wasm_find_binding_index_by_name(&func->local_bindings, &var->name); if (result == -1) return result; @@ -393,21 +251,6 @@ WasmExpr* wasm_new_empty_expr(struct WasmAllocator* allocator, return result; } -static void destroy_binding_hash_entry(WasmAllocator* allocator, - WasmBindingHashEntry* entry) { - wasm_destroy_string_slice(allocator, &entry->binding.name); -} - -static void destroy_binding_hash(WasmAllocator* allocator, - WasmBindingHash* hash) { - /* Can't use WASM_DESTROY_VECTOR_AND_ELEMENTS, because it loops over size, not - * capacity. */ - size_t i; - for (i = 0; i < hash->entries.capacity; ++i) - destroy_binding_hash_entry(allocator, &hash->entries.data[i]); - wasm_destroy_binding_hash_entry_vector(allocator, &hash->entries); -} - void wasm_destroy_var(WasmAllocator* allocator, WasmVar* var) { if (var->type == WASM_VAR_TYPE_NAME) wasm_destroy_string_slice(allocator, &var->name); @@ -513,8 +356,8 @@ void wasm_destroy_func(WasmAllocator* allocator, WasmFunc* func) { wasm_destroy_string_slice(allocator, &func->name); wasm_destroy_func_declaration(allocator, &func->decl); wasm_destroy_type_vector(allocator, &func->local_types); - destroy_binding_hash(allocator, &func->param_bindings); - destroy_binding_hash(allocator, &func->local_bindings); + wasm_destroy_binding_hash(allocator, &func->param_bindings); + wasm_destroy_binding_hash(allocator, &func->local_bindings); wasm_destroy_expr_list(allocator, func->first_expr); } diff --git a/src/wasm-ast.h b/src/wasm-ast.h index 244a1cb7..5d6b6f2d 100644 --- a/src/wasm-ast.h +++ b/src/wasm-ast.h @@ -21,6 +21,7 @@ #include <stddef.h> #include <stdint.h> +#include "wasm-binding-hash.h" #include "wasm-common.h" #include "wasm-type-vector.h" #include "wasm-vector.h" @@ -85,24 +86,6 @@ typedef enum WasmExprType { WASM_EXPR_TYPE_UNREACHABLE, } WasmExprType; -typedef struct WasmBinding { - WasmLocation loc; - WasmStringSlice name; - int index; -} WasmBinding; - -typedef struct WasmBindingHashEntry { - WasmBinding binding; - struct WasmBindingHashEntry* next; - struct WasmBindingHashEntry* prev; /* only valid when this entry is unused */ -} WasmBindingHashEntry; -WASM_DEFINE_VECTOR(binding_hash_entry, WasmBindingHashEntry); - -typedef struct WasmBindingHash { - WasmBindingHashEntryVector entries; - WasmBindingHashEntry* free_head; -} WasmBindingHash; - typedef WasmTypeVector WasmBlockSignature; typedef struct WasmBlock { @@ -411,12 +394,6 @@ typedef struct WasmExprVisitor { } WasmExprVisitor; WASM_EXTERN_C_BEGIN -WasmBinding* wasm_insert_binding(struct WasmAllocator*, - WasmBindingHash*, - const WasmStringSlice*); - -WasmBool wasm_hash_entry_is_free(const WasmBindingHashEntry*); - WasmModuleField* wasm_append_module_field(struct WasmAllocator*, WasmModule*); /* ownership of the function signature is passed to the module */ WasmFuncType* wasm_append_implicit_func_type(struct WasmAllocator*, diff --git a/src/wasm-binary-reader-interpreter.c b/src/wasm-binary-reader-interpreter.c index 57d2fa23..62ac3ee1 100644 --- a/src/wasm-binary-reader-interpreter.c +++ b/src/wasm-binary-reader-interpreter.c @@ -17,6 +17,7 @@ #include "wasm-binary-reader-interpreter.h" #include <assert.h> +#include <inttypes.h> #include <stdarg.h> #include <stdio.h> @@ -51,7 +52,7 @@ #define CHECK_LOCAL(ctx, local_index) \ do { \ uint32_t max_local_index = \ - (ctx)->current_func->param_and_local_types.size; \ + (ctx)->current_func->defined.param_and_local_types.size; \ if ((local_index) >= max_local_index) { \ print_error((ctx), "invalid local_index: %d (max %d)", (local_index), \ max_local_index); \ @@ -61,7 +62,7 @@ #define CHECK_GLOBAL(ctx, global_index) \ do { \ - uint32_t max_global_index = (ctx)->module->globals.size; \ + uint32_t max_global_index = (ctx)->global_index_mapping.size; \ if ((global_index) >= max_global_index) { \ print_error((ctx), "invalid global_index: %d (max %d)", (global_index), \ max_global_index); \ @@ -110,6 +111,7 @@ typedef struct Context { WasmBinaryReader* reader; WasmBinaryErrorHandler* error_handler; WasmAllocator* memory_allocator; + WasmInterpreterEnvironment* env; WasmInterpreterModule* module; WasmInterpreterFunc* current_func; WasmTypeVector type_stack; @@ -119,9 +121,20 @@ typedef struct Context { uint32_t depth; WasmMemoryWriter istream_writer; uint32_t istream_offset; + /* mappings from module index space to env index space; this won't just be a + * translation, because imported values will be resolved as well */ + Uint32Vector sig_index_mapping; + Uint32Vector func_index_mapping; + Uint32Vector global_index_mapping; + + uint32_t num_func_imports; + + /* values cached in the Context so they can be shared between callbacks */ WasmInterpreterTypedValue init_expr_value; uint32_t table_offset; - uint32_t num_func_imports; + WasmBool is_host_import; + WasmInterpreterModule* host_import_module; + uint32_t import_index; } Context; static Label* get_label(Context* ctx, uint32_t depth) { @@ -140,50 +153,91 @@ static void handle_error(uint32_t offset, const char* message, Context* ctx) { } } -static void print_error(Context* ctx, const char* format, ...) { +static void WASM_PRINTF_FORMAT(2, 3) + print_error(Context* ctx, const char* format, ...) { WASM_SNPRINTF_ALLOCA(buffer, length, format); handle_error(WASM_INVALID_OFFSET, buffer, ctx); } -static WasmInterpreterFunc* get_func(Context* ctx, uint32_t func_index) { - assert(func_index < ctx->module->funcs.size); - return &ctx->module->funcs.data[func_index]; +static uint32_t translate_sig_index_to_env(Context* ctx, uint32_t sig_index) { + assert(sig_index < ctx->sig_index_mapping.size); + return ctx->sig_index_mapping.data[sig_index]; +} + +static WasmInterpreterFuncSignature* get_signature_by_env_index( + Context* ctx, + uint32_t sig_index) { + assert(sig_index < ctx->env->sigs.size); + return &ctx->env->sigs.data[sig_index]; +} + +static WasmInterpreterFuncSignature* get_signature_by_module_index( + Context* ctx, + uint32_t sig_index) { + return get_signature_by_env_index(ctx, + translate_sig_index_to_env(ctx, sig_index)); +} + +static uint32_t translate_func_index_to_env(Context* ctx, uint32_t func_index) { + assert(func_index < ctx->func_index_mapping.size); + return ctx->func_index_mapping.data[func_index]; } -static WasmInterpreterFunc* get_defined_func(Context* ctx, - uint32_t func_index) { +static uint32_t translate_defined_func_index_to_env(Context* ctx, + uint32_t func_index) { /* all function imports are first, so skip over those */ func_index += ctx->num_func_imports; - assert(func_index < ctx->module->funcs.size); - return &ctx->module->funcs.data[func_index]; + assert(func_index < ctx->func_index_mapping.size); + return ctx->func_index_mapping.data[func_index]; } -static WasmInterpreterFuncSignature* get_signature(Context* ctx, - uint32_t sig_index) { - assert(sig_index < ctx->module->sigs.size); - return &ctx->module->sigs.data[sig_index]; +static WasmInterpreterFunc* get_func_by_env_index(Context* ctx, + uint32_t func_index) { + assert(func_index < ctx->env->funcs.size); + return &ctx->env->funcs.data[func_index]; } -static WasmInterpreterFuncSignature* get_func_signature( - Context* ctx, - WasmInterpreterFunc* func) { - return get_signature(ctx, func->sig_index); +static WasmInterpreterFunc* get_func_by_module_index(Context* ctx, + uint32_t func_index) { + return get_func_by_env_index(ctx, + translate_func_index_to_env(ctx, func_index)); } -static WasmType get_local_type_by_index(WasmInterpreterFunc* func, - uint32_t local_index) { - assert(local_index < func->param_and_local_types.size); - return func->param_and_local_types.data[local_index]; +static WasmInterpreterFunc* get_func_by_defined_index(Context* ctx, + uint32_t func_index) { + return get_func_by_env_index( + ctx, translate_defined_func_index_to_env(ctx, func_index)); +} + +static uint32_t translate_global_index_to_env(Context* ctx, + uint32_t global_index) { + assert(global_index < ctx->global_index_mapping.size); + return ctx->global_index_mapping.data[global_index]; +} + +static WasmInterpreterGlobal* get_global_by_env_index(Context* ctx, + uint32_t global_index) { + assert(global_index < ctx->env->globals.size); + return &ctx->env->globals.data[global_index]; +} + +static WasmInterpreterGlobal* get_global_by_module_index( + Context* ctx, + uint32_t global_index) { + return get_global_by_env_index( + ctx, translate_global_index_to_env(ctx, global_index)); } -static WasmInterpreterGlobal* get_global_by_index(Context* ctx, - uint32_t global_index) { - assert(global_index < ctx->module->globals.size); - return &ctx->module->globals.data[global_index]; +static WasmType get_global_type_by_module_index(Context* ctx, + uint32_t global_index) { + return get_global_by_module_index(ctx, global_index)->typed_value.type; } -static WasmType get_global_type_by_index(Context* ctx, uint32_t global_index) { - return get_global_by_index(ctx, global_index)->typed_value.type; +static WasmType get_local_type_by_index(WasmInterpreterFunc* func, + uint32_t local_index) { + assert(!func->is_host); + assert(local_index < func->defined.param_and_local_types.size); + return func->defined.param_and_local_types.data[local_index]; } static uint32_t translate_local_index(Context* ctx, uint32_t local_index) { @@ -314,9 +368,9 @@ static WasmResult fixup_top_label(Context* ctx, uint32_t offset) { static WasmResult emit_func_offset(Context* ctx, WasmInterpreterFunc* func, uint32_t func_index) { - if (func->offset == WASM_INVALID_OFFSET) + if (func->defined.offset == WASM_INVALID_OFFSET) CHECK_RESULT(append_fixup(ctx, &ctx->func_fixups, func_index)); - CHECK_RESULT(emit_i32(ctx, func->offset)); + CHECK_RESULT(emit_i32(ctx, func->defined.offset)); return WASM_OK; } @@ -326,8 +380,12 @@ static void on_error(WasmBinaryReaderContext* ctx, const char* message) { static WasmResult on_signature_count(uint32_t count, void* user_data) { Context* ctx = user_data; - wasm_new_interpreter_func_signature_array(ctx->allocator, &ctx->module->sigs, - count); + wasm_resize_uint32_vector(ctx->allocator, &ctx->sig_index_mapping, count); + uint32_t i; + for (i = 0; i < count; ++i) + ctx->sig_index_mapping.data[i] = ctx->env->sigs.size + i; + wasm_resize_interpreter_func_signature_vector(ctx->allocator, &ctx->env->sigs, + ctx->env->sigs.size + count); return WASM_OK; } @@ -338,7 +396,7 @@ static WasmResult on_signature(uint32_t index, WasmType* result_types, void* user_data) { Context* ctx = user_data; - WasmInterpreterFuncSignature* sig = get_signature(ctx, index); + WasmInterpreterFuncSignature* sig = get_signature_by_module_index(ctx, index); wasm_reserve_types(ctx->allocator, &sig->param_types, param_count); sig->param_types.size = param_count; @@ -352,8 +410,8 @@ static WasmResult on_signature(uint32_t index, static WasmResult on_import_count(uint32_t count, void* user_data) { Context* ctx = user_data; - wasm_new_interpreter_import_array(ctx->allocator, &ctx->module->imports, - count); + wasm_new_interpreter_import_array(ctx->allocator, + &ctx->module->defined.imports, count); return WASM_OK; } @@ -362,63 +420,240 @@ static WasmResult on_import(uint32_t index, WasmStringSlice field_name, void* user_data) { Context* ctx = user_data; - assert(index < ctx->module->imports.size); - WasmInterpreterImport* import = &ctx->module->imports.data[index]; + assert(index < ctx->module->defined.imports.size); + WasmInterpreterImport* import = &ctx->module->defined.imports.data[index]; import->module_name = wasm_dup_string_slice(ctx->allocator, module_name); import->field_name = wasm_dup_string_slice(ctx->allocator, field_name); + int module_index = wasm_find_binding_index_by_name(&ctx->env->module_bindings, + &import->module_name); + if (module_index < 0) { + print_error(ctx, "unknown import module \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(import->module_name)); + return WASM_ERROR; + } + + assert((size_t)module_index < ctx->env->modules.size); + WasmInterpreterModule* module = &ctx->env->modules.data[module_index]; + if (module->is_host) { + /* We don't yet know the kind of a host import module, so just assume it + * exists for now. We'll fail later (in on_import_* below) if it doesn't + * exist). */ + ctx->is_host_import = WASM_TRUE; + ctx->host_import_module = module; + } else { + WasmInterpreterExport* export = + wasm_get_interpreter_export_by_name(module, &import->field_name); + if (!export) { + print_error(ctx, "unknown module field \"" PRIstringslice "\"", + WASM_PRINTF_STRING_SLICE_ARG(import->field_name)); + return WASM_ERROR; + } + + import->kind = export->kind; + ctx->is_host_import = WASM_FALSE; + ctx->import_index = export->index; + } + return WASM_OK; +} + +static WasmResult check_import_kind(Context* ctx, + WasmInterpreterImport* import, + WasmExternalKind expected_kind) { + if (import->kind != expected_kind) { + print_error(ctx, "expected import \"" PRIstringslice "." PRIstringslice + "\" to have kind %s, not %s", + WASM_PRINTF_STRING_SLICE_ARG(import->module_name), + WASM_PRINTF_STRING_SLICE_ARG(import->field_name), + wasm_get_kind_name(expected_kind), + wasm_get_kind_name(import->kind)); + return WASM_ERROR; + } return WASM_OK; } +static void append_export(Context* ctx, + WasmInterpreterModule* module, + WasmExternalKind kind, + uint32_t item_index, + WasmStringSlice name) { + WasmInterpreterExport* export = + wasm_append_interpreter_export(ctx->allocator, &module->exports); + export->name = wasm_dup_string_slice(ctx->allocator, name); + export->kind = kind; + export->index = item_index; + + WasmBinding* binding = wasm_insert_binding( + ctx->allocator, &module->export_bindings, &export->name); + binding->index = module->exports.size - 1; +} + static WasmResult on_import_func(uint32_t index, uint32_t sig_index, void* user_data) { Context* ctx = user_data; - assert(index < ctx->module->imports.size); - WasmInterpreterImport* import = &ctx->module->imports.data[index]; - assert(sig_index < ctx->module->sigs.size); - import->kind = WASM_EXTERNAL_KIND_FUNC; - import->func.sig_index = sig_index; - - WasmInterpreterFunc* func = - wasm_append_interpreter_func(ctx->allocator, &ctx->module->funcs); - func->sig_index = sig_index; - func->offset = WASM_INVALID_OFFSET; - func->is_host = WASM_TRUE; - func->import_index = index; - + assert(index < ctx->module->defined.imports.size); + WasmInterpreterImport* import = &ctx->module->defined.imports.data[index]; + assert(sig_index < ctx->env->sigs.size); + import->func.sig_index = translate_sig_index_to_env(ctx, sig_index); + + uint32_t func_index; + if (ctx->is_host_import) { + WasmInterpreterFunc* func = + wasm_append_interpreter_func(ctx->allocator, &ctx->env->funcs); + func->is_host = WASM_TRUE; + func->sig_index = import->func.sig_index; + func->host.module_name = import->module_name; + func->host.field_name = import->field_name; + + WasmInterpreterHostImportDelegate* host_delegate = + &ctx->host_import_module->host.import_delegate; + WasmInterpreterFuncSignature* sig = &ctx->env->sigs.data[func->sig_index]; + CHECK_RESULT(host_delegate->import_func(import, func, sig, + host_delegate->user_data)); + assert(func->host.callback); + + func_index = ctx->env->funcs.size - 1; + append_export(ctx, ctx->host_import_module, WASM_EXTERNAL_KIND_FUNC, + func_index, import->field_name); + } else { + CHECK_RESULT(check_import_kind(ctx, import, WASM_EXTERNAL_KIND_FUNC)); + // TODO: check signature matches + func_index = ctx->import_index; + } + wasm_append_uint32_value(ctx->allocator, &ctx->func_index_mapping, + &func_index); ctx->num_func_imports++; return WASM_OK; } -/* TODO(binji): implement import_table, import_memory, import_global */ static WasmResult on_import_table(uint32_t index, uint32_t elem_type, const WasmLimits* elem_limits, void* user_data) { - /* TODO */ - return WASM_ERROR; + Context* ctx = user_data; + if (ctx->module->table_index != WASM_INVALID_INDEX) { + print_error(ctx, "only one table allowed"); + return WASM_ERROR; + } + + assert(index < ctx->module->defined.imports.size); + WasmInterpreterImport* import = &ctx->module->defined.imports.data[index]; + if (ctx->is_host_import) { + WasmInterpreterTable* table = + wasm_append_interpreter_table(ctx->allocator, &ctx->env->tables); + table->limits = *elem_limits; + wasm_new_uint32_array(ctx->allocator, &table->func_indexes, + table->limits.initial); + + WasmInterpreterHostImportDelegate* host_delegate = + &ctx->host_import_module->host.import_delegate; + CHECK_RESULT( + host_delegate->import_table(import, table, host_delegate->user_data)); + + uint32_t table_index = ctx->env->tables.size - 1; + append_export(ctx, ctx->host_import_module, WASM_EXTERNAL_KIND_TABLE, + table_index, import->field_name); + } else { + CHECK_RESULT(check_import_kind(ctx, import, WASM_EXTERNAL_KIND_TABLE)); + // TODO: check limits + import->table.limits = *elem_limits; + ctx->module->table_index = ctx->import_index; + } + return WASM_OK; +} + +static WasmInterpreterMemory* append_memory(Context* ctx, + const WasmLimits* page_limits) { + WasmInterpreterMemory* memory = + wasm_append_interpreter_memory(ctx->allocator, &ctx->env->memories); + memory->allocator = ctx->memory_allocator; + memory->page_size = page_limits->initial; + memory->byte_size = page_limits->initial * WASM_PAGE_SIZE; + memory->max_page_size = + page_limits->has_max ? page_limits->max : WASM_MAX_PAGES; + memory->data = wasm_alloc_zero(ctx->memory_allocator, memory->byte_size, + WASM_DEFAULT_ALIGN); + return memory; } static WasmResult on_import_memory(uint32_t index, const WasmLimits* page_limits, void* user_data) { - /* TODO */ - return WASM_ERROR; + Context* ctx = user_data; + if (ctx->module->memory_index != WASM_INVALID_INDEX) { + print_error(ctx, "only one memory allowed"); + return WASM_ERROR; + } + + assert(index < ctx->module->defined.imports.size); + WasmInterpreterImport* import = &ctx->module->defined.imports.data[index]; + + if (ctx->is_host_import) { + WasmInterpreterMemory* memory = append_memory(ctx, page_limits); + + WasmInterpreterHostImportDelegate* host_delegate = + &ctx->host_import_module->host.import_delegate; + CHECK_RESULT( + host_delegate->import_memory(import, memory, host_delegate->user_data)); + + uint32_t memory_index = ctx->env->memories.size - 1; + append_export(ctx, ctx->host_import_module, WASM_EXTERNAL_KIND_MEMORY, + memory_index, import->field_name); + } else { + CHECK_RESULT(check_import_kind(ctx, import, WASM_EXTERNAL_KIND_MEMORY)); + // TODO: check limits + import->memory.limits = *page_limits; + ctx->module->memory_index = ctx->import_index; + } + return WASM_OK; } static WasmResult on_import_global(uint32_t index, WasmType type, - WasmBool mutable_, + WasmBool mutable, void* user_data) { - /* TODO */ - return WASM_ERROR; + Context* ctx = user_data; + assert(index < ctx->module->defined.imports.size); + WasmInterpreterImport* import = &ctx->module->defined.imports.data[index]; + + uint32_t global_index = ctx->env->globals.size - 1; + if (ctx->is_host_import) { + WasmInterpreterGlobal* global = + wasm_append_interpreter_global(ctx->allocator, &ctx->env->globals); + global->typed_value.type = type; + global->mutable_ = mutable; + + WasmInterpreterHostImportDelegate* host_delegate = + &ctx->host_import_module->host.import_delegate; + CHECK_RESULT( + host_delegate->import_global(import, global, host_delegate->user_data)); + + global_index = ctx->env->globals.size - 1; + append_export(ctx, ctx->host_import_module, WASM_EXTERNAL_KIND_GLOBAL, + global_index, import->field_name); + } else { + CHECK_RESULT(check_import_kind(ctx, import, WASM_EXTERNAL_KIND_GLOBAL)); + // TODO: check type and mutability + import->global.type = type; + import->global.mutable_ = mutable; + global_index = ctx->import_index; + } + wasm_append_uint32_value(ctx->allocator, &ctx->global_index_mapping, + &global_index); + return WASM_OK; } static WasmResult on_function_signatures_count(uint32_t count, void* user_data) { Context* ctx = user_data; - wasm_resize_interpreter_func_vector(ctx->allocator, &ctx->module->funcs, - ctx->module->funcs.size + count); + wasm_resize_uint32_vector(ctx->allocator, &ctx->func_index_mapping, + ctx->func_index_mapping.size + count); + uint32_t i; + for (i = 0; i < count; ++i) + ctx->func_index_mapping.data[ctx->num_func_imports + i] = + ctx->env->funcs.size + i; + wasm_resize_interpreter_func_vector(ctx->allocator, &ctx->env->funcs, + ctx->env->funcs.size + count); wasm_resize_uint32_vector_vector(ctx->allocator, &ctx->func_fixups, count); return WASM_OK; } @@ -427,10 +662,9 @@ static WasmResult on_function_signature(uint32_t index, uint32_t sig_index, void* user_data) { Context* ctx = user_data; - assert(sig_index < ctx->module->sigs.size); - WasmInterpreterFunc* func = get_defined_func(ctx, index); - func->offset = WASM_INVALID_OFFSET; - func->sig_index = sig_index; + WasmInterpreterFunc* func = get_func_by_defined_index(ctx, index); + func->defined.offset = WASM_INVALID_OFFSET; + func->sig_index = translate_sig_index_to_env(ctx, sig_index); return WASM_OK; } @@ -439,8 +673,15 @@ static WasmResult on_table(uint32_t index, const WasmLimits* elem_limits, void* user_data) { Context* ctx = user_data; - wasm_new_uint32_array(ctx->allocator, &ctx->module->func_table, + if (ctx->module->table_index != WASM_INVALID_INDEX) { + print_error(ctx, "only one table allowed"); + return WASM_ERROR; + } + WasmInterpreterTable* table = + wasm_append_interpreter_table(ctx->allocator, &ctx->env->tables); + wasm_new_uint32_array(ctx->allocator, &table->func_indexes, elem_limits->initial); + ctx->module->table_index = ctx->env->tables.size - 1; return WASM_OK; } @@ -448,21 +689,23 @@ static WasmResult on_memory(uint32_t index, const WasmLimits* page_limits, void* user_data) { Context* ctx = user_data; - WasmInterpreterMemory* memory = &ctx->module->memory; - memory->allocator = ctx->memory_allocator; - memory->page_size = page_limits->initial; - memory->byte_size = page_limits->initial * WASM_PAGE_SIZE; - memory->max_page_size = - page_limits->has_max ? page_limits->max : WASM_MAX_PAGES; - memory->data = wasm_alloc_zero(ctx->memory_allocator, memory->byte_size, - WASM_DEFAULT_ALIGN); + if (ctx->module->memory_index != WASM_INVALID_INDEX) { + print_error(ctx, "only one memory allowed"); + return WASM_ERROR; + } + append_memory(ctx, page_limits); + ctx->module->memory_index = ctx->env->memories.size - 1; return WASM_OK; } static WasmResult on_global_count(uint32_t count, void* user_data) { Context* ctx = user_data; - wasm_new_interpreter_global_array(ctx->allocator, &ctx->module->globals, - count); + wasm_resize_uint32_vector(ctx->allocator, &ctx->global_index_mapping, count); + uint32_t i; + for (i = 0; i < count; ++i) + ctx->global_index_mapping.data[i] = ctx->env->globals.size + i; + wasm_resize_interpreter_global_vector(ctx->allocator, &ctx->env->globals, + ctx->env->globals.size + count); return WASM_OK; } @@ -471,8 +714,7 @@ static WasmResult begin_global(uint32_t index, WasmBool mutable_, void* user_data) { Context* ctx = user_data; - assert(index < ctx->module->globals.size); - WasmInterpreterGlobal* global = &ctx->module->globals.data[index]; + WasmInterpreterGlobal* global = get_global_by_module_index(ctx, index); global->typed_value.type = type; global->mutable_ = mutable_; return WASM_OK; @@ -480,8 +722,7 @@ static WasmResult begin_global(uint32_t index, static WasmResult end_global_init_expr(uint32_t index, void* user_data) { Context* ctx = user_data; - assert(index < ctx->module->globals.size); - WasmInterpreterGlobal* global = &ctx->module->globals.data[index]; + WasmInterpreterGlobal* global = get_global_by_module_index(ctx, index); global->typed_value = ctx->init_expr_value; return WASM_OK; } @@ -508,8 +749,8 @@ static WasmResult on_init_expr_get_global_expr(uint32_t index, uint32_t global_index, void* user_data) { Context* ctx = user_data; - assert(global_index < ctx->module->globals.size); - WasmInterpreterGlobal* ref_global = &ctx->module->globals.data[global_index]; + WasmInterpreterGlobal* ref_global = + get_global_by_module_index(ctx, global_index); ctx->init_expr_value = ref_global->typed_value; return WASM_OK; } @@ -532,30 +773,41 @@ static WasmResult on_init_expr_i64_const_expr(uint32_t index, return WASM_OK; } -static WasmResult on_export_count(uint32_t count, void* user_data) { - Context* ctx = user_data; - wasm_new_interpreter_export_array(ctx->allocator, &ctx->module->exports, - count); - return WASM_OK; -} - static WasmResult on_export(uint32_t index, WasmExternalKind kind, uint32_t item_index, WasmStringSlice name, void* user_data) { Context* ctx = user_data; - WasmInterpreterExport* export = &ctx->module->exports.data[index]; - export->name = wasm_dup_string_slice(ctx->allocator, name); - export->kind = kind; - export->index = item_index; + switch (kind) { + case WASM_EXTERNAL_KIND_FUNC: + item_index = translate_func_index_to_env(ctx, item_index); + break; + + case WASM_EXTERNAL_KIND_TABLE: + item_index = ctx->module->table_index; + break; + + case WASM_EXTERNAL_KIND_MEMORY: + item_index = ctx->module->memory_index; + break; + + case WASM_EXTERNAL_KIND_GLOBAL: + item_index = translate_global_index_to_env(ctx, item_index); + break; + + case WASM_NUM_EXTERNAL_KINDS: + assert(0); + break; + } + append_export(ctx, ctx->module, kind, item_index, name); return WASM_OK; } static WasmResult on_start_function(uint32_t func_index, void* user_data) { Context* ctx = user_data; - assert(func_index < ctx->module->funcs.size); - ctx->module->start_func_index = func_index; + ctx->module->defined.start_func_index = + translate_func_index_to_env(ctx, func_index); return WASM_OK; } @@ -570,10 +822,18 @@ static WasmResult on_elem_segment_function_index(uint32_t index, uint32_t func_index, void* user_data) { Context* ctx = user_data; - assert(ctx->table_offset < ctx->module->func_table.size); - assert(index < ctx->module->func_table.size); - uint32_t* entry = &ctx->module->func_table.data[ctx->table_offset++]; - *entry = func_index; + assert(ctx->module->table_index != WASM_INVALID_INDEX); + WasmInterpreterTable* table = + &ctx->env->tables.data[ctx->module->table_index]; + if (ctx->table_offset >= table->func_indexes.size) { + print_error(ctx, + "elem segment offset is out of bounds: %u >= max value %" PRIzd, + ctx->table_offset, table->func_indexes.size); + return WASM_ERROR; + } + + table->func_indexes.data[ctx->table_offset++] = + translate_func_index_to_env(ctx, func_index); return WASM_OK; } @@ -582,23 +842,23 @@ static WasmResult on_data_segment_data(uint32_t index, uint32_t size, void* user_data) { Context* ctx = user_data; - WasmInterpreterMemory* memory = &ctx->module->memory; + assert(ctx->module->memory_index != WASM_INVALID_INDEX); + WasmInterpreterMemory* memory = + &ctx->env->memories.data[ctx->module->memory_index]; assert(ctx->init_expr_value.type == WASM_TYPE_I32); uint32_t address = ctx->init_expr_value.value.i32; uint8_t* dst_data = memory->data; - if (size > 0 && (uint64_t)address + (uint64_t)size > memory->byte_size) + uint64_t end_address = (uint64_t)address + (uint64_t)size; + if (size > 0 && end_address > memory->byte_size) { + print_error(ctx, "data segment is out of bounds: [%u, %" PRIu64 + ") >= max value %u", + address, end_address, memory->byte_size); return WASM_ERROR; + } memcpy(&dst_data[address], src_data, size); return WASM_OK; } -static WasmResult on_function_bodies_count(uint32_t count, void* user_data) { - Context* ctx = user_data; - assert(ctx->num_func_imports + count == ctx->module->funcs.size); - WASM_USE(ctx); - return WASM_OK; -} - static uint32_t translate_depth(Context* ctx, uint32_t depth) { assert(depth < ctx->label_stack.size); return ctx->label_stack.size - 1 - depth; @@ -643,7 +903,8 @@ static WasmType top_type(Context* ctx) { } static WasmBool top_type_is_any(Context* ctx) { - if (ctx->type_stack.size > ctx->current_func->param_and_local_types.size) { + if (ctx->type_stack.size > + ctx->current_func->defined.param_and_local_types.size) { WasmType top_type = ctx->type_stack.data[ctx->type_stack.size - 1]; if (top_type == WASM_TYPE_ANY) return WASM_TRUE; @@ -810,12 +1071,14 @@ static WasmResult drop_types_for_return(Context* ctx, uint32_t arity) { static WasmResult begin_function_body(uint32_t index, void* user_data) { Context* ctx = user_data; - WasmInterpreterFunc* func = get_defined_func(ctx, index); - WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); + WasmInterpreterFunc* func = get_func_by_defined_index(ctx, index); + WasmInterpreterFuncSignature* sig = + get_signature_by_env_index(ctx, func->sig_index); - func->offset = get_istream_offset(ctx); - func->local_decl_count = 0; - func->local_count = 0; + func->is_host = WASM_FALSE; + func->defined.offset = get_istream_offset(ctx); + func->defined.local_decl_count = 0; + func->defined.local_count = 0; ctx->current_func = func; ctx->depth_fixups.size = 0; @@ -827,12 +1090,13 @@ static WasmResult begin_function_body(uint32_t index, void* user_data) { uint32_t i; Uint32Vector* fixups = &ctx->func_fixups.data[index]; for (i = 0; i < fixups->size; ++i) - CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], func->offset)); + CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], func->defined.offset)); /* append param types */ for (i = 0; i < sig->param_types.size; ++i) { WasmType type = sig->param_types.data[i]; - wasm_append_type_value(ctx->allocator, &func->param_and_local_types, &type); + wasm_append_type_value(ctx->allocator, &func->defined.param_and_local_types, + &type); wasm_append_type_value(ctx->allocator, &ctx->type_stack, &type); } @@ -873,7 +1137,7 @@ static WasmResult end_function_body(uint32_t index, void* user_data) { static WasmResult on_local_decl_count(uint32_t count, void* user_data) { Context* ctx = user_data; WasmInterpreterFunc* func = ctx->current_func; - func->local_decl_count = count; + func->defined.local_decl_count = count; return WASM_OK; } @@ -884,22 +1148,23 @@ static WasmResult on_local_decl(uint32_t decl_index, Context* ctx = user_data; LOGF("%3" PRIzd ": alloca\n", ctx->type_stack.size); WasmInterpreterFunc* func = ctx->current_func; - func->local_count += count; + func->defined.local_count += count; uint32_t i; for (i = 0; i < count; ++i) { - wasm_append_type_value(ctx->allocator, &func->param_and_local_types, &type); + wasm_append_type_value(ctx->allocator, &func->defined.param_and_local_types, + &type); push_type(ctx, type); } - if (decl_index == func->local_decl_count - 1) { + if (decl_index == func->defined.local_decl_count - 1) { /* last local declaration, allocate space for all locals. */ CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_ALLOCA)); - CHECK_RESULT(emit_i32(ctx, func->local_count)); + CHECK_RESULT(emit_i32(ctx, func->defined.local_count)); /* fixup the function label's type_stack_limit to include these values. */ Label* label = top_label(ctx); assert(label->label_type == LABEL_TYPE_FUNC); - label->type_stack_limit += func->local_count; + label->type_stack_limit += func->defined.local_count; } return WASM_OK; } @@ -1079,8 +1344,9 @@ static WasmResult on_br_table_expr(WasmBinaryReaderContext* context, static WasmResult on_call_expr(uint32_t func_index, void* user_data) { Context* ctx = user_data; - WasmInterpreterFunc* func = get_func(ctx, func_index); - WasmInterpreterFuncSignature* sig = get_signature(ctx, func->sig_index); + WasmInterpreterFunc* func = get_func_by_module_index(ctx, func_index); + WasmInterpreterFuncSignature* sig = + get_signature_by_env_index(ctx, func->sig_index); CHECK_RESULT(check_type_stack_limit(ctx, sig->param_types.size, "call")); uint32_t i; @@ -1091,7 +1357,7 @@ static WasmResult on_call_expr(uint32_t func_index, void* user_data) { if (func->is_host) { CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_HOST)); - CHECK_RESULT(emit_i32(ctx, func_index)); + CHECK_RESULT(emit_i32(ctx, translate_func_index_to_env(ctx, func_index))); } else { CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_FUNCTION)); CHECK_RESULT(emit_func_offset(ctx, func, func_index)); @@ -1103,7 +1369,8 @@ static WasmResult on_call_expr(uint32_t func_index, void* user_data) { static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) { Context* ctx = user_data; - WasmInterpreterFuncSignature* sig = get_signature(ctx, sig_index); + WasmInterpreterFuncSignature* sig = + get_signature_by_module_index(ctx, sig_index); CHECK_RESULT(pop_and_check_1_type(ctx, WASM_TYPE_I32, "call_indirect")); CHECK_RESULT( check_type_stack_limit(ctx, sig->param_types.size, "call_indirect")); @@ -1116,7 +1383,7 @@ static WasmResult on_call_indirect_expr(uint32_t sig_index, void* user_data) { } CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_CALL_INDIRECT)); - CHECK_RESULT(emit_i32(ctx, sig_index)); + CHECK_RESULT(emit_i32(ctx, translate_sig_index_to_env(ctx, sig_index))); push_types(ctx, &sig->result_types); return WASM_OK; } @@ -1164,7 +1431,7 @@ static WasmResult on_f64_const_expr(uint64_t value_bits, void* user_data) { static WasmResult on_get_global_expr(uint32_t global_index, void* user_data) { Context* ctx = user_data; CHECK_GLOBAL(ctx, global_index); - WasmType type = get_global_type_by_index(ctx, global_index); + WasmType type = get_global_type_by_module_index(ctx, global_index); CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_GET_GLOBAL)); CHECK_RESULT(emit_i32(ctx, global_index)); push_type(ctx, type); @@ -1174,7 +1441,7 @@ static WasmResult on_get_global_expr(uint32_t global_index, void* user_data) { static WasmResult on_set_global_expr(uint32_t global_index, void* user_data) { Context* ctx = user_data; CHECK_GLOBAL(ctx, global_index); - WasmInterpreterGlobal* global = get_global_by_index(ctx, global_index); + WasmInterpreterGlobal* global = get_global_by_module_index(ctx, global_index); if (global->mutable_ != WASM_TRUE) { print_error(ctx, "can't set_global on immutable global at index %u.", global_index); @@ -1263,7 +1530,7 @@ static WasmResult on_nop_expr(void* user_data) { static WasmResult on_return_expr(void* user_data) { Context* ctx = user_data; WasmInterpreterFuncSignature* sig = - get_func_signature(ctx, ctx->current_func); + get_signature_by_env_index(ctx, ctx->current_func->sig_index); CHECK_RESULT(check_n_types(ctx, &sig->result_types, "return")); CHECK_RESULT(drop_types_for_return(ctx, sig->result_types.size)); CHECK_RESULT(emit_opcode(ctx, WASM_OPCODE_RETURN)); @@ -1317,12 +1584,10 @@ static WasmBinaryReader s_binary_reader = { .begin_global = begin_global, .end_global_init_expr = end_global_init_expr, - .on_export_count = on_export_count, .on_export = on_export, .on_start_function = on_start_function, - .on_function_bodies_count = on_function_bodies_count, .begin_function_body = begin_function_body, .on_local_decl_count = on_local_decl_count, .on_local_decl = on_local_decl, @@ -1379,28 +1644,42 @@ static void destroy_context(Context* ctx) { uint32_vector); WASM_DESTROY_VECTOR_AND_ELEMENTS(ctx->allocator, ctx->func_fixups, uint32_vector); + wasm_destroy_uint32_vector(ctx->allocator, &ctx->sig_index_mapping); + wasm_destroy_uint32_vector(ctx->allocator, &ctx->func_index_mapping); + wasm_destroy_uint32_vector(ctx->allocator, &ctx->global_index_mapping); } WasmResult wasm_read_binary_interpreter(WasmAllocator* allocator, WasmAllocator* memory_allocator, + WasmInterpreterEnvironment* env, const void* data, size_t size, const WasmReadBinaryOptions* options, WasmBinaryErrorHandler* error_handler, - WasmInterpreterModule* out_module) { + WasmInterpreterModule** out_module) { Context ctx; WasmBinaryReader reader; WASM_ZERO_MEMORY(ctx); WASM_ZERO_MEMORY(reader); + WasmInterpreterModule* module = + wasm_append_interpreter_module(allocator, &env->modules); + ctx.allocator = allocator; ctx.reader = &reader; ctx.error_handler = error_handler; ctx.memory_allocator = memory_allocator; - ctx.module = out_module; - ctx.module->start_func_index = WASM_INVALID_FUNC_INDEX; - CHECK_RESULT(wasm_init_mem_writer(allocator, &ctx.istream_writer)); + ctx.env = env; + ctx.module = module; + ctx.module->is_host = WASM_FALSE; + ctx.module->table_index = WASM_INVALID_INDEX; + ctx.module->memory_index = WASM_INVALID_INDEX; + ctx.module->defined.start_func_index = WASM_INVALID_INDEX; + ctx.module->defined.istream_start = env->istream.size; + ctx.istream_offset = env->istream.size; + CHECK_RESULT( + wasm_init_mem_writer_existing(&ctx.istream_writer, &env->istream)); reader = s_binary_reader; reader.user_data = &ctx; @@ -1409,11 +1688,13 @@ WasmResult wasm_read_binary_interpreter(WasmAllocator* allocator, WasmResult result = wasm_read_binary(allocator, data, size, &reader, num_function_passes, options); if (WASM_SUCCEEDED(result)) { - wasm_steal_mem_writer_output_buffer(&ctx.istream_writer, - &out_module->istream); - out_module->istream.size = ctx.istream_offset; + wasm_steal_mem_writer_output_buffer(&ctx.istream_writer, &env->istream); + env->istream.size = ctx.istream_offset; + ctx.module->defined.istream_end = env->istream.size; + *out_module = module; } else { wasm_close_mem_writer(&ctx.istream_writer); + *out_module = NULL; } destroy_context(&ctx); return result; diff --git a/src/wasm-binary-reader-interpreter.h b/src/wasm-binary-reader-interpreter.h index bd701b92..2cb26652 100644 --- a/src/wasm-binary-reader-interpreter.h +++ b/src/wasm-binary-reader-interpreter.h @@ -20,6 +20,7 @@ #include "wasm-common.h" struct WasmAllocator; +struct WasmInterpreterEnvironment; struct WasmInterpreterModule; struct WasmReadBinaryOptions; @@ -27,11 +28,12 @@ WASM_EXTERN_C_BEGIN WasmResult wasm_read_binary_interpreter( struct WasmAllocator* allocator, struct WasmAllocator* memory_allocator, + struct WasmInterpreterEnvironment* env, const void* data, size_t size, const struct WasmReadBinaryOptions* options, WasmBinaryErrorHandler*, - struct WasmInterpreterModule* out_module); + struct WasmInterpreterModule** out_module); WASM_EXTERN_C_END #endif /* WASM_BINARY_READER_INTERPRETER_H_ */ diff --git a/src/wasm-binary-reader.c b/src/wasm-binary-reader.c index 378f5927..d30473d5 100644 --- a/src/wasm-binary-reader.c +++ b/src/wasm-binary-reader.c @@ -2015,7 +2015,7 @@ WasmResult wasm_read_binary(WasmAllocator* allocator, CALLBACK_CTX0(end_data_section); } - CALLBACK_CTX0(end_module); + CALLBACK0(end_module); destroy_context(allocator, ctx); return WASM_OK; } diff --git a/src/wasm-binding-hash.c b/src/wasm-binding-hash.c new file mode 100644 index 00000000..68b80eb7 --- /dev/null +++ b/src/wasm-binding-hash.c @@ -0,0 +1,175 @@ +/* + * 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 "wasm-binding-hash.h" + +#define INITIAL_HASH_CAPACITY 8 + +static size_t hash_name(const WasmStringSlice* name) { + // FNV-1a hash + const uint32_t fnv_prime = 0x01000193; + const uint8_t* bp = (const uint8_t*)name->start; + const uint8_t* be = bp + name->length; + uint32_t hval = 0x811c9dc5; + while (bp < be) { + hval ^= (uint32_t)*bp++; + hval *= fnv_prime; + } + return hval; +} + +static WasmBindingHashEntry* hash_main_entry(const WasmBindingHash* hash, + const WasmStringSlice* name) { + return &hash->entries.data[hash_name(name) % hash->entries.capacity]; +} + +WasmBool wasm_hash_entry_is_free(const WasmBindingHashEntry* entry) { + return !entry->binding.name.start; +} + +static WasmBindingHashEntry* hash_new_entry(WasmBindingHash* hash, + const WasmStringSlice* name) { + WasmBindingHashEntry* entry = hash_main_entry(hash, name); + if (!wasm_hash_entry_is_free(entry)) { + assert(hash->free_head); + WasmBindingHashEntry* free_entry = hash->free_head; + hash->free_head = free_entry->next; + if (free_entry->next) + free_entry->next->prev = NULL; + + /* our main position is already claimed. Check to see if the entry in that + * position is in its main position */ + WasmBindingHashEntry* other_entry = + hash_main_entry(hash, &entry->binding.name); + if (other_entry == entry) { + /* yes, so add this new entry to the chain, even if it is already there */ + /* add as the second entry in the chain */ + free_entry->next = entry->next; + entry->next = free_entry; + entry = free_entry; + } else { + /* no, move the entry to the free entry */ + assert(!wasm_hash_entry_is_free(other_entry)); + while (other_entry->next != entry) + other_entry = other_entry->next; + + other_entry->next = free_entry; + *free_entry = *entry; + entry->next = NULL; + } + } else { + /* remove from the free list */ + if (entry->next) + entry->next->prev = entry->prev; + if (entry->prev) + entry->prev->next = entry->next; + else + hash->free_head = entry->next; + entry->next = NULL; + } + + WASM_ZERO_MEMORY(entry->binding); + entry->binding.name = *name; + entry->prev = NULL; + /* entry->next is set above */ + return entry; +} + +static void hash_resize(WasmAllocator* allocator, + WasmBindingHash* hash, + size_t desired_capacity) { + WasmBindingHash new_hash; + WASM_ZERO_MEMORY(new_hash); + /* TODO(binji): better plural */ + wasm_reserve_binding_hash_entrys(allocator, &new_hash.entries, + desired_capacity); + + /* update the free list */ + size_t i; + for (i = 0; i < new_hash.entries.capacity; ++i) { + WasmBindingHashEntry* entry = &new_hash.entries.data[i]; + if (new_hash.free_head) + new_hash.free_head->prev = entry; + + WASM_ZERO_MEMORY(entry->binding.name); + entry->next = new_hash.free_head; + new_hash.free_head = entry; + } + new_hash.free_head->prev = NULL; + + /* copy from the old hash to the new hash */ + for (i = 0; i < hash->entries.capacity; ++i) { + WasmBindingHashEntry* old_entry = &hash->entries.data[i]; + if (wasm_hash_entry_is_free(old_entry)) + continue; + + WasmStringSlice* name = &old_entry->binding.name; + WasmBindingHashEntry* new_entry = hash_new_entry(&new_hash, name); + new_entry->binding = old_entry->binding; + } + + /* we are sharing the WasmStringSlices, so we only need to destroy the old + * binding vector */ + wasm_destroy_binding_hash_entry_vector(allocator, &hash->entries); + *hash = new_hash; +} + +WasmBinding* wasm_insert_binding(WasmAllocator* allocator, + WasmBindingHash* hash, + const WasmStringSlice* name) { + if (hash->entries.size == 0) + hash_resize(allocator, hash, INITIAL_HASH_CAPACITY); + + if (!hash->free_head) { + /* no more free space, allocate more */ + hash_resize(allocator, hash, hash->entries.capacity * 2); + } + + WasmBindingHashEntry* entry = hash_new_entry(hash, name); + assert(entry); + hash->entries.size++; + return &entry->binding; +} + +int wasm_find_binding_index_by_name(const WasmBindingHash* hash, + const WasmStringSlice* name) { + if (hash->entries.capacity == 0) + return -1; + + WasmBindingHashEntry* entry = hash_main_entry(hash, name); + do { + if (wasm_string_slices_are_equal(&entry->binding.name, name)) + return entry->binding.index; + + entry = entry->next; + } while (entry && !wasm_hash_entry_is_free(entry)); + return -1; +} + +static void destroy_binding_hash_entry(WasmAllocator* allocator, + WasmBindingHashEntry* entry) { + wasm_destroy_string_slice(allocator, &entry->binding.name); +} + +void wasm_destroy_binding_hash(WasmAllocator* allocator, + WasmBindingHash* hash) { + /* Can't use WASM_DESTROY_VECTOR_AND_ELEMENTS, because it loops over size, not + * capacity. */ + size_t i; + for (i = 0; i < hash->entries.capacity; ++i) + destroy_binding_hash_entry(allocator, &hash->entries.data[i]); + wasm_destroy_binding_hash_entry_vector(allocator, &hash->entries); +} diff --git a/src/wasm-binding-hash.h b/src/wasm-binding-hash.h new file mode 100644 index 00000000..2897e44a --- /dev/null +++ b/src/wasm-binding-hash.h @@ -0,0 +1,54 @@ +/* + * 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_BINDING_HASH_H_ +#define WASM_BINDING_HASH_H_ + +#include "wasm-common.h" +#include "wasm-vector.h" + +struct WasmAllocator; + +typedef struct WasmBinding { + WasmLocation loc; + WasmStringSlice name; + int index; +} WasmBinding; + +typedef struct WasmBindingHashEntry { + WasmBinding binding; + struct WasmBindingHashEntry* next; + struct WasmBindingHashEntry* prev; /* only valid when this entry is unused */ +} WasmBindingHashEntry; +WASM_DEFINE_VECTOR(binding_hash_entry, WasmBindingHashEntry); + +typedef struct WasmBindingHash { + WasmBindingHashEntryVector entries; + WasmBindingHashEntry* free_head; +} WasmBindingHash; + +WASM_EXTERN_C_BEGIN +WasmBinding* wasm_insert_binding(struct WasmAllocator*, + WasmBindingHash*, + const WasmStringSlice*); +WasmBool wasm_hash_entry_is_free(const WasmBindingHashEntry*); +/* returns -1 if the name is not in the hash */ +int wasm_find_binding_index_by_name(const WasmBindingHash*, + const WasmStringSlice* name); +void wasm_destroy_binding_hash(struct WasmAllocator*, WasmBindingHash*); +WASM_EXTERN_C_END + +#endif /* WASM_BINDING_HASH_H_ */ diff --git a/src/wasm-common.c b/src/wasm-common.c index 1df2e335..f439c01d 100644 --- a/src/wasm-common.c +++ b/src/wasm-common.c @@ -56,6 +56,13 @@ WasmStringSlice wasm_empty_string_slice(void) { return result; } +WasmStringSlice wasm_string_slice_from_cstr(const char* string) { + WasmStringSlice result; + result.start = string; + result.length = strlen(string); + return result; +} + WasmBool wasm_string_slices_are_equal(const WasmStringSlice* a, const WasmStringSlice* b) { return a->start && b->start && a->length == b->length && diff --git a/src/wasm-common.h b/src/wasm-common.h index 365572da..4f257872 100644 --- a/src/wasm-common.h +++ b/src/wasm-common.h @@ -151,13 +151,6 @@ typedef enum WasmExternalKind { WASM_NUM_EXTERNAL_KINDS, } WasmExternalKind; -extern const char* g_wasm_kind_name[]; - -static WASM_INLINE const char* wasm_get_kind_name(WasmExternalKind kind) { - assert(kind < WASM_NUM_EXTERNAL_KINDS); - return g_wasm_kind_name[kind]; -} - typedef struct WasmLimits { uint64_t initial; uint64_t max; @@ -390,6 +383,7 @@ WasmBool wasm_is_naturally_aligned(WasmOpcode opcode, uint32_t alignment); uint32_t wasm_get_opcode_alignment(WasmOpcode opcode, uint32_t alignment); WasmStringSlice wasm_empty_string_slice(void); +WasmStringSlice wasm_string_slice_from_cstr(const char* string); WasmBool wasm_string_slices_are_equal(const WasmStringSlice*, const WasmStringSlice*); void wasm_destroy_string_slice(struct WasmAllocator*, WasmStringSlice*); @@ -452,6 +446,15 @@ static WASM_INLINE int wasm_get_opcode_memory_size(WasmOpcode opcode) { return g_wasm_opcode_info[opcode].memory_size; } +/* external kind */ + +extern const char* g_wasm_kind_name[]; + +static WASM_INLINE const char* wasm_get_kind_name(WasmExternalKind kind) { + assert(kind < WASM_NUM_EXTERNAL_KINDS); + return g_wasm_kind_name[kind]; +} + WASM_EXTERN_C_END #endif /* WASM_COMMON_H_ */ diff --git a/src/wasm-interpreter.c b/src/wasm-interpreter.c index 66d3042e..c004ce80 100644 --- a/src/wasm-interpreter.c +++ b/src/wasm-interpreter.c @@ -22,6 +22,8 @@ #include "wasm-stream.h" +#define INITIAL_ISTREAM_CAPACITY (64 * 1024) + #define V(rtype, type1, type2, mem_size, code, NAME, text) [code] = text, static const char* s_interpreter_opcode_name[] = { WASM_FOREACH_OPCODE(V) @@ -44,47 +46,127 @@ static const char* wasm_get_interpreter_opcode_name(uint8_t opcode) { return s_interpreter_opcode_name[opcode]; } -static WasmResult trapping_host_func_callback( - const WasmInterpreterFuncSignature* sig, - const WasmStringSlice* module_name, - const WasmStringSlice* field_name, - uint32_t num_args, - WasmInterpreterTypedValue* args, - uint32_t num_results, - WasmInterpreterTypedValue* out_results, - void* user_data) { - return WASM_ERROR; +void wasm_init_interpreter_environment(WasmAllocator* allocator, + WasmInterpreterEnvironment* env) { + WASM_ZERO_MEMORY(*env); + wasm_init_output_buffer(allocator, &env->istream, INITIAL_ISTREAM_CAPACITY); +} + +static void wasm_destroy_interpreter_func_signature( + WasmAllocator* allocator, + WasmInterpreterFuncSignature* sig) { + wasm_destroy_type_vector(allocator, &sig->param_types); + wasm_destroy_type_vector(allocator, &sig->result_types); +} + +static void wasm_destroy_interpreter_func( + WasmAllocator* allocator, + WasmInterpreterFunc* func) { + if (!func->is_host) + wasm_destroy_type_vector(allocator, &func->defined.param_and_local_types); +} + +static void wasm_destroy_interpreter_memory(WasmAllocator* unused, + WasmInterpreterMemory* memory) { + if (memory->allocator) { + wasm_free(memory->allocator, memory->data); + } else { + assert(memory->data == NULL); + } +} + +static void wasm_destroy_interpreter_table(WasmAllocator* allocator, + WasmInterpreterTable* table) { + wasm_destroy_uint32_array(allocator, &table->func_indexes); +} + +static void wasm_destroy_interpreter_import(WasmAllocator* allocator, + WasmInterpreterImport* import) { + wasm_destroy_string_slice(allocator, &import->module_name); + wasm_destroy_string_slice(allocator, &import->field_name); +} + +static void wasm_destroy_interpreter_module(WasmAllocator* allocator, + WasmInterpreterModule* module) { + wasm_destroy_interpreter_export_vector(allocator, &module->exports); + wasm_destroy_binding_hash(allocator, &module->export_bindings); + if (!module->is_host) { + WASM_DESTROY_ARRAY_AND_ELEMENTS(allocator, module->defined.imports, + interpreter_import); + } +} + +void wasm_destroy_interpreter_environment(WasmAllocator* allocator, + WasmInterpreterEnvironment* env) { + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, env->modules, + interpreter_module); + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, env->sigs, + interpreter_func_signature); + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, env->funcs, interpreter_func); + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, env->memories, + interpreter_memory); + WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, env->tables, interpreter_table); + wasm_destroy_interpreter_global_vector(allocator, &env->globals); + wasm_destroy_output_buffer(&env->istream); + wasm_destroy_binding_hash(allocator, &env->module_bindings); +} + +WasmInterpreterModule* wasm_append_host_module(WasmAllocator* allocator, + WasmInterpreterEnvironment* env, + WasmStringSlice name) { + WasmInterpreterModule* module = + wasm_append_interpreter_module(allocator, &env->modules); + module->name = wasm_dup_string_slice(allocator, name); + module->memory_index = WASM_INVALID_INDEX; + module->table_index = WASM_INVALID_INDEX; + module->is_host = WASM_TRUE; + + WasmBinding* binding = + wasm_insert_binding(allocator, &env->module_bindings, &module->name); + binding->index = env->modules.size - 1; + return module; } WasmResult wasm_init_interpreter_thread(WasmAllocator* allocator, + WasmInterpreterEnvironment* env, WasmInterpreterModule* module, WasmInterpreterThread* thread, WasmInterpreterThreadOptions* options) { + assert(!module->is_host); + WASM_ZERO_MEMORY(*thread); wasm_new_interpreter_value_array(allocator, &thread->value_stack, options->value_stack_size); wasm_new_uint32_array(allocator, &thread->call_stack, options->call_stack_size); + thread->env = env; + thread->module = module; thread->value_stack_top = thread->value_stack.data; thread->value_stack_end = thread->value_stack.data + thread->value_stack.size; thread->call_stack_top = thread->call_stack.data; thread->call_stack_end = thread->call_stack.data + thread->call_stack.size; thread->pc = options->pc; - thread->host_func.callback = trapping_host_func_callback; + /* cache this module's memory and table, for convenience */ + if (module->memory_index != WASM_INVALID_INDEX) + thread->memory = &env->memories.data[module->memory_index]; + if (module->table_index != WASM_INVALID_INDEX) + thread->table = &env->tables.data[module->table_index]; - /* allocate import_args based on the signature with the most params */ + /* allocate host_args based on the signature with the most params */ /* TODO(binji): move this elsewhere? */ uint32_t i; - uint32_t max_import_params = 0; - for (i = 0; i < module->funcs.size; ++i) { - WasmInterpreterFunc* func = &module->funcs.data[i]; - assert(func->sig_index < module->sigs.size); - WasmInterpreterFuncSignature* sig = &module->sigs.data[func->sig_index]; - if (sig->param_types.size > max_import_params) - max_import_params = sig->param_types.size; + uint32_t max_host_params = 0; + for (i = 0; i < env->funcs.size; ++i) { + WasmInterpreterFunc* func = &env->funcs.data[i]; + if (!func->is_host) + continue; + assert(func->sig_index < env->sigs.size); + WasmInterpreterFuncSignature* sig = &env->sigs.data[func->sig_index]; + if (sig->param_types.size > max_host_params) + max_host_params = sig->param_types.size; } - wasm_new_interpreter_typed_value_array(allocator, &thread->import_args, - max_import_params); + wasm_new_interpreter_typed_value_array(allocator, &thread->host_args, + max_host_params); return WASM_OK; } @@ -99,35 +181,19 @@ WasmInterpreterResult wasm_push_thread_value(WasmInterpreterThread* thread, WasmInterpreterExport* wasm_get_interpreter_export_by_name( WasmInterpreterModule* module, WasmStringSlice* name) { - uint32_t i; - for (i = 0; i < module->exports.size; ++i) { - WasmInterpreterExport* export = &module->exports.data[i]; - if (wasm_string_slices_are_equal(name, &export->name)) - return export; - } - return NULL; -} - -WasmInterpreterImport* wasm_get_interpreter_import_by_name( - WasmInterpreterModule* module, - WasmStringSlice* module_name, - WasmStringSlice* func_name) { - uint32_t i; - for (i = 0; i < module->imports.size; ++i) { - WasmInterpreterImport* import = &module->imports.data[i]; - if (wasm_string_slices_are_equal(module_name, &import->module_name) && - wasm_string_slices_are_equal(func_name, &import->field_name)) { - return import; - } - } - return NULL; + int field_index = + wasm_find_binding_index_by_name(&module->export_bindings, name); + if (field_index < 0) + return NULL; + assert((size_t)field_index < module->exports.size); + return &module->exports.data[field_index]; } void wasm_destroy_interpreter_thread(WasmAllocator* allocator, WasmInterpreterThread* thread) { wasm_destroy_interpreter_value_array(allocator, &thread->value_stack); wasm_destroy_uint32_array(allocator, &thread->call_stack); - wasm_destroy_interpreter_typed_value_array(allocator, &thread->import_args); + wasm_destroy_interpreter_typed_value_array(allocator, &thread->host_args); } /* 3 32222222 222...00 @@ -421,26 +487,26 @@ DEFINE_BITCAST(bitcast_u64_to_f64, uint64_t, double) #define POP_CALL() (*--thread->call_stack_top) -#define LOAD(type, mem_type) \ - do { \ - uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \ - MEM_TYPE_##mem_type value; \ - TRAP_IF(offset + sizeof(value) > module->memory.byte_size, \ - MEMORY_ACCESS_OUT_OF_BOUNDS); \ - void* src = (void*)((intptr_t)module->memory.data + (uint32_t)offset); \ - memcpy(&value, src, sizeof(MEM_TYPE_##mem_type)); \ - PUSH_##type((MEM_TYPE_EXTEND_##type##_##mem_type)value); \ +#define LOAD(type, mem_type) \ + do { \ + uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \ + MEM_TYPE_##mem_type value; \ + TRAP_IF(offset + sizeof(value) > thread->memory->byte_size, \ + MEMORY_ACCESS_OUT_OF_BOUNDS); \ + void* src = (void*)((intptr_t)thread->memory->data + (uint32_t)offset); \ + memcpy(&value, src, sizeof(MEM_TYPE_##mem_type)); \ + PUSH_##type((MEM_TYPE_EXTEND_##type##_##mem_type)value); \ } while (0) -#define STORE(type, mem_type) \ - do { \ - VALUE_TYPE_##type value = POP_##type(); \ - uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \ - MEM_TYPE_##mem_type src = (MEM_TYPE_##mem_type)value; \ - TRAP_IF(offset + sizeof(src) > module->memory.byte_size, \ - MEMORY_ACCESS_OUT_OF_BOUNDS); \ - void* dst = (void*)((intptr_t)module->memory.data + (uint32_t)offset); \ - memcpy(dst, &src, sizeof(MEM_TYPE_##mem_type)); \ +#define STORE(type, mem_type) \ + do { \ + VALUE_TYPE_##type value = POP_##type(); \ + uint64_t offset = (uint64_t)POP_I32() + read_u32(&pc); \ + MEM_TYPE_##mem_type src = (MEM_TYPE_##mem_type)value; \ + TRAP_IF(offset + sizeof(src) > thread->memory->byte_size, \ + MEMORY_ACCESS_OUT_OF_BOUNDS); \ + void* dst = (void*)((intptr_t)thread->memory->data + (uint32_t)offset); \ + memcpy(dst, &src, sizeof(MEM_TYPE_##mem_type)); \ } while (0) #define BINOP(rtype, type, op) \ @@ -625,32 +691,31 @@ static WASM_INLINE void read_table_entry_at(const uint8_t* pc, *out_keep = *(pc + WASM_TABLE_ENTRY_KEEP_OFFSET); } -static WasmBool signatures_are_equal(WasmInterpreterModule* module, +static WasmBool signatures_are_equal(WasmInterpreterEnvironment* env, uint32_t sig_index_0, uint32_t sig_index_1) { if (sig_index_0 == sig_index_1) return WASM_TRUE; - WasmInterpreterFuncSignature* sig_0 = &module->sigs.data[sig_index_0]; - WasmInterpreterFuncSignature* sig_1 = &module->sigs.data[sig_index_1]; + WasmInterpreterFuncSignature* sig_0 = &env->sigs.data[sig_index_0]; + WasmInterpreterFuncSignature* sig_1 = &env->sigs.data[sig_index_1]; return wasm_type_vectors_are_equal(&sig_0->param_types, &sig_1->param_types) && wasm_type_vectors_are_equal(&sig_0->result_types, &sig_1->result_types); } -WasmInterpreterResult wasm_call_host(WasmInterpreterModule* module, - WasmInterpreterThread* thread, - WasmInterpreterImport* import) { - uint32_t sig_index = import->func.sig_index; - assert(sig_index < module->sigs.size); - WasmInterpreterFuncSignature* sig = &module->sigs.data[sig_index]; +WasmInterpreterResult wasm_call_host(WasmInterpreterThread* thread, + WasmInterpreterFunc* func) { + assert(func->is_host); + assert(func->sig_index < thread->env->sigs.size); + WasmInterpreterFuncSignature* sig = &thread->env->sigs.data[func->sig_index]; uint32_t num_args = sig->param_types.size; uint32_t i; - assert(num_args <= thread->import_args.size); + assert(num_args <= thread->host_args.size); for (i = num_args; i > 0; --i) { WasmInterpreterValue value = POP(); - WasmInterpreterTypedValue* arg = &thread->import_args.data[i - 1]; + WasmInterpreterTypedValue* arg = &thread->host_args.data[i - 1]; arg->type = sig->param_types.data[i - 1]; arg->value = value; } @@ -659,11 +724,9 @@ WasmInterpreterResult wasm_call_host(WasmInterpreterModule* module, WasmInterpreterTypedValue* call_result_values = alloca(sizeof(WasmInterpreterTypedValue) * num_results); - assert(thread->host_func.callback); - WasmResult call_result = thread->host_func.callback( - sig, &import->module_name, &import->field_name, num_args, - thread->import_args.data, num_results, call_result_values, - thread->host_func.user_data); + WasmResult call_result = func->host.callback( + func, sig, num_args, thread->host_args.data, num_results, + call_result_values, func->host.user_data); TRAP_IF(call_result != WASM_OK, HOST_TRAPPED); for (i = 0; i < num_results; ++i) { @@ -675,14 +738,16 @@ WasmInterpreterResult wasm_call_host(WasmInterpreterModule* module, return WASM_INTERPRETER_OK; } -WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, - WasmInterpreterThread* thread, +WasmInterpreterResult wasm_run_interpreter(WasmInterpreterThread* thread, uint32_t num_instructions, uint32_t* call_stack_return_top) { WasmInterpreterResult result = WASM_INTERPRETER_OK; assert(call_stack_return_top < thread->call_stack_end); - const uint8_t* istream = module->istream.start; + WasmInterpreterEnvironment* env = thread->env; + WasmInterpreterModule* module = thread->module; + + const uint8_t* istream = env->istream.start; const uint8_t* pc = &istream[thread->pc]; uint32_t i; for (i = 0; i < num_instructions; ++i) { @@ -753,15 +818,15 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, case WASM_OPCODE_GET_GLOBAL: { uint32_t index = read_u32(&pc); - assert(index < module->globals.size); - PUSH(module->globals.data[index].typed_value.value); + assert(index < env->globals.size); + PUSH(env->globals.data[index].typed_value.value); break; } case WASM_OPCODE_SET_GLOBAL: { uint32_t index = read_u32(&pc); - assert(index < module->globals.size); - module->globals.data[index].typed_value.value = POP(); + assert(index < env->globals.size); + env->globals.data[index].typed_value.value = POP(); break; } @@ -789,36 +854,29 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, } case WASM_OPCODE_CALL_INDIRECT: { + WasmInterpreterTable* table = &env->tables.data[module->table_index]; uint32_t sig_index = read_u32(&pc); - assert(sig_index < module->sigs.size); + assert(sig_index < env->sigs.size); VALUE_TYPE_I32 entry_index = POP_I32(); - TRAP_IF(entry_index >= module->func_table.size, UNDEFINED_TABLE_INDEX); - uint32_t func_index = module->func_table.data[entry_index]; - WasmInterpreterFunc* func = &module->funcs.data[func_index]; + TRAP_IF(entry_index >= table->func_indexes.size, UNDEFINED_TABLE_INDEX); + uint32_t func_index = table->func_indexes.data[entry_index]; + WasmInterpreterFunc* func = &env->funcs.data[func_index]; + TRAP_UNLESS(signatures_are_equal(env, func->sig_index, sig_index), + INDIRECT_CALL_SIGNATURE_MISMATCH); if (func->is_host) { - TRAP_UNLESS(signatures_are_equal(module, func->sig_index, sig_index), - INDIRECT_CALL_SIGNATURE_MISMATCH); - uint32_t import_index = func->import_index; - assert(import_index < module->imports.size); - WasmInterpreterImport* import = &module->imports.data[import_index]; - wasm_call_host(module, thread, import); + wasm_call_host(thread, func); } else { - TRAP_UNLESS(signatures_are_equal(module, func->sig_index, sig_index), - INDIRECT_CALL_SIGNATURE_MISMATCH); PUSH_CALL(); - GOTO(func->offset); + GOTO(func->defined.offset); } break; } case WASM_OPCODE_CALL_HOST: { uint32_t func_index = read_u32(&pc); - assert(func_index < module->funcs.size); - WasmInterpreterFunc* func = &module->funcs.data[func_index]; - uint32_t import_index = func->import_index; - assert(import_index < module->imports.size); - WasmInterpreterImport* import = &module->imports.data[import_index]; - wasm_call_host(module, thread, import); + assert(func_index < env->funcs.size); + WasmInterpreterFunc* func = &env->funcs.data[func_index]; + wasm_call_host(thread, func); break; } @@ -915,27 +973,27 @@ WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, break; case WASM_OPCODE_CURRENT_MEMORY: - PUSH_I32(module->memory.page_size); + PUSH_I32(thread->memory->page_size); break; case WASM_OPCODE_GROW_MEMORY: { - uint32_t old_page_size = module->memory.page_size; - uint32_t old_byte_size = module->memory.byte_size; + uint32_t old_page_size = thread->memory->page_size; + uint32_t old_byte_size = thread->memory->byte_size; VALUE_TYPE_I32 grow_pages = POP_I32(); uint32_t new_page_size = old_page_size + grow_pages; - PUSH_NEG_1_AND_BREAK_IF(new_page_size > module->memory.max_page_size); + PUSH_NEG_1_AND_BREAK_IF(new_page_size > thread->memory->max_page_size); PUSH_NEG_1_AND_BREAK_IF((uint64_t)new_page_size * WASM_PAGE_SIZE > UINT32_MAX); uint32_t new_byte_size = new_page_size * WASM_PAGE_SIZE; - WasmAllocator* allocator = module->memory.allocator; - void* new_data = wasm_realloc(allocator, module->memory.data, + WasmAllocator* allocator = thread->memory->allocator; + void* new_data = wasm_realloc(allocator, thread->memory->data, new_byte_size, WASM_DEFAULT_ALIGN); PUSH_NEG_1_AND_BREAK_IF(new_data == NULL); memset((void*)((intptr_t)new_data + old_byte_size), 0, new_byte_size - old_byte_size); - module->memory.data = new_data; - module->memory.page_size = new_page_size; - module->memory.byte_size = new_byte_size; + thread->memory->data = new_data; + thread->memory->page_size = new_page_size; + thread->memory->byte_size = new_byte_size; PUSH_I32(old_page_size); break; } @@ -1581,16 +1639,14 @@ exit_loop: return result; } -void wasm_trace_pc(WasmInterpreterModule* module, - WasmInterpreterThread* thread, - WasmStream* stream) { - const uint8_t* istream = module->istream.start; +void wasm_trace_pc(WasmInterpreterThread* thread, WasmStream* stream) { + const uint8_t* istream = thread->env->istream.start; const uint8_t* pc = &istream[thread->pc]; size_t value_stack_depth = thread->value_stack_top - thread->value_stack.data; size_t call_stack_depth = thread->call_stack_top - thread->call_stack.data; wasm_writef(stream, "#%" PRIzd ". %4" PRIzd ": V:%-3" PRIzd "| ", - call_stack_depth, pc - (uint8_t*)module->istream.start, + call_stack_depth, pc - (uint8_t*)thread->env->istream.start, value_stack_depth); uint8_t opcode = *pc++; @@ -1934,17 +1990,17 @@ void wasm_trace_pc(WasmInterpreterModule* module, } } -void wasm_disassemble_module(WasmInterpreterModule* module, - WasmStream* stream, - uint32_t from, - uint32_t to) { +void wasm_disassemble(WasmInterpreterEnvironment* env, + WasmStream* stream, + uint32_t from, + uint32_t to) { /* TODO(binji): mark function entries */ /* TODO(binji): track value stack size */ - if (from >= module->istream.size) + if (from >= env->istream.size) return; - if (to > module->istream.size) - to = module->istream.size; - const uint8_t* istream = module->istream.start; + if (to > env->istream.size) + to = env->istream.size; + const uint8_t* istream = env->istream.start; const uint8_t* pc = &istream[from]; while ((uint32_t)(pc - istream) < to) { @@ -2247,49 +2303,11 @@ void wasm_disassemble_module(WasmInterpreterModule* module, } } -static void wasm_destroy_memory(WasmInterpreterMemory* memory) { - if (memory->allocator) { - wasm_free(memory->allocator, memory->data); - } else { - assert(memory->data == NULL); - } -} - -static void wasm_destroy_interpreter_func_signature( - WasmAllocator* allocator, - WasmInterpreterFuncSignature* sig) { - wasm_destroy_type_vector(allocator, &sig->param_types); - wasm_destroy_type_vector(allocator, &sig->result_types); -} - -static void wasm_destroy_interpreter_func( - WasmAllocator* allocator, - WasmInterpreterFunc* func) { - wasm_destroy_type_vector(allocator, &func->param_and_local_types); -} - -static void wasm_destroy_interpreter_import(WasmAllocator* allocator, - WasmInterpreterImport* import) { - wasm_destroy_string_slice(allocator, &import->module_name); - wasm_destroy_string_slice(allocator, &import->field_name); -} - -static void wasm_destroy_interpreter_export(WasmAllocator* allocator, - WasmInterpreterExport* export) { - wasm_destroy_string_slice(allocator, &export->name); +void wasm_disassemble_module(WasmInterpreterEnvironment* env, + WasmStream* stream, + WasmInterpreterModule* module) { + assert(!module->is_host); + wasm_disassemble(env, stream, module->defined.istream_start, + module->defined.istream_end); } -void wasm_destroy_interpreter_module(WasmAllocator* allocator, - WasmInterpreterModule* module) { - wasm_destroy_memory(&module->memory); - WASM_DESTROY_ARRAY_AND_ELEMENTS(allocator, module->sigs, - interpreter_func_signature); - WASM_DESTROY_VECTOR_AND_ELEMENTS(allocator, module->funcs, interpreter_func); - wasm_destroy_uint32_array(allocator, &module->func_table); - WASM_DESTROY_ARRAY_AND_ELEMENTS(allocator, module->imports, - interpreter_import); - WASM_DESTROY_ARRAY_AND_ELEMENTS(allocator, module->exports, - interpreter_export); - wasm_destroy_interpreter_global_array(allocator, &module->globals); - wasm_destroy_output_buffer(&module->istream); -} diff --git a/src/wasm-interpreter.h b/src/wasm-interpreter.h index b172e5ca..2b470348 100644 --- a/src/wasm-interpreter.h +++ b/src/wasm-interpreter.h @@ -20,6 +20,7 @@ #include <stdint.h> #include "wasm-array.h" +#include "wasm-binding-hash.h" #include "wasm-type-vector.h" #include "wasm-vector.h" #include "wasm-writer.h" @@ -60,7 +61,7 @@ typedef enum WasmInterpreterResult { #undef V } WasmInterpreterResult; -#define WASM_INVALID_FUNC_INDEX ((uint32_t)~0) +#define WASM_INVALID_INDEX ((uint32_t)~0) #define WASM_INVALID_OFFSET ((uint32_t)~0) #define WASM_TABLE_ENTRY_SIZE (sizeof(uint32_t) * 2 + sizeof(uint8_t)) #define WASM_TABLE_ENTRY_OFFSET_OFFSET 0 @@ -78,12 +79,21 @@ enum { }; WASM_STATIC_ASSERT(WASM_NUM_INTERPRETER_OPCODES <= 256); +typedef uint32_t WasmUint32; +WASM_DEFINE_ARRAY(uint32, WasmUint32); + /* TODO(binji): identical to WasmFuncSignature. Share? */ typedef struct WasmInterpreterFuncSignature { WasmTypeVector param_types; WasmTypeVector result_types; } WasmInterpreterFuncSignature; -WASM_DEFINE_ARRAY(interpreter_func_signature, WasmInterpreterFuncSignature); +WASM_DEFINE_VECTOR(interpreter_func_signature, WasmInterpreterFuncSignature); + +typedef struct WasmInterpreterTable { + WasmLimits limits; + WasmUint32Array func_indexes; +} WasmInterpreterTable; +WASM_DEFINE_VECTOR(interpreter_table, WasmInterpreterTable); typedef struct WasmInterpreterMemory { WasmAllocator* allocator; @@ -92,12 +102,7 @@ typedef struct WasmInterpreterMemory { uint32_t byte_size; uint32_t max_page_size; } WasmInterpreterMemory; - -typedef struct WasmInterpreterFuncTableEntry { - uint32_t sig_index; - uint32_t func_index; -} WasmInterpreterFuncTableEntry; -WASM_DEFINE_ARRAY(interpreter_func_table_entry, WasmInterpreterFuncTableEntry); +WASM_DEFINE_VECTOR(interpreter_memory, WasmInterpreterMemory); typedef union WasmInterpreterValue { uint32_t i32; @@ -117,8 +122,9 @@ WASM_DEFINE_VECTOR(interpreter_typed_value, WasmInterpreterTypedValue); typedef struct WasmInterpreterGlobal { WasmInterpreterTypedValue typed_value; WasmBool mutable_; + uint32_t import_index; /* or INVALID_INDEX if not imported */ } WasmInterpreterGlobal; -WASM_DEFINE_ARRAY(interpreter_global, WasmInterpreterGlobal); +WASM_DEFINE_VECTOR(interpreter_global, WasmInterpreterGlobal); typedef struct WasmInterpreterImport { WasmStringSlice module_name; @@ -128,56 +134,109 @@ typedef struct WasmInterpreterImport { struct { uint32_t sig_index; } func; + struct { + WasmLimits limits; + } table, memory; + struct { + WasmType type; + WasmBool mutable_; + } global; }; } WasmInterpreterImport; -typedef WasmInterpreterImport* WasmInterpreterImportPtr; WASM_DEFINE_ARRAY(interpreter_import, WasmInterpreterImport); -WASM_DEFINE_ARRAY(interpreter_import_ptr, WasmInterpreterImportPtr); + +struct WasmInterpreterFunc; + +typedef WasmResult (*WasmInterpreterHostFuncCallback)( + const struct WasmInterpreterFunc* func, + const WasmInterpreterFuncSignature* sig, + uint32_t num_args, + WasmInterpreterTypedValue* args, + uint32_t num_results, + WasmInterpreterTypedValue* out_results, + void* user_data); typedef struct WasmInterpreterFunc { uint32_t sig_index; - uint32_t offset; - uint32_t local_decl_count; - uint32_t local_count; - WasmTypeVector param_and_local_types; WasmBool is_host; - uint32_t import_index; /* or INVALID_FUNC_INDEX if not imported */ + union { + struct { + uint32_t offset; + uint32_t local_decl_count; + uint32_t local_count; + WasmTypeVector param_and_local_types; + } defined; + struct { + WasmStringSlice module_name; + WasmStringSlice field_name; + WasmInterpreterHostFuncCallback callback; + void* user_data; + } host; + }; } WasmInterpreterFunc; WASM_DEFINE_VECTOR(interpreter_func, WasmInterpreterFunc); typedef struct WasmInterpreterExport { - WasmStringSlice name; + WasmStringSlice name; /* Owned by the export_bindings hash */ WasmExternalKind kind; uint32_t index; } WasmInterpreterExport; -WASM_DEFINE_ARRAY(interpreter_export, WasmInterpreterExport); +WASM_DEFINE_VECTOR(interpreter_export, WasmInterpreterExport); -typedef uint32_t WasmUint32; -WASM_DEFINE_ARRAY(uint32, WasmUint32); +typedef struct WasmInterpreterHostImportDelegate { + void *user_data; + WasmResult (*import_func)(WasmInterpreterImport*, + WasmInterpreterFunc*, + WasmInterpreterFuncSignature*, + void* user_data); + WasmResult (*import_table)(WasmInterpreterImport*, + WasmInterpreterTable*, + void* user_data); + WasmResult (*import_memory)(WasmInterpreterImport*, + WasmInterpreterMemory*, + void* user_data); + WasmResult (*import_global)(WasmInterpreterImport*, + WasmInterpreterGlobal*, + void* user_data); +} WasmInterpreterHostImportDelegate; typedef struct WasmInterpreterModule { - WasmInterpreterMemory memory; - WasmInterpreterFuncSignatureArray sigs; - WasmInterpreterFuncVector funcs; - WasmUint32Array func_table; - WasmInterpreterImportArray imports; - WasmInterpreterExportArray exports; - WasmInterpreterGlobalArray globals; - WasmOutputBuffer istream; - uint32_t start_func_index; /* == INVALID_FUNC_INDEX if not defined */ + WasmStringSlice name; + WasmInterpreterExportVector exports; + WasmBindingHash export_bindings; + uint32_t memory_index; /* INVALID_INDEX if not defined */ + uint32_t table_index; /* INVALID_INDEX if not defined */ + WasmBool is_host; + union { + struct { + WasmInterpreterImportArray imports; + uint32_t start_func_index; /* INVALID_INDEX if not defined */ + size_t istream_start; + size_t istream_end; + } defined; + struct { + WasmInterpreterHostImportDelegate import_delegate; + } host; + }; } WasmInterpreterModule; +WASM_DEFINE_VECTOR(interpreter_module, WasmInterpreterModule); -typedef WasmResult (*WasmInterpreterHostFuncCallback)( - const WasmInterpreterFuncSignature* sig, - const WasmStringSlice* module_name, - const WasmStringSlice* field_name, - uint32_t num_args, - WasmInterpreterTypedValue* args, - uint32_t num_results, - WasmInterpreterTypedValue* out_results, - void* user_data); +typedef struct WasmInterpreterEnvironment { + WasmInterpreterModuleVector modules; + WasmInterpreterFuncSignatureVector sigs; + WasmInterpreterFuncVector funcs; + WasmInterpreterMemoryVector memories; + WasmInterpreterTableVector tables; + WasmInterpreterGlobalVector globals; + WasmOutputBuffer istream; + WasmBindingHash module_bindings; +} WasmInterpreterEnvironment; typedef struct WasmInterpreterThread { + WasmInterpreterEnvironment* env; + WasmInterpreterModule* module; + WasmInterpreterTable* table; + WasmInterpreterMemory* memory; WasmInterpreterValueArray value_stack; WasmUint32Array call_stack; WasmInterpreterValue* value_stack_top; @@ -186,13 +245,8 @@ typedef struct WasmInterpreterThread { uint32_t* call_stack_end; uint32_t pc; - struct { - WasmInterpreterHostFuncCallback callback; - void* user_data; - } host_func; - - /* a temporary buffer that is for passing args to import functions */ - WasmInterpreterTypedValueArray import_args; + /* a temporary buffer that is for passing args to host functions */ + WasmInterpreterTypedValueArray host_args; } WasmInterpreterThread; #define WASM_INTERPRETER_THREAD_OPTIONS_DEFAULT \ @@ -205,7 +259,15 @@ typedef struct WasmInterpreterThreadOptions { } WasmInterpreterThreadOptions; WASM_EXTERN_C_BEGIN +void wasm_init_interpreter_environment(WasmAllocator* allocator, + WasmInterpreterEnvironment* env); +void wasm_destroy_interpreter_environment(WasmAllocator* allocator, + WasmInterpreterEnvironment* env); +WasmInterpreterModule* wasm_append_host_module(WasmAllocator* allocator, + WasmInterpreterEnvironment* env, + WasmStringSlice name); WasmResult wasm_init_interpreter_thread(WasmAllocator* allocator, + WasmInterpreterEnvironment* env, WasmInterpreterModule* module, WasmInterpreterThread* thread, WasmInterpreterThreadOptions* options); @@ -213,22 +275,19 @@ WasmInterpreterResult wasm_push_thread_value(WasmInterpreterThread* thread, WasmInterpreterValue value); void wasm_destroy_interpreter_thread(WasmAllocator* allocator, WasmInterpreterThread* thread); -WasmInterpreterResult wasm_call_host(WasmInterpreterModule* module, - WasmInterpreterThread* thread, - WasmInterpreterImport* import); -WasmInterpreterResult wasm_run_interpreter(WasmInterpreterModule* module, - WasmInterpreterThread* thread, +WasmInterpreterResult wasm_call_host(WasmInterpreterThread* thread, + WasmInterpreterFunc* func); +WasmInterpreterResult wasm_run_interpreter(WasmInterpreterThread* thread, uint32_t num_instructions, uint32_t* call_stack_return_top); -void wasm_trace_pc(WasmInterpreterModule* module, - WasmInterpreterThread* thread, - struct WasmStream* stream); -void wasm_disassemble_module(WasmInterpreterModule* module, +void wasm_trace_pc(WasmInterpreterThread* thread, struct WasmStream* stream); +void wasm_disassemble(WasmInterpreterEnvironment* env, + struct WasmStream* stream, + uint32_t from, + uint32_t to); +void wasm_disassemble_module(WasmInterpreterEnvironment* env, struct WasmStream* stream, - uint32_t from, - uint32_t to); -void wasm_destroy_interpreter_module(WasmAllocator* allocator, - WasmInterpreterModule* module); + WasmInterpreterModule* module); WasmInterpreterExport* wasm_get_interpreter_export_by_name( WasmInterpreterModule* module, diff --git a/src/wasm-writer.c b/src/wasm-writer.c index 1805078e..bef915b0 100644 --- a/src/wasm-writer.c +++ b/src/wasm-writer.c @@ -85,9 +85,10 @@ void wasm_close_file_writer(WasmFileWriter* writer) { fclose(writer->file); } -static void init_output_buffer(WasmAllocator* allocator, - WasmOutputBuffer* buf, - size_t initial_capacity) { +void wasm_init_output_buffer(WasmAllocator* allocator, + WasmOutputBuffer* buf, + size_t initial_capacity) { + assert(initial_capacity != 0); buf->allocator = allocator; buf->start = wasm_alloc(allocator, initial_capacity, WASM_DEFAULT_ALIGN); buf->size = 0; @@ -97,6 +98,7 @@ static void init_output_buffer(WasmAllocator* allocator, static void ensure_output_buffer_capacity(WasmOutputBuffer* buf, size_t ensure_capacity) { if (ensure_capacity > buf->capacity) { + assert(buf->capacity != 0); size_t new_capacity = buf->capacity * 2; while (new_capacity < ensure_capacity) new_capacity *= 2; @@ -142,7 +144,20 @@ WasmResult wasm_init_mem_writer(WasmAllocator* allocator, writer->base.user_data = writer; writer->base.write_data = write_data_to_output_buffer; writer->base.move_data = move_data_in_output_buffer; - init_output_buffer(allocator, &writer->buf, INITIAL_OUTPUT_BUFFER_CAPACITY); + wasm_init_output_buffer(allocator, &writer->buf, + INITIAL_OUTPUT_BUFFER_CAPACITY); + return WASM_OK; +} + +WasmResult wasm_init_mem_writer_existing(WasmMemoryWriter* writer, + WasmOutputBuffer* buf) { + WASM_ZERO_MEMORY(*writer); + writer->base.user_data = writer; + writer->base.write_data = write_data_to_output_buffer; + writer->base.move_data = move_data_in_output_buffer; + writer->buf = *buf; + /* Clear buffer, since ownership has passed to the writer. */ + WASM_ZERO_MEMORY(*buf); return WASM_OK; } diff --git a/src/wasm-writer.h b/src/wasm-writer.h index 11382a18..ad6046fd 100644 --- a/src/wasm-writer.h +++ b/src/wasm-writer.h @@ -62,11 +62,17 @@ void wasm_close_file_writer(WasmFileWriter* writer); /* WasmMemoryWriter */ WasmResult wasm_init_mem_writer(WasmAllocator* allocator, WasmMemoryWriter* writer); +/* Passes ownership of the buffer to writer */ +WasmResult wasm_init_mem_writer_existing(WasmMemoryWriter* writer, + WasmOutputBuffer* buf); void wasm_steal_mem_writer_output_buffer(WasmMemoryWriter* writer, WasmOutputBuffer* out_buf); void wasm_close_mem_writer(WasmMemoryWriter* writer); /* WasmOutputBuffer */ +void wasm_init_output_buffer(WasmAllocator* allocator, + WasmOutputBuffer* buf, + size_t initial_capacity); WasmResult wasm_write_output_buffer_to_file(WasmOutputBuffer* buf, const char* filename); void wasm_destroy_output_buffer(WasmOutputBuffer* buf); diff --git a/test/binary/bad-data-size.txt b/test/binary/bad-data-size.txt index 9ae34b53..3d60af8e 100644 --- a/test/binary/bad-data-size.txt +++ b/test/binary/bad-data-size.txt @@ -15,6 +15,7 @@ section(DATA) { } (;; STDERR ;;; Error running "wasm-interp": +error: data segment is out of bounds: [0, 8) >= max value 0 error: @0x0000001d: on_data_segment_data callback failed ;;; STDERR ;;) diff --git a/test/interp/callimport-zero-args.txt b/test/interp/callimport-zero-args.txt index 4ef162c7..68c86b18 100644 --- a/test/interp/callimport-zero-args.txt +++ b/test/interp/callimport-zero-args.txt @@ -1,12 +1,12 @@ ;;; TOOL: run-interp (module - (import "foo" "bar" (func $imported (result i32))) + (import "spectest" "print" (func $imported (result i32))) (func (export "f") (result i32) (i32.add (i32.const 13) (call $imported)))) (;; STDOUT ;;; -called host foo.bar() => (i32:0) +called host spectest.print() => (i32:0) f() => i32:13 ;;; STDOUT ;;) diff --git a/test/interp/import.txt b/test/interp/import.txt index fc02b0d4..90899592 100644 --- a/test/interp/import.txt +++ b/test/interp/import.txt @@ -1,14 +1,14 @@ ;;; TOOL: run-interp (module - (import "stdio" "print" (func $print_i32 (param i32))) - (import "stdio" "print" (func $print_i32_i32 (param i32 i32))) + (import "spectest" "print" (func $print_i32 (param i32))) + (import "spectest" "print" (func $print_i32_i32 (param i32 i32))) (func (export "test") (result i32) (call $print_i32 (i32.const 100)) (call $print_i32_i32 (i32.const 200) (i32.const 300)) (return (i32.const 1))) ) (;; STDOUT ;;; -called host stdio.print(i32:100) => () -called host stdio.print(i32:200, i32:300) => () +called host spectest.print(i32:100) => () +called host spectest.print(i32:200, i32:300) => () test() => i32:1 ;;; STDOUT ;;) |