summaryrefslogtreecommitdiff
path: root/src/resolve-names.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve-names.c')
-rw-r--r--src/resolve-names.c444
1 files changed, 444 insertions, 0 deletions
diff --git a/src/resolve-names.c b/src/resolve-names.c
new file mode 100644
index 00000000..ea73ed2a
--- /dev/null
+++ b/src/resolve-names.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright 2016 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "resolve-names.h"
+
+#include <assert.h>
+#include <stdio.h>
+
+#include "allocator.h"
+#include "ast.h"
+#include "ast-parser-lexer-shared.h"
+
+typedef WasmLabel* LabelPtr;
+WASM_DEFINE_VECTOR(label_ptr, LabelPtr);
+
+typedef struct Context {
+ WasmAllocator* allocator;
+ WasmSourceErrorHandler* error_handler;
+ WasmAstLexer* lexer;
+ WasmScript* script;
+ WasmModule* current_module;
+ WasmFunc* current_func;
+ WasmExprVisitor visitor;
+ LabelPtrVector labels;
+ WasmResult result;
+} Context;
+
+static void WASM_PRINTF_FORMAT(3, 4)
+ print_error(Context* ctx, const WasmLocation* loc, const char* fmt, ...) {
+ ctx->result = WASM_ERROR;
+ va_list args;
+ va_start(args, fmt);
+ wasm_ast_format_error(ctx->error_handler, loc, ctx->lexer, fmt, args);
+ va_end(args);
+}
+
+static void push_label(Context* ctx, WasmLabel* label) {
+ wasm_append_label_ptr_value(ctx->allocator, &ctx->labels, &label);
+}
+
+static void pop_label(Context* ctx) {
+ assert(ctx->labels.size > 0);
+ ctx->labels.size--;
+}
+
+static void check_duplicate_bindings(Context* ctx,
+ const WasmBindingHash* bindings,
+ const char* desc) {
+ size_t i;
+ for (i = 0; i < bindings->entries.capacity; ++i) {
+ WasmBindingHashEntry* entry = &bindings->entries.data[i];
+ if (wasm_hash_entry_is_free(entry))
+ continue;
+
+ /* only follow the chain if this is the first entry in the chain */
+ if (entry->prev != NULL)
+ continue;
+
+ WasmBindingHashEntry* a = entry;
+ for (; a; a = a->next) {
+ WasmBindingHashEntry* b = a->next;
+ for (; b; b = b->next) {
+ if (wasm_string_slices_are_equal(&a->binding.name, &b->binding.name)) {
+ /* choose the location that is later in the file */
+ WasmLocation* a_loc = &a->binding.loc;
+ WasmLocation* b_loc = &b->binding.loc;
+ WasmLocation* loc = a_loc->line > b_loc->line ? a_loc : b_loc;
+ print_error(ctx, loc, "redefinition of %s \"" PRIstringslice "\"",
+ desc, WASM_PRINTF_STRING_SLICE_ARG(a->binding.name));
+ }
+ }
+ }
+ }
+}
+
+static void resolve_label_var(Context* ctx, WasmVar* var) {
+ if (var->type == WASM_VAR_TYPE_NAME) {
+ int i;
+ for (i = ctx->labels.size - 1; i >= 0; --i) {
+ WasmLabel* label = ctx->labels.data[i];
+ if (wasm_string_slices_are_equal(label, &var->name)) {
+ wasm_destroy_string_slice(ctx->allocator, &var->name);
+ var->type = WASM_VAR_TYPE_INDEX;
+ var->index = ctx->labels.size - i - 1;
+ return;
+ }
+ }
+ print_error(ctx, &var->loc,
+ "undefined label variable \"" PRIstringslice "\"",
+ WASM_PRINTF_STRING_SLICE_ARG(var->name));
+ }
+}
+
+static void resolve_var(Context* ctx,
+ const WasmBindingHash* bindings,
+ WasmVar* var,
+ const char* desc) {
+ if (var->type == WASM_VAR_TYPE_NAME) {
+ int index = wasm_get_index_from_var(bindings, var);
+ if (index == -1) {
+ print_error(ctx, &var->loc,
+ "undefined %s variable \"" PRIstringslice "\"", desc,
+ WASM_PRINTF_STRING_SLICE_ARG(var->name));
+ return;
+ }
+
+ wasm_destroy_string_slice(ctx->allocator, &var->name);
+ var->index = index;
+ var->type = WASM_VAR_TYPE_INDEX;
+ }
+}
+
+static void resolve_func_var(Context* ctx, WasmVar* var) {
+ resolve_var(ctx, &ctx->current_module->func_bindings, var, "function");
+}
+
+static void resolve_global_var(Context* ctx, WasmVar* var) {
+ resolve_var(ctx, &ctx->current_module->global_bindings, var, "global");
+}
+
+static void resolve_func_type_var(Context* ctx, WasmVar* var) {
+ resolve_var(ctx, &ctx->current_module->func_type_bindings, var,
+ "function type");
+}
+
+static void resolve_table_var(Context* ctx, WasmVar* var) {
+ resolve_var(ctx, &ctx->current_module->table_bindings, var, "table");
+}
+
+static void resolve_memory_var(Context* ctx, WasmVar* var) {
+ resolve_var(ctx, &ctx->current_module->memory_bindings, var, "memory");
+}
+
+static void resolve_local_var(Context* ctx, WasmVar* var) {
+ if (var->type == WASM_VAR_TYPE_NAME) {
+ int index = wasm_get_local_index_by_var(ctx->current_func, var);
+ if (index == -1) {
+ print_error(ctx, &var->loc,
+ "undefined local variable \"" PRIstringslice "\"",
+ WASM_PRINTF_STRING_SLICE_ARG(var->name));
+ return;
+ }
+
+ wasm_destroy_string_slice(ctx->allocator, &var->name);
+ var->index = index;
+ var->type = WASM_VAR_TYPE_INDEX;
+ }
+}
+
+static WasmResult begin_block_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ push_label(ctx, &expr->block.label);
+ return WASM_OK;
+}
+
+static WasmResult end_block_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ pop_label(ctx);
+ return WASM_OK;
+}
+
+static WasmResult begin_loop_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ push_label(ctx, &expr->loop.label);
+ return WASM_OK;
+}
+
+static WasmResult end_loop_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ pop_label(ctx);
+ return WASM_OK;
+}
+
+static WasmResult on_br_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_label_var(ctx, &expr->br.var);
+ return WASM_OK;
+}
+
+static WasmResult on_br_if_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_label_var(ctx, &expr->br_if.var);
+ return WASM_OK;
+}
+
+static WasmResult on_br_table_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ size_t i;
+ WasmVarVector* targets = &expr->br_table.targets;
+ for (i = 0; i < targets->size; ++i) {
+ WasmVar* target = &targets->data[i];
+ resolve_label_var(ctx, target);
+ }
+
+ resolve_label_var(ctx, &expr->br_table.default_target);
+ return WASM_OK;
+}
+
+static WasmResult on_call_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_func_var(ctx, &expr->call.var);
+ return WASM_OK;
+}
+
+static WasmResult on_call_indirect_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_func_type_var(ctx, &expr->call_indirect.var);
+ return WASM_OK;
+}
+
+static WasmResult on_get_global_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_global_var(ctx, &expr->get_global.var);
+ return WASM_OK;
+}
+
+static WasmResult on_get_local_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_local_var(ctx, &expr->get_local.var);
+ return WASM_OK;
+}
+
+static WasmResult begin_if_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ push_label(ctx, &expr->if_.true_.label);
+ return WASM_OK;
+}
+
+static WasmResult end_if_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ pop_label(ctx);
+ return WASM_OK;
+}
+
+static WasmResult on_set_global_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_global_var(ctx, &expr->set_global.var);
+ return WASM_OK;
+}
+
+static WasmResult on_set_local_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_local_var(ctx, &expr->set_local.var);
+ return WASM_OK;
+}
+
+static WasmResult on_tee_local_expr(WasmExpr* expr, void* user_data) {
+ Context* ctx = user_data;
+ resolve_local_var(ctx, &expr->tee_local.var);
+ return WASM_OK;
+}
+
+static void visit_func(Context* ctx, WasmFunc* func) {
+ ctx->current_func = func;
+ if (wasm_decl_has_func_type(&func->decl))
+ resolve_func_type_var(ctx, &func->decl.type_var);
+
+ check_duplicate_bindings(ctx, &func->param_bindings, "parameter");
+ check_duplicate_bindings(ctx, &func->local_bindings, "local");
+
+ wasm_visit_func(func, &ctx->visitor);
+ ctx->current_func = NULL;
+}
+
+static void visit_export(Context* ctx, WasmExport* export) {
+ switch (export->kind) {
+ case WASM_EXTERNAL_KIND_FUNC:
+ resolve_func_var(ctx, &export->var);
+ break;
+
+ case WASM_EXTERNAL_KIND_TABLE:
+ resolve_table_var(ctx, &export->var);
+ break;
+
+ case WASM_EXTERNAL_KIND_MEMORY:
+ resolve_memory_var(ctx, &export->var);
+ break;
+
+ case WASM_EXTERNAL_KIND_GLOBAL:
+ resolve_global_var(ctx, &export->var);
+ break;
+
+ case WASM_NUM_EXTERNAL_KINDS:
+ assert(0);
+ break;
+ }
+}
+
+static void visit_global(Context* ctx, WasmGlobal* global) {
+ wasm_visit_expr_list(global->init_expr, &ctx->visitor);
+}
+
+static void visit_elem_segment(Context* ctx, WasmElemSegment* segment) {
+ size_t i;
+ resolve_table_var(ctx, &segment->table_var);
+ wasm_visit_expr_list(segment->offset, &ctx->visitor);
+ for (i = 0; i < segment->vars.size; ++i)
+ resolve_func_var(ctx, &segment->vars.data[i]);
+}
+
+static void visit_data_segment(Context* ctx, WasmDataSegment* segment) {
+ resolve_memory_var(ctx, &segment->memory_var);
+ wasm_visit_expr_list(segment->offset, &ctx->visitor);
+}
+
+static void visit_module(Context* ctx, WasmModule* module) {
+ ctx->current_module = module;
+ check_duplicate_bindings(ctx, &module->func_bindings, "function");
+ check_duplicate_bindings(ctx, &module->global_bindings, "global");
+ check_duplicate_bindings(ctx, &module->export_bindings, "export");
+ check_duplicate_bindings(ctx, &module->func_type_bindings, "function type");
+ check_duplicate_bindings(ctx, &module->table_bindings, "table");
+ check_duplicate_bindings(ctx, &module->memory_bindings, "memory");
+
+ size_t i;
+ for (i = 0; i < module->funcs.size; ++i)
+ visit_func(ctx, module->funcs.data[i]);
+ for (i = 0; i < module->exports.size; ++i)
+ visit_export(ctx, module->exports.data[i]);
+ for (i = 0; i < module->globals.size; ++i)
+ visit_global(ctx, module->globals.data[i]);
+ for (i = 0; i < module->elem_segments.size; ++i)
+ visit_elem_segment(ctx, module->elem_segments.data[i]);
+ for (i = 0; i < module->data_segments.size; ++i)
+ visit_data_segment(ctx, module->data_segments.data[i]);
+ if (module->start)
+ resolve_func_var(ctx, module->start);
+ ctx->current_module = NULL;
+}
+
+static void visit_raw_module(Context* ctx, WasmRawModule* raw_module) {
+ if (raw_module->type == WASM_RAW_MODULE_TYPE_TEXT)
+ visit_module(ctx, raw_module->text);
+}
+
+static void visit_command(Context* ctx, WasmCommand* command) {
+ switch (command->type) {
+ case WASM_COMMAND_TYPE_MODULE:
+ visit_module(ctx, &command->module);
+ break;
+
+ case WASM_COMMAND_TYPE_ACTION:
+ case WASM_COMMAND_TYPE_ASSERT_RETURN:
+ case WASM_COMMAND_TYPE_ASSERT_RETURN_NAN:
+ case WASM_COMMAND_TYPE_ASSERT_TRAP:
+ case WASM_COMMAND_TYPE_REGISTER:
+ /* Don't resolve a module_var, since it doesn't really behave like other
+ * vars. You can't reference a module by index. */
+ break;
+
+ case WASM_COMMAND_TYPE_ASSERT_MALFORMED:
+ /* Malformed modules should not be text; the whole point of this
+ * assertion is to test for malformed binary modules. */
+ break;
+
+ case WASM_COMMAND_TYPE_ASSERT_INVALID:
+ /* The module may be invalid because the names cannot be resolved (or are
+ * duplicates). In either case, don't resolve. */
+ break;
+
+ case WASM_COMMAND_TYPE_ASSERT_UNLINKABLE:
+ visit_raw_module(ctx, &command->assert_unlinkable.module);
+ break;
+
+ case WASM_COMMAND_TYPE_ASSERT_UNINSTANTIABLE:
+ visit_raw_module(ctx, &command->assert_uninstantiable.module);
+ break;
+
+ case WASM_NUM_COMMAND_TYPES:
+ assert(0);
+ break;
+ }
+}
+
+static void visit_script(Context* ctx, WasmScript* script) {
+ size_t i;
+ for (i = 0; i < script->commands.size; ++i)
+ visit_command(ctx, &script->commands.data[i]);
+}
+
+static void init_context(Context* ctx,
+ WasmAllocator* allocator,
+ WasmAstLexer* lexer,
+ WasmScript* script,
+ WasmSourceErrorHandler* error_handler) {
+ WASM_ZERO_MEMORY(*ctx);
+ ctx->allocator = allocator;
+ ctx->lexer = lexer;
+ ctx->error_handler = error_handler;
+ ctx->result = WASM_OK;
+ ctx->script = script;
+ ctx->visitor.user_data = ctx;
+ ctx->visitor.begin_block_expr = begin_block_expr;
+ ctx->visitor.end_block_expr = end_block_expr;
+ ctx->visitor.begin_loop_expr = begin_loop_expr;
+ ctx->visitor.end_loop_expr = end_loop_expr;
+ ctx->visitor.on_br_expr = on_br_expr;
+ ctx->visitor.on_br_if_expr = on_br_if_expr;
+ ctx->visitor.on_br_table_expr = on_br_table_expr;
+ ctx->visitor.on_call_expr = on_call_expr;
+ ctx->visitor.on_call_indirect_expr = on_call_indirect_expr;
+ ctx->visitor.on_get_global_expr = on_get_global_expr;
+ ctx->visitor.on_get_local_expr = on_get_local_expr;
+ ctx->visitor.begin_if_expr = begin_if_expr;
+ ctx->visitor.end_if_expr = end_if_expr;
+ ctx->visitor.on_set_global_expr = on_set_global_expr;
+ ctx->visitor.on_set_local_expr = on_set_local_expr;
+ ctx->visitor.on_tee_local_expr = on_tee_local_expr;
+}
+
+WasmResult wasm_resolve_names_module(WasmAllocator* allocator,
+ WasmAstLexer* lexer,
+ WasmModule* module,
+ WasmSourceErrorHandler* error_handler) {
+ Context ctx;
+ init_context(&ctx, allocator, lexer, NULL, error_handler);
+ visit_module(&ctx, module);
+ wasm_destroy_label_ptr_vector(allocator, &ctx.labels);
+ return ctx.result;
+}
+
+WasmResult wasm_resolve_names_script(WasmAllocator* allocator,
+ WasmAstLexer* lexer,
+ WasmScript* script,
+ WasmSourceErrorHandler* error_handler) {
+ Context ctx;
+ init_context(&ctx, allocator, lexer, script, error_handler);
+ visit_script(&ctx, script);
+ wasm_destroy_label_ptr_vector(allocator, &ctx.labels);
+ return ctx.result;
+}