//==--- AbstractBasicWriter.h - Abstract basic value serialization --------===//
 
//
 
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 
// See https://llvm.org/LICENSE.txt for license information.
 
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
//
 
//===----------------------------------------------------------------------===//
 
 
 
#ifndef LLVM_CLANG_AST_ABSTRACTBASICWRITER_H
 
#define LLVM_CLANG_AST_ABSTRACTBASICWRITER_H
 
 
 
#include "clang/AST/ASTContext.h"
 
#include "clang/AST/DeclTemplate.h"
 
#include <optional>
 
 
 
namespace clang {
 
namespace serialization {
 
 
 
template <class T>
 
inline std::optional<T> makeOptionalFromNullable(const T &value) {
 
  return (value.isNull() ? std::optional<T>() : std::optional<T>(value));
 
}
 
 
 
template <class T> inline std::optional<T *> makeOptionalFromPointer(T *value) {
 
  return (value ? std::optional<T *>(value) : std::optional<T *>());
 
}
 
 
 
// PropertyWriter is a class concept that requires the following method:
 
//   BasicWriter find(llvm::StringRef propertyName);
 
// where BasicWriter is some class conforming to the BasicWriter concept.
 
// An abstract AST-node writer is created with a PropertyWriter and
 
// performs a sequence of calls like so:
 
//   propertyWriter.find(propertyName).write##TypeName(value)
 
// to write the properties of the node it is serializing.
 
 
 
// BasicWriter is a class concept that requires methods like:
 
//   void write##TypeName(ValueType value);
 
// where TypeName is the name of a PropertyType node from PropertiesBase.td
 
// and ValueType is the corresponding C++ type name.
 
//
 
// In addition to the concrete property types, BasicWriter is expected
 
// to implement these methods:
 
//
 
//   template <class EnumType>
 
//   void writeEnum(T value);
 
//
 
//     Writes an enum value as the current property.  EnumType will always
 
//     be an enum type.  Only necessary if the BasicWriter doesn't provide
 
//     type-specific writers for all the enum types.
 
//
 
//   template <class ValueType>
 
//   void writeOptional(std::optional<ValueType> value);
 
//
 
//     Writes an optional value as the current property.
 
//
 
//   template <class ValueType>
 
//   void writeArray(ArrayRef<ValueType> value);
 
//
 
//     Writes an array of values as the current property.
 
//
 
//   PropertyWriter writeObject();
 
//
 
//     Writes an object as the current property; the returned property
 
//     writer will be subjected to a sequence of property writes and then
 
//     discarded before any other properties are written to the "outer"
 
//     property writer (which need not be the same type).  The sub-writer
 
//     will be used as if with the following code:
 
//
 
//       {
 
//         auto &&widget = W.find("widget").writeObject();
 
//         widget.find("kind").writeWidgetKind(...);
 
//         widget.find("declaration").writeDeclRef(...);
 
//       }
 
 
 
// WriteDispatcher is a template which does type-based forwarding to one
 
// of the write methods of the BasicWriter passed in:
 
//
 
// template <class ValueType>
 
// struct WriteDispatcher {
 
//   template <class BasicWriter>
 
//   static void write(BasicWriter &W, ValueType value);
 
// };
 
 
 
// BasicWriterBase provides convenience implementations of the write
 
// methods for EnumPropertyType and SubclassPropertyType types that just
 
// defer to the "underlying" implementations (for UInt32 and the base class,
 
// respectively).
 
//
 
// template <class Impl>
 
// class BasicWriterBase {
 
// protected:
 
//   Impl &asImpl();
 
// public:
 
//   ...
 
// };
 
 
 
// The actual classes are auto-generated; see ClangASTPropertiesEmitter.cpp.
 
#include "clang/AST/AbstractBasicWriter.inc"
 
 
 
/// DataStreamBasicWriter provides convenience implementations for many
 
/// BasicWriter methods based on the assumption that the
 
/// ultimate writer implementation is based on a variable-length stream
 
/// of unstructured data (like Clang's module files).  It is designed
 
/// to pair with DataStreamBasicReader.
 
///
 
/// This class can also act as a PropertyWriter, implementing find("...")
 
/// by simply forwarding to itself.
 
///
 
/// Unimplemented methods:
 
///   writeBool
 
///   writeUInt32
 
///   writeUInt64
 
///   writeIdentifier
 
///   writeSelector
 
///   writeSourceLocation
 
///   writeQualType
 
///   writeStmtRef
 
///   writeDeclRef
 
template <class Impl>
 
class DataStreamBasicWriter : public BasicWriterBase<Impl> {
 
protected:
 
  using BasicWriterBase<Impl>::asImpl;
 
  DataStreamBasicWriter(ASTContext &ctx) : BasicWriterBase<Impl>(ctx) {}
 
 
 
public:
 
  /// Implement property-find by ignoring it.  We rely on properties being
 
  /// serialized and deserialized in a reliable order instead.
 
  Impl &find(const char *propertyName) {
 
    return asImpl();
 
  }
 
 
 
  // Implement object writing by forwarding to this, collapsing the
 
  // structure into a single data stream.
 
  Impl &writeObject() { return asImpl(); }
 
 
 
  template <class T>
 
  void writeEnum(T value) {
 
    asImpl().writeUInt32(uint32_t(value));
 
  }
 
 
 
  template <class T>
 
  void writeArray(llvm::ArrayRef<T> array) {
 
    asImpl().writeUInt32(array.size());
 
    for (const T &elt : array) {
 
      WriteDispatcher<T>::write(asImpl(), elt);
 
    }
 
  }
 
 
 
  template <class T> void writeOptional(std::optional<T> value) {
 
    WriteDispatcher<T>::write(asImpl(), PackOptionalValue<T>::pack(value));
 
  }
 
 
 
  void writeAPSInt(const llvm::APSInt &value) {
 
    asImpl().writeBool(value.isUnsigned());
 
    asImpl().writeAPInt(value);
 
  }
 
 
 
  void writeAPInt(const llvm::APInt &value) {
 
    asImpl().writeUInt32(value.getBitWidth());
 
    const uint64_t *words = value.getRawData();
 
    for (size_t i = 0, e = value.getNumWords(); i != e; ++i)
 
      asImpl().writeUInt64(words[i]);
 
  }
 
 
 
  void writeFixedPointSemantics(const llvm::FixedPointSemantics &sema) {
 
    asImpl().writeUInt32(sema.getWidth());
 
    asImpl().writeUInt32(sema.getScale());
 
    asImpl().writeUInt32(sema.isSigned() | sema.isSaturated() << 1 |
 
                         sema.hasUnsignedPadding() << 2);
 
  }
 
 
 
  void writeLValuePathSerializationHelper(
 
      APValue::LValuePathSerializationHelper lvaluePath) {
 
    ArrayRef<APValue::LValuePathEntry> path = lvaluePath.Path;
 
    QualType elemTy = lvaluePath.getType();
 
    asImpl().writeQualType(elemTy);
 
    asImpl().writeUInt32(path.size());
 
    auto &ctx = ((BasicWriterBase<Impl> *)this)->getASTContext();
 
    for (auto elem : path) {
 
      if (elemTy->getAs<RecordType>()) {
 
        asImpl().writeUInt32(elem.getAsBaseOrMember().getInt());
 
        const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer();
 
        if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) {
 
          asImpl().writeDeclRef(recordDecl);
 
          elemTy = ctx.getRecordType(recordDecl);
 
        } else {
 
          const auto *valueDecl = cast<ValueDecl>(baseOrMember);
 
          asImpl().writeDeclRef(valueDecl);
 
          elemTy = valueDecl->getType();
 
        }
 
      } else {
 
        asImpl().writeUInt32(elem.getAsArrayIndex());
 
        elemTy = ctx.getAsArrayType(elemTy)->getElementType();
 
      }
 
    }
 
  }
 
 
 
  void writeQualifiers(Qualifiers value) {
 
    static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint32_t),
 
                  "update this if the value size changes");
 
    asImpl().writeUInt32(value.getAsOpaqueValue());
 
  }
 
 
 
  void writeExceptionSpecInfo(
 
                        const FunctionProtoType::ExceptionSpecInfo &esi) {
 
    asImpl().writeUInt32(uint32_t(esi.Type));
 
    if (esi.Type == EST_Dynamic) {
 
      asImpl().writeArray(esi.Exceptions);
 
    } else if (isComputedNoexcept(esi.Type)) {
 
      asImpl().writeExprRef(esi.NoexceptExpr);
 
    } else if (esi.Type == EST_Uninstantiated) {
 
      asImpl().writeDeclRef(esi.SourceDecl);
 
      asImpl().writeDeclRef(esi.SourceTemplate);
 
    } else if (esi.Type == EST_Unevaluated) {
 
      asImpl().writeDeclRef(esi.SourceDecl);
 
    }
 
  }
 
 
 
  void writeExtParameterInfo(FunctionProtoType::ExtParameterInfo epi) {
 
    static_assert(sizeof(epi.getOpaqueValue()) <= sizeof(uint32_t),
 
                  "opaque value doesn't fit into uint32_t");
 
    asImpl().writeUInt32(epi.getOpaqueValue());
 
  }
 
 
 
  void writeNestedNameSpecifier(NestedNameSpecifier *NNS) {
 
    // Nested name specifiers usually aren't too long. I think that 8 would
 
    // typically accommodate the vast majority.
 
    SmallVector<NestedNameSpecifier *, 8> nestedNames;
 
 
 
    // Push each of the NNS's onto a stack for serialization in reverse order.
 
    while (NNS) {
 
      nestedNames.push_back(NNS);
 
      NNS = NNS->getPrefix();
 
    }
 
 
 
    asImpl().writeUInt32(nestedNames.size());
 
    while (!nestedNames.empty()) {
 
      NNS = nestedNames.pop_back_val();
 
      NestedNameSpecifier::SpecifierKind kind = NNS->getKind();
 
      asImpl().writeNestedNameSpecifierKind(kind);
 
      switch (kind) {
 
      case NestedNameSpecifier::Identifier:
 
        asImpl().writeIdentifier(NNS->getAsIdentifier());
 
        continue;
 
 
 
      case NestedNameSpecifier::Namespace:
 
        asImpl().writeNamespaceDeclRef(NNS->getAsNamespace());
 
        continue;
 
 
 
      case NestedNameSpecifier::NamespaceAlias:
 
        asImpl().writeNamespaceAliasDeclRef(NNS->getAsNamespaceAlias());
 
        continue;
 
 
 
      case NestedNameSpecifier::TypeSpec:
 
      case NestedNameSpecifier::TypeSpecWithTemplate:
 
        asImpl().writeQualType(QualType(NNS->getAsType(), 0));
 
        continue;
 
 
 
      case NestedNameSpecifier::Global:
 
        // Don't need to write an associated value.
 
        continue;
 
 
 
      case NestedNameSpecifier::Super:
 
        asImpl().writeDeclRef(NNS->getAsRecordDecl());
 
        continue;
 
      }
 
      llvm_unreachable("bad nested name specifier kind");
 
    }
 
  }
 
};
 
 
 
} // end namespace serialization
 
} // end namespace clang
 
 
 
#endif