//===--- Demangle.h - Interface to Swift symbol demangling ------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_DEMANGLE_H
#define SWIFT_BASIC_DEMANGLE_H

#include <memory>
#include <string>
#include <vector>
#include <cassert>
#include <cstdint>
#include "llvm/ADT/StringRef.h"
#include "swift/Basic/Malloc.h"

namespace llvm {
  class raw_ostream;
}

namespace swift {
namespace Demangle {

struct DemangleOptions {
  bool SynthesizeSugarOnTypes = false;
  bool DisplayTypeOfIVarFieldOffset = true;
  bool DisplayDebuggerGeneratedModule = true;
  bool QualifyEntities = true;
  bool DisplayExtensionContexts = true;
  bool DisplayUnmangledSuffix = true;
  bool DisplayModuleNames = true;
  bool DisplayGenericSpecializations = true;
  bool DisplayProtocolConformances = true;
  bool DisplayWhereClauses = true;
  bool DisplayEntityTypes = true;
  bool ShortenPartialApply = false;
  bool ShortenThunk = false;
  bool ShortenValueWitness = false;
  bool ShortenArchetype = false;

  DemangleOptions() {}

  static DemangleOptions SimplifiedUIDemangleOptions() {
    auto Opt = DemangleOptions();
    Opt.SynthesizeSugarOnTypes = true;
    Opt.QualifyEntities = true;
    Opt.DisplayExtensionContexts = false;
    Opt.DisplayUnmangledSuffix = false;
    Opt.DisplayModuleNames = false;
    Opt.DisplayGenericSpecializations = false;
    Opt.DisplayProtocolConformances = false;
    Opt.DisplayWhereClauses = false;
    Opt.DisplayEntityTypes = false;
    Opt.ShortenPartialApply = true;
    Opt.ShortenThunk = true;
    Opt.ShortenValueWitness = true;
    Opt.ShortenArchetype = true;
    return Opt;
  };
};

class Node;
typedef std::shared_ptr<Node> NodePointer;

enum class FunctionSigSpecializationParamKind : unsigned {
  // Option Flags use bits 0-5. This give us 6 bits implying 64 entries to
  // work with.
  ConstantPropFunction = 0,
  ConstantPropGlobal = 1,
  ConstantPropInteger = 2,
  ConstantPropFloat = 3,
  ConstantPropString = 4,
  ClosureProp = 5,
  BoxToValue = 6,
  BoxToStack = 7,

  // Option Set Flags use bits 6-31. This gives us 26 bits to use for option
  // flags.
  Dead = 1 << 6,
  OwnedToGuaranteed = 1 << 7,
  SROA = 1 << 8,
};

/// The pass that caused the specialization to occur. We use this to make sure
/// that two passes that generate similar changes do not yield the same
/// mangling. This currently cannot happen, so this is just a safety measure
/// that creates separate name spaces.
enum class SpecializationPass : uint8_t {
  AllocBoxToStack,
  ClosureSpecializer,
  CapturePromotion,
  CapturePropagation,
  FunctionSignatureOpts,
  GenericSpecializer,
};

static inline char encodeSpecializationPass(SpecializationPass Pass) {
  return char(uint8_t(Pass)) + '0';
}

enum class ValueWitnessKind {
  AllocateBuffer,
  AssignWithCopy,
  AssignWithTake,
  DeallocateBuffer,
  Destroy,
  DestroyBuffer,
  InitializeBufferWithCopyOfBuffer,
  InitializeBufferWithCopy,
  InitializeWithCopy,
  InitializeBufferWithTake,
  InitializeWithTake,
  ProjectBuffer,
  InitializeBufferWithTakeOfBuffer,
  DestroyArray,
  InitializeArrayWithCopy,
  InitializeArrayWithTakeFrontToBack,
  InitializeArrayWithTakeBackToFront,
  StoreExtraInhabitant,
  GetExtraInhabitantIndex,
  GetEnumTag,
  DestructiveProjectEnumData
};

enum class Directness {
  Direct, Indirect
};

class Node : public std::enable_shared_from_this<Node> {
public:
  enum class Kind : uint16_t {
#define NODE(ID) ID,
#include "swift/Basic/DemangleNodes.def"
  };

  typedef uint64_t IndexType;

private:
  Kind NodeKind;

  enum class PayloadKind : uint8_t {
    None, Text, Index
  };
  PayloadKind NodePayloadKind;

  union {
    std::string TextPayload;
    IndexType IndexPayload;
  };

  // FIXME: use allocator.
  typedef std::vector<NodePointer> NodeVector;
  NodeVector Children;

  Node(Kind k)
      : NodeKind(k), NodePayloadKind(PayloadKind::None) {
  }
  Node(Kind k, std::string &&t)
      : NodeKind(k), NodePayloadKind(PayloadKind::Text) {
    new (&TextPayload) std::string(std::move(t));
  }
  Node(Kind k, IndexType index)
      : NodeKind(k), NodePayloadKind(PayloadKind::Index) {
    IndexPayload = index;
  }
  Node(const Node &) = delete;
  Node &operator=(const Node &) = delete;

  friend struct NodeFactory;

public:
  ~Node();

  Kind getKind() const { return NodeKind; }

  bool hasText() const { return NodePayloadKind == PayloadKind::Text; }
  const std::string &getText() const {
    assert(hasText());
    return TextPayload;
  }

  bool hasIndex() const { return NodePayloadKind == PayloadKind::Index; }
  uint64_t getIndex() const {
    assert(hasIndex());
    return IndexPayload;
  }
  
  typedef NodeVector::iterator iterator;
  typedef NodeVector::const_iterator const_iterator;
  typedef NodeVector::size_type size_type;

  bool hasChildren() const { return !Children.empty(); }
  size_t getNumChildren() const { return Children.size(); }
  iterator begin() { return Children.begin(); }
  iterator end() { return Children.end(); }
  const_iterator begin() const { return Children.begin(); }
  const_iterator end() const { return Children.end(); }

  NodePointer getFirstChild() const { return Children.front(); }
  NodePointer getChild(size_t index) const { return Children[index]; }

  /// Add a new node as a child of this one.
  ///
  /// \param child - should have no parent or siblings
  /// \returns child
  NodePointer addChild(NodePointer child) {
    assert(child && "adding null child!");
    Children.push_back(child);
    return child;
  }

  /// A convenience method for adding two children at once.
  void addChildren(NodePointer child1, NodePointer child2) {
    addChild(std::move(child1));
    addChild(std::move(child2));
  }
};

/// \brief Demangle the given string as a Swift symbol.
///
/// Typical usage:
/// \code
///   NodePointer aDemangledName =
/// swift::Demangler::demangleSymbolAsNode("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A parse tree for the demangled string - or a null pointer
/// on failure.
///
NodePointer
demangleSymbolAsNode(const char *mangledName, size_t mangledNameLength,
                     const DemangleOptions &options = DemangleOptions());

inline NodePointer
demangleSymbolAsNode(const std::string &mangledName,
                     const DemangleOptions &options = DemangleOptions()) {
  return demangleSymbolAsNode(mangledName.data(), mangledName.size(), options);
}

/// \brief Demangle the given string as a Swift symbol.
///
/// Typical usage:
/// \code
///   std::string aDemangledName =
/// swift::Demangler::demangleSymbol("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A string representing the demangled name.
///
std::string
demangleSymbolAsString(const char *mangledName, size_t mangledNameLength,
                       const DemangleOptions &options = DemangleOptions());

inline std::string
demangleSymbolAsString(const std::string &mangledName,
                       const DemangleOptions &options = DemangleOptions()) {
  return demangleSymbolAsString(mangledName.data(), mangledName.size(),
                                options);
}

/// \brief Demangle the given string as a Swift type.
///
/// Typical usage:
/// \code
///   NodePointer aDemangledName =
/// swift::Demangler::demangleTypeAsNode("SomeSwiftMangledName")
/// \endcode
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A parse tree for the demangled string - or a null pointer
/// on failure.
///
NodePointer
demangleTypeAsNode(const char *mangledName, size_t mangledNameLength,
                   const DemangleOptions &options = DemangleOptions());

inline NodePointer
demangleTypeAsNode(const std::string &mangledName,
                   const DemangleOptions &options = DemangleOptions()) {
  return demangleTypeAsNode(mangledName.data(), mangledName.size(), options);
}

/// \brief Demangle the given string as a Swift type mangling.
///
/// \param mangledName The mangled string.
/// \param options An object encapsulating options to use to perform this demangling.
///
///
/// \returns A string representing the demangled name.
std::string
demangleTypeAsString(const char *mangledName, size_t mangledNameLength,
                     const DemangleOptions &options = DemangleOptions());

inline std::string
demangleTypeAsString(const std::string &mangledName,
                     const DemangleOptions &options = DemangleOptions()) {
  return demangleTypeAsString(mangledName.data(), mangledName.size(), options);
}

enum class OperatorKind {
  NotOperator,
  Prefix,
  Postfix,
  Infix
};

/// \brief Mangle an identifier using Swift's mangling rules.
void mangleIdentifier(const char *data, size_t length,
                      OperatorKind operatorKind, std::string &out,
                      bool usePunycode = true);

/// \brief Remangle a demangled parse tree.
///
/// This should always round-trip perfectly with demangleSymbolAsNode.
std::string mangleNode(const NodePointer &root);

/// \brief Transform the node structure in a string.
///
/// Typical usage:
/// \code
///   std::string aDemangledName =
/// swift::Demangler::nodeToString(aNode)
/// \endcode
///
/// \param Root A pointer to a parse tree generated by the demangler.
/// \param Options An object encapsulating options to use to perform this demangling.
///
/// \returns A string representing the demangled name.
///
std::string nodeToString(NodePointer Root,
                         const DemangleOptions &Options = DemangleOptions());

struct NodeFactory {
  static NodePointer create(Node::Kind K) {
    return NodePointer(new Node(K));
  }
  static NodePointer create(Node::Kind K, Node::IndexType Index) {
    return NodePointer(new Node(K, Index));
  }
  static NodePointer create(Node::Kind K, llvm::StringRef Text) {
    return NodePointer(new Node(K, Text));
  }
  static NodePointer create(Node::Kind K, std::string &&Text) {
    return NodePointer(new Node(K, std::move(Text)));
  }
  template <size_t N>
  static NodePointer create(Node::Kind K, const char (&Text)[N]) {
    return NodePointer(new Node(K, llvm::StringRef(Text)));
  }
};

  /// A class for printing to a std::string.
class DemanglerPrinter {
public:
  DemanglerPrinter(std::string &out) : Stream(out) {}
  DemanglerPrinter(std::string &&out) : Stream(out) {}

  DemanglerPrinter &operator<<(llvm::StringRef Value) & {
    Stream.append(Value.data(), Value.size());
    return *this;
  }
  
  DemanglerPrinter &operator<<(char c) & {
    Stream.push_back(c);
    return *this;
  }
  DemanglerPrinter &operator<<(unsigned long long n) &;
  DemanglerPrinter &operator<<(long long n) &;
  DemanglerPrinter &operator<<(unsigned long n) & {
    return *this << (unsigned long long)n;
  }
  DemanglerPrinter &operator<<(long n) & {
    return *this << (long long)n;
  }
  DemanglerPrinter &operator<<(unsigned n) & {
    return *this << (unsigned long long)n;
  }
  DemanglerPrinter &operator<<(int n) & {
    return *this << (long long)n;
  }
  
  template<typename T>
  DemanglerPrinter &&operator<<(T &&x) && {
    return std::move(*this << std::forward<T>(x));
  }
  
  std::string &&str() && { return std::move(Stream); }
  
private:
  std::string &Stream;
};

/// Is a character considered a digit by the demangling grammar?
///
/// Yes, this is equivalent to the standard C isdigit(3), but some platforms
/// give isdigit suboptimal implementations.
static inline bool isDigit(int c) {
  return c >= '0' && c <= '9';
}
  
} // end namespace Demangle
} // end namespace swift

#endif

