/*
 * 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 (name[-1] == 'K' || name[-1] == 'V') {
      // Special case, const/volatile apply as a single entity.
      assert(!cur_state_.suffixes.empty());
      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 {
    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()) {
    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);
}
