• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2024 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "sandboxed_api/tools/clang_generator/emitter_base.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <string>
20 #include <vector>
21 
22 #include "absl/random/random.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/match.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/strings/str_join.h"
29 #include "absl/strings/string_view.h"
30 #include "clang/AST/Decl.h"
31 #include "clang/AST/DeclBase.h"
32 #include "clang/AST/DeclCXX.h"
33 #include "clang/AST/DeclTemplate.h"
34 #include "clang/AST/QualTypeNames.h"
35 #include "clang/Format/Format.h"
36 #include "clang/Tooling/Core/Replacement.h"
37 #include "llvm/ADT/ArrayRef.h"
38 #include "llvm/Support/Casting.h"
39 #include "llvm/Support/Error.h"
40 #include "llvm/Support/raw_ostream.h"
41 #include "sandboxed_api/tools/clang_generator/generator.h"
42 
43 namespace sapi {
44 
45 // Text template arguments:
46 //   1. Include for embedded sandboxee objects
47 constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h"
48 
49 )";
50 
51 // Text template arguments:
52 //   1. Class name
53 //   2. Embedded object identifier
54 constexpr absl::string_view kEmbedClassTemplate = R"(
55 // Sandbox with embedded sandboxee and default policy
56 class %1$s : public ::sapi::Sandbox {
57  public:
58   %1$s()
59       : ::sapi::Sandbox([]() {
60           static auto* fork_client_context =
61               new ::sapi::ForkClientContext(%2$s_embed_create());
62           return fork_client_context;
63         }()) {}
64 };
65 
66 )";
67 
68 // Sandboxed API class template.
69 // Text template arguments:
70 //   1. Class name
71 constexpr absl::string_view kClassHeaderTemplate = R"(
72 // Sandboxed API
73 class %1$s {
74  public:
75   explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {}
76 
77   ABSL_DEPRECATED("Call sandbox() instead")
78   ::sapi::Sandbox* GetSandbox() const { return sandbox(); }
79   ::sapi::Sandbox* sandbox() const { return sandbox_; }
80 )";
81 
82 // Sandboxed API class template footer.
83 constexpr absl::string_view kClassFooterTemplate = R"(
84  private:
85   ::sapi::Sandbox* sandbox_;
86 };
87 )";
88 
89 namespace internal {
90 
ReformatGoogleStyle(const std::string & filename,const std::string & code,int column_limit)91 absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
92                                                 const std::string& code,
93                                                 int column_limit) {
94   // Configure code style based on Google style, but enforce pointer alignment
95   clang::format::FormatStyle style =
96       clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
97   style.DerivePointerAlignment = false;
98   style.PointerAlignment = clang::format::FormatStyle::PAS_Left;
99   if (column_limit >= 0) {
100     style.ColumnLimit = column_limit;
101   }
102 
103   clang::tooling::Replacements replacements = clang::format::reformat(
104       style, code, llvm::ArrayRef(clang::tooling::Range(0, code.size())),
105       filename);
106 
107   llvm::Expected<std::string> formatted_header =
108       clang::tooling::applyAllReplacements(code, replacements);
109   if (!formatted_header) {
110     return absl::InternalError(llvm::toString(formatted_header.takeError()));
111   }
112   return *formatted_header;
113 }
114 
115 }  // namespace internal
116 
117 namespace {
118 
119 // Returns the namespace components of a declaration's qualified name.
GetNamespacePath(const clang::TypeDecl * decl)120 std::vector<std::string> GetNamespacePath(const clang::TypeDecl* decl) {
121   std::vector<std::string> comps;
122   for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) {
123     if (const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx)) {
124       comps.push_back(nd->getName().str());
125     }
126   }
127   std::reverse(comps.begin(), comps.end());
128   return comps;
129 }
130 
131 // Returns the template arguments for a given record.
PrintRecordTemplateArguments(const clang::CXXRecordDecl * record)132 std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) {
133   const auto* template_inst_decl = record->getTemplateInstantiationPattern();
134   if (!template_inst_decl) {
135     return "";
136   }
137   const auto* template_decl = template_inst_decl->getDescribedClassTemplate();
138   if (!template_decl) {
139     return "";
140   }
141   const auto* template_params = template_decl->getTemplateParameters();
142   if (!template_params) {
143     return "";
144   }
145   clang::ASTContext& context = record->getASTContext();
146   std::vector<std::string> params;
147   params.reserve(template_params->size());
148   for (const auto& template_param : *template_params) {
149     if (const auto* p =
150             llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_param)) {
151       // TODO(cblichmann): Should be included by CollectRelatedTypes().
152       params.push_back(clang::TypeName::getFullyQualifiedName(
153           p->getType().getDesugaredType(context), context,
154           context.getPrintingPolicy()));
155     } else {  // Also covers template template parameters
156       params.push_back("typename");
157     }
158     absl::StrAppend(&params.back(), " /*",
159                     std::string(template_param->getName()), "*/");
160   }
161   return absl::StrCat("template <", absl::StrJoin(params, ", "), ">");
162 }
163 
164 // Serializes the given Clang AST declaration back into compilable source code.
PrintDecl(const clang::Decl * decl)165 std::string PrintDecl(const clang::Decl* decl) {
166   std::string pretty;
167   llvm::raw_string_ostream os(pretty);
168   decl->print(os);
169   return os.str();
170 }
171 
172 // Returns the spelling for a given declaration will be emitted to the final
173 // header. This may rewrite declarations (like converting typedefs to using,
174 // etc.). Note that the resulting spelling will need to be wrapped inside a
175 // namespace if the original declaration was inside one.
GetSpelling(const clang::Decl * decl)176 std::string GetSpelling(const clang::Decl* decl) {
177   // TODO(cblichmann): Make types nicer
178   //   - Rewrite typedef to using
179   //   - Rewrite function pointers using std::add_pointer_t<>;
180 
181   if (const auto* typedef_decl = llvm::dyn_cast<clang::TypedefNameDecl>(decl)) {
182     // Special case: anonymous enum/struct
183     if (auto* tag_decl = typedef_decl->getAnonDeclWithTypedefName()) {
184       return absl::StrCat("typedef ", PrintDecl(tag_decl), " ",
185                           ToStringView(typedef_decl->getName()));
186     }
187   }
188 
189   if (const auto* record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
190     if (record_decl->hasDefinition() &&
191         // Aggregates capture all C-like structs, but also structs with
192         // non-static members that have default initializers.
193         record_decl->isAggregate() &&
194         // Make sure to skip non-POD types with user-defined methods
195         // (including constructors).
196         (record_decl->isPOD() || record_decl->methods().empty())) {
197       return PrintDecl(decl);
198     }
199     // For unsupported types or types with no definition, only emit a forward
200     // declaration.
201     return absl::StrCat(PrintRecordTemplateArguments(record_decl),
202                         record_decl->isClass() ? "class " : "struct ",
203                         ToStringView(record_decl->getName()));
204   }
205   return PrintDecl(decl);
206 }
207 
208 }  // namespace
209 
GetIncludeGuard(absl::string_view filename)210 std::string GetIncludeGuard(absl::string_view filename) {
211   if (filename.empty()) {
212     static auto* bit_gen = new absl::BitGen();
213     return absl::StrCat(
214         // Copybara will transform the string. This is intentional.
215         "SANDBOXED_API_GENERATED_HEADER_",
216         absl::AsciiStrToUpper(absl::StrCat(
217             absl::Hex(absl::Uniform<uint64_t>(*bit_gen), absl::kZeroPad16))),
218         "_");
219   }
220 
221   constexpr absl::string_view kUnderscorePrefix = "SAPI_";
222   std::string guard;
223   guard.reserve(filename.size() + kUnderscorePrefix.size() + 1);
224   for (auto c : filename) {
225     if (absl::ascii_isalpha(c)) {
226       guard += absl::ascii_toupper(c);
227       continue;
228     }
229     if (guard.empty()) {
230       guard = kUnderscorePrefix;
231     }
232     if (absl::ascii_isdigit(c)) {
233       guard += c;
234     } else if (guard.back() != '_') {
235       guard += '_';
236     }
237   }
238   if (!absl::EndsWith(guard, "_")) {
239     guard += '_';
240   }
241   return guard;
242 }
243 
EmitType(clang::TypeDecl * type_decl)244 void EmitterBase::EmitType(clang::TypeDecl* type_decl) {
245   if (!type_decl) {
246     return;
247   }
248 
249   // Skip types defined in system headers.
250   // TODO(cblichmann): Instead of this and the hard-coded entities below, we
251   //                   should map types and add the correct (system) headers to
252   //                   the generated output.
253   if (type_decl->getASTContext().getSourceManager().isInSystemHeader(
254           type_decl->getBeginLoc())) {
255     return;
256   }
257 
258   const std::vector<std::string> ns_path = GetNamespacePath(type_decl);
259   std::string ns_name;
260   if (!ns_path.empty()) {
261     const auto& ns_root = ns_path.front();
262     // Filter out declarations from the C++ standard library, from SAPI itself
263     // and from other well-known namespaces.
264     if (ns_root == "std" || ns_root == "__gnu_cxx" || ns_root == "sapi") {
265       return;
266     }
267     if (ns_root == "absl") {
268       // Skip Abseil internal namespaces
269       if (ns_path.size() > 1 && absl::EndsWith(ns_path[1], "_internal")) {
270         return;
271       }
272       // Skip types from Abseil that will already be included in the generated
273       // header.
274       if (auto name = ToStringView(type_decl->getName());
275           name == "CordMemoryAccounting" || name == "Duration" ||
276           name == "LogEntry" || name == "LogSeverity" || name == "Span" ||
277           name == "StatusCode" || name == "StatusToStringMode" ||
278           name == "SynchLocksHeld" || name == "SynchWaitParams" ||
279           name == "Time" || name == "string_view" || name == "tid_t") {
280         return;
281       }
282     }
283     // Skip Protocol Buffers namespaces
284     if (ns_root == "google" && ns_path.size() > 1 && ns_path[1] == "protobuf") {
285       return;
286     }
287     ns_name = absl::StrJoin(ns_path, "::");
288   }
289 
290   std::string spelling = GetSpelling(type_decl);
291   if (const auto& [it, inserted] = rendered_types_.emplace(ns_name, spelling);
292       inserted) {
293     rendered_types_ordered_.push_back(&*it);
294   }
295 }
296 
AddTypeDeclarations(const std::vector<clang::TypeDecl * > & type_decls)297 void EmitterBase::AddTypeDeclarations(
298     const std::vector<clang::TypeDecl*>& type_decls) {
299   for (clang::TypeDecl* type_decl : type_decls) {
300     EmitType(type_decl);
301   }
302 }
303 
304 }  // namespace sapi
305