diff --git a/sandboxed_api/tools/clang_generator/BUILD b/sandboxed_api/tools/clang_generator/BUILD index 17978dc2..927ff8f8 100644 --- a/sandboxed_api/tools/clang_generator/BUILD +++ b/sandboxed_api/tools/clang_generator/BUILD @@ -21,12 +21,14 @@ cc_library( srcs = [ "diagnostics.cc", "emitter.cc", + "emitter_base.cc", "generator.cc", "types.cc", ], hdrs = [ "diagnostics.h", "emitter.h", + "emitter_base.h", "generator.h", "types.h", ], diff --git a/sandboxed_api/tools/clang_generator/CMakeLists.txt b/sandboxed_api/tools/clang_generator/CMakeLists.txt index fab6cf1e..a4526d8f 100644 --- a/sandboxed_api/tools/clang_generator/CMakeLists.txt +++ b/sandboxed_api/tools/clang_generator/CMakeLists.txt @@ -24,6 +24,8 @@ add_library(sapi_generator diagnostics.h emitter.h emitter.cc + emitter_base.h + emitter_base.cc generator.h generator.cc types.h diff --git a/sandboxed_api/tools/clang_generator/emitter.cc b/sandboxed_api/tools/clang_generator/emitter.cc index 2892430f..276849f5 100644 --- a/sandboxed_api/tools/clang_generator/emitter.cc +++ b/sandboxed_api/tools/clang_generator/emitter.cc @@ -14,14 +14,12 @@ #include "sandboxed_api/tools/clang_generator/emitter.h" -#include #include #include #include #include #include "absl/container/flat_hash_set.h" -#include "absl/container/node_hash_set.h" #include "absl/log/log.h" #include "absl/random/random.h" #include "absl/status/status.h" @@ -39,16 +37,9 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" -#include "clang/AST/DeclTemplate.h" -#include "clang/AST/QualTypeNames.h" #include "clang/AST/Type.h" -#include "clang/Format/Format.h" -#include "clang/Tooling/Core/Replacement.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/Error.h" -#include "llvm/Support/raw_ostream.h" #include "sandboxed_api/tools/clang_generator/diagnostics.h" +#include "sandboxed_api/tools/clang_generator/emitter_base.h" #include "sandboxed_api/tools/clang_generator/generator.h" #include "sandboxed_api/tools/clang_generator/types.h" #include "sandboxed_api/util/status_macros.h" @@ -132,6 +123,7 @@ class %1$s { public: explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {} + ABSL_DEPRECATED("Call sandbox() instead") ::sapi::Sandbox* GetSandbox() const { return sandbox(); } ::sapi::Sandbox* sandbox() const { return sandbox_; } @@ -144,34 +136,6 @@ constexpr absl::string_view kClassFooterTemplate = R"( }; )"; -namespace internal { - -absl::StatusOr ReformatGoogleStyle(const std::string& filename, - const std::string& code, - int column_limit) { - // Configure code style based on Google style, but enforce pointer alignment - clang::format::FormatStyle style = - clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp); - style.DerivePointerAlignment = false; - style.PointerAlignment = clang::format::FormatStyle::PAS_Left; - if (column_limit >= 0) { - style.ColumnLimit = column_limit; - } - - clang::tooling::Replacements replacements = clang::format::reformat( - style, code, llvm::ArrayRef(clang::tooling::Range(0, code.size())), - filename); - - llvm::Expected formatted_header = - clang::tooling::applyAllReplacements(code, replacements); - if (!formatted_header) { - return absl::InternalError(llvm::toString(formatted_header.takeError())); - } - return *formatted_header; -} - -} // namespace internal - std::string GetIncludeGuard(absl::string_view filename) { if (filename.empty()) { static auto* bit_gen = new absl::BitGen(); @@ -206,104 +170,6 @@ std::string GetIncludeGuard(absl::string_view filename) { return guard; } -// Returns the namespace components of a declaration's qualified name. -std::vector GetNamespacePath(const clang::TypeDecl* decl) { - std::vector comps; - for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) { - if (const auto* nd = llvm::dyn_cast(ctx)) { - comps.push_back(nd->getName().str()); - } - } - std::reverse(comps.begin(), comps.end()); - return comps; -} - -// Returns the template arguments for a given record. -std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) { - const auto* template_inst_decl = record->getTemplateInstantiationPattern(); - if (!template_inst_decl) { - return ""; - } - const auto* template_decl = template_inst_decl->getDescribedClassTemplate(); - if (!template_decl) { - return ""; - } - const auto* template_params = template_decl->getTemplateParameters(); - if (!template_params) { - return ""; - } - clang::ASTContext& context = record->getASTContext(); - std::vector params; - params.reserve(template_params->size()); - for (const auto& template_param : *template_params) { - if (const auto* p = - llvm::dyn_cast(template_param)) { - // TODO(cblichmann): Should be included by CollectRelatedTypes(). - params.push_back(clang::TypeName::getFullyQualifiedName( - p->getType().getDesugaredType(context), context, - context.getPrintingPolicy())); - } else { // Also covers template template parameters - params.push_back("typename"); - } - absl::StrAppend(¶ms.back(), " /*", - std::string(template_param->getName()), "*/"); - } - return absl::StrCat("template <", absl::StrJoin(params, ", "), ">"); -} - -// Serializes the given Clang AST declaration back into compilable source code. -std::string PrintDecl(const clang::Decl* decl) { - std::string pretty; - llvm::raw_string_ostream os(pretty); - decl->print(os); - return os.str(); -} - -// Returns the spelling for a given declaration will be emitted to the final -// header. This may rewrite declarations (like converting typedefs to using, -// etc.). Note that the resulting spelling will need to be wrapped inside a -// namespace if the original declaration was inside one. -std::string GetSpelling(const clang::Decl* decl) { - // TODO(cblichmann): Make types nicer - // - Rewrite typedef to using - // - Rewrite function pointers using std::add_pointer_t<>; - - if (const auto* typedef_decl = llvm::dyn_cast(decl)) { - // Special case: anonymous enum/struct - if (auto* tag_decl = typedef_decl->getAnonDeclWithTypedefName()) { - return absl::StrCat("typedef ", PrintDecl(tag_decl), " ", - ToStringView(typedef_decl->getName())); - } - } - - if (const auto* record_decl = llvm::dyn_cast(decl)) { - if (record_decl->hasDefinition() && - // Aggregates capture all C-like structs, but also structs with - // non-static members that have default initializers. - record_decl->isAggregate() && - // Make sure to skip types with user-defined methods (including - // constructors). - record_decl->methods().empty()) { - return PrintDecl(decl); - } - // For unsupported types or types with no definition, only emit a forward - // declaration. - return absl::StrCat(PrintRecordTemplateArguments(record_decl), - record_decl->isClass() ? "class " : "struct ", - ToStringView(record_decl->getName())); - } - return PrintDecl(decl); -} - -// Returns a unique name for a parameter. If `decl` has no name, a unique name -// will be generated in the form of `unnamed_`. -std::string GetParamName(const clang::ParmVarDecl* decl, int index) { - if (std::string name = decl->getName().str(); !name.empty()) { - return absl::StrCat(name, "_"); // Suffix to avoid collisions - } - return absl::StrCat("unnamed", index, "_"); -} - // Returns a comment for the given function `decl` which represents the // unsandboxed function signature. absl::StatusOr PrintFunctionPrototypeComment( @@ -429,14 +295,13 @@ absl::StatusOr EmitHeader( // the number of functions generated. if (!options.function_names.empty() && (options.function_names.size() != function_definitions.size())) { - LOG(WARNING) - << "Generated output has fewer functions than expected - some function " - "signatures might use language features that SAPI does not support. " - "For debugging, we recommend you compare " - "the list of functions in your sapi_library() rule with " - "the generated *.sapi.h file. Expected: " - << options.function_names.size() - << ", generated: " << function_definitions.size(); + LOG(WARNING) << "Generated output has fewer functions than expected - some " + "function signatures might use language features that " + "SAPI does not support. For debugging, we recommend you " + "compare the list of functions in your sapi_library() rule " + "with the generated *.sapi.h file. Expected: " + << options.function_names.size() + << ", generated: " << function_definitions.size(); } std::string out; const std::string include_guard = GetIncludeGuard(options.out_file); @@ -505,66 +370,6 @@ absl::StatusOr EmitHeader( return out; } -void Emitter::EmitType(clang::TypeDecl* type_decl) { - if (!type_decl) { - return; - } - - // Skip types defined in system headers. - // TODO(cblichmann): Instead of this and the hard-coded entities below, we - // should map types and add the correct (system) headers to - // the generated output. - if (type_decl->getASTContext().getSourceManager().isInSystemHeader( - type_decl->getBeginLoc())) { - return; - } - - const std::vector ns_path = GetNamespacePath(type_decl); - std::string ns_name; - if (!ns_path.empty()) { - const auto& ns_root = ns_path.front(); - // Filter out declarations from the C++ standard library, from SAPI itself - // and from other well-known namespaces. - if (ns_root == "std" || ns_root == "__gnu_cxx" || ns_root == "sapi") { - return; - } - if (ns_root == "absl") { - // Skip Abseil internal namespaces - if (ns_path.size() > 1 && absl::EndsWith(ns_path[1], "_internal")) { - return; - } - // Skip types from Abseil that will already be included in the generated - // header. - if (auto name = ToStringView(type_decl->getName()); - name == "CordMemoryAccounting" || name == "Duration" || - name == "LogEntry" || name == "LogSeverity" || name == "Span" || - name == "StatusCode" || name == "StatusToStringMode" || - name == "SynchLocksHeld" || name == "SynchWaitParams" || - name == "Time" || name == "string_view" || name == "tid_t") { - return; - } - } - // Skip Protocol Buffers namespaces - if (ns_root == "google" && ns_path.size() > 1 && ns_path[1] == "protobuf") { - return; - } - ns_name = absl::StrJoin(ns_path, "::"); - } - - std::string spelling = GetSpelling(type_decl); - if (const auto& [it, inserted] = rendered_types_.emplace(ns_name, spelling); - inserted) { - rendered_types_ordered_.push_back(&*it); - } -} - -void Emitter::AddTypeDeclarations( - const std::vector& type_decls) { - for (clang::TypeDecl* type_decl : type_decls) { - EmitType(type_decl); - } -} - absl::Status Emitter::AddFunction(clang::FunctionDecl* decl) { if (rendered_functions_.insert(decl->getQualifiedNameAsString()).second) { SAPI_ASSIGN_OR_RETURN(std::string function, EmitFunction(decl)); diff --git a/sandboxed_api/tools/clang_generator/emitter.h b/sandboxed_api/tools/clang_generator/emitter.h index 2a76f99f..2f03c791 100644 --- a/sandboxed_api/tools/clang_generator/emitter.h +++ b/sandboxed_api/tools/clang_generator/emitter.h @@ -16,87 +16,33 @@ #define SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_H_ #include -#include #include -#include "absl/container/flat_hash_set.h" -#include "absl/container/node_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "clang/AST/Decl.h" #include "clang/AST/Type.h" +#include "sandboxed_api/tools/clang_generator/emitter_base.h" namespace sapi { -// TODO b/347118045 - Refactor the naming of internal namespaces across the -// codebase. -namespace internal { - -// Returns a string that is the reformatted given code to conform to the Google -// style. -absl::StatusOr ReformatGoogleStyle(const std::string& filename, - const std::string& code, - int column_limit = -1); - -} // namespace internal // Forward declaration to avoid circular dependencies. struct GeneratorOptions; -class RenderedType { - public: - RenderedType(std::string ns_name, std::string spelling) - : ns_name(std::move(ns_name)), spelling(std::move(spelling)) {} - - bool operator==(const RenderedType& other) const { - return ns_name == other.ns_name && spelling == other.spelling; - } - - template - friend H AbslHashValue(H h, RenderedType rt) { - return H::combine(std::move(h), rt.ns_name, rt.spelling); - } - - std::string ns_name; - std::string spelling; -}; - // Responsible for emitting the actual textual representation of the generated // Sandboxed API header. -class Emitter { +class Emitter : public EmitterBase { public: - // Adds the declarations of previously collected types to the emitter, - // recording the spelling of each one. Types/declarations that are not - // supported by the current generator settings or that are unwanted or - // unnecessary are skipped. Other filtered types include C++ constructs or - // well-known standard library elements. The latter can be replaced by - // including the correct headers in the emitted header. - void AddTypeDeclarations(const std::vector& type_decls); - - // Adds the declarations of previously collected functions to the emitter. - absl::Status AddFunction(clang::FunctionDecl* decl); + // Adds a function to the list of functions to be rendered. In addition, it + // stores the original and SAPI function information for safe drop-in + // generation. + absl::Status AddFunction(clang::FunctionDecl* decl) override; // Outputs a formatted header for a list of functions and their related types. absl::StatusOr EmitHeader(const GeneratorOptions& options); - private: - // Emits the given type declaration to the member variables `rendered_types_` - // and `rendered_types_ordered_`. - void EmitType(clang::TypeDecl* type_decl); - protected: - // Stores namespaces and a list of spellings for types. Keeps track of types - // that have been rendered so far. Using a node_hash_set for pointer - // stability. - absl::node_hash_set rendered_types_; - - // A vector to preserve the order of type declarations needs to be preserved. - std::vector rendered_types_ordered_; - - // Fully qualified names of functions for the sandboxed API. Keeps track of - // functions that have been rendered so far. - absl::flat_hash_set rendered_functions_; - // Rendered function bodies, as a vector to preserve source order. This is // not strictly necessary, but makes the output look less surprising. std::vector rendered_functions_ordered_; diff --git a/sandboxed_api/tools/clang_generator/emitter_base.cc b/sandboxed_api/tools/clang_generator/emitter_base.cc new file mode 100644 index 00000000..3cadcb98 --- /dev/null +++ b/sandboxed_api/tools/clang_generator/emitter_base.cc @@ -0,0 +1,318 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sandboxed_api/tools/clang_generator/emitter_base.h" + +#include +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/str_join.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/QualTypeNames.h" +#include "clang/Format/Format.h" +#include "clang/Tooling/Core/Replacement.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include "sandboxed_api/tools/clang_generator/generator.h" + +namespace sapi { + +// Common header file prolog with auto-generation notice. +// Note: The includes will be adjusted by Copybara when converting to/from +// internal code. This is intentional. +// Text template arguments: +// 1. Header guard +constexpr absl::string_view kHeaderProlog = + R"(// AUTO-GENERATED by the Sandboxed API generator. +// Edits will be discarded when regenerating this file. + +#ifndef %1$s +#define %1$s + +#include +#include + +#include "absl/base/macros.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "sandboxed_api/sandbox.h" +#include "sandboxed_api/util/status_macros.h" +#include "sandboxed_api/vars.h" + +)"; + +// Text template arguments: +// 1. Header guard +constexpr absl::string_view kHeaderEpilog = + R"( +#endif // %1$s)"; + +// Text template arguments: +// 1. Include for embedded sandboxee objects +constexpr absl::string_view kEmbedInclude = R"(#include "%1$s_embed.h" + +)"; + +// Text template arguments: +// 1. Namespace name +constexpr absl::string_view kNamespaceBeginTemplate = + R"( +namespace %1$s { + +)"; + +// Text template arguments: +// 1. Namespace name +constexpr absl::string_view kNamespaceEndTemplate = + R"( +} // namespace %1$s +)"; + +// Text template arguments: +// 1. Class name +// 2. Embedded object identifier +constexpr absl::string_view kEmbedClassTemplate = R"( +// Sandbox with embedded sandboxee and default policy +class %1$s : public ::sapi::Sandbox { + public: + %1$s() + : ::sapi::Sandbox([]() { + static auto* fork_client_context = + new ::sapi::ForkClientContext(%2$s_embed_create()); + return fork_client_context; + }()) {} +}; + +)"; + +// Sandboxed API class template. +// Text template arguments: +// 1. Class name +constexpr absl::string_view kClassHeaderTemplate = R"( +// Sandboxed API +class %1$s { + public: + explicit %1$s(::sapi::Sandbox* sandbox) : sandbox_(sandbox) {} + + ABSL_DEPRECATED("Call sandbox() instead") + ::sapi::Sandbox* GetSandbox() const { return sandbox(); } + ::sapi::Sandbox* sandbox() const { return sandbox_; } +)"; + +// Sandboxed API class template footer. +constexpr absl::string_view kClassFooterTemplate = R"( + private: + ::sapi::Sandbox* sandbox_; +}; +)"; + +namespace internal { + +absl::StatusOr ReformatGoogleStyle(const std::string& filename, + const std::string& code, + int column_limit) { + // Configure code style based on Google style, but enforce pointer alignment + clang::format::FormatStyle style = + clang::format::getGoogleStyle(clang::format::FormatStyle::LK_Cpp); + style.DerivePointerAlignment = false; + style.PointerAlignment = clang::format::FormatStyle::PAS_Left; + if (column_limit >= 0) { + style.ColumnLimit = column_limit; + } + + clang::tooling::Replacements replacements = clang::format::reformat( + style, code, llvm::ArrayRef(clang::tooling::Range(0, code.size())), + filename); + + llvm::Expected formatted_header = + clang::tooling::applyAllReplacements(code, replacements); + if (!formatted_header) { + return absl::InternalError(llvm::toString(formatted_header.takeError())); + } + return *formatted_header; +} + +} // namespace internal + +namespace { + +// Returns the namespace components of a declaration's qualified name. +std::vector GetNamespacePath(const clang::TypeDecl* decl) { + std::vector comps; + for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) { + if (const auto* nd = llvm::dyn_cast(ctx)) { + comps.push_back(nd->getName().str()); + } + } + std::reverse(comps.begin(), comps.end()); + return comps; +} + +// Returns the template arguments for a given record. +std::string PrintRecordTemplateArguments(const clang::CXXRecordDecl* record) { + const auto* template_inst_decl = record->getTemplateInstantiationPattern(); + if (!template_inst_decl) { + return ""; + } + const auto* template_decl = template_inst_decl->getDescribedClassTemplate(); + if (!template_decl) { + return ""; + } + const auto* template_params = template_decl->getTemplateParameters(); + if (!template_params) { + return ""; + } + clang::ASTContext& context = record->getASTContext(); + std::vector params; + params.reserve(template_params->size()); + for (const auto& template_param : *template_params) { + if (const auto* p = + llvm::dyn_cast(template_param)) { + // TODO(cblichmann): Should be included by CollectRelatedTypes(). + params.push_back(clang::TypeName::getFullyQualifiedName( + p->getType().getDesugaredType(context), context, + context.getPrintingPolicy())); + } else { // Also covers template template parameters + params.push_back("typename"); + } + absl::StrAppend(¶ms.back(), " /*", + std::string(template_param->getName()), "*/"); + } + return absl::StrCat("template <", absl::StrJoin(params, ", "), ">"); +} + +// Serializes the given Clang AST declaration back into compilable source code. +std::string PrintDecl(const clang::Decl* decl) { + std::string pretty; + llvm::raw_string_ostream os(pretty); + decl->print(os); + return os.str(); +} + +// Returns the spelling for a given declaration will be emitted to the final +// header. This may rewrite declarations (like converting typedefs to using, +// etc.). Note that the resulting spelling will need to be wrapped inside a +// namespace if the original declaration was inside one. +std::string GetSpelling(const clang::Decl* decl) { + // TODO(cblichmann): Make types nicer + // - Rewrite typedef to using + // - Rewrite function pointers using std::add_pointer_t<>; + + if (const auto* typedef_decl = llvm::dyn_cast(decl)) { + // Special case: anonymous enum/struct + if (auto* tag_decl = typedef_decl->getAnonDeclWithTypedefName()) { + return absl::StrCat("typedef ", PrintDecl(tag_decl), " ", + ToStringView(typedef_decl->getName())); + } + } + + if (const auto* record_decl = llvm::dyn_cast(decl)) { + if (record_decl->hasDefinition() && + // Aggregates capture all C-like structs, but also structs with + // non-static members that have default initializers. + record_decl->isAggregate() && + // Make sure to skip types with user-defined methods (including + // constructors). + record_decl->methods().empty()) { + return PrintDecl(decl); + } + // For unsupported types or types with no definition, only emit a forward + // declaration. + return absl::StrCat(PrintRecordTemplateArguments(record_decl), + record_decl->isClass() ? "class " : "struct ", + ToStringView(record_decl->getName())); + } + return PrintDecl(decl); +} + +} // namespace + +std::string GetParamName(const clang::ParmVarDecl* decl, int index) { + if (std::string name = decl->getName().str(); !name.empty()) { + return absl::StrCat(name, "_"); // Suffix to avoid collisions + } + return absl::StrCat("unnamed", index, "_"); +} + +void EmitterBase::EmitType(clang::TypeDecl* type_decl) { + if (!type_decl) { + return; + } + + // Skip types defined in system headers. + // TODO(cblichmann): Instead of this and the hard-coded entities below, we + // should map types and add the correct (system) headers to + // the generated output. + if (type_decl->getASTContext().getSourceManager().isInSystemHeader( + type_decl->getBeginLoc())) { + return; + } + + const std::vector ns_path = GetNamespacePath(type_decl); + std::string ns_name; + if (!ns_path.empty()) { + const auto& ns_root = ns_path.front(); + // Filter out declarations from the C++ standard library, from SAPI itself + // and from other well-known namespaces. + if (ns_root == "std" || ns_root == "__gnu_cxx" || ns_root == "sapi") { + return; + } + if (ns_root == "absl") { + // Skip Abseil internal namespaces + if (ns_path.size() > 1 && absl::EndsWith(ns_path[1], "_internal")) { + return; + } + // Skip types from Abseil that will already be included in the generated + // header. + if (auto name = ToStringView(type_decl->getName()); + name == "CordMemoryAccounting" || name == "Duration" || + name == "LogEntry" || name == "LogSeverity" || name == "Span" || + name == "StatusCode" || name == "StatusToStringMode" || + name == "SynchLocksHeld" || name == "SynchWaitParams" || + name == "Time" || name == "string_view" || name == "tid_t") { + return; + } + } + // Skip Protocol Buffers namespaces + if (ns_root == "google" && ns_path.size() > 1 && ns_path[1] == "protobuf") { + return; + } + ns_name = absl::StrJoin(ns_path, "::"); + } + + std::string spelling = GetSpelling(type_decl); + if (const auto& [it, inserted] = rendered_types_.emplace(ns_name, spelling); + inserted) { + rendered_types_ordered_.push_back(&*it); + } +} + +void EmitterBase::AddTypeDeclarations( + const std::vector& type_decls) { + for (clang::TypeDecl* type_decl : type_decls) { + EmitType(type_decl); + } +} + +} // namespace sapi diff --git a/sandboxed_api/tools/clang_generator/emitter_base.h b/sandboxed_api/tools/clang_generator/emitter_base.h new file mode 100644 index 00000000..f3c67f3b --- /dev/null +++ b/sandboxed_api/tools/clang_generator/emitter_base.h @@ -0,0 +1,98 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_BASE_H_ +#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_BASE_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_set.h" +#include "absl/container/node_hash_set.h" +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Type.h" + +namespace sapi { +// TODO b/347118045 - Refactor the naming of internal namespaces across the +// codebase. +namespace internal { + +// Returns a string of the specified code reformatted to conform to the Google +// style. +// Ill-formed code will return an error status. +absl::StatusOr ReformatGoogleStyle(const std::string& filename, + const std::string& code, + int column_limit = -1); + +} // namespace internal + +class RenderedType { + public: + RenderedType(std::string ns_name, std::string spelling) + : ns_name(std::move(ns_name)), spelling(std::move(spelling)) {} + + bool operator==(const RenderedType& other) const { + return ns_name == other.ns_name && spelling == other.spelling; + } + + template + friend H AbslHashValue(H h, RenderedType rt) { + return H::combine(std::move(h), rt.ns_name, rt.spelling); + } + + std::string ns_name; + std::string spelling; +}; + +class EmitterBase { + public: + virtual ~EmitterBase() = default; + + // Adds the declarations of previously collected types to the emitter, + // recording the spelling of each one. Types/declarations that are not + // supported by the current generator settings or that are unwanted or + // unnecessary are skipped. Other filtered types include C++ constructs or + // well-known standard library elements. The latter can be replaced by + // including the correct headers in the emitted header. + void AddTypeDeclarations(const std::vector& type_decls); + + // Adds the declarations of previously collected functions to the emitter. + virtual absl::Status AddFunction(clang::FunctionDecl* decl) = 0; + + // Stores namespaces and a list of spellings for types. Keeps track of types + // that have been rendered so far. Using a node_hash_set for pointer + // stability. + absl::node_hash_set rendered_types_; + + // A vector to preserve the order of type declarations needs to be preserved. + std::vector rendered_types_ordered_; + + // Fully qualified names of functions for the sandboxed API. Keeps track of + // functions that have been rendered so far. + absl::flat_hash_set rendered_functions_; + + private: + void EmitType(clang::TypeDecl* type_decl); +}; + +// Returns a unique name for a parameter. If `decl` has no name, a unique name +// will be generated in the form of `unnamed_`. +std::string GetParamName(const clang::ParmVarDecl* decl, int index); + +} // namespace sapi + +#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_BASE_H_ diff --git a/sandboxed_api/tools/clang_generator/emitter_test.cc b/sandboxed_api/tools/clang_generator/emitter_test.cc index b69ad371..14445739 100644 --- a/sandboxed_api/tools/clang_generator/emitter_test.cc +++ b/sandboxed_api/tools/clang_generator/emitter_test.cc @@ -24,6 +24,7 @@ #include "absl/status/statusor.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" +#include "sandboxed_api/tools/clang_generator/emitter_base.h" #include "sandboxed_api/tools/clang_generator/frontend_action_test_util.h" #include "sandboxed_api/tools/clang_generator/generator.h" #include "sandboxed_api/util/status_matchers.h" diff --git a/sandboxed_api/tools/clang_generator/generator.cc b/sandboxed_api/tools/clang_generator/generator.cc index eda96221..a89c6c7c 100644 --- a/sandboxed_api/tools/clang_generator/generator.cc +++ b/sandboxed_api/tools/clang_generator/generator.cc @@ -37,7 +37,7 @@ #include "clang/Serialization/PCHContainerOperations.h" #include "clang/Tooling/Tooling.h" #include "sandboxed_api/tools/clang_generator/diagnostics.h" -#include "sandboxed_api/tools/clang_generator/emitter.h" +#include "sandboxed_api/tools/clang_generator/emitter_base.h" namespace sapi { namespace { diff --git a/sandboxed_api/tools/clang_generator/generator.h b/sandboxed_api/tools/clang_generator/generator.h index dd24a787..3431d86f 100644 --- a/sandboxed_api/tools/clang_generator/generator.h +++ b/sandboxed_api/tools/clang_generator/generator.h @@ -34,6 +34,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Config/llvm-config.h" #include "sandboxed_api/tools/clang_generator/emitter.h" +#include "sandboxed_api/tools/clang_generator/emitter_base.h" #include "sandboxed_api/tools/clang_generator/types.h" namespace sapi { @@ -98,7 +99,7 @@ class GeneratorASTVisitor class GeneratorASTConsumer : public clang::ASTConsumer { public: - GeneratorASTConsumer(std::string in_file, Emitter& emitter, + GeneratorASTConsumer(std::string in_file, EmitterBase& emitter, const GeneratorOptions& options) : in_file_(std::move(in_file)), visitor_(options), emitter_(emitter) {} @@ -107,12 +108,12 @@ class GeneratorASTConsumer : public clang::ASTConsumer { std::string in_file_; GeneratorASTVisitor visitor_; - Emitter& emitter_; + EmitterBase& emitter_; }; class GeneratorAction : public clang::ASTFrontendAction { public: - GeneratorAction(Emitter& emitter, const GeneratorOptions& options) + GeneratorAction(EmitterBase& emitter, const GeneratorOptions& options) : emitter_(emitter), options_(options) {} private: @@ -129,14 +130,14 @@ class GeneratorAction : public clang::ASTFrontendAction { bool hasCodeCompletionSupport() const override { return false; } - Emitter& emitter_; + EmitterBase& emitter_; const GeneratorOptions& options_; }; class GeneratorFactory : public clang::tooling::FrontendActionFactory { public: // Does not take ownership - GeneratorFactory(Emitter& emitter, const GeneratorOptions& options) + GeneratorFactory(EmitterBase& emitter, const GeneratorOptions& options) : emitter_(emitter), options_(options) {} private: @@ -156,7 +157,7 @@ class GeneratorFactory : public clang::tooling::FrontendActionFactory { std::shared_ptr pch_container_ops, clang::DiagnosticConsumer* diag_consumer) override; - Emitter& emitter_; + EmitterBase& emitter_; const GeneratorOptions& options_; };