Source code

Revision control

Copy as Markdown

Other Tools

/*
* 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
*
*
* 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 "wabt/generate-names.h"
#include <cassert>
#include <cstdio>
#include <string>
#include <vector>
#include "wabt/cast.h"
#include "wabt/expr-visitor.h"
#include "wabt/ir.h"
namespace wabt {
namespace {
class NameGenerator : public ExprVisitor::DelegateNop {
public:
NameGenerator(NameOpts opts);
Result VisitModule(Module* module);
// Implementation of ExprVisitor::DelegateNop.
Result BeginBlockExpr(BlockExpr* expr) override;
Result BeginTryExpr(TryExpr* expr) override;
Result BeginLoopExpr(LoopExpr* expr) override;
Result BeginIfExpr(IfExpr* expr) override;
private:
static bool HasName(const std::string& str);
// Generate a name with the given prefix, followed by the index and
// optionally a disambiguating number. If index == kInvalidIndex, the index
// is not appended.
void GenerateName(const char* prefix,
Index index,
unsigned disambiguator,
std::string* out_str);
// Like GenerateName, but only generates a name if |out_str| is empty.
void MaybeGenerateName(const char* prefix, Index index, std::string* out_str);
// Generate a name via GenerateName and bind it to the given binding hash. If
// the name already exists, the name will be disambiguated until it can be
// added.
void GenerateAndBindName(BindingHash* bindings,
const char* prefix,
Index index,
std::string* out_str);
// Like GenerateAndBindName, but only generates a name if |out_str| is empty.
void MaybeGenerateAndBindName(BindingHash* bindings,
const char* prefix,
Index index,
std::string* out_str);
// Like MaybeGenerateAndBindName but uses the name directly, without
// appending the index. If the name already exists, a disambiguating suffix
// is added.
void MaybeUseAndBindName(BindingHash* bindings,
const char* name,
Index index,
std::string* out_str);
void GenerateAndBindLocalNames(Func* func);
template <typename T>
Result VisitAll(const std::vector<T*>& items,
Result (NameGenerator::*func)(Index, T*));
Result VisitFunc(Index func_index, Func* func);
Result VisitGlobal(Index global_index, Global* global);
Result VisitType(Index func_type_index, TypeEntry* type);
Result VisitTable(Index table_index, Table* table);
Result VisitMemory(Index memory_index, Memory* memory);
Result VisitTag(Index tag_index, Tag* tag);
Result VisitDataSegment(Index data_segment_index, DataSegment* data_segment);
Result VisitElemSegment(Index elem_segment_index, ElemSegment* elem_segment);
Result VisitImport(Import* import);
Result VisitExport(Export* export_);
Module* module_ = nullptr;
ExprVisitor visitor_;
Index label_count_ = 0;
Index num_func_imports_ = 0;
Index num_table_imports_ = 0;
Index num_memory_imports_ = 0;
Index num_global_imports_ = 0;
Index num_tag_imports_ = 0;
NameOpts opts_;
};
NameGenerator::NameGenerator(NameOpts opts) : visitor_(this), opts_(opts) {}
// static
bool NameGenerator::HasName(const std::string& str) {
return !str.empty();
}
void NameGenerator::GenerateName(const char* prefix,
Index index,
unsigned disambiguator,
std::string* str) {
*str = "$";
*str += prefix;
if (index != kInvalidIndex) {
if (opts_ & NameOpts::AlphaNames) {
// For params and locals, do not use a prefix char.
if (!strcmp(prefix, "p") || !strcmp(prefix, "l")) {
str->pop_back();
} else {
*str += '_';
}
*str += IndexToAlphaName(index);
} else {
*str += std::to_string(index);
}
}
if (disambiguator != 0) {
*str += '_' + std::to_string(disambiguator);
}
}
void NameGenerator::MaybeGenerateName(const char* prefix,
Index index,
std::string* str) {
if (!HasName(*str)) {
// There's no bindings hash, so the name can't be a duplicate. Therefore it
// doesn't need a disambiguating number.
GenerateName(prefix, index, 0, str);
}
}
void NameGenerator::GenerateAndBindName(BindingHash* bindings,
const char* prefix,
Index index,
std::string* str) {
unsigned disambiguator = 0;
while (true) {
GenerateName(prefix, index, disambiguator, str);
if (bindings->find(*str) == bindings->end()) {
bindings->emplace(*str, Binding(index));
break;
}
disambiguator++;
}
}
void NameGenerator::MaybeGenerateAndBindName(BindingHash* bindings,
const char* prefix,
Index index,
std::string* str) {
if (!HasName(*str)) {
GenerateAndBindName(bindings, prefix, index, str);
}
}
void NameGenerator::MaybeUseAndBindName(BindingHash* bindings,
const char* name,
Index index,
std::string* str) {
if (!HasName(*str)) {
unsigned disambiguator = 0;
while (true) {
GenerateName(name, kInvalidIndex, disambiguator, str);
if (bindings->find(*str) == bindings->end()) {
bindings->emplace(*str, Binding(index));
break;
}
disambiguator++;
}
}
}
void NameGenerator::GenerateAndBindLocalNames(Func* func) {
std::vector<std::string> index_to_name;
MakeTypeBindingReverseMapping(func->GetNumParamsAndLocals(), func->bindings,
&index_to_name);
for (size_t i = 0; i < index_to_name.size(); ++i) {
const std::string& old_name = index_to_name[i];
if (!old_name.empty()) {
continue;
}
const char* prefix = i < func->GetNumParams() ? "p" : "l";
std::string new_name;
GenerateAndBindName(&func->bindings, prefix, i, &new_name);
index_to_name[i] = new_name;
}
}
Result NameGenerator::BeginBlockExpr(BlockExpr* expr) {
MaybeGenerateName("B", label_count_++, &expr->block.label);
return Result::Ok;
}
Result NameGenerator::BeginTryExpr(TryExpr* expr) {
MaybeGenerateName("T", label_count_++, &expr->block.label);
return Result::Ok;
}
Result NameGenerator::BeginLoopExpr(LoopExpr* expr) {
MaybeGenerateName("L", label_count_++, &expr->block.label);
return Result::Ok;
}
Result NameGenerator::BeginIfExpr(IfExpr* expr) {
MaybeGenerateName("I", label_count_++, &expr->true_.label);
return Result::Ok;
}
Result NameGenerator::VisitFunc(Index func_index, Func* func) {
MaybeGenerateAndBindName(&module_->func_bindings, "f", func_index,
&func->name);
GenerateAndBindLocalNames(func);
label_count_ = 0;
CHECK_RESULT(visitor_.VisitFunc(func));
return Result::Ok;
}
Result NameGenerator::VisitGlobal(Index global_index, Global* global) {
MaybeGenerateAndBindName(&module_->global_bindings, "g", global_index,
&global->name);
return Result::Ok;
}
Result NameGenerator::VisitType(Index type_index, TypeEntry* type) {
MaybeGenerateAndBindName(&module_->type_bindings, "t", type_index,
&type->name);
return Result::Ok;
}
Result NameGenerator::VisitTable(Index table_index, Table* table) {
MaybeGenerateAndBindName(&module_->table_bindings, "T", table_index,
&table->name);
return Result::Ok;
}
Result NameGenerator::VisitMemory(Index memory_index, Memory* memory) {
MaybeGenerateAndBindName(&module_->memory_bindings, "M", memory_index,
&memory->name);
return Result::Ok;
}
Result NameGenerator::VisitTag(Index tag_index, Tag* tag) {
MaybeGenerateAndBindName(&module_->tag_bindings, "e", tag_index, &tag->name);
return Result::Ok;
}
Result NameGenerator::VisitDataSegment(Index data_segment_index,
DataSegment* data_segment) {
MaybeGenerateAndBindName(&module_->data_segment_bindings, "d",
data_segment_index, &data_segment->name);
return Result::Ok;
}
Result NameGenerator::VisitElemSegment(Index elem_segment_index,
ElemSegment* elem_segment) {
MaybeGenerateAndBindName(&module_->elem_segment_bindings, "e",
elem_segment_index, &elem_segment->name);
return Result::Ok;
}
Result NameGenerator::VisitImport(Import* import) {
BindingHash* bindings = nullptr;
std::string* name = nullptr;
Index index = kInvalidIndex;
switch (import->kind()) {
case ExternalKind::Func:
if (auto* func_import = cast<FuncImport>(import)) {
bindings = &module_->func_bindings;
name = &func_import->func.name;
index = num_func_imports_++;
}
break;
case ExternalKind::Table:
if (auto* table_import = cast<TableImport>(import)) {
bindings = &module_->table_bindings;
name = &table_import->table.name;
index = num_table_imports_++;
}
break;
case ExternalKind::Memory:
if (auto* memory_import = cast<MemoryImport>(import)) {
bindings = &module_->memory_bindings;
name = &memory_import->memory.name;
index = num_memory_imports_++;
}
break;
case ExternalKind::Global:
if (auto* global_import = cast<GlobalImport>(import)) {
bindings = &module_->global_bindings;
name = &global_import->global.name;
index = num_global_imports_++;
}
break;
case ExternalKind::Tag:
if (auto* tag_import = cast<TagImport>(import)) {
bindings = &module_->tag_bindings;
name = &tag_import->tag.name;
index = num_tag_imports_++;
}
break;
}
if (bindings && name) {
assert(index != kInvalidIndex);
std::string new_name = import->module_name + '.' + import->field_name;
MaybeUseAndBindName(bindings, new_name.c_str(), index, name);
}
return Result::Ok;
}
Result NameGenerator::VisitExport(Export* export_) {
BindingHash* bindings = nullptr;
std::string* name = nullptr;
Index index = kInvalidIndex;
switch (export_->kind) {
case ExternalKind::Func:
if (Func* func = module_->GetFunc(export_->var)) {
index = module_->GetFuncIndex(export_->var);
bindings = &module_->func_bindings;
name = &func->name;
}
break;
case ExternalKind::Table:
if (Table* table = module_->GetTable(export_->var)) {
index = module_->GetTableIndex(export_->var);
bindings = &module_->table_bindings;
name = &table->name;
}
break;
case ExternalKind::Memory:
if (Memory* memory = module_->GetMemory(export_->var)) {
index = module_->GetMemoryIndex(export_->var);
bindings = &module_->memory_bindings;
name = &memory->name;
}
break;
case ExternalKind::Global:
if (Global* global = module_->GetGlobal(export_->var)) {
index = module_->GetGlobalIndex(export_->var);
bindings = &module_->global_bindings;
name = &global->name;
}
break;
case ExternalKind::Tag:
if (Tag* tag = module_->GetTag(export_->var)) {
index = module_->GetTagIndex(export_->var);
bindings = &module_->tag_bindings;
name = &tag->name;
}
break;
}
if (bindings && name) {
MaybeUseAndBindName(bindings, export_->name.c_str(), index, name);
}
return Result::Ok;
}
template <typename T>
Result NameGenerator::VisitAll(const std::vector<T*>& items,
Result (NameGenerator::*func)(Index, T*)) {
for (Index i = 0; i < items.size(); ++i) {
CHECK_RESULT((this->*func)(i, items[i]));
}
return Result::Ok;
}
Result NameGenerator::VisitModule(Module* module) {
module_ = module;
// Visit imports and exports first to give better names, derived from the
// import/export name.
for (auto* import : module->imports) {
CHECK_RESULT(VisitImport(import));
}
for (auto* export_ : module->exports) {
CHECK_RESULT(VisitExport(export_));
}
VisitAll(module->globals, &NameGenerator::VisitGlobal);
VisitAll(module->types, &NameGenerator::VisitType);
VisitAll(module->funcs, &NameGenerator::VisitFunc);
VisitAll(module->tables, &NameGenerator::VisitTable);
VisitAll(module->memories, &NameGenerator::VisitMemory);
VisitAll(module->tags, &NameGenerator::VisitTag);
VisitAll(module->data_segments, &NameGenerator::VisitDataSegment);
VisitAll(module->elem_segments, &NameGenerator::VisitElemSegment);
module_ = nullptr;
return Result::Ok;
}
} // end anonymous namespace
Result GenerateNames(Module* module, NameOpts opts) {
NameGenerator generator(opts);
return generator.VisitModule(module);
}
} // namespace wabt