1 //==--- AbstractBasicWriter.h - Abstract basic value serialization --------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CLANG_AST_ABSTRACTBASICWRITER_H 10 #define LLVM_CLANG_AST_ABSTRACTBASICWRITER_H 11 12 #include "clang/AST/ASTContext.h" 13 #include "clang/AST/DeclTemplate.h" 14 #include <optional> 15 16 namespace clang { 17 namespace serialization { 18 19 template <class T> 20 inline std::optional<T> makeOptionalFromNullable(const T &value) { 21 return (value.isNull() ? std::optional<T>() : std::optional<T>(value)); 22 } 23 24 template <class T> inline std::optional<T *> makeOptionalFromPointer(T *value) { 25 return (value ? std::optional<T *>(value) : std::optional<T *>()); 26 } 27 28 // PropertyWriter is a class concept that requires the following method: 29 // BasicWriter find(llvm::StringRef propertyName); 30 // where BasicWriter is some class conforming to the BasicWriter concept. 31 // An abstract AST-node writer is created with a PropertyWriter and 32 // performs a sequence of calls like so: 33 // propertyWriter.find(propertyName).write##TypeName(value) 34 // to write the properties of the node it is serializing. 35 36 // BasicWriter is a class concept that requires methods like: 37 // void write##TypeName(ValueType value); 38 // where TypeName is the name of a PropertyType node from PropertiesBase.td 39 // and ValueType is the corresponding C++ type name. 40 // 41 // In addition to the concrete property types, BasicWriter is expected 42 // to implement these methods: 43 // 44 // template <class EnumType> 45 // void writeEnum(T value); 46 // 47 // Writes an enum value as the current property. EnumType will always 48 // be an enum type. Only necessary if the BasicWriter doesn't provide 49 // type-specific writers for all the enum types. 50 // 51 // template <class ValueType> 52 // void writeOptional(std::optional<ValueType> value); 53 // 54 // Writes an optional value as the current property. 55 // 56 // template <class ValueType> 57 // void writeArray(ArrayRef<ValueType> value); 58 // 59 // Writes an array of values as the current property. 60 // 61 // PropertyWriter writeObject(); 62 // 63 // Writes an object as the current property; the returned property 64 // writer will be subjected to a sequence of property writes and then 65 // discarded before any other properties are written to the "outer" 66 // property writer (which need not be the same type). The sub-writer 67 // will be used as if with the following code: 68 // 69 // { 70 // auto &&widget = W.find("widget").writeObject(); 71 // widget.find("kind").writeWidgetKind(...); 72 // widget.find("declaration").writeDeclRef(...); 73 // } 74 75 // WriteDispatcher is a template which does type-based forwarding to one 76 // of the write methods of the BasicWriter passed in: 77 // 78 // template <class ValueType> 79 // struct WriteDispatcher { 80 // template <class BasicWriter> 81 // static void write(BasicWriter &W, ValueType value); 82 // }; 83 84 // BasicWriterBase provides convenience implementations of the write 85 // methods for EnumPropertyType and SubclassPropertyType types that just 86 // defer to the "underlying" implementations (for UInt32 and the base class, 87 // respectively). 88 // 89 // template <class Impl> 90 // class BasicWriterBase { 91 // protected: 92 // Impl &asImpl(); 93 // public: 94 // ... 95 // }; 96 97 // The actual classes are auto-generated; see ClangASTPropertiesEmitter.cpp. 98 #include "clang/AST/AbstractBasicWriter.inc" 99 100 /// DataStreamBasicWriter provides convenience implementations for many 101 /// BasicWriter methods based on the assumption that the 102 /// ultimate writer implementation is based on a variable-length stream 103 /// of unstructured data (like Clang's module files). It is designed 104 /// to pair with DataStreamBasicReader. 105 /// 106 /// This class can also act as a PropertyWriter, implementing find("...") 107 /// by simply forwarding to itself. 108 /// 109 /// Unimplemented methods: 110 /// writeBool 111 /// writeUInt32 112 /// writeUInt64 113 /// writeIdentifier 114 /// writeSelector 115 /// writeSourceLocation 116 /// writeQualType 117 /// writeStmtRef 118 /// writeDeclRef 119 template <class Impl> 120 class DataStreamBasicWriter : public BasicWriterBase<Impl> { 121 protected: 122 using BasicWriterBase<Impl>::asImpl; 123 DataStreamBasicWriter(ASTContext &ctx) : BasicWriterBase<Impl>(ctx) {} 124 125 public: 126 /// Implement property-find by ignoring it. We rely on properties being 127 /// serialized and deserialized in a reliable order instead. 128 Impl &find(const char *propertyName) { 129 return asImpl(); 130 } 131 132 // Implement object writing by forwarding to this, collapsing the 133 // structure into a single data stream. 134 Impl &writeObject() { return asImpl(); } 135 136 template <class T> 137 void writeEnum(T value) { 138 asImpl().writeUInt32(uint32_t(value)); 139 } 140 141 template <class T> 142 void writeArray(llvm::ArrayRef<T> array) { 143 asImpl().writeUInt32(array.size()); 144 for (const T &elt : array) { 145 WriteDispatcher<T>::write(asImpl(), elt); 146 } 147 } 148 149 template <class T> void writeOptional(std::optional<T> value) { 150 WriteDispatcher<T>::write(asImpl(), PackOptionalValue<T>::pack(value)); 151 } 152 153 void writeAPSInt(const llvm::APSInt &value) { 154 asImpl().writeBool(value.isUnsigned()); 155 asImpl().writeAPInt(value); 156 } 157 158 void writeAPInt(const llvm::APInt &value) { 159 asImpl().writeUInt32(value.getBitWidth()); 160 const uint64_t *words = value.getRawData(); 161 for (size_t i = 0, e = value.getNumWords(); i != e; ++i) 162 asImpl().writeUInt64(words[i]); 163 } 164 165 void writeFixedPointSemantics(const llvm::FixedPointSemantics &sema) { 166 asImpl().writeUInt32(sema.getWidth()); 167 asImpl().writeUInt32(sema.getScale()); 168 asImpl().writeUInt32(sema.isSigned() | sema.isSaturated() << 1 | 169 sema.hasUnsignedPadding() << 2); 170 } 171 172 void writeLValuePathSerializationHelper( 173 APValue::LValuePathSerializationHelper lvaluePath) { 174 ArrayRef<APValue::LValuePathEntry> path = lvaluePath.Path; 175 QualType elemTy = lvaluePath.getType(); 176 asImpl().writeQualType(elemTy); 177 asImpl().writeUInt32(path.size()); 178 auto &ctx = ((BasicWriterBase<Impl> *)this)->getASTContext(); 179 for (auto elem : path) { 180 if (elemTy->getAs<RecordType>()) { 181 asImpl().writeUInt32(elem.getAsBaseOrMember().getInt()); 182 const Decl *baseOrMember = elem.getAsBaseOrMember().getPointer(); 183 if (const auto *recordDecl = dyn_cast<CXXRecordDecl>(baseOrMember)) { 184 asImpl().writeDeclRef(recordDecl); 185 elemTy = ctx.getRecordType(recordDecl); 186 } else { 187 const auto *valueDecl = cast<ValueDecl>(baseOrMember); 188 asImpl().writeDeclRef(valueDecl); 189 elemTy = valueDecl->getType(); 190 } 191 } else { 192 asImpl().writeUInt32(elem.getAsArrayIndex()); 193 elemTy = ctx.getAsArrayType(elemTy)->getElementType(); 194 } 195 } 196 } 197 198 void writeQualifiers(Qualifiers value) { 199 static_assert(sizeof(value.getAsOpaqueValue()) <= sizeof(uint64_t), 200 "update this if the value size changes"); 201 asImpl().writeUInt64(value.getAsOpaqueValue()); 202 } 203 204 void writeExceptionSpecInfo( 205 const FunctionProtoType::ExceptionSpecInfo &esi) { 206 asImpl().writeUInt32(uint32_t(esi.Type)); 207 if (esi.Type == EST_Dynamic) { 208 asImpl().writeArray(esi.Exceptions); 209 } else if (isComputedNoexcept(esi.Type)) { 210 asImpl().writeExprRef(esi.NoexceptExpr); 211 } else if (esi.Type == EST_Uninstantiated) { 212 asImpl().writeDeclRef(esi.SourceDecl); 213 asImpl().writeDeclRef(esi.SourceTemplate); 214 } else if (esi.Type == EST_Unevaluated) { 215 asImpl().writeDeclRef(esi.SourceDecl); 216 } 217 } 218 219 void writeExtParameterInfo(FunctionProtoType::ExtParameterInfo epi) { 220 static_assert(sizeof(epi.getOpaqueValue()) <= sizeof(uint32_t), 221 "opaque value doesn't fit into uint32_t"); 222 asImpl().writeUInt32(epi.getOpaqueValue()); 223 } 224 225 void writeFunctionEffect(FunctionEffect E) { 226 asImpl().writeUInt32(E.toOpaqueInt32()); 227 } 228 229 void writeEffectConditionExpr(EffectConditionExpr CE) { 230 asImpl().writeExprRef(CE.getCondition()); 231 } 232 233 void writeNestedNameSpecifier(NestedNameSpecifier *NNS) { 234 // Nested name specifiers usually aren't too long. I think that 8 would 235 // typically accommodate the vast majority. 236 SmallVector<NestedNameSpecifier *, 8> nestedNames; 237 238 // Push each of the NNS's onto a stack for serialization in reverse order. 239 while (NNS) { 240 nestedNames.push_back(NNS); 241 NNS = NNS->getPrefix(); 242 } 243 244 asImpl().writeUInt32(nestedNames.size()); 245 while (!nestedNames.empty()) { 246 NNS = nestedNames.pop_back_val(); 247 NestedNameSpecifier::SpecifierKind kind = NNS->getKind(); 248 asImpl().writeNestedNameSpecifierKind(kind); 249 switch (kind) { 250 case NestedNameSpecifier::Identifier: 251 asImpl().writeIdentifier(NNS->getAsIdentifier()); 252 continue; 253 254 case NestedNameSpecifier::Namespace: 255 asImpl().writeNamespaceDeclRef(NNS->getAsNamespace()); 256 continue; 257 258 case NestedNameSpecifier::NamespaceAlias: 259 asImpl().writeNamespaceAliasDeclRef(NNS->getAsNamespaceAlias()); 260 continue; 261 262 case NestedNameSpecifier::TypeSpec: 263 case NestedNameSpecifier::TypeSpecWithTemplate: 264 asImpl().writeQualType(QualType(NNS->getAsType(), 0)); 265 continue; 266 267 case NestedNameSpecifier::Global: 268 // Don't need to write an associated value. 269 continue; 270 271 case NestedNameSpecifier::Super: 272 asImpl().writeDeclRef(NNS->getAsRecordDecl()); 273 continue; 274 } 275 llvm_unreachable("bad nested name specifier kind"); 276 } 277 } 278 }; 279 280 } // end namespace serialization 281 } // end namespace clang 282 283 #endif 284