Where Online Learning is simpler!
The C and C++ Include Header Files
cat -n /usr/include/wabt/decompiler-naming.h
1 /* 2 * Copyright 2019 WebAssembly Community Group participants 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef WABT_DECOMPILER_NAMING_H_ 18 #define WABT_DECOMPILER_NAMING_H_ 19 20 #include "wabt/decompiler-ast.h" 21 22 #include <set> 23 24 namespace wabt { 25 26 inline void RenameToIdentifier(std::string& name, 27 Index i, 28 BindingHash& bh, 29 const std::set<std::string_view>* filter) { 30 // Filter out non-identifier characters, and try to reduce the size of 31 // gigantic C++ signature names. 32 std::string s; 33 size_t nesting = 0; 34 size_t read = 0; 35 size_t word_start = 0; 36 for (auto c : name) { 37 read++; 38 // We most certainly don't want to parse the entirety of C++ signatures, 39 // but these names are sometimes several lines long, so would be great 40 // to trim down. One quick way to do that is to remove anything between 41 // nested (), which usually means the parameter list. 42 if (c == '(') { 43 nesting++; 44 } 45 if (c == ')') { 46 nesting--; 47 } 48 if (nesting) { 49 continue; 50 } 51 if (!isalnum(static_cast<unsigned char>(c))) { 52 c = '_'; 53 } 54 if (c == '_') { 55 if (s.empty()) { 56 continue; // Skip leading. 57 } 58 if (s.back() == '_') { 59 continue; // Consecutive. 60 } 61 } 62 s += c; 63 if (filter && (c == '_' || read == name.size())) { 64 // We found a "word" inside a snake_case identifier. 65 auto word_end = s.size(); 66 if (c == '_') { 67 word_end--; 68 } 69 assert(word_end > word_start); 70 auto word = 71 std::string_view(s.c_str() + word_start, word_end - word_start); 72 if (filter->find(word) != filter->end()) { 73 s.resize(word_start); 74 } 75 word_start = s.size(); 76 } 77 } 78 if (!s.empty() && s.back() == '_') { 79 s.pop_back(); // Trailing. 80 } 81 // If after all this culling, we're still gigantic (STL identifier can 82 // easily be hundreds of chars in size), just cut the identifier 83 // down, it will be disambiguated below, if needed. 84 const size_t max_identifier_length = 100; 85 if (s.size() > max_identifier_length) { 86 s.resize(max_identifier_length); 87 } 88 if (s.empty()) { 89 s = "__empty"; 90 } 91 // Remove original binding first, such that it doesn't match with our 92 // new name. 93 bh.erase(name); 94 // Find a unique name. 95 Index disambiguator = 0; 96 auto base_len = s.size(); 97 for (;;) { 98 if (bh.count(s) == 0) { 99 break; 100 } 101 disambiguator++; 102 s.resize(base_len); 103 s += '_'; 104 s += std::to_string(disambiguator); 105 } 106 // Replace name in bindings. 107 name = s; 108 bh.emplace(s, Binding(i)); 109 } 110 111 template <typename T> 112 void RenameToIdentifiers(std::vector<T*>& things, 113 BindingHash& bh, 114 const std::set<std::string_view>* filter) { 115 Index i = 0; 116 for (auto thing : things) { 117 RenameToIdentifier(thing->name, i++, bh, filter); 118 } 119 } 120 121 enum { 122 // This a bit arbitrary, change at will. 123 min_content_identifier_size = 7, 124 max_content_identifier_size = 30 125 }; 126 127 void RenameToContents(std::vector<DataSegment*>& segs, BindingHash& bh) { 128 std::string s; 129 for (auto seg : segs) { 130 if (seg->name.substr(0, 2) != "d_") { 131 // This segment was named explicitly by a symbol. 132 // FIXME: this is not a great check, a symbol could start with d_. 133 continue; 134 } 135 s = "d_"; 136 for (auto c : seg->data) { 137 if (isalnum(c) || c == '_') { 138 s += static_cast<char>(c); 139 } 140 if (s.size() >= max_content_identifier_size) { 141 // We truncate any very long names, since those make for hard to 142 // format output. They can be somewhat long though, since data segment 143 // references tend to not occur that often. 144 break; 145 } 146 } 147 if (s.size() < min_content_identifier_size) { 148 // It is useful to have a minimum, since if there few printable characters 149 // in a data section, that is probably a sign of binary, and those few 150 // characters are not going to be very significant. 151 continue; 152 } 153 // We could do the same disambiguition as RenameToIdentifier and 154 // GenerateNames do, but if we come up with a clashing name here it is 155 // likely a sign of not very meaningful binary data, so it is easier to 156 // just keep the original generated name in that case. 157 if (bh.count(s) != 0) { 158 continue; 159 } 160 // Remove original entry. 161 bh.erase(seg->name); 162 seg->name = s; 163 bh.emplace(s, Binding(static_cast<Index>(&seg - &segs[0]))); 164 } 165 } 166 167 // Function names may contain arbitrary C++ syntax, so we want to 168 // filter those to look like identifiers. A function name may be set 169 // by a name section (applied in ReadBinaryIr, called before this function) 170 // or by an export (applied by GenerateNames, called before this function), 171 // to both the Func and func_bindings. 172 // Those names then further perculate down the IR in ApplyNames (called after 173 // this function). 174 // To not have to add too many decompiler-specific code into those systems 175 // (using a callback??) we instead rename everything here. 176 // Also do data section renaming here. 177 void RenameAll(Module& module) { 178 // We also filter common C++ keywords/STL idents that make for huge 179 // identifiers. 180 // FIXME: this can obviously give bad results if the input is not C++.. 181 std::set<std::string_view> filter = { 182 {"const"}, {"std"}, {"allocator"}, {"char"}, {"basic"}, 183 {"traits"}, {"wchar"}, {"t"}, {"void"}, {"int"}, 184 {"unsigned"}, {"2"}, {"cxxabiv1"}, {"short"}, {"4096ul"}, 185 }; 186 RenameToIdentifiers(module.funcs, module.func_bindings, &filter); 187 // Also do this for some other kinds of names, but without the keyword 188 // substitution. 189 RenameToIdentifiers(module.globals, module.global_bindings, nullptr); 190 RenameToIdentifiers(module.tables, module.table_bindings, nullptr); 191 RenameToIdentifiers(module.tags, module.tag_bindings, nullptr); 192 RenameToIdentifiers(module.exports, module.export_bindings, nullptr); 193 RenameToIdentifiers(module.types, module.type_bindings, nullptr); 194 RenameToIdentifiers(module.memories, module.memory_bindings, nullptr); 195 RenameToIdentifiers(module.data_segments, module.data_segment_bindings, 196 nullptr); 197 RenameToIdentifiers(module.elem_segments, module.elem_segment_bindings, 198 nullptr); 199 // Special purpose naming for data segments. 200 RenameToContents(module.data_segments, module.data_segment_bindings); 201 } 202 203 } // namespace wabt 204 205 #endif // WABT_DECOMPILER_NAMING_H_