You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
748 lines
17 KiB
748 lines
17 KiB
/* |
|
* Copyright (C) 2017 The Android Open Source Project |
|
* |
|
* 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 <assert.h> |
|
|
|
#include <cctype> |
|
#include <stack> |
|
#include <string> |
|
#include <vector> |
|
|
|
#include "Demangler.h" |
|
|
|
constexpr const char* Demangler::kTypes[]; |
|
constexpr const char* Demangler::kDTypes[]; |
|
constexpr const char* Demangler::kSTypes[]; |
|
|
|
void Demangler::Save(const std::string& str, bool is_name) { |
|
saves_.push_back(str); |
|
last_save_name_ = is_name; |
|
} |
|
|
|
std::string Demangler::GetArgumentsString() { |
|
size_t num_args = cur_state_.args.size(); |
|
std::string arg_str; |
|
if (num_args > 0) { |
|
arg_str = cur_state_.args[0]; |
|
for (size_t i = 1; i < num_args; i++) { |
|
arg_str += ", " + cur_state_.args[i]; |
|
} |
|
} |
|
return arg_str; |
|
} |
|
|
|
const char* Demangler::AppendOperatorString(const char* name) { |
|
const char* oper = nullptr; |
|
switch (*name) { |
|
case 'a': |
|
name++; |
|
switch (*name) { |
|
case 'a': |
|
oper = "operator&&"; |
|
break; |
|
case 'd': |
|
case 'n': |
|
oper = "operator&"; |
|
break; |
|
case 'N': |
|
oper = "operator&="; |
|
break; |
|
case 'S': |
|
oper = "operator="; |
|
break; |
|
} |
|
break; |
|
case 'c': |
|
name++; |
|
switch (*name) { |
|
case 'l': |
|
oper = "operator()"; |
|
break; |
|
case 'm': |
|
oper = "operator,"; |
|
break; |
|
case 'o': |
|
oper = "operator~"; |
|
break; |
|
} |
|
break; |
|
case 'd': |
|
name++; |
|
switch (*name) { |
|
case 'a': |
|
oper = "operator delete[]"; |
|
break; |
|
case 'e': |
|
oper = "operator*"; |
|
break; |
|
case 'l': |
|
oper = "operator delete"; |
|
break; |
|
case 'v': |
|
oper = "operator/"; |
|
break; |
|
case 'V': |
|
oper = "operator/="; |
|
break; |
|
} |
|
break; |
|
case 'e': |
|
name++; |
|
switch (*name) { |
|
case 'o': |
|
oper = "operator^"; |
|
break; |
|
case 'O': |
|
oper = "operator^="; |
|
break; |
|
case 'q': |
|
oper = "operator=="; |
|
break; |
|
} |
|
break; |
|
case 'g': |
|
name++; |
|
switch (*name) { |
|
case 'e': |
|
oper = "operator>="; |
|
break; |
|
case 't': |
|
oper = "operator>"; |
|
break; |
|
} |
|
break; |
|
case 'i': |
|
name++; |
|
switch (*name) { |
|
case 'x': |
|
oper = "operator[]"; |
|
break; |
|
} |
|
break; |
|
case 'l': |
|
name++; |
|
switch (*name) { |
|
case 'e': |
|
oper = "operator<="; |
|
break; |
|
case 's': |
|
oper = "operator<<"; |
|
break; |
|
case 'S': |
|
oper = "operator<<="; |
|
break; |
|
case 't': |
|
oper = "operator<"; |
|
break; |
|
} |
|
break; |
|
case 'm': |
|
name++; |
|
switch (*name) { |
|
case 'i': |
|
oper = "operator-"; |
|
break; |
|
case 'I': |
|
oper = "operator-="; |
|
break; |
|
case 'l': |
|
oper = "operator*"; |
|
break; |
|
case 'L': |
|
oper = "operator*="; |
|
break; |
|
case 'm': |
|
oper = "operator--"; |
|
break; |
|
} |
|
break; |
|
case 'n': |
|
name++; |
|
switch (*name) { |
|
case 'a': |
|
oper = "operator new[]"; |
|
break; |
|
case 'e': |
|
oper = "operator!="; |
|
break; |
|
case 'g': |
|
oper = "operator-"; |
|
break; |
|
case 't': |
|
oper = "operator!"; |
|
break; |
|
case 'w': |
|
oper = "operator new"; |
|
break; |
|
} |
|
break; |
|
case 'o': |
|
name++; |
|
switch (*name) { |
|
case 'o': |
|
oper = "operator||"; |
|
break; |
|
case 'r': |
|
oper = "operator|"; |
|
break; |
|
case 'R': |
|
oper = "operator|="; |
|
break; |
|
} |
|
break; |
|
case 'p': |
|
name++; |
|
switch (*name) { |
|
case 'm': |
|
oper = "operator->*"; |
|
break; |
|
case 'l': |
|
oper = "operator+"; |
|
break; |
|
case 'L': |
|
oper = "operator+="; |
|
break; |
|
case 'p': |
|
oper = "operator++"; |
|
break; |
|
case 's': |
|
oper = "operator+"; |
|
break; |
|
case 't': |
|
oper = "operator->"; |
|
break; |
|
} |
|
break; |
|
case 'q': |
|
name++; |
|
switch (*name) { |
|
case 'u': |
|
oper = "operator?"; |
|
break; |
|
} |
|
break; |
|
case 'r': |
|
name++; |
|
switch (*name) { |
|
case 'm': |
|
oper = "operator%"; |
|
break; |
|
case 'M': |
|
oper = "operator%="; |
|
break; |
|
case 's': |
|
oper = "operator>>"; |
|
break; |
|
case 'S': |
|
oper = "operator>>="; |
|
break; |
|
} |
|
break; |
|
} |
|
if (oper == nullptr) { |
|
return nullptr; |
|
} |
|
AppendCurrent(oper); |
|
cur_state_.last_save = oper; |
|
return name + 1; |
|
} |
|
|
|
const char* Demangler::GetStringFromLength(const char* name, std::string* str) { |
|
assert(std::isdigit(*name)); |
|
|
|
size_t length = *name - '0'; |
|
name++; |
|
while (*name != '\0' && std::isdigit(*name)) { |
|
length = length * 10 + *name - '0'; |
|
name++; |
|
} |
|
|
|
std::string read_str; |
|
while (*name != '\0' && length != 0) { |
|
read_str += *name; |
|
name++; |
|
length--; |
|
} |
|
if (length != 0) { |
|
return nullptr; |
|
} |
|
// Special replacement of _GLOBAL__N_1 to (anonymous namespace). |
|
if (read_str == "_GLOBAL__N_1") { |
|
*str += "(anonymous namespace)"; |
|
} else { |
|
*str += read_str; |
|
} |
|
return name; |
|
} |
|
|
|
void Demangler::AppendCurrent(const std::string& str) { |
|
if (!cur_state_.str.empty()) { |
|
cur_state_.str += "::"; |
|
} |
|
cur_state_.str += str; |
|
} |
|
|
|
void Demangler::AppendCurrent(const char* str) { |
|
if (!cur_state_.str.empty()) { |
|
cur_state_.str += "::"; |
|
} |
|
cur_state_.str += str; |
|
} |
|
|
|
const char* Demangler::ParseS(const char* name) { |
|
if (std::islower(*name)) { |
|
const char* type = kSTypes[*name - 'a']; |
|
if (type == nullptr) { |
|
return nullptr; |
|
} |
|
AppendCurrent(type); |
|
return name + 1; |
|
} |
|
|
|
if (saves_.empty()) { |
|
return nullptr; |
|
} |
|
|
|
if (*name == '_') { |
|
last_save_name_ = false; |
|
AppendCurrent(saves_[0]); |
|
return name + 1; |
|
} |
|
|
|
bool isdigit = std::isdigit(*name); |
|
if (!isdigit && !std::isupper(*name)) { |
|
return nullptr; |
|
} |
|
|
|
size_t index; |
|
if (isdigit) { |
|
index = *name - '0' + 1; |
|
} else { |
|
index = *name - 'A' + 11; |
|
} |
|
name++; |
|
if (*name != '_') { |
|
return nullptr; |
|
} |
|
|
|
if (index >= saves_.size()) { |
|
return nullptr; |
|
} |
|
|
|
last_save_name_ = false; |
|
AppendCurrent(saves_[index]); |
|
return name + 1; |
|
} |
|
|
|
const char* Demangler::ParseFunctionName(const char* name) { |
|
if (*name == 'E') { |
|
if (parse_funcs_.empty()) { |
|
return nullptr; |
|
} |
|
parse_func_ = parse_funcs_.back(); |
|
parse_funcs_.pop_back(); |
|
|
|
// Remove the last saved part so that the full function name is not saved. |
|
// But only if the last save was not something like a substitution. |
|
if (!saves_.empty() && last_save_name_) { |
|
saves_.pop_back(); |
|
} |
|
|
|
function_name_ = cur_state_.str; |
|
while (!cur_state_.suffixes.empty()) { |
|
function_suffix_ += cur_state_.suffixes.back(); |
|
cur_state_.suffixes.pop_back(); |
|
} |
|
cur_state_.Clear(); |
|
|
|
return name + 1; |
|
} |
|
|
|
return ParseComplexString(name); |
|
} |
|
|
|
const char* Demangler::ParseComplexArgument(const char* name) { |
|
if (*name == 'E') { |
|
if (parse_funcs_.empty()) { |
|
return nullptr; |
|
} |
|
parse_func_ = parse_funcs_.back(); |
|
parse_funcs_.pop_back(); |
|
|
|
AppendArgument(cur_state_.str); |
|
cur_state_.str.clear(); |
|
|
|
return name + 1; |
|
} |
|
|
|
return ParseComplexString(name); |
|
} |
|
|
|
void Demangler::FinalizeTemplate() { |
|
std::string arg_str(GetArgumentsString()); |
|
cur_state_ = state_stack_.top(); |
|
state_stack_.pop(); |
|
cur_state_.str += '<' + arg_str + '>'; |
|
} |
|
|
|
const char* Demangler::ParseComplexString(const char* name) { |
|
if (*name == 'S') { |
|
name++; |
|
if (*name == 't') { |
|
AppendCurrent("std"); |
|
return name + 1; |
|
} |
|
return ParseS(name); |
|
} |
|
if (*name == 'L') { |
|
name++; |
|
if (!std::isdigit(*name)) { |
|
return nullptr; |
|
} |
|
} |
|
if (std::isdigit(*name)) { |
|
std::string str; |
|
name = GetStringFromLength(name, &str); |
|
if (name == nullptr) { |
|
return name; |
|
} |
|
AppendCurrent(str); |
|
Save(cur_state_.str, true); |
|
cur_state_.last_save = std::move(str); |
|
return name; |
|
} |
|
if (*name == 'D') { |
|
name++; |
|
if (saves_.empty() || (*name != '0' && *name != '1' && *name != '2' |
|
&& *name != '5')) { |
|
return nullptr; |
|
} |
|
last_save_name_ = false; |
|
AppendCurrent("~" + cur_state_.last_save); |
|
return name + 1; |
|
} |
|
if (*name == 'C') { |
|
name++; |
|
if (saves_.empty() || (*name != '1' && *name != '2' && *name != '3' |
|
&& *name != '5')) { |
|
return nullptr; |
|
} |
|
last_save_name_ = false; |
|
AppendCurrent(cur_state_.last_save); |
|
return name + 1; |
|
} |
|
if (*name == 'K') { |
|
cur_state_.suffixes.push_back(" const"); |
|
return name + 1; |
|
} |
|
if (*name == 'V') { |
|
cur_state_.suffixes.push_back(" volatile"); |
|
return name + 1; |
|
} |
|
if (*name == 'I') { |
|
// Save the current argument state. |
|
state_stack_.push(cur_state_); |
|
cur_state_.Clear(); |
|
|
|
parse_funcs_.push_back(parse_func_); |
|
parse_func_ = &Demangler::ParseTemplateArgumentsComplex; |
|
return name + 1; |
|
} |
|
name = AppendOperatorString(name); |
|
if (name != nullptr) { |
|
Save(cur_state_.str, true); |
|
} |
|
return name; |
|
} |
|
|
|
void Demangler::AppendArgument(const std::string& str) { |
|
std::string arg(str); |
|
while (!cur_state_.suffixes.empty()) { |
|
arg += cur_state_.suffixes.back(); |
|
cur_state_.suffixes.pop_back(); |
|
Save(arg, false); |
|
} |
|
cur_state_.args.push_back(arg); |
|
} |
|
|
|
const char* Demangler::ParseFunctionArgument(const char* name) { |
|
if (*name == 'E') { |
|
// The first argument is the function modifier. |
|
// The second argument is the function type. |
|
// The third argument is the return type of the function. |
|
// The rest of the arguments are the function arguments. |
|
size_t num_args = cur_state_.args.size(); |
|
if (num_args < 4) { |
|
return nullptr; |
|
} |
|
std::string function_modifier = cur_state_.args[0]; |
|
std::string function_type = cur_state_.args[1]; |
|
|
|
std::string str = cur_state_.args[2] + ' '; |
|
if (!cur_state_.args[1].empty()) { |
|
str += '(' + cur_state_.args[1] + ')'; |
|
} |
|
|
|
if (num_args == 4 && cur_state_.args[3] == "void") { |
|
str += "()"; |
|
} else { |
|
str += '(' + cur_state_.args[3]; |
|
for (size_t i = 4; i < num_args; i++) { |
|
str += ", " + cur_state_.args[i]; |
|
} |
|
str += ')'; |
|
} |
|
str += cur_state_.args[0]; |
|
|
|
cur_state_ = state_stack_.top(); |
|
state_stack_.pop(); |
|
cur_state_.args.emplace_back(std::move(str)); |
|
|
|
parse_func_ = parse_funcs_.back(); |
|
parse_funcs_.pop_back(); |
|
return name + 1; |
|
} |
|
return ParseArguments(name); |
|
} |
|
|
|
const char* Demangler::ParseArguments(const char* name) { |
|
switch (*name) { |
|
case 'P': |
|
cur_state_.suffixes.push_back("*"); |
|
return name + 1; |
|
|
|
case 'R': |
|
// This should always be okay because the string is guaranteed to have |
|
// at least two characters before this. A mangled string always starts |
|
// with _Z. |
|
if (name[-1] != 'R') { |
|
// Multiple 'R's in a row only add a single &. |
|
cur_state_.suffixes.push_back("&"); |
|
} |
|
return name + 1; |
|
|
|
case 'K': |
|
case 'V': { |
|
const char* suffix; |
|
if (*name == 'K') { |
|
suffix = " const"; |
|
} else { |
|
suffix = " volatile"; |
|
} |
|
if (!cur_state_.suffixes.empty() && (name[-1] == 'K' || name[-1] == 'V')) { |
|
// Special case, const/volatile apply as a single entity. |
|
size_t index = cur_state_.suffixes.size(); |
|
cur_state_.suffixes[index-1].insert(0, suffix); |
|
} else { |
|
cur_state_.suffixes.push_back(suffix); |
|
} |
|
return name + 1; |
|
} |
|
|
|
case 'F': { |
|
std::string function_modifier; |
|
std::string function_type; |
|
if (!cur_state_.suffixes.empty()) { |
|
// If the first element starts with a ' ', then this modifies the |
|
// function itself. |
|
if (cur_state_.suffixes.back()[0] == ' ') { |
|
function_modifier = cur_state_.suffixes.back(); |
|
cur_state_.suffixes.pop_back(); |
|
} |
|
while (!cur_state_.suffixes.empty()) { |
|
function_type += cur_state_.suffixes.back(); |
|
cur_state_.suffixes.pop_back(); |
|
} |
|
} |
|
|
|
state_stack_.push(cur_state_); |
|
|
|
cur_state_.Clear(); |
|
|
|
// The function parameter has this format: |
|
// First argument is the function modifier. |
|
// Second argument is the function type. |
|
// Third argument will be the return function type but has not |
|
// been parsed yet. |
|
// Any other parameters are the arguments to the function. There |
|
// must be at least one or this isn't valid. |
|
cur_state_.args.push_back(function_modifier); |
|
cur_state_.args.push_back(function_type); |
|
|
|
parse_funcs_.push_back(parse_func_); |
|
parse_func_ = &Demangler::ParseFunctionArgument; |
|
return name + 1; |
|
} |
|
|
|
case 'N': |
|
parse_funcs_.push_back(parse_func_); |
|
parse_func_ = &Demangler::ParseComplexArgument; |
|
return name + 1; |
|
|
|
case 'S': |
|
name++; |
|
if (*name == 't') { |
|
cur_state_.str = "std::"; |
|
return name + 1; |
|
} |
|
name = ParseS(name); |
|
if (name == nullptr) { |
|
return nullptr; |
|
} |
|
AppendArgument(cur_state_.str); |
|
cur_state_.str.clear(); |
|
return name; |
|
|
|
case 'D': |
|
name++; |
|
if (*name >= 'a' && *name <= 'z') { |
|
const char* arg = Demangler::kDTypes[*name - 'a']; |
|
if (arg == nullptr) { |
|
return nullptr; |
|
} |
|
AppendArgument(arg); |
|
return name + 1; |
|
} |
|
return nullptr; |
|
|
|
case 'I': |
|
// Save the current argument state. |
|
state_stack_.push(cur_state_); |
|
cur_state_.Clear(); |
|
|
|
parse_funcs_.push_back(parse_func_); |
|
parse_func_ = &Demangler::ParseTemplateArguments; |
|
return name + 1; |
|
|
|
case 'v': |
|
AppendArgument("void"); |
|
return name + 1; |
|
|
|
default: |
|
if (*name >= 'a' && *name <= 'z') { |
|
const char* arg = Demangler::kTypes[*name - 'a']; |
|
if (arg == nullptr) { |
|
return nullptr; |
|
} |
|
AppendArgument(arg); |
|
return name + 1; |
|
} else if (std::isdigit(*name)) { |
|
std::string arg = cur_state_.str; |
|
name = GetStringFromLength(name, &arg); |
|
if (name == nullptr) { |
|
return nullptr; |
|
} |
|
Save(arg, true); |
|
if (*name == 'I') { |
|
// There is one case where this argument is not complete, and that's |
|
// where this is a template argument. |
|
cur_state_.str = arg; |
|
} else { |
|
AppendArgument(arg); |
|
cur_state_.str.clear(); |
|
} |
|
return name; |
|
} |
|
} |
|
return nullptr; |
|
} |
|
|
|
const char* Demangler::ParseTemplateArgumentsComplex(const char* name) { |
|
if (*name == 'E') { |
|
if (parse_funcs_.empty()) { |
|
return nullptr; |
|
} |
|
parse_func_ = parse_funcs_.back(); |
|
parse_funcs_.pop_back(); |
|
FinalizeTemplate(); |
|
Save(cur_state_.str, false); |
|
return name + 1; |
|
} |
|
return ParseArguments(name); |
|
} |
|
|
|
const char* Demangler::ParseTemplateArguments(const char* name) { |
|
if (*name == 'E') { |
|
if (parse_funcs_.empty()) { |
|
return nullptr; |
|
} |
|
parse_func_ = parse_funcs_.back(); |
|
parse_funcs_.pop_back(); |
|
FinalizeTemplate(); |
|
AppendArgument(cur_state_.str); |
|
cur_state_.str.clear(); |
|
return name + 1; |
|
} |
|
return ParseArguments(name); |
|
} |
|
|
|
const char* Demangler::FindFunctionName(const char* name) { |
|
if (*name == 'N') { |
|
parse_funcs_.push_back(&Demangler::ParseArguments); |
|
parse_func_ = &Demangler::ParseFunctionName; |
|
return name + 1; |
|
} |
|
|
|
if (std::isdigit(*name)) { |
|
name = GetStringFromLength(name, &function_name_); |
|
} else if (*name == 'L' && std::isdigit(name[1])) { |
|
name = GetStringFromLength(name + 1, &function_name_); |
|
} else { |
|
name = AppendOperatorString(name); |
|
function_name_ = cur_state_.str; |
|
} |
|
parse_func_ = &Demangler::ParseArguments; |
|
cur_state_.Clear(); |
|
return name; |
|
} |
|
|
|
std::string Demangler::Parse(const char* name, size_t max_length) { |
|
if (name[0] == '\0' || name[0] != '_' || name[1] == '\0' || name[1] != 'Z') { |
|
// Name is not mangled. |
|
return name; |
|
} |
|
|
|
Clear(); |
|
|
|
parse_func_ = &Demangler::FindFunctionName; |
|
parse_funcs_.push_back(&Demangler::Fail); |
|
const char* cur_name = name + 2; |
|
while (cur_name != nullptr && *cur_name != '\0' |
|
&& static_cast<size_t>(cur_name - name) < max_length) { |
|
cur_name = (this->*parse_func_)(cur_name); |
|
} |
|
if (cur_name == nullptr || *cur_name != '\0' || function_name_.empty() || |
|
!cur_state_.suffixes.empty()) { |
|
return name; |
|
} |
|
|
|
std::string arg_str; |
|
if (cur_state_.args.size() == 1 && cur_state_.args[0] == "void") { |
|
// If the only argument is void, then don't print any args. |
|
arg_str = "()"; |
|
} else { |
|
arg_str = GetArgumentsString(); |
|
if (!arg_str.empty()) { |
|
arg_str = '(' + arg_str + ')'; |
|
} |
|
} |
|
return function_name_ + arg_str + function_suffix_; |
|
} |
|
|
|
std::string demangle(const char* name) { |
|
Demangler demangler; |
|
return demangler.Parse(name); |
|
}
|
|
|