summaryrefslogtreecommitdiff
path: root/src/binary-reader-interpreter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/binary-reader-interpreter.cc')
-rw-r--r--src/binary-reader-interpreter.cc1585
1 files changed, 1585 insertions, 0 deletions
diff --git a/src/binary-reader-interpreter.cc b/src/binary-reader-interpreter.cc
new file mode 100644
index 00000000..b325e1f3
--- /dev/null
+++ b/src/binary-reader-interpreter.cc
@@ -0,0 +1,1585 @@
+/*
+ * 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 "binary-reader-interpreter.h"
+
+#include <assert.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "binary-reader.h"
+#include "interpreter.h"
+#include "type-checker.h"
+#include "writer.h"
+
+#define CHECK_RESULT(expr) \
+ do { \
+ if (WABT_FAILED(expr)) \
+ return WABT_ERROR; \
+ } while (0)
+
+#define CHECK_LOCAL(ctx, local_index) \
+ do { \
+ uint32_t max_local_index = \
+ (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); \
+ return WABT_ERROR; \
+ } \
+ } while (0)
+
+#define CHECK_GLOBAL(ctx, global_index) \
+ do { \
+ 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); \
+ return WABT_ERROR; \
+ } \
+ } while (0)
+
+typedef uint32_t Uint32;
+WABT_DEFINE_VECTOR(uint32, Uint32);
+WABT_DEFINE_VECTOR(uint32_vector, Uint32Vector);
+
+typedef struct Label {
+ uint32_t offset; /* branch location in the istream */
+ uint32_t fixup_offset;
+} Label;
+WABT_DEFINE_VECTOR(label, Label);
+
+typedef struct Context {
+ WabtBinaryReader* reader;
+ WabtBinaryErrorHandler* error_handler;
+ WabtInterpreterEnvironment* env;
+ WabtInterpreterModule* module;
+ WabtInterpreterFunc* current_func;
+ WabtTypeChecker typechecker;
+ LabelVector label_stack;
+ Uint32VectorVector func_fixups;
+ Uint32VectorVector depth_fixups;
+ WabtMemoryWriter 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;
+ uint32_t num_global_imports;
+
+ /* values cached in the Context so they can be shared between callbacks */
+ WabtInterpreterTypedValue init_expr_value;
+ uint32_t table_offset;
+ bool is_host_import;
+ WabtInterpreterModule* host_import_module;
+ uint32_t import_env_index;
+} Context;
+
+static Label* get_label(Context* ctx, uint32_t depth) {
+ assert(depth < ctx->label_stack.size);
+ return &ctx->label_stack.data[ctx->label_stack.size - depth - 1];
+}
+
+static Label* top_label(Context* ctx) {
+ return get_label(ctx, 0);
+}
+
+static void handle_error(uint32_t offset, const char* message, Context* ctx) {
+ if (ctx->error_handler->on_error) {
+ ctx->error_handler->on_error(offset, message,
+ ctx->error_handler->user_data);
+ }
+}
+
+static void WABT_PRINTF_FORMAT(2, 3)
+ print_error(Context* ctx, const char* format, ...) {
+ WABT_SNPRINTF_ALLOCA(buffer, length, format);
+ handle_error(WABT_INVALID_OFFSET, buffer, ctx);
+}
+
+static void on_typechecker_error(const char* msg, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ print_error(ctx, "%s", msg);
+}
+
+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 WabtInterpreterFuncSignature* 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 WabtInterpreterFuncSignature* 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 uint32_t translate_module_func_index_to_defined(Context* ctx,
+ uint32_t func_index) {
+ assert(func_index >= ctx->num_func_imports);
+ return func_index - ctx->num_func_imports;
+}
+
+static WabtInterpreterFunc* 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 WabtInterpreterFunc* 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 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 WabtInterpreterGlobal* 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 WabtInterpreterGlobal* 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 WabtType 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 WabtType get_local_type_by_index(WabtInterpreterFunc* 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 get_istream_offset(Context* ctx) {
+ return ctx->istream_offset;
+}
+
+static WabtResult emit_data_at(Context* ctx,
+ size_t offset,
+ const void* data,
+ size_t size) {
+ return ctx->istream_writer.base.write_data(
+ offset, data, size, ctx->istream_writer.base.user_data);
+}
+
+static WabtResult emit_data(Context* ctx, const void* data, size_t size) {
+ CHECK_RESULT(emit_data_at(ctx, ctx->istream_offset, data, size));
+ ctx->istream_offset += size;
+ return WABT_OK;
+}
+
+static WabtResult emit_opcode(Context* ctx, WabtOpcode opcode) {
+ return emit_data(ctx, &opcode, sizeof(uint8_t));
+}
+
+static WabtResult emit_i8(Context* ctx, uint8_t value) {
+ return emit_data(ctx, &value, sizeof(value));
+}
+
+static WabtResult emit_i32(Context* ctx, uint32_t value) {
+ return emit_data(ctx, &value, sizeof(value));
+}
+
+static WabtResult emit_i64(Context* ctx, uint64_t value) {
+ return emit_data(ctx, &value, sizeof(value));
+}
+
+static WabtResult emit_i32_at(Context* ctx, uint32_t offset, uint32_t value) {
+ return emit_data_at(ctx, offset, &value, sizeof(value));
+}
+
+static WabtResult emit_drop_keep(Context* ctx, uint32_t drop, uint8_t keep) {
+ assert(drop != UINT32_MAX);
+ assert(keep <= 1);
+ if (drop > 0) {
+ if (drop == 1 && keep == 0) {
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_DROP));
+ } else {
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_DROP_KEEP));
+ CHECK_RESULT(emit_i32(ctx, drop));
+ CHECK_RESULT(emit_i8(ctx, keep));
+ }
+ }
+ return WABT_OK;
+}
+
+static WabtResult append_fixup(Context* ctx,
+ Uint32VectorVector* fixups_vector,
+ uint32_t index) {
+ if (index >= fixups_vector->size)
+ wabt_resize_uint32_vector_vector(fixups_vector, index + 1);
+ Uint32Vector* fixups = &fixups_vector->data[index];
+ uint32_t offset = get_istream_offset(ctx);
+ wabt_append_uint32_value(fixups, &offset);
+ return WABT_OK;
+}
+
+static WabtResult emit_br_offset(Context* ctx,
+ uint32_t depth,
+ uint32_t offset) {
+ if (offset == WABT_INVALID_OFFSET) {
+ /* depth_fixups stores the depth counting up from zero, where zero is the
+ * top-level function scope. */
+ depth = ctx->label_stack.size - 1 - depth;
+ CHECK_RESULT(
+ append_fixup(ctx, &ctx->depth_fixups, depth));
+ }
+ CHECK_RESULT(emit_i32(ctx, offset));
+ return WABT_OK;
+}
+
+static WabtResult get_br_drop_keep_count(Context* ctx,
+ uint32_t depth,
+ uint32_t* out_drop_count,
+ uint32_t* out_keep_count) {
+ WabtTypeCheckerLabel* label;
+ CHECK_RESULT(wabt_typechecker_get_label(&ctx->typechecker, depth, &label));
+ *out_keep_count =
+ label->label_type != WABT_LABEL_TYPE_LOOP ? label->sig.size : 0;
+ if (wabt_typechecker_is_unreachable(&ctx->typechecker)) {
+ *out_drop_count = 0;
+ } else {
+ *out_drop_count =
+ (ctx->typechecker.type_stack.size - label->type_stack_limit) -
+ *out_keep_count;
+ }
+ return WABT_OK;
+}
+
+static WabtResult get_return_drop_keep_count(Context* ctx,
+ uint32_t* out_drop_count,
+ uint32_t* out_keep_count) {
+ if (WABT_FAILED(get_br_drop_keep_count(ctx, ctx->label_stack.size - 1,
+ out_drop_count, out_keep_count))) {
+ return WABT_ERROR;
+ }
+
+ *out_drop_count += ctx->current_func->defined.param_and_local_types.size;
+ return WABT_OK;
+}
+
+static WabtResult emit_br(Context* ctx,
+ uint32_t depth,
+ uint32_t drop_count,
+ uint32_t keep_count) {
+ CHECK_RESULT(emit_drop_keep(ctx, drop_count, keep_count));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_BR));
+ CHECK_RESULT(emit_br_offset(ctx, depth, get_label(ctx, depth)->offset));
+ return WABT_OK;
+}
+
+static WabtResult emit_br_table_offset(Context* ctx, uint32_t depth) {
+ uint32_t drop_count, keep_count;
+ CHECK_RESULT(get_br_drop_keep_count(ctx, depth, &drop_count, &keep_count));
+ CHECK_RESULT(emit_br_offset(ctx, depth, get_label(ctx, depth)->offset));
+ CHECK_RESULT(emit_i32(ctx, drop_count));
+ CHECK_RESULT(emit_i8(ctx, keep_count));
+ return WABT_OK;
+}
+
+static WabtResult fixup_top_label(Context* ctx) {
+ uint32_t offset = get_istream_offset(ctx);
+ uint32_t top = ctx->label_stack.size - 1;
+ if (top >= ctx->depth_fixups.size) {
+ /* nothing to fixup */
+ return WABT_OK;
+ }
+
+ Uint32Vector* fixups = &ctx->depth_fixups.data[top];
+ uint32_t i;
+ for (i = 0; i < fixups->size; ++i)
+ CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], offset));
+ /* reduce the size to 0 in case this gets reused. Keep the allocations for
+ * later use */
+ fixups->size = 0;
+ return WABT_OK;
+}
+
+static WabtResult emit_func_offset(Context* ctx,
+ WabtInterpreterFunc* func,
+ uint32_t func_index) {
+ if (func->defined.offset == WABT_INVALID_OFFSET) {
+ uint32_t defined_index =
+ translate_module_func_index_to_defined(ctx, func_index);
+ CHECK_RESULT(append_fixup(ctx, &ctx->func_fixups, defined_index));
+ }
+ CHECK_RESULT(emit_i32(ctx, func->defined.offset));
+ return WABT_OK;
+}
+
+static void on_error(WabtBinaryReaderContext* ctx, const char* message) {
+ handle_error(ctx->offset, message, (Context*)ctx->user_data);
+}
+
+static WabtResult on_signature_count(uint32_t count, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ wabt_resize_uint32_vector(&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;
+ wabt_resize_interpreter_func_signature_vector(&ctx->env->sigs,
+ ctx->env->sigs.size + count);
+ return WABT_OK;
+}
+
+static WabtResult on_signature(uint32_t index,
+ uint32_t param_count,
+ WabtType* param_types,
+ uint32_t result_count,
+ WabtType* result_types,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterFuncSignature* sig = get_signature_by_module_index(ctx, index);
+
+ wabt_reserve_types(&sig->param_types, param_count);
+ sig->param_types.size = param_count;
+ memcpy(sig->param_types.data, param_types, param_count * sizeof(WabtType));
+
+ wabt_reserve_types(&sig->result_types, result_count);
+ sig->result_types.size = result_count;
+ memcpy(sig->result_types.data, result_types, result_count * sizeof(WabtType));
+ return WABT_OK;
+}
+
+static WabtResult on_import_count(uint32_t count, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ wabt_new_interpreter_import_array(&ctx->module->defined.imports, count);
+ return WABT_OK;
+}
+
+static WabtResult on_import(uint32_t index,
+ WabtStringSlice module_name,
+ WabtStringSlice field_name,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(index < ctx->module->defined.imports.size);
+ WabtInterpreterImport* import = &ctx->module->defined.imports.data[index];
+ import->module_name = wabt_dup_string_slice(module_name);
+ import->field_name = wabt_dup_string_slice(field_name);
+ int module_index = wabt_find_binding_index_by_name(
+ &ctx->env->registered_module_bindings, &import->module_name);
+ if (module_index < 0) {
+ print_error(ctx, "unknown import module \"" PRIstringslice "\"",
+ WABT_PRINTF_STRING_SLICE_ARG(import->module_name));
+ return WABT_ERROR;
+ }
+
+ assert((size_t)module_index < ctx->env->modules.size);
+ WabtInterpreterModule* 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 = true;
+ ctx->host_import_module = module;
+ } else {
+ WabtInterpreterExport* export_ =
+ wabt_get_interpreter_export_by_name(module, &import->field_name);
+ if (!export_) {
+ print_error(ctx, "unknown module field \"" PRIstringslice "\"",
+ WABT_PRINTF_STRING_SLICE_ARG(import->field_name));
+ return WABT_ERROR;
+ }
+
+ import->kind = export_->kind;
+ ctx->is_host_import = false;
+ ctx->import_env_index = export_->index;
+ }
+ return WABT_OK;
+}
+
+static WabtResult check_import_kind(Context* ctx,
+ WabtInterpreterImport* import,
+ WabtExternalKind expected_kind) {
+ if (import->kind != expected_kind) {
+ print_error(ctx, "expected import \"" PRIstringslice "." PRIstringslice
+ "\" to have kind %s, not %s",
+ WABT_PRINTF_STRING_SLICE_ARG(import->module_name),
+ WABT_PRINTF_STRING_SLICE_ARG(import->field_name),
+ wabt_get_kind_name(expected_kind),
+ wabt_get_kind_name(import->kind));
+ return WABT_ERROR;
+ }
+ return WABT_OK;
+}
+
+static WabtResult check_import_limits(Context* ctx,
+ const WabtLimits* declared_limits,
+ const WabtLimits* actual_limits) {
+ if (actual_limits->initial < declared_limits->initial) {
+ print_error(ctx,
+ "actual size (%" PRIu64 ") smaller than declared (%" PRIu64 ")",
+ actual_limits->initial, declared_limits->initial);
+ return WABT_ERROR;
+ }
+
+ if (declared_limits->has_max) {
+ if (!actual_limits->has_max) {
+ print_error(ctx,
+ "max size (unspecified) larger than declared (%" PRIu64 ")",
+ declared_limits->max);
+ return WABT_ERROR;
+ } else if (actual_limits->max > declared_limits->max) {
+ print_error(ctx,
+ "max size (%" PRIu64 ") larger than declared (%" PRIu64 ")",
+ actual_limits->max, declared_limits->max);
+ return WABT_ERROR;
+ }
+ }
+
+ return WABT_OK;
+}
+
+static WabtResult append_export(Context* ctx,
+ WabtInterpreterModule* module,
+ WabtExternalKind kind,
+ uint32_t item_index,
+ WabtStringSlice name) {
+ if (wabt_find_binding_index_by_name(&module->export_bindings, &name) != -1) {
+ print_error(ctx, "duplicate export \"" PRIstringslice "\"",
+ WABT_PRINTF_STRING_SLICE_ARG(name));
+ return WABT_ERROR;
+ }
+
+ WabtInterpreterExport* export_ =
+ wabt_append_interpreter_export(&module->exports);
+ export_->name = wabt_dup_string_slice(name);
+ export_->kind = kind;
+ export_->index = item_index;
+
+ WabtBinding* binding =
+ wabt_insert_binding(&module->export_bindings, &export_->name);
+ binding->index = module->exports.size - 1;
+ return WABT_OK;
+}
+
+static void on_host_import_print_error(const char* msg, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ print_error(ctx, "%s", msg);
+}
+
+static WabtPrintErrorCallback make_print_error_callback(Context* ctx) {
+ WabtPrintErrorCallback result;
+ result.print_error = on_host_import_print_error;
+ result.user_data = ctx;
+ return result;
+}
+
+static WabtResult on_import_func(uint32_t import_index,
+ uint32_t func_index,
+ uint32_t sig_index,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(import_index < ctx->module->defined.imports.size);
+ WabtInterpreterImport* import =
+ &ctx->module->defined.imports.data[import_index];
+ assert(sig_index < ctx->env->sigs.size);
+ import->func.sig_index = translate_sig_index_to_env(ctx, sig_index);
+
+ uint32_t func_env_index;
+ if (ctx->is_host_import) {
+ WabtInterpreterFunc* func = wabt_append_interpreter_func(&ctx->env->funcs);
+ func->is_host = true;
+ func->sig_index = import->func.sig_index;
+ func->host.module_name = import->module_name;
+ func->host.field_name = import->field_name;
+
+ WabtInterpreterHostImportDelegate* host_delegate =
+ &ctx->host_import_module->host.import_delegate;
+ WabtInterpreterFuncSignature* sig = &ctx->env->sigs.data[func->sig_index];
+ CHECK_RESULT(host_delegate->import_func(import, func, sig,
+ make_print_error_callback(ctx),
+ host_delegate->user_data));
+ assert(func->host.callback);
+
+ func_env_index = ctx->env->funcs.size - 1;
+ append_export(ctx, ctx->host_import_module, WABT_EXTERNAL_KIND_FUNC,
+ func_env_index, import->field_name);
+ } else {
+ CHECK_RESULT(check_import_kind(ctx, import, WABT_EXTERNAL_KIND_FUNC));
+ assert(ctx->import_env_index < ctx->env->funcs.size);
+ WabtInterpreterFunc* func = &ctx->env->funcs.data[ctx->import_env_index];
+ if (!wabt_func_signatures_are_equal(ctx->env, import->func.sig_index,
+ func->sig_index)) {
+ print_error(ctx, "import signature mismatch");
+ return WABT_ERROR;
+ }
+
+ func_env_index = ctx->import_env_index;
+ }
+ wabt_append_uint32_value(&ctx->func_index_mapping, &func_env_index);
+ ctx->num_func_imports++;
+ return WABT_OK;
+}
+
+static void init_table_func_indexes(Context* ctx, WabtInterpreterTable* table) {
+ wabt_new_uint32_array(&table->func_indexes, table->limits.initial);
+ size_t i;
+ for (i = 0; i < table->func_indexes.size; ++i)
+ table->func_indexes.data[i] = WABT_INVALID_INDEX;
+}
+
+static WabtResult on_import_table(uint32_t import_index,
+ uint32_t table_index,
+ WabtType elem_type,
+ const WabtLimits* elem_limits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->module->table_index != WABT_INVALID_INDEX) {
+ print_error(ctx, "only one table allowed");
+ return WABT_ERROR;
+ }
+
+ assert(import_index < ctx->module->defined.imports.size);
+ WabtInterpreterImport* import =
+ &ctx->module->defined.imports.data[import_index];
+
+ if (ctx->is_host_import) {
+ WabtInterpreterTable* table =
+ wabt_append_interpreter_table(&ctx->env->tables);
+ table->limits = *elem_limits;
+ init_table_func_indexes(ctx, table);
+
+ WabtInterpreterHostImportDelegate* host_delegate =
+ &ctx->host_import_module->host.import_delegate;
+ CHECK_RESULT(host_delegate->import_table(import, table,
+ make_print_error_callback(ctx),
+ host_delegate->user_data));
+
+ CHECK_RESULT(check_import_limits(ctx, elem_limits, &table->limits));
+
+ ctx->module->table_index = ctx->env->tables.size - 1;
+ append_export(ctx, ctx->host_import_module, WABT_EXTERNAL_KIND_TABLE,
+ ctx->module->table_index, import->field_name);
+ } else {
+ CHECK_RESULT(check_import_kind(ctx, import, WABT_EXTERNAL_KIND_TABLE));
+ assert(ctx->import_env_index < ctx->env->tables.size);
+ WabtInterpreterTable* table = &ctx->env->tables.data[ctx->import_env_index];
+ CHECK_RESULT(check_import_limits(ctx, elem_limits, &table->limits));
+
+ import->table.limits = *elem_limits;
+ ctx->module->table_index = ctx->import_env_index;
+ }
+ return WABT_OK;
+}
+
+static WabtResult on_import_memory(uint32_t import_index,
+ uint32_t memory_index,
+ const WabtLimits* page_limits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->module->memory_index != WABT_INVALID_INDEX) {
+ print_error(ctx, "only one memory allowed");
+ return WABT_ERROR;
+ }
+
+ assert(import_index < ctx->module->defined.imports.size);
+ WabtInterpreterImport* import =
+ &ctx->module->defined.imports.data[import_index];
+
+ if (ctx->is_host_import) {
+ WabtInterpreterMemory* memory =
+ wabt_append_interpreter_memory(&ctx->env->memories);
+
+ WabtInterpreterHostImportDelegate* host_delegate =
+ &ctx->host_import_module->host.import_delegate;
+ CHECK_RESULT(host_delegate->import_memory(import, memory,
+ make_print_error_callback(ctx),
+ host_delegate->user_data));
+
+ assert(memory->data);
+ CHECK_RESULT(check_import_limits(ctx, page_limits, &memory->page_limits));
+
+ ctx->module->memory_index = ctx->env->memories.size - 1;
+ append_export(ctx, ctx->host_import_module, WABT_EXTERNAL_KIND_MEMORY,
+ ctx->module->memory_index, import->field_name);
+ } else {
+ CHECK_RESULT(check_import_kind(ctx, import, WABT_EXTERNAL_KIND_MEMORY));
+ assert(ctx->import_env_index < ctx->env->memories.size);
+ WabtInterpreterMemory* memory =
+ &ctx->env->memories.data[ctx->import_env_index];
+ CHECK_RESULT(check_import_limits(ctx, page_limits, &memory->page_limits));
+
+ import->memory.limits = *page_limits;
+ ctx->module->memory_index = ctx->import_env_index;
+ }
+ return WABT_OK;
+}
+
+static WabtResult on_import_global(uint32_t import_index,
+ uint32_t global_index,
+ WabtType type,
+ bool mutable_,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(import_index < ctx->module->defined.imports.size);
+ WabtInterpreterImport* import =
+ &ctx->module->defined.imports.data[import_index];
+
+ uint32_t global_env_index = ctx->env->globals.size - 1;
+ if (ctx->is_host_import) {
+ WabtInterpreterGlobal* global =
+ wabt_append_interpreter_global(&ctx->env->globals);
+ global->typed_value.type = type;
+ global->mutable_ = mutable_;
+
+ WabtInterpreterHostImportDelegate* host_delegate =
+ &ctx->host_import_module->host.import_delegate;
+ CHECK_RESULT(host_delegate->import_global(import, global,
+ make_print_error_callback(ctx),
+ host_delegate->user_data));
+
+ global_env_index = ctx->env->globals.size - 1;
+ append_export(ctx, ctx->host_import_module, WABT_EXTERNAL_KIND_GLOBAL,
+ global_env_index, import->field_name);
+ } else {
+ CHECK_RESULT(check_import_kind(ctx, import, WABT_EXTERNAL_KIND_GLOBAL));
+ // TODO: check type and mutability
+ import->global.type = type;
+ import->global.mutable_ = mutable_;
+ global_env_index = ctx->import_env_index;
+ }
+ wabt_append_uint32_value(&ctx->global_index_mapping, &global_env_index);
+ ctx->num_global_imports++;
+ return WABT_OK;
+}
+
+static WabtResult on_function_signatures_count(uint32_t count,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ size_t old_size = ctx->func_index_mapping.size;
+ wabt_resize_uint32_vector(&ctx->func_index_mapping, old_size + count);
+ uint32_t i;
+ for (i = 0; i < count; ++i)
+ ctx->func_index_mapping.data[old_size + i] = ctx->env->funcs.size + i;
+ wabt_resize_interpreter_func_vector(&ctx->env->funcs,
+ ctx->env->funcs.size + count);
+ wabt_resize_uint32_vector_vector(&ctx->func_fixups, count);
+ return WABT_OK;
+}
+
+static WabtResult on_function_signature(uint32_t index,
+ uint32_t sig_index,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterFunc* func = get_func_by_module_index(ctx, index);
+ func->defined.offset = WABT_INVALID_OFFSET;
+ func->sig_index = translate_sig_index_to_env(ctx, sig_index);
+ return WABT_OK;
+}
+
+static WabtResult on_table(uint32_t index,
+ WabtType elem_type,
+ const WabtLimits* elem_limits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->module->table_index != WABT_INVALID_INDEX) {
+ print_error(ctx, "only one table allowed");
+ return WABT_ERROR;
+ }
+ WabtInterpreterTable* table =
+ wabt_append_interpreter_table(&ctx->env->tables);
+ table->limits = *elem_limits;
+ init_table_func_indexes(ctx, table);
+ ctx->module->table_index = ctx->env->tables.size - 1;
+ return WABT_OK;
+}
+
+static WabtResult on_memory(uint32_t index,
+ const WabtLimits* page_limits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->module->memory_index != WABT_INVALID_INDEX) {
+ print_error(ctx, "only one memory allowed");
+ return WABT_ERROR;
+ }
+ WabtInterpreterMemory* memory =
+ wabt_append_interpreter_memory(&ctx->env->memories);
+ memory->page_limits = *page_limits;
+ memory->byte_size = page_limits->initial * WABT_PAGE_SIZE;
+ memory->data = wabt_alloc_zero(memory->byte_size);
+ ctx->module->memory_index = ctx->env->memories.size - 1;
+ return WABT_OK;
+}
+
+static WabtResult on_global_count(uint32_t count, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ size_t old_size = ctx->global_index_mapping.size;
+ wabt_resize_uint32_vector(&ctx->global_index_mapping, old_size + count);
+ uint32_t i;
+ for (i = 0; i < count; ++i)
+ ctx->global_index_mapping.data[old_size + i] = ctx->env->globals.size + i;
+ wabt_resize_interpreter_global_vector(&ctx->env->globals,
+ ctx->env->globals.size + count);
+ return WABT_OK;
+}
+
+static WabtResult begin_global(uint32_t index,
+ WabtType type,
+ bool mutable_,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterGlobal* global = get_global_by_module_index(ctx, index);
+ global->typed_value.type = type;
+ global->mutable_ = mutable_;
+ return WABT_OK;
+}
+
+static WabtResult end_global_init_expr(uint32_t index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterGlobal* global = get_global_by_module_index(ctx, index);
+ if (ctx->init_expr_value.type != global->typed_value.type) {
+ print_error(ctx, "type mismatch in global, expected %s but got %s.",
+ wabt_get_type_name(global->typed_value.type),
+ wabt_get_type_name(ctx->init_expr_value.type));
+ return WABT_ERROR;
+ }
+ global->typed_value = ctx->init_expr_value;
+ return WABT_OK;
+}
+
+static WabtResult on_init_expr_f32_const_expr(uint32_t index,
+ uint32_t value_bits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ ctx->init_expr_value.type = WABT_TYPE_F32;
+ ctx->init_expr_value.value.f32_bits = value_bits;
+ return WABT_OK;
+}
+
+static WabtResult on_init_expr_f64_const_expr(uint32_t index,
+ uint64_t value_bits,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ ctx->init_expr_value.type = WABT_TYPE_F64;
+ ctx->init_expr_value.value.f64_bits = value_bits;
+ return WABT_OK;
+}
+
+static WabtResult on_init_expr_get_global_expr(uint32_t index,
+ uint32_t global_index,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (global_index >= ctx->num_global_imports) {
+ print_error(ctx,
+ "initializer expression can only reference an imported global");
+ return WABT_ERROR;
+ }
+ WabtInterpreterGlobal* ref_global =
+ get_global_by_module_index(ctx, global_index);
+ if (ref_global->mutable_) {
+ print_error(ctx,
+ "initializer expression cannot reference a mutable global");
+ return WABT_ERROR;
+ }
+ ctx->init_expr_value = ref_global->typed_value;
+ return WABT_OK;
+}
+
+static WabtResult on_init_expr_i32_const_expr(uint32_t index,
+ uint32_t value,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ ctx->init_expr_value.type = WABT_TYPE_I32;
+ ctx->init_expr_value.value.i32 = value;
+ return WABT_OK;
+}
+
+static WabtResult on_init_expr_i64_const_expr(uint32_t index,
+ uint64_t value,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ ctx->init_expr_value.type = WABT_TYPE_I64;
+ ctx->init_expr_value.value.i64 = value;
+ return WABT_OK;
+}
+
+static WabtResult on_export(uint32_t index,
+ WabtExternalKind kind,
+ uint32_t item_index,
+ WabtStringSlice name,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ switch (kind) {
+ case WABT_EXTERNAL_KIND_FUNC:
+ item_index = translate_func_index_to_env(ctx, item_index);
+ break;
+
+ case WABT_EXTERNAL_KIND_TABLE:
+ item_index = ctx->module->table_index;
+ break;
+
+ case WABT_EXTERNAL_KIND_MEMORY:
+ item_index = ctx->module->memory_index;
+ break;
+
+ case WABT_EXTERNAL_KIND_GLOBAL: {
+ item_index = translate_global_index_to_env(ctx, item_index);
+ WabtInterpreterGlobal* global = &ctx->env->globals.data[item_index];
+ if (global->mutable_) {
+ print_error(ctx, "mutable globals cannot be exported");
+ return WABT_ERROR;
+ }
+ break;
+ }
+
+ case WABT_NUM_EXTERNAL_KINDS:
+ assert(0);
+ break;
+ }
+ return append_export(ctx, ctx->module, kind, item_index, name);
+}
+
+static WabtResult on_start_function(uint32_t func_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ uint32_t start_func_index = translate_func_index_to_env(ctx, func_index);
+ WabtInterpreterFunc* start_func =
+ get_func_by_env_index(ctx, start_func_index);
+ WabtInterpreterFuncSignature* sig =
+ get_signature_by_env_index(ctx, start_func->sig_index);
+ if (sig->param_types.size != 0) {
+ print_error(ctx, "start function must be nullary");
+ return WABT_ERROR;
+ }
+ if (sig->result_types.size != 0) {
+ print_error(ctx, "start function must not return anything");
+ return WABT_ERROR;
+ }
+ ctx->module->defined.start_func_index = start_func_index;
+ return WABT_OK;
+}
+
+static WabtResult end_elem_segment_init_expr(uint32_t index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->init_expr_value.type != WABT_TYPE_I32) {
+ print_error(ctx, "type mismatch in elem segment, expected i32 but got %s",
+ wabt_get_type_name(ctx->init_expr_value.type));
+ return WABT_ERROR;
+ }
+ ctx->table_offset = ctx->init_expr_value.value.i32;
+ return WABT_OK;
+}
+
+static WabtResult on_elem_segment_function_index_check(uint32_t index,
+ uint32_t func_index,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(ctx->module->table_index != WABT_INVALID_INDEX);
+ WabtInterpreterTable* 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 WABT_ERROR;
+ }
+
+ uint32_t max_func_index = ctx->func_index_mapping.size;
+ if (func_index >= max_func_index) {
+ print_error(ctx, "invalid func_index: %d (max %d)", func_index,
+ max_func_index);
+ return WABT_ERROR;
+ }
+
+ table->func_indexes.data[ctx->table_offset++] =
+ translate_func_index_to_env(ctx, func_index);
+ return WABT_OK;
+}
+
+static WabtResult on_elem_segment_function_index(uint32_t index,
+ uint32_t func_index,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(ctx->module->table_index != WABT_INVALID_INDEX);
+ WabtInterpreterTable* table =
+ &ctx->env->tables.data[ctx->module->table_index];
+ table->func_indexes.data[ctx->table_offset++] =
+ translate_func_index_to_env(ctx, func_index);
+ return WABT_OK;
+}
+
+static WabtResult on_data_segment_data_check(uint32_t index,
+ const void* src_data,
+ uint32_t size,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(ctx->module->memory_index != WABT_INVALID_INDEX);
+ WabtInterpreterMemory* memory =
+ &ctx->env->memories.data[ctx->module->memory_index];
+ if (ctx->init_expr_value.type != WABT_TYPE_I32) {
+ print_error(ctx, "type mismatch in data segment, expected i32 but got %s",
+ wabt_get_type_name(ctx->init_expr_value.type));
+ return WABT_ERROR;
+ }
+ uint32_t address = ctx->init_expr_value.value.i32;
+ uint64_t end_address = (uint64_t)address + (uint64_t)size;
+ if (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 WABT_ERROR;
+ }
+ return WABT_OK;
+}
+
+static WabtResult on_data_segment_data(uint32_t index,
+ const void* src_data,
+ uint32_t size,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ assert(ctx->module->memory_index != WABT_INVALID_INDEX);
+ WabtInterpreterMemory* memory =
+ &ctx->env->memories.data[ctx->module->memory_index];
+ uint32_t address = ctx->init_expr_value.value.i32;
+ uint8_t* dst_data = (uint8_t*)memory->data;
+ memcpy(&dst_data[address], src_data, size);
+ return WABT_OK;
+}
+
+static void push_label(Context* ctx, uint32_t offset, uint32_t fixup_offset) {
+ Label* label = wabt_append_label(&ctx->label_stack);
+ label->offset = offset;
+ label->fixup_offset = fixup_offset;
+}
+
+static void pop_label(Context* ctx) {
+ ctx->label_stack.size--;
+ /* reduce the depth_fixups stack as well, but it may be smaller than
+ * label_stack so only do it conditionally. */
+ if (ctx->depth_fixups.size > ctx->label_stack.size) {
+ uint32_t from = ctx->label_stack.size;
+ uint32_t to = ctx->depth_fixups.size;
+ uint32_t i;
+ for (i = from; i < to; ++i)
+ wabt_destroy_uint32_vector(&ctx->depth_fixups.data[i]);
+ ctx->depth_fixups.size = ctx->label_stack.size;
+ }
+}
+
+static WabtResult begin_function_body(WabtBinaryReaderContext* context,
+ uint32_t index) {
+ Context* ctx = (Context*)context->user_data;
+ WabtInterpreterFunc* func = get_func_by_module_index(ctx, index);
+ WabtInterpreterFuncSignature* sig =
+ get_signature_by_env_index(ctx, func->sig_index);
+
+ func->is_host = 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;
+ ctx->label_stack.size = 0;
+
+ /* fixup function references */
+ uint32_t defined_index = translate_module_func_index_to_defined(ctx, index);
+ uint32_t i;
+ Uint32Vector* fixups = &ctx->func_fixups.data[defined_index];
+ for (i = 0; i < fixups->size; ++i)
+ CHECK_RESULT(emit_i32_at(ctx, fixups->data[i], func->defined.offset));
+
+ /* append param types */
+ for (i = 0; i < sig->param_types.size; ++i) {
+ wabt_append_type_value(&func->defined.param_and_local_types,
+ &sig->param_types.data[i]);
+ }
+
+ CHECK_RESULT(
+ wabt_typechecker_begin_function(&ctx->typechecker, &sig->result_types));
+
+ /* push implicit func label (equivalent to return) */
+ push_label(ctx, WABT_INVALID_OFFSET, WABT_INVALID_OFFSET);
+ return WABT_OK;
+}
+
+static WabtResult end_function_body(uint32_t index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ fixup_top_label(ctx);
+ uint32_t drop_count, keep_count;
+ CHECK_RESULT(get_return_drop_keep_count(ctx, &drop_count, &keep_count));
+ CHECK_RESULT(wabt_typechecker_end_function(&ctx->typechecker));
+ CHECK_RESULT(emit_drop_keep(ctx, drop_count, keep_count));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_RETURN));
+ pop_label(ctx);
+ ctx->current_func = NULL;
+ return WABT_OK;
+}
+
+static WabtResult on_local_decl_count(uint32_t count, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterFunc* func = ctx->current_func;
+ func->defined.local_decl_count = count;
+ return WABT_OK;
+}
+
+static WabtResult on_local_decl(uint32_t decl_index,
+ uint32_t count,
+ WabtType type,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterFunc* func = ctx->current_func;
+ func->defined.local_count += count;
+
+ uint32_t i;
+ for (i = 0; i < count; ++i) {
+ wabt_append_type_value(&func->defined.param_and_local_types, &type);
+ }
+
+ if (decl_index == func->defined.local_decl_count - 1) {
+ /* last local declaration, allocate space for all locals. */
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_ALLOCA));
+ CHECK_RESULT(emit_i32(ctx, func->defined.local_count));
+ }
+ return WABT_OK;
+}
+
+static WabtResult check_has_memory(Context* ctx, WabtOpcode opcode) {
+ if (ctx->module->memory_index == WABT_INVALID_INDEX) {
+ print_error(ctx, "%s requires an imported or defined memory.",
+ wabt_get_opcode_name(opcode));
+ return WABT_ERROR;
+ }
+ return WABT_OK;
+}
+
+static WabtResult check_align(Context* ctx,
+ uint32_t alignment_log2,
+ uint32_t natural_alignment) {
+ if (alignment_log2 >= 32 || (1U << alignment_log2) > natural_alignment) {
+ print_error(ctx, "alignment must not be larger than natural alignment (%u)",
+ natural_alignment);
+ return WABT_ERROR;
+ }
+ return WABT_OK;
+}
+
+static WabtResult on_unary_expr(WabtOpcode opcode, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_unary(&ctx->typechecker, opcode));
+ CHECK_RESULT(emit_opcode(ctx, opcode));
+ return WABT_OK;
+}
+
+static WabtResult on_binary_expr(WabtOpcode opcode, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_binary(&ctx->typechecker, opcode));
+ CHECK_RESULT(emit_opcode(ctx, opcode));
+ return WABT_OK;
+}
+
+static WabtResult on_block_expr(uint32_t num_types,
+ WabtType* sig_types,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtTypeVector sig;
+ sig.size = num_types;
+ sig.data = sig_types;
+ CHECK_RESULT(wabt_typechecker_on_block(&ctx->typechecker, &sig));
+ push_label(ctx, WABT_INVALID_OFFSET, WABT_INVALID_OFFSET);
+ return WABT_OK;
+}
+
+static WabtResult on_loop_expr(uint32_t num_types,
+ WabtType* sig_types,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtTypeVector sig;
+ sig.size = num_types;
+ sig.data = sig_types;
+ CHECK_RESULT(wabt_typechecker_on_loop(&ctx->typechecker, &sig));
+ push_label(ctx, get_istream_offset(ctx), WABT_INVALID_OFFSET);
+ return WABT_OK;
+}
+
+static WabtResult on_if_expr(uint32_t num_types,
+ WabtType* sig_types,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtTypeVector sig;
+ sig.size = num_types;
+ sig.data = sig_types;
+ CHECK_RESULT(wabt_typechecker_on_if(&ctx->typechecker, &sig));
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_BR_UNLESS));
+ uint32_t fixup_offset = get_istream_offset(ctx);
+ CHECK_RESULT(emit_i32(ctx, WABT_INVALID_OFFSET));
+ push_label(ctx, WABT_INVALID_OFFSET, fixup_offset);
+ return WABT_OK;
+}
+
+static WabtResult on_else_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_else(&ctx->typechecker));
+ Label* label = top_label(ctx);
+ uint32_t fixup_cond_offset = label->fixup_offset;
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_BR));
+ label->fixup_offset = get_istream_offset(ctx);
+ CHECK_RESULT(emit_i32(ctx, WABT_INVALID_OFFSET));
+ CHECK_RESULT(emit_i32_at(ctx, fixup_cond_offset, get_istream_offset(ctx)));
+ return WABT_OK;
+}
+
+static WabtResult on_end_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtTypeCheckerLabel* label;
+ CHECK_RESULT(wabt_typechecker_get_label(&ctx->typechecker, 0, &label));
+ WabtLabelType label_type = label->label_type;
+ CHECK_RESULT(wabt_typechecker_on_end(&ctx->typechecker));
+ if (label_type == WABT_LABEL_TYPE_IF || label_type == WABT_LABEL_TYPE_ELSE) {
+ CHECK_RESULT(emit_i32_at(ctx, top_label(ctx)->fixup_offset,
+ get_istream_offset(ctx)));
+ }
+ fixup_top_label(ctx);
+ pop_label(ctx);
+ return WABT_OK;
+}
+
+static WabtResult on_br_expr(uint32_t depth, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ uint32_t drop_count, keep_count;
+ CHECK_RESULT(get_br_drop_keep_count(ctx, depth, &drop_count, &keep_count));
+ CHECK_RESULT(wabt_typechecker_on_br(&ctx->typechecker, depth));
+ CHECK_RESULT(emit_br(ctx, depth, drop_count, keep_count));
+ return WABT_OK;
+}
+
+static WabtResult on_br_if_expr(uint32_t depth, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ uint32_t drop_count, keep_count;
+ CHECK_RESULT(wabt_typechecker_on_br_if(&ctx->typechecker, depth));
+ CHECK_RESULT(get_br_drop_keep_count(ctx, depth, &drop_count, &keep_count));
+ /* flip the br_if so if <cond> is true it can drop values from the stack */
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_BR_UNLESS));
+ uint32_t fixup_br_offset = get_istream_offset(ctx);
+ CHECK_RESULT(emit_i32(ctx, WABT_INVALID_OFFSET));
+ CHECK_RESULT(emit_br(ctx, depth, drop_count, keep_count));
+ CHECK_RESULT(emit_i32_at(ctx, fixup_br_offset, get_istream_offset(ctx)));
+ return WABT_OK;
+}
+
+static WabtResult on_br_table_expr(WabtBinaryReaderContext* context,
+ uint32_t num_targets,
+ uint32_t* target_depths,
+ uint32_t default_target_depth) {
+ Context* ctx = (Context*)context->user_data;
+ CHECK_RESULT(wabt_typechecker_begin_br_table(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_BR_TABLE));
+ CHECK_RESULT(emit_i32(ctx, num_targets));
+ uint32_t fixup_table_offset = get_istream_offset(ctx);
+ CHECK_RESULT(emit_i32(ctx, WABT_INVALID_OFFSET));
+ /* not necessary for the interpreter, but it makes it easier to disassemble.
+ * This opcode specifies how many bytes of data follow. */
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_DATA));
+ CHECK_RESULT(emit_i32(ctx, (num_targets + 1) * WABT_TABLE_ENTRY_SIZE));
+ CHECK_RESULT(emit_i32_at(ctx, fixup_table_offset, get_istream_offset(ctx)));
+
+ uint32_t i;
+ for (i = 0; i <= num_targets; ++i) {
+ uint32_t depth = i != num_targets ? target_depths[i] : default_target_depth;
+ CHECK_RESULT(wabt_typechecker_on_br_table_target(&ctx->typechecker, depth));
+ CHECK_RESULT(emit_br_table_offset(ctx, depth));
+ }
+
+ CHECK_RESULT(wabt_typechecker_end_br_table(&ctx->typechecker));
+ return WABT_OK;
+}
+
+static WabtResult on_call_expr(uint32_t func_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ WabtInterpreterFunc* func = get_func_by_module_index(ctx, func_index);
+ WabtInterpreterFuncSignature* sig =
+ get_signature_by_env_index(ctx, func->sig_index);
+ CHECK_RESULT(wabt_typechecker_on_call(&ctx->typechecker, &sig->param_types,
+ &sig->result_types));
+
+ if (func->is_host) {
+ CHECK_RESULT(emit_opcode(ctx, (WabtOpcode)WABT_OPCODE_CALL_HOST));
+ CHECK_RESULT(emit_i32(ctx, translate_func_index_to_env(ctx, func_index)));
+ } else {
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_CALL));
+ CHECK_RESULT(emit_func_offset(ctx, func, func_index));
+ }
+
+ return WABT_OK;
+}
+
+static WabtResult on_call_indirect_expr(uint32_t sig_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ if (ctx->module->table_index == WABT_INVALID_INDEX) {
+ print_error(ctx, "found call_indirect operator, but no table");
+ return WABT_ERROR;
+ }
+ WabtInterpreterFuncSignature* sig =
+ get_signature_by_module_index(ctx, sig_index);
+ CHECK_RESULT(wabt_typechecker_on_call_indirect(
+ &ctx->typechecker, &sig->param_types, &sig->result_types));
+
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_CALL_INDIRECT));
+ CHECK_RESULT(emit_i32(ctx, ctx->module->table_index));
+ CHECK_RESULT(emit_i32(ctx, translate_sig_index_to_env(ctx, sig_index)));
+ return WABT_OK;
+}
+
+static WabtResult on_drop_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_drop(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_DROP));
+ return WABT_OK;
+}
+
+static WabtResult on_i32_const_expr(uint32_t value, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_const(&ctx->typechecker, WABT_TYPE_I32));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_I32_CONST));
+ CHECK_RESULT(emit_i32(ctx, value));
+ return WABT_OK;
+}
+
+static WabtResult on_i64_const_expr(uint64_t value, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_const(&ctx->typechecker, WABT_TYPE_I64));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_I64_CONST));
+ CHECK_RESULT(emit_i64(ctx, value));
+ return WABT_OK;
+}
+
+static WabtResult on_f32_const_expr(uint32_t value_bits, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_const(&ctx->typechecker, WABT_TYPE_F32));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_F32_CONST));
+ CHECK_RESULT(emit_i32(ctx, value_bits));
+ return WABT_OK;
+}
+
+static WabtResult on_f64_const_expr(uint64_t value_bits, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_const(&ctx->typechecker, WABT_TYPE_F64));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_F64_CONST));
+ CHECK_RESULT(emit_i64(ctx, value_bits));
+ return WABT_OK;
+}
+
+static WabtResult on_get_global_expr(uint32_t global_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_GLOBAL(ctx, global_index);
+ WabtType type = get_global_type_by_module_index(ctx, global_index);
+ CHECK_RESULT(wabt_typechecker_on_get_global(&ctx->typechecker, type));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_GET_GLOBAL));
+ CHECK_RESULT(emit_i32(ctx, translate_global_index_to_env(ctx, global_index)));
+ return WABT_OK;
+}
+
+static WabtResult on_set_global_expr(uint32_t global_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_GLOBAL(ctx, global_index);
+ WabtInterpreterGlobal* global = get_global_by_module_index(ctx, global_index);
+ if (!global->mutable_) {
+ print_error(ctx, "can't set_global on immutable global at index %u.",
+ global_index);
+ return WABT_ERROR;
+ }
+ CHECK_RESULT(wabt_typechecker_on_set_global(&ctx->typechecker,
+ global->typed_value.type));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_SET_GLOBAL));
+ CHECK_RESULT(emit_i32(ctx, translate_global_index_to_env(ctx, global_index)));
+ return WABT_OK;
+}
+
+static uint32_t translate_local_index(Context* ctx, uint32_t local_index) {
+ return ctx->typechecker.type_stack.size +
+ ctx->current_func->defined.param_and_local_types.size - local_index;
+}
+
+static WabtResult on_get_local_expr(uint32_t local_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_LOCAL(ctx, local_index);
+ WabtType type = get_local_type_by_index(ctx->current_func, local_index);
+ /* Get the translated index before calling wabt_typechecker_on_get_local
+ * because it will update the type stack size. We need the index to be
+ * relative to the old stack size. */
+ uint32_t translated_local_index = translate_local_index(ctx, local_index);
+ CHECK_RESULT(wabt_typechecker_on_get_local(&ctx->typechecker, type));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_GET_LOCAL));
+ CHECK_RESULT(emit_i32(ctx, translated_local_index));
+ return WABT_OK;
+}
+
+static WabtResult on_set_local_expr(uint32_t local_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_LOCAL(ctx, local_index);
+ WabtType type = get_local_type_by_index(ctx->current_func, local_index);
+ CHECK_RESULT(wabt_typechecker_on_set_local(&ctx->typechecker, type));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_SET_LOCAL));
+ CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index)));
+ return WABT_OK;
+}
+
+static WabtResult on_tee_local_expr(uint32_t local_index, void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_LOCAL(ctx, local_index);
+ WabtType type = get_local_type_by_index(ctx->current_func, local_index);
+ CHECK_RESULT(wabt_typechecker_on_tee_local(&ctx->typechecker, type));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_TEE_LOCAL));
+ CHECK_RESULT(emit_i32(ctx, translate_local_index(ctx, local_index)));
+ return WABT_OK;
+}
+
+static WabtResult on_grow_memory_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(check_has_memory(ctx, WABT_OPCODE_GROW_MEMORY));
+ CHECK_RESULT(wabt_typechecker_on_grow_memory(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_GROW_MEMORY));
+ CHECK_RESULT(emit_i32(ctx, ctx->module->memory_index));
+ return WABT_OK;
+}
+
+static WabtResult on_load_expr(WabtOpcode opcode,
+ uint32_t alignment_log2,
+ uint32_t offset,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(check_has_memory(ctx, opcode));
+ CHECK_RESULT(
+ check_align(ctx, alignment_log2, wabt_get_opcode_memory_size(opcode)));
+ CHECK_RESULT(wabt_typechecker_on_load(&ctx->typechecker, opcode));
+ CHECK_RESULT(emit_opcode(ctx, opcode));
+ CHECK_RESULT(emit_i32(ctx, ctx->module->memory_index));
+ CHECK_RESULT(emit_i32(ctx, offset));
+ return WABT_OK;
+}
+
+static WabtResult on_store_expr(WabtOpcode opcode,
+ uint32_t alignment_log2,
+ uint32_t offset,
+ void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(check_has_memory(ctx, opcode));
+ CHECK_RESULT(
+ check_align(ctx, alignment_log2, wabt_get_opcode_memory_size(opcode)));
+ CHECK_RESULT(wabt_typechecker_on_store(&ctx->typechecker, opcode));
+ CHECK_RESULT(emit_opcode(ctx, opcode));
+ CHECK_RESULT(emit_i32(ctx, ctx->module->memory_index));
+ CHECK_RESULT(emit_i32(ctx, offset));
+ return WABT_OK;
+}
+
+static WabtResult on_current_memory_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(check_has_memory(ctx, WABT_OPCODE_CURRENT_MEMORY));
+ CHECK_RESULT(wabt_typechecker_on_current_memory(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_CURRENT_MEMORY));
+ CHECK_RESULT(emit_i32(ctx, ctx->module->memory_index));
+ return WABT_OK;
+}
+
+static WabtResult on_nop_expr(void* user_data) {
+ return WABT_OK;
+}
+
+static WabtResult on_return_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ uint32_t drop_count, keep_count;
+ CHECK_RESULT(get_return_drop_keep_count(ctx, &drop_count, &keep_count));
+ CHECK_RESULT(wabt_typechecker_on_return(&ctx->typechecker));
+ CHECK_RESULT(emit_drop_keep(ctx, drop_count, keep_count));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_RETURN));
+ return WABT_OK;
+}
+
+static WabtResult on_select_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_select(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_SELECT));
+ return WABT_OK;
+}
+
+static WabtResult on_unreachable_expr(void* user_data) {
+ Context* ctx = (Context*)user_data;
+ CHECK_RESULT(wabt_typechecker_on_unreachable(&ctx->typechecker));
+ CHECK_RESULT(emit_opcode(ctx, WABT_OPCODE_UNREACHABLE));
+ return WABT_OK;
+}
+
+static void destroy_context(Context* ctx) {
+ wabt_destroy_label_vector(&ctx->label_stack);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(ctx->depth_fixups, uint32_vector);
+ WABT_DESTROY_VECTOR_AND_ELEMENTS(ctx->func_fixups, uint32_vector);
+ wabt_destroy_uint32_vector(&ctx->sig_index_mapping);
+ wabt_destroy_uint32_vector(&ctx->func_index_mapping);
+ wabt_destroy_uint32_vector(&ctx->global_index_mapping);
+ wabt_destroy_typechecker(&ctx->typechecker);
+}
+
+WabtResult wabt_read_binary_interpreter(WabtInterpreterEnvironment* env,
+ const void* data,
+ size_t size,
+ const WabtReadBinaryOptions* options,
+ WabtBinaryErrorHandler* error_handler,
+ WabtInterpreterModule** out_module) {
+ Context ctx;
+ WabtBinaryReader reader;
+
+ WabtInterpreterEnvironmentMark mark = wabt_mark_interpreter_environment(env);
+
+ WabtInterpreterModule* module = wabt_append_interpreter_module(&env->modules);
+
+ WABT_ZERO_MEMORY(ctx);
+ WABT_ZERO_MEMORY(reader);
+
+ ctx.reader = &reader;
+ ctx.error_handler = error_handler;
+ ctx.env = env;
+ ctx.module = module;
+ ctx.module->is_host = false;
+ ctx.module->table_index = WABT_INVALID_INDEX;
+ ctx.module->memory_index = WABT_INVALID_INDEX;
+ ctx.module->defined.start_func_index = WABT_INVALID_INDEX;
+ ctx.module->defined.istream_start = env->istream.size;
+ ctx.istream_offset = env->istream.size;
+ CHECK_RESULT(
+ wabt_init_mem_writer_existing(&ctx.istream_writer, &env->istream));
+
+ WabtTypeCheckerErrorHandler tc_error_handler;
+ tc_error_handler.on_error = on_typechecker_error;
+ tc_error_handler.user_data = &ctx;
+ ctx.typechecker.error_handler = &tc_error_handler;
+
+ WABT_ZERO_MEMORY(reader);
+ reader.user_data = &ctx;
+ reader.on_error = on_error;
+ reader.on_signature_count = on_signature_count;
+ reader.on_signature = on_signature;
+ reader.on_import_count = on_import_count;
+ reader.on_import = on_import;
+ reader.on_import_func = on_import_func;
+ reader.on_import_table = on_import_table;
+ reader.on_import_memory = on_import_memory;
+ reader.on_import_global = on_import_global;
+ reader.on_function_signatures_count = on_function_signatures_count;
+ reader.on_function_signature = on_function_signature;
+ reader.on_table = on_table;
+ reader.on_memory = on_memory;
+ reader.on_global_count = on_global_count;
+ reader.begin_global = begin_global;
+ reader.end_global_init_expr = end_global_init_expr;
+ reader.on_export = on_export;
+ reader.on_start_function = on_start_function;
+ reader.begin_function_body = begin_function_body;
+ reader.on_local_decl_count = on_local_decl_count;
+ reader.on_local_decl = on_local_decl;
+ reader.on_binary_expr = on_binary_expr;
+ reader.on_block_expr = on_block_expr;
+ reader.on_br_expr = on_br_expr;
+ reader.on_br_if_expr = on_br_if_expr;
+ reader.on_br_table_expr = on_br_table_expr;
+ reader.on_call_expr = on_call_expr;
+ reader.on_call_indirect_expr = on_call_indirect_expr;
+ reader.on_compare_expr = on_binary_expr;
+ reader.on_convert_expr = on_unary_expr;
+ reader.on_current_memory_expr = on_current_memory_expr;
+ reader.on_drop_expr = on_drop_expr;
+ reader.on_else_expr = on_else_expr;
+ reader.on_end_expr = on_end_expr;
+ reader.on_f32_const_expr = on_f32_const_expr;
+ reader.on_f64_const_expr = on_f64_const_expr;
+ reader.on_get_global_expr = on_get_global_expr;
+ reader.on_get_local_expr = on_get_local_expr;
+ reader.on_grow_memory_expr = on_grow_memory_expr;
+ reader.on_i32_const_expr = on_i32_const_expr;
+ reader.on_i64_const_expr = on_i64_const_expr;
+ reader.on_if_expr = on_if_expr;
+ reader.on_load_expr = on_load_expr;
+ reader.on_loop_expr = on_loop_expr;
+ reader.on_nop_expr = on_nop_expr;
+ reader.on_return_expr = on_return_expr;
+ reader.on_select_expr = on_select_expr;
+ reader.on_set_global_expr = on_set_global_expr;
+ reader.on_set_local_expr = on_set_local_expr;
+ reader.on_store_expr = on_store_expr;
+ reader.on_tee_local_expr = on_tee_local_expr;
+ reader.on_unary_expr = on_unary_expr;
+ reader.on_unreachable_expr = on_unreachable_expr;
+ reader.end_function_body = end_function_body;
+ reader.end_elem_segment_init_expr = end_elem_segment_init_expr;
+ reader.on_elem_segment_function_index = on_elem_segment_function_index_check;
+ reader.on_data_segment_data = on_data_segment_data_check;
+ reader.on_init_expr_f32_const_expr = on_init_expr_f32_const_expr;
+ reader.on_init_expr_f64_const_expr = on_init_expr_f64_const_expr;
+ reader.on_init_expr_get_global_expr = on_init_expr_get_global_expr;
+ reader.on_init_expr_i32_const_expr = on_init_expr_i32_const_expr;
+ reader.on_init_expr_i64_const_expr = on_init_expr_i64_const_expr;
+
+ const uint32_t num_function_passes = 1;
+ WabtResult result =
+ wabt_read_binary(data, size, &reader, num_function_passes, options);
+ wabt_steal_mem_writer_output_buffer(&ctx.istream_writer, &env->istream);
+ if (WABT_SUCCEEDED(result)) {
+ /* Another pass on the read binary to assign data and elem segments. */
+ WABT_ZERO_MEMORY(reader);
+ reader.user_data = &ctx;
+ reader.on_error = on_error;
+ reader.end_elem_segment_init_expr = end_elem_segment_init_expr;
+ reader.on_elem_segment_function_index = on_elem_segment_function_index;
+ reader.on_data_segment_data = on_data_segment_data;
+ reader.on_init_expr_f32_const_expr = on_init_expr_f32_const_expr;
+ reader.on_init_expr_f64_const_expr = on_init_expr_f64_const_expr;
+ reader.on_init_expr_get_global_expr = on_init_expr_get_global_expr;
+ reader.on_init_expr_i32_const_expr = on_init_expr_i32_const_expr;
+ reader.on_init_expr_i64_const_expr = on_init_expr_i64_const_expr;
+
+ result =
+ wabt_read_binary(data, size, &reader, num_function_passes, options);
+ assert(WABT_SUCCEEDED(result));
+
+ env->istream.size = ctx.istream_offset;
+ ctx.module->defined.istream_end = env->istream.size;
+ *out_module = module;
+ } else {
+ wabt_reset_interpreter_environment_to_mark(env, mark);
+ *out_module = NULL;
+ }
+ destroy_context(&ctx);
+ return result;
+}