xref: /aosp_15_r20/external/sandboxed-api/sandboxed_api/tools/clang_generator/emitter.cc (revision ec63e07ab9515d95e79c211197c445ef84cefa6a)
1 // Copyright 2020 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.h"
16 
17 #include <algorithm>
18 #include <cstdint>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_set.h"
24 #include "absl/container/node_hash_set.h"
25 #include "absl/random/random.h"
26 #include "absl/status/status.h"
27 #include "absl/status/statusor.h"
28 #include "absl/strings/ascii.h"
29 #include "absl/strings/match.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_format.h"
32 #include "absl/strings/str_join.h"
33 #include "absl/strings/str_replace.h"
34 #include "absl/strings/str_split.h"
35 #include "absl/strings/string_view.h"
36 #include "absl/strings/strip.h"
37 #include "clang/AST/ASTContext.h"
38 #include "clang/AST/Decl.h"
39 #include "clang/AST/DeclCXX.h"
40 #include "clang/AST/DeclTemplate.h"
41 #include "clang/AST/QualTypeNames.h"
42 #include "clang/AST/Type.h"
43 #include "clang/Format/Format.h"
44 #include "sandboxed_api/tools/clang_generator/diagnostics.h"
45 #include "sandboxed_api/tools/clang_generator/generator.h"
46 #include "sandboxed_api/tools/clang_generator/types.h"
47 #include "sandboxed_api/util/status_macros.h"
48 
49 namespace sapi {
50 
51 // Common file prolog with auto-generation notice.
52 // Note: The includes will be adjusted by Copybara when converting to/from
53 //       internal code. This is intentional.
54 // Text template arguments:
55 //   1. Header guard
56 constexpr absl::string_view kHeaderProlog =
57     R"(// AUTO-GENERATED by the Sandboxed API generator.
58 // Edits will be discarded when regenerating this file.
59 
60 #ifndef %1$s
61 #define %1$s
62 
63 #include <cstdint>
64 #include <type_traits>
65 
66 #include "absl/base/macros.h"
67 #include "absl/status/status.h"
68 #include "absl/status/statusor.h"
69 #include "sandboxed_api/sandbox.h"
70 #include "sandboxed_api/util/status_macros.h"
71 #include "sandboxed_api/vars.h"
72 
73 )";
74 constexpr absl::string_view kHeaderEpilog =
75     R"(
76 #endif  // %1$s)";
77 
78 // Text template arguments:
79 //   1. Include for embedded sandboxee objects
80 constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h"
81 
82 )";
83 
84 // Text template arguments:
85 //   1. Namespace name
86 constexpr absl::string_view kNamespaceBeginTemplate =
87     R"(
88 namespace %1$s {
89 
90 )";
91 constexpr absl::string_view kNamespaceEndTemplate =
92     R"(
93 }  // namespace %1$s
94 )";
95 
96 // Text template arguments:
97 //   1. Class name
98 //   2. Embedded object identifier
99 constexpr absl::string_view kEmbedClassTemplate = R"(
100 // Sandbox with embedded sandboxee and default policy
101 class %1$s : public ::sapi::Sandbox {
102  public:
103   %1$s() : ::sapi::Sandbox(%2$s_embed_create()) {}
104 };
105 
106 )";
107 
108 // Text template arguments:
109 //   1. Class name
110 constexpr absl::string_view kClassHeaderTemplate = R"(
111 // Sandboxed API
112 class %1$s {
113  public:
114   explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {}
115 
116   ABSL_DEPRECATED("Call sandbox() instead")
117   ::sapi::Sandbox* GetSandbox() const { return sandbox(); }
118   ::sapi::Sandbox* sandbox() const { return sandbox_; }
119 )";
120 
121 constexpr absl::string_view kClassFooterTemplate = R"(
122  private:
123   ::sapi::Sandbox* sandbox_;
124 };
125 )";
126 
127 namespace internal {
128 
ReformatGoogleStyle(const std::string & filename,const std::string & code,int column_limit)129 absl::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
130                                                 const std::string& code,
131                                                 int column_limit) {
132   // Configure code style based on Google style, but enforce pointer alignment
133   clang::format::FormatStyle style =
134       clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp);
135   style.DerivePointerAlignment = false;
136   style.PointerAlignment = clang::format::FormatStyle::PAS_Left;
137   if (column_limit >= 0) {
138     style.ColumnLimit = column_limit;
139   }
140 
141   clang::tooling::Replacements replacements = clang::format::reformat(
142       style, code, llvm::ArrayRef(clang::tooling::Range(0, code.size())),
143       filename);
144 
145   llvm::Expected<std::string> formatted_header =
146       clang::tooling::applyAllReplacements(code, replacements);
147   if (!formatted_header) {
148     return absl::InternalError(llvm::toString(formatted_header.takeError()));
149   }
150   return *formatted_header;
151 }
152 
153 }  // namespace internal
154 
GetIncludeGuard(absl::string_view filename)155 std::string GetIncludeGuard(absl::string_view filename) {
156   if (filename.empty()) {
157     static auto* bit_gen = new absl::BitGen();
158     return absl::StrCat(
159         // Copybara will transform the string. This is intentional.
160         "SANDBOXED_API_GENERATED_HEADER_",
161         absl::AsciiStrToUpper(absl::StrCat(
162             absl::Hex(absl::Uniform<uint64_t>(*bit_gen), absl::kZeroPad16))),
163         "_");
164   }
165 
166   constexpr absl::string_view kUnderscorePrefix = "SAPI_";
167   std::string guard;
168   guard.reserve(filename.size() + kUnderscorePrefix.size() + 1);
169   for (auto c : filename) {
170     if (absl::ascii_isalpha(c)) {
171       guard += absl::ascii_toupper(c);
172       continue;
173     }
174     if (guard.empty()) {
175       guard = kUnderscorePrefix;
176     }
177     if (absl::ascii_isdigit(c)) {
178       guard += c;
179     } else if (guard.back() != '_') {
180       guard += '_';
181     }
182   }
183   if (!absl::EndsWith(guard, "_")) {
184     guard += '_';
185   }
186   return guard;
187 }
188 
189 // Returns the namespace components of a declaration's qualified name.
GetNamespacePath(const clang::TypeDecl * decl)190 std::vector<std::string> GetNamespacePath(const clang::TypeDecl* decl) {
191   std::vector<std::string> comps;
192   for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) {
193     if (const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx)) {
194       comps.push_back(nd->getName().str());
195     }
196   }
197   std::reverse(comps.begin(), comps.end());
198   return comps;
199 }
200 
PrintRecordTemplateArguments(const clang::CXXRecordDecl * record)201 std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) {
202   const auto* template_inst_decl = record->getTemplateInstantiationPattern();
203   if (!template_inst_decl) {
204     return "";
205   }
206   const auto* template_decl = template_inst_decl->getDescribedClassTemplate();
207   if (!template_decl) {
208     return "";
209   }
210   const auto* template_params = template_decl->getTemplateParameters();
211   if (!template_params) {
212     return "";
213   }
214   clang::ASTContext& context = record->getASTContext();
215   std::vector<std::string> params;
216   params.reserve(template_params->size());
217   for (const auto& template_param : *template_params) {
218     if (const auto* p =
219             llvm::dyn_cast<clang::NonTypeTemplateParmDecl>(template_param)) {
220       // TODO(cblichmann): Should be included by CollectRelatedTypes().
221       params.push_back(clang::TypeName::getFullyQualifiedName(
222           p->getType().getDesugaredType(context), context,
223           context.getPrintingPolicy()));
224     } else {  // Also covers template template parameters
225       params.push_back("typename");
226     }
227     absl::StrAppend(&params.back(), " /*",
228                     std::string(template_param->getName()), "*/");
229   }
230   return absl::StrCat("template <", absl::StrJoin(params, ", "), ">");
231 }
232 
233 // Serializes the given Clang AST declaration back into compilable source code.
PrintDecl(const clang::Decl * decl)234 std::string PrintDecl(const clang::Decl* decl) {
235   std::string pretty;
236   llvm::raw_string_ostream os(pretty);
237   decl->print(os);
238   return os.str();
239 }
240 
241 // Returns the spelling for a given declaration will be emitted to the final
242 // header. This may rewrite declarations (like converting typedefs to using,
243 // etc.). Note that the resulting spelling will need to be wrapped inside a
244 // namespace if the original declaration was inside one.
GetSpelling(const clang::Decl * decl)245 std::string GetSpelling(const clang::Decl* decl) {
246   // TODO(cblichmann): Make types nicer
247   //   - Rewrite typedef to using
248   //   - Rewrite function pointers using std::add_pointer_t<>;
249 
250   if (const auto* typedef_decl = llvm::dyn_cast<clang::TypedefNameDecl>(decl)) {
251     // Special case: anonymous enum/struct
252     if (auto* tag_decl = typedef_decl->getAnonDeclWithTypedefName()) {
253       return absl::StrCat("typedef ", PrintDecl(tag_decl), " ",
254                           ToStringView(typedef_decl->getName()));
255     }
256   }
257 
258   if (const auto* record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
259     if (record_decl->hasDefinition() &&
260         // Aggregates capture all C-like structs, but also structs with
261         // non-static members that have default initializers.
262         record_decl->isAggregate() &&
263         // Make sure to skip types with user-defined methods (including
264         // constructors).
265         record_decl->methods().empty()) {
266       return PrintDecl(decl);
267     }
268     // For unsupported types or types with no definition, only emit a forward
269     // declaration.
270     return absl::StrCat(PrintRecordTemplateArguments(record_decl),
271                         record_decl->isClass() ? "class " : "struct ",
272                         ToStringView(record_decl->getName()));
273   }
274   return PrintDecl(decl);
275 }
276 
GetParamName(const clang::ParmVarDecl * decl,int index)277 std::string GetParamName(const clang::ParmVarDecl* decl, int index) {
278   if (std::string name = decl->getName().str(); !name.empty()) {
279     return absl::StrCat(name, "_");  // Suffix to avoid collisions
280   }
281   return absl::StrCat("unnamed", index, "_");
282 }
283 
PrintFunctionPrototypeComment(const clang::FunctionDecl * decl)284 absl::StatusOr<std::string> PrintFunctionPrototypeComment(
285     const clang::FunctionDecl* decl) {
286   const clang::ASTContext& context = decl->getASTContext();
287 
288   std::string out = absl::StrCat(
289       MapQualTypeParameterForCxx(context, decl->getDeclaredReturnType()), " ",
290       decl->getQualifiedNameAsString(), "(");
291 
292   std::string print_separator;
293   for (int i = 0; i < decl->getNumParams(); ++i) {
294     const clang::ParmVarDecl* param = decl->getParamDecl(i);
295 
296     absl::StrAppend(&out, print_separator);
297     print_separator = ", ";
298     absl::StrAppend(&out,
299                     MapQualTypeParameterForCxx(context, param->getType()));
300     if (std::string name = param->getName().str(); !name.empty()) {
301       absl::StrAppend(&out, " ", name);
302     }
303   }
304   absl::StrAppend(&out, ")");
305 
306   SAPI_ASSIGN_OR_RETURN(
307       std::string formatted,
308       internal::ReformatGoogleStyle(/*filename=*/"input", out, 75));
309   out.clear();
310   for (const auto& line : absl::StrSplit(formatted, '\n')) {
311     absl::StrAppend(&out, "// ", line, "\n");
312   }
313   return out;
314 }
315 
EmitFunction(const clang::FunctionDecl * decl)316 absl::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
317   const clang::QualType return_type = decl->getDeclaredReturnType();
318   if (return_type->isRecordType()) {
319     return MakeStatusWithDiagnostic(
320         decl->getBeginLoc(), absl::StatusCode::kCancelled,
321         "returning record by value, skipping function");
322   }
323   std::string out;
324 
325   SAPI_ASSIGN_OR_RETURN(std::string prototype,
326                         PrintFunctionPrototypeComment(decl));
327   absl::StrAppend(&out, "\n", prototype);
328 
329   auto function_name = ToStringView(decl->getName());
330   const bool returns_void = return_type->isVoidType();
331 
332   const clang::ASTContext& context = decl->getASTContext();
333 
334   // "Status<OptionalReturn> FunctionName("
335   absl::StrAppend(&out, MapQualTypeReturn(context, return_type), " ",
336                   function_name, "(");
337 
338   struct ParameterInfo {
339     clang::QualType qual;
340     std::string name;
341   };
342   std::vector<ParameterInfo> params;
343 
344   std::string print_separator;
345   for (int i = 0; i < decl->getNumParams(); ++i) {
346     const clang::ParmVarDecl* param = decl->getParamDecl(i);
347     if (param->getType()->isRecordType()) {
348       return MakeStatusWithDiagnostic(
349           param->getBeginLoc(), absl::StatusCode::kCancelled,
350           absl::StrCat("passing record parameter '",
351                        ToStringView(param->getName()),
352                        "' by value, skipping function"));
353     }
354 
355     ParameterInfo& param_info = params.emplace_back();
356     param_info.qual = param->getType();
357     param_info.name = GetParamName(param, i);
358 
359     absl::StrAppend(&out, print_separator);
360     print_separator = ", ";
361     absl::StrAppend(&out, MapQualTypeParameter(context, param_info.qual), " ",
362                     param_info.name);
363   }
364 
365   absl::StrAppend(&out, ") {\n");
366   absl::StrAppend(&out, MapQualType(context, return_type), " v_ret_;\n");
367   for (const auto& [qual, name] : params) {
368     if (!IsPointerOrReference(qual)) {
369       absl::StrAppend(&out, MapQualType(context, qual), " v_", name, "(", name,
370                       ");\n");
371     }
372   }
373   absl::StrAppend(&out, "\nSAPI_RETURN_IF_ERROR(sandbox_->Call(\"",
374                   function_name, "\", &v_ret_");
375   for (const auto& [qual, name] : params) {
376     absl::StrAppend(&out, ", ", IsPointerOrReference(qual) ? "" : "&v_", name);
377   }
378   absl::StrAppend(&out, "));\nreturn ",
379                   (returns_void ? "::absl::OkStatus()" : "v_ret_.GetValue()"),
380                   ";\n}\n");
381   return out;
382 }
383 
EmitHeader(const std::vector<std::string> & function_definitions,const std::vector<const RenderedType * > & rendered_types,const GeneratorOptions & options)384 absl::StatusOr<std::string> EmitHeader(
385     const std::vector<std::string>& function_definitions,
386     const std::vector<const RenderedType*>& rendered_types,
387     const GeneratorOptions& options) {
388   std::string out;
389   const std::string include_guard = GetIncludeGuard(options.out_file);
390   absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
391   // When embedding the sandboxee, add embed header include
392   if (!options.embed_name.empty()) {
393     // Not using JoinPath() because even on Windows include paths use plain
394     // slashes.
395     std::string include_file(absl::StripSuffix(
396         absl::StrReplaceAll(options.embed_dir, {{"\\", "/"}}), "/"));
397     if (!include_file.empty()) {
398       absl::StrAppend(&include_file, "/");
399     }
400     absl::StrAppend(&include_file, options.embed_name);
401     absl::StrAppendFormat(&out, kEmbedInclude, include_file);
402   }
403 
404   // If specified, wrap the generated API in a namespace
405   if (options.has_namespace()) {
406     absl::StrAppendFormat(&out, kNamespaceBeginTemplate,
407                           options.namespace_name);
408   }
409 
410   // Emit type dependencies
411   if (!rendered_types.empty()) {
412     absl::StrAppend(&out, "// Types this API depends on\n");
413     std::string last_ns_name = options.namespace_name;
414     for (const RenderedType* rt : rendered_types) {
415       const auto& [ns_name, spelling] = *rt;
416       if (last_ns_name != ns_name) {
417         if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
418           absl::StrAppend(&out, "}  // namespace ", last_ns_name, "\n\n");
419         }
420 
421         if (!ns_name.empty() && ns_name != options.namespace_name) {
422           absl::StrAppend(&out, "namespace ", ns_name, " {\n");
423         }
424         last_ns_name = ns_name;
425       }
426 
427       absl::StrAppend(&out, spelling, ";\n");
428     }
429     if (!last_ns_name.empty() && last_ns_name != options.namespace_name) {
430       absl::StrAppend(&out, "}  // namespace ", last_ns_name, "\n\n");
431     }
432   }
433 
434   // Optionally emit a default sandbox that instantiates an embedded sandboxee
435   if (!options.embed_name.empty()) {
436     // TODO(cblichmann): Make the "Sandbox" suffix configurable.
437     absl::StrAppendFormat(
438         &out, kEmbedClassTemplate, absl::StrCat(options.name, "Sandbox"),
439         absl::StrReplaceAll(options.embed_name, {{"-", "_"}}));
440   }
441 
442   // Emit the actual Sandboxed API
443   // TODO(cblichmann): Make the "Api" suffix configurable or at least optional.
444   absl::StrAppendFormat(&out, kClassHeaderTemplate,
445                         absl::StrCat(options.name, "Api"));
446   absl::StrAppend(&out, absl::StrJoin(function_definitions, "\n"));
447   absl::StrAppend(&out, kClassFooterTemplate);
448 
449   // Close out the header: close namespace (if needed) and end include guard
450   if (options.has_namespace()) {
451     absl::StrAppendFormat(&out, kNamespaceEndTemplate, options.namespace_name);
452   }
453   absl::StrAppendFormat(&out, kHeaderEpilog, include_guard);
454   return out;
455 }
456 
EmitType(clang::TypeDecl * type_decl)457 void Emitter::EmitType(clang::TypeDecl* type_decl) {
458   if (!type_decl) {
459     return;
460   }
461 
462   // Skip types defined in system headers.
463   // TODO(cblichmann): Instead of this and the hard-coded entities below, we
464   //                   should map types and add the correct (system) headers to
465   //                   the generated output.
466   if (type_decl->getASTContext().getSourceManager().isInSystemHeader(
467           type_decl->getBeginLoc())) {
468     return;
469   }
470 
471   const std::vector<std::string> ns_path = GetNamespacePath(type_decl);
472   std::string ns_name;
473   if (!ns_path.empty()) {
474     const auto& ns_root = ns_path.front();
475     // Filter out declarations from the C++ standard library, from SAPI itself
476     // and from other well-known namespaces.
477     if (ns_root == "std" || ns_root == "__gnu_cxx" || ns_root == "sapi") {
478       return;
479     }
480     if (ns_root == "absl") {
481       // Skip Abseil internal namespaces
482       if (ns_path.size() > 1 && absl::EndsWith(ns_path[1], "_internal")) {
483         return;
484       }
485       // Skip types from Abseil that will already be included in the generated
486       // header.
487       if (auto name = ToStringView(type_decl->getName());
488           name == "CordMemoryAccounting" || name == "Duration" ||
489           name == "LogEntry" || name == "LogSeverity" || name == "Span" ||
490           name == "StatusCode" || name == "StatusToStringMode" ||
491           name == "SynchLocksHeld" || name == "SynchWaitParams" ||
492           name == "Time" || name == "string_view" || name == "tid_t") {
493         return;
494       }
495     }
496     // Skip Protocol Buffers namespaces
497     if (ns_root == "google" && ns_path.size() > 1 && ns_path[1] == "protobuf") {
498       return;
499     }
500     ns_name = absl::StrJoin(ns_path, "::");
501   }
502 
503   std::string spelling = GetSpelling(type_decl);
504   if (const auto& [it, inserted] = rendered_types_.emplace(ns_name, spelling);
505       inserted) {
506     rendered_types_ordered_.push_back(&*it);
507   }
508 }
509 
AddTypeDeclarations(const std::vector<clang::TypeDecl * > & type_decls)510 void Emitter::AddTypeDeclarations(
511     const std::vector<clang::TypeDecl*>& type_decls) {
512   for (clang::TypeDecl* type_decl : type_decls) {
513     EmitType(type_decl);
514   }
515 }
516 
AddFunction(clang::FunctionDecl * decl)517 absl::Status Emitter::AddFunction(clang::FunctionDecl* decl) {
518   if (rendered_functions_.insert(decl->getQualifiedNameAsString()).second) {
519     SAPI_ASSIGN_OR_RETURN(std::string function, EmitFunction(decl));
520     rendered_functions_ordered_.push_back(function);
521   }
522   return absl::OkStatus();
523 }
524 
EmitHeader(const GeneratorOptions & options)525 absl::StatusOr<std::string> Emitter::EmitHeader(
526     const GeneratorOptions& options) {
527   SAPI_ASSIGN_OR_RETURN(const std::string header,
528                         ::sapi::EmitHeader(rendered_functions_ordered_,
529                                            rendered_types_ordered_, options));
530   return internal::ReformatGoogleStyle(options.out_file, header);
531 }
532 
533 }  // namespace sapi
534