[libc] Expose APIGenerator.
[lldb.git] / libc / utils / HdrGen / PublicAPICommand.cpp
1 //===-- Implementation of PublicAPICommand --------------------------------===//
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 #include "PublicAPICommand.h"
10
11 #include "llvm/ADT/StringExtras.h"
12 #include "llvm/ADT/StringRef.h"
13 #include "llvm/Support/SourceMgr.h"
14 #include "llvm/TableGen/Error.h"
15 #include "llvm/TableGen/Record.h"
16
17 static const char NamedTypeClassName[] = "NamedType";
18 static const char PtrTypeClassName[] = "PtrType";
19 static const char RestrictedPtrTypeClassName[] = "RestrictedPtrType";
20 static const char ConstTypeClassName[] = "ConstType";
21 static const char StructTypeClassName[] = "Struct";
22
23 static const char StandardSpecClassName[] = "StandardSpec";
24 static const char PublicAPIClassName[] = "PublicAPI";
25
26 static bool isa(llvm::Record *Def, llvm::Record *TypeClass) {
27   llvm::RecordRecTy *RecordType = Def->getType();
28   llvm::ArrayRef<llvm::Record *> Classes = RecordType->getClasses();
29   // We want exact types. That is, we don't want the classes listed in
30   // spec.td to be subclassed. Hence, we do not want the record |Def|
31   // to be of more than one class type..
32   if (Classes.size() != 1)
33     return false;
34   return Classes[0] == TypeClass;
35 }
36
37 // Text blocks for macro definitions and type decls can be indented to
38 // suit the surrounding tablegen listing. We need to dedent such blocks
39 // before writing them out.
40 static void dedentAndWrite(llvm::StringRef Text, llvm::raw_ostream &OS) {
41   llvm::SmallVector<llvm::StringRef, 10> Lines;
42   llvm::SplitString(Text, Lines, "\n");
43   size_t shortest_indent = 1024;
44   for (llvm::StringRef L : Lines) {
45     llvm::StringRef Indent = L.take_while([](char c) { return c == ' '; });
46     size_t IndentSize = Indent.size();
47     if (Indent.size() == L.size()) {
48       // Line is all spaces so no point noting the indent.
49       continue;
50     }
51     if (IndentSize < shortest_indent)
52       shortest_indent = IndentSize;
53   }
54   for (llvm::StringRef L : Lines) {
55     if (L.size() >= shortest_indent)
56       OS << L.drop_front(shortest_indent) << '\n';
57   }
58 }
59
60 namespace llvm_libc {
61
62 bool APIIndexer::isaNamedType(llvm::Record *Def) {
63   return isa(Def, NamedTypeClass);
64 }
65
66 bool APIIndexer::isaStructType(llvm::Record *Def) {
67   return isa(Def, StructClass);
68 }
69
70 bool APIIndexer::isaPtrType(llvm::Record *Def) {
71   return isa(Def, PtrTypeClass);
72 }
73
74 bool APIIndexer::isaConstType(llvm::Record *Def) {
75   return isa(Def, ConstTypeClass);
76 }
77
78 bool APIIndexer::isaRestrictedPtrType(llvm::Record *Def) {
79   return isa(Def, RestrictedPtrTypeClass);
80 }
81
82 bool APIIndexer::isaStandardSpec(llvm::Record *Def) {
83   return isa(Def, StandardSpecClass);
84 }
85
86 bool APIIndexer::isaPublicAPI(llvm::Record *Def) {
87   return isa(Def, PublicAPIClass);
88 }
89
90 std::string APIIndexer::getTypeAsString(llvm::Record *TypeRecord) {
91   if (isaNamedType(TypeRecord) || isaStructType(TypeRecord)) {
92     return std::string(TypeRecord->getValueAsString("Name"));
93   } else if (isaPtrType(TypeRecord)) {
94     return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) + " *";
95   } else if (isaConstType(TypeRecord)) {
96     return std::string("const ") +
97            getTypeAsString(TypeRecord->getValueAsDef("UnqualifiedType"));
98   } else if (isaRestrictedPtrType(TypeRecord)) {
99     return getTypeAsString(TypeRecord->getValueAsDef("PointeeType")) +
100            " *__restrict";
101   } else {
102     llvm::PrintFatalError(TypeRecord->getLoc(), "Invalid type.\n");
103   }
104 }
105
106 void APIIndexer::indexStandardSpecDef(llvm::Record *StandardSpec) {
107   auto HeaderSpecList = StandardSpec->getValueAsListOfDefs("Headers");
108   for (llvm::Record *HeaderSpec : HeaderSpecList) {
109     llvm::StringRef Header = HeaderSpec->getValueAsString("Name");
110     if (!StdHeader.hasValue() || Header == StdHeader) {
111       PublicHeaders.emplace(Header);
112       auto MacroSpecList = HeaderSpec->getValueAsListOfDefs("Macros");
113       // TODO: Trigger a fatal error on duplicate specs.
114       for (llvm::Record *MacroSpec : MacroSpecList)
115         MacroSpecMap[std::string(MacroSpec->getValueAsString("Name"))] =
116             MacroSpec;
117
118       auto TypeSpecList = HeaderSpec->getValueAsListOfDefs("Types");
119       for (llvm::Record *TypeSpec : TypeSpecList)
120         TypeSpecMap[std::string(TypeSpec->getValueAsString("Name"))] = TypeSpec;
121
122       auto FunctionSpecList = HeaderSpec->getValueAsListOfDefs("Functions");
123       for (llvm::Record *FunctionSpec : FunctionSpecList) {
124         FunctionSpecMap[std::string(FunctionSpec->getValueAsString("Name"))] =
125             FunctionSpec;
126       }
127
128       auto EnumerationSpecList =
129           HeaderSpec->getValueAsListOfDefs("Enumerations");
130       for (llvm::Record *EnumerationSpec : EnumerationSpecList) {
131         EnumerationSpecMap[std::string(
132             EnumerationSpec->getValueAsString("Name"))] = EnumerationSpec;
133       }
134     }
135   }
136 }
137
138 void APIIndexer::indexPublicAPIDef(llvm::Record *PublicAPI) {
139   // While indexing the public API, we do not check if any of the entities
140   // requested is from an included standard. Such a check is done while
141   // generating the API.
142   auto MacroDefList = PublicAPI->getValueAsListOfDefs("Macros");
143   for (llvm::Record *MacroDef : MacroDefList)
144     MacroDefsMap[std::string(MacroDef->getValueAsString("Name"))] = MacroDef;
145
146   auto TypeDeclList = PublicAPI->getValueAsListOfDefs("TypeDeclarations");
147   for (llvm::Record *TypeDecl : TypeDeclList)
148     TypeDeclsMap[std::string(TypeDecl->getValueAsString("Name"))] = TypeDecl;
149
150   auto StructList = PublicAPI->getValueAsListOfStrings("Structs");
151   for (llvm::StringRef StructName : StructList)
152     Structs.insert(std::string(StructName));
153
154   auto FunctionList = PublicAPI->getValueAsListOfStrings("Functions");
155   for (llvm::StringRef FunctionName : FunctionList)
156     Functions.insert(std::string(FunctionName));
157
158   auto EnumerationList = PublicAPI->getValueAsListOfStrings("Enumerations");
159   for (llvm::StringRef EnumerationName : EnumerationList)
160     Enumerations.insert(std::string(EnumerationName));
161 }
162
163 void APIIndexer::index(llvm::RecordKeeper &Records) {
164   NamedTypeClass = Records.getClass(NamedTypeClassName);
165   PtrTypeClass = Records.getClass(PtrTypeClassName);
166   RestrictedPtrTypeClass = Records.getClass(RestrictedPtrTypeClassName);
167   StructClass = Records.getClass(StructTypeClassName);
168   ConstTypeClass = Records.getClass(ConstTypeClassName);
169   StandardSpecClass = Records.getClass(StandardSpecClassName);
170   PublicAPIClass = Records.getClass(PublicAPIClassName);
171
172   const auto &DefsMap = Records.getDefs();
173   for (auto &Pair : DefsMap) {
174     llvm::Record *Def = Pair.second.get();
175     if (isaStandardSpec(Def))
176       indexStandardSpecDef(Def);
177     if (isaPublicAPI(Def)) {
178       if (!StdHeader.hasValue() ||
179           Def->getValueAsString("HeaderName") == StdHeader)
180         indexPublicAPIDef(Def);
181     }
182   }
183 }
184
185 void writeAPIFromIndex(APIIndexer &G, llvm::raw_ostream &OS) {
186   for (auto &Pair : G.MacroDefsMap) {
187     const std::string &Name = Pair.first;
188     if (G.MacroSpecMap.find(Name) == G.MacroSpecMap.end())
189       llvm::PrintFatalError(Name + " not found in any standard spec.\n");
190
191     llvm::Record *MacroDef = Pair.second;
192     dedentAndWrite(MacroDef->getValueAsString("Defn"), OS);
193
194     OS << '\n';
195   }
196
197   for (auto &Pair : G.TypeDeclsMap) {
198     const std::string &Name = Pair.first;
199     if (G.TypeSpecMap.find(Name) == G.TypeSpecMap.end())
200       llvm::PrintFatalError(Name + " not found in any standard spec.\n");
201
202     llvm::Record *TypeDecl = Pair.second;
203     dedentAndWrite(TypeDecl->getValueAsString("Decl"), OS);
204
205     OS << '\n';
206   }
207
208   if (G.Enumerations.size() != 0)
209     OS << "enum {" << '\n';
210   for (const auto &Name : G.Enumerations) {
211     if (G.EnumerationSpecMap.find(Name) == G.EnumerationSpecMap.end())
212       llvm::PrintFatalError(
213           Name + " is not listed as an enumeration in any standard spec.\n");
214
215     llvm::Record *EnumerationSpec = G.EnumerationSpecMap[Name];
216     OS << "  " << EnumerationSpec->getValueAsString("Name");
217     auto Value = EnumerationSpec->getValueAsString("Value");
218     if (Value == "__default__") {
219       OS << ",\n";
220     } else {
221       OS << " = " << Value << ",\n";
222     }
223   }
224   if (G.Enumerations.size() != 0)
225     OS << "};\n\n";
226
227   OS << "__BEGIN_C_DECLS\n\n";
228   for (auto &Name : G.Functions) {
229     if (G.FunctionSpecMap.find(Name) == G.FunctionSpecMap.end())
230       llvm::PrintFatalError(Name + " not found in any standard spec.\n");
231
232     llvm::Record *FunctionSpec = G.FunctionSpecMap[Name];
233     llvm::Record *RetValSpec = FunctionSpec->getValueAsDef("Return");
234     llvm::Record *ReturnType = RetValSpec->getValueAsDef("ReturnType");
235
236     OS << G.getTypeAsString(ReturnType) << " " << Name << "(";
237
238     auto ArgsList = FunctionSpec->getValueAsListOfDefs("Args");
239     for (size_t i = 0; i < ArgsList.size(); ++i) {
240       llvm::Record *ArgType = ArgsList[i]->getValueAsDef("ArgType");
241       OS << G.getTypeAsString(ArgType);
242       if (i < ArgsList.size() - 1)
243         OS << ", ";
244     }
245
246     OS << ");\n\n";
247   }
248   OS << "__END_C_DECLS\n";
249 }
250
251 void writePublicAPI(llvm::raw_ostream &OS, llvm::RecordKeeper &Records) {}
252
253 const char PublicAPICommand::Name[] = "public_api";
254
255 void PublicAPICommand::run(llvm::raw_ostream &OS, const ArgVector &Args,
256                            llvm::StringRef StdHeader,
257                            llvm::RecordKeeper &Records,
258                            const Command::ErrorReporter &Reporter) const {
259   if (Args.size() != 0) {
260     Reporter.printFatalError("public_api command does not take any arguments.");
261   }
262
263   APIIndexer G(StdHeader, Records);
264   writeAPIFromIndex(G, OS);
265 }
266
267 } // namespace llvm_libc