mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Merge pull request #40 from cblichmann:clang-tool
PiperOrigin-RevId: 313577454 Change-Id: I4de93e1ffca003899ae3c7110ab3fd10f700907c
This commit is contained in:
commit
e76e73dfe8
5
.gitignore
vendored
5
.gitignore
vendored
|
@ -2,11 +2,16 @@
|
|||
.DS_Store
|
||||
# Build files
|
||||
bazel-*
|
||||
# Local CMake build dir
|
||||
build/
|
||||
# Editor backup files
|
||||
*.swp
|
||||
*~
|
||||
# Compiled Python files and cache
|
||||
*.py[co]
|
||||
__py_cache__/
|
||||
# IDE files
|
||||
.clangd/
|
||||
.vscode/
|
||||
.idea
|
||||
compile_commands.json
|
||||
|
|
|
@ -31,6 +31,24 @@ include(SapiDeps)
|
|||
include(SapiUtil)
|
||||
include(SapiBuildDefs)
|
||||
|
||||
# Fix Ninja generator output to not rebuild entire sub-trees needlessly.
|
||||
if(CMAKE_GENERATOR MATCHES "Ninja")
|
||||
file(WRITE "${SAPI_BINARY_DIR}/UserMakeRulesOverride.cmake"
|
||||
"STRING(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_C \"\${CMAKE_DEPFILE_FLAGS_C}\")\n"
|
||||
"STRING(REPLACE \"-MD\" \"-MMD\" CMAKE_DEPFILE_FLAGS_CXX \"\${CMAKE_DEPFILE_FLAGS_CXX}\")\n"
|
||||
)
|
||||
set(CMAKE_USER_MAKE_RULES_OVERRIDE
|
||||
"${SAPI_BINARY_DIR}/UserMakeRulesOverride.cmake" CACHE INTERNAL "")
|
||||
endif()
|
||||
|
||||
if (SAPI_FORCE_COLOR_OUTPUT)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # GCC
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") # Clang or Apple Clang
|
||||
add_compile_options(-fcolor-diagnostics)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Make Bazel-style includes work
|
||||
configure_file(cmake/libcap_capability.h.in
|
||||
libcap/include/sys/capability.h
|
||||
|
|
|
@ -128,25 +128,42 @@ function(add_sapi_library)
|
|||
get_filename_component(src "${src}" ABSOLUTE)
|
||||
list(APPEND _sapi_full_inputs "${src}")
|
||||
endforeach()
|
||||
list_join(_sapi_full_inputs "," _sapi_full_inputs)
|
||||
if(NOT _sapi_NOEMBED)
|
||||
set(_sapi_embed_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(_sapi_embed_name "${_sapi_NAME}")
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT "${_sapi_gen_header}"
|
||||
COMMAND "${SAPI_PYTHON3_EXECUTABLE}" -B
|
||||
"${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py"
|
||||
"--sapi_name=${_sapi_LIBRARY_NAME}"
|
||||
"--sapi_out=${_sapi_gen_header}"
|
||||
"--sapi_embed_dir=${_sapi_embed_dir}"
|
||||
"--sapi_embed_name=${_sapi_embed_name}"
|
||||
"--sapi_functions=${_sapi_funcs}"
|
||||
"--sapi_ns=${_sapi_NAMESPACE}"
|
||||
# TODO(cblichmann): Implement sapi_isystem
|
||||
"--sapi_in=${_sapi_full_inputs}"
|
||||
COMMENT "Generating interface"
|
||||
)
|
||||
# TODO(cblichmann): Implement sapi_isystem
|
||||
if(SAPI_ENABLE_GENERATOR)
|
||||
add_custom_command(
|
||||
OUTPUT "${_sapi_gen_header}"
|
||||
COMMAND sapi_generator_tool
|
||||
"--sapi_name=${_sapi_LIBRARY_NAME}"
|
||||
"--sapi_out=${_sapi_gen_header}"
|
||||
"--sapi_embed_dir=${_sapi_embed_dir}"
|
||||
"--sapi_embed_name=${_sapi_embed_name}"
|
||||
"--sapi_functions=${_sapi_funcs}"
|
||||
"--sapi_ns=${_sapi_NAMESPACE}"
|
||||
${_sapi_full_inputs}
|
||||
COMMENT "Generating interface"
|
||||
DEPENDS ${_sapi_INPUTS}
|
||||
VERBATIM
|
||||
)
|
||||
else()
|
||||
list_join(_sapi_full_inputs "," _sapi_full_inputs)
|
||||
add_custom_command(
|
||||
OUTPUT "${_sapi_gen_header}"
|
||||
COMMAND "${SAPI_PYTHON3_EXECUTABLE}" -B
|
||||
"${SAPI_SOURCE_DIR}/sandboxed_api/tools/generator2/sapi_generator.py"
|
||||
"--sapi_name=${_sapi_LIBRARY_NAME}"
|
||||
"--sapi_out=${_sapi_gen_header}"
|
||||
"--sapi_embed_dir=${_sapi_embed_dir}"
|
||||
"--sapi_embed_name=${_sapi_embed_name}"
|
||||
"--sapi_functions=${_sapi_funcs}"
|
||||
"--sapi_ns=${_sapi_NAMESPACE}"
|
||||
"--sapi_in=${_sapi_full_inputs}"
|
||||
COMMENT "Generating interface"
|
||||
)
|
||||
endif()
|
||||
|
||||
# Library with the interface
|
||||
if(NOT _sapi_SOURCES)
|
||||
|
|
|
@ -89,10 +89,12 @@ if(SAPI_ENABLE_EXAMPLES)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# Find Python 3 and add its location to the cache so that its available in
|
||||
# the add_sapi_library() macro in embedding projects.
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
set(SAPI_PYTHON3_EXECUTABLE "${Python3_EXECUTABLE}" CACHE INTERNAL "" FORCE)
|
||||
if(NOT SAPI_ENABLE_GENERATOR)
|
||||
# Find Python 3 and add its location to the cache so that its available in
|
||||
# the add_sapi_library() macro in embedding projects.
|
||||
find_package(Python3 COMPONENTS Interpreter REQUIRED)
|
||||
set(SAPI_PYTHON3_EXECUTABLE "${Python3_EXECUTABLE}" CACHE INTERNAL "" FORCE)
|
||||
endif()
|
||||
|
||||
# Undo global change
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_sapi_saved_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
|
|
|
@ -34,3 +34,6 @@ option(SAPI_ENABLE_EXAMPLES "Build example code" ON)
|
|||
option(SAPI_DOWNLOAD_ZLIB "Download zlib at config time (only if SAPI_ENABLE_EXAMPLES is set)" ON)
|
||||
|
||||
option(SAPI_ENABLE_TESTS "Build unit tests" ON)
|
||||
option(SAPI_ENABLE_GENERATOR "Build Clang based code generator from source" OFF)
|
||||
|
||||
option(SAPI_FORCE_COLOR_OUTPUT "Force colored compiler diagnostics when using Ninja" ON)
|
||||
|
|
|
@ -36,6 +36,7 @@ endif()
|
|||
set(WITH_GFLAGS OFF CACHE BOOL "" FORCE)
|
||||
set(HAVE_LIB_GFLAGS 1)
|
||||
|
||||
set(WITH_UNWIND OFF CACHE BOOL "" FORCE)
|
||||
set(UNWIND_LIBRARY FALSE)
|
||||
set(HAVE_PWD_H FALSE)
|
||||
|
||||
|
|
|
@ -13,9 +13,12 @@
|
|||
# limitations under the License.
|
||||
|
||||
add_subdirectory(bazel) # For filewrapper
|
||||
add_subdirectory(examples)
|
||||
add_subdirectory(sandbox2)
|
||||
add_subdirectory(util)
|
||||
if(SAPI_ENABLE_GENERATOR)
|
||||
add_subdirectory(tools/clang_generator)
|
||||
endif()
|
||||
add_subdirectory(examples)
|
||||
|
||||
# sandboxed_api:proto_arg
|
||||
sapi_protobuf_generate_cpp(_sapi_proto_arg_pb_cc _sapi_proto_arg_pb_h
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
||||
# sandboxed_api/sandbox2/examples/zlib:zpipe_sandbox
|
||||
add_executable(sandbox2_zpipe_sandbox
|
||||
zpipe_sandbox.cc
|
||||
|
|
|
@ -48,8 +48,8 @@ int FDCloser::Release() {
|
|||
|
||||
bool GetCWD(std::string* result) {
|
||||
// Calling getcwd() with a nullptr buffer is a commonly implemented extension.
|
||||
std::unique_ptr<char, void (*)(char*)> cwd{getcwd(nullptr, 0),
|
||||
[](char* p) { free(p); }};
|
||||
std::unique_ptr<char, void (*)(char*)> cwd(getcwd(nullptr, 0),
|
||||
[](char* p) { free(p); });
|
||||
if (!cwd) {
|
||||
return false;
|
||||
}
|
||||
|
@ -57,6 +57,12 @@ bool GetCWD(std::string* result) {
|
|||
return true;
|
||||
}
|
||||
|
||||
std::string GetCWD() {
|
||||
std::string cwd;
|
||||
GetCWD(&cwd);
|
||||
return cwd;
|
||||
}
|
||||
|
||||
// Makes a path absolute with respect to base. Returns true on success. Result
|
||||
// may be an alias of base or filename.
|
||||
bool MakeAbsolute(const std::string& filename, const std::string& base,
|
||||
|
|
|
@ -42,6 +42,10 @@ class FDCloser {
|
|||
int fd_;
|
||||
};
|
||||
|
||||
// Returns the current working directory. On error, returns an empty string. Use
|
||||
// errno/GetLastError() to check the root cause in that case.
|
||||
std::string GetCWD();
|
||||
|
||||
// Returns the target of a symlink. Returns an empty string on failure.
|
||||
std::string ReadLink(const std::string& filename);
|
||||
|
||||
|
|
62
sandboxed_api/tools/clang_generator/CMakeLists.txt
Normal file
62
sandboxed_api/tools/clang_generator/CMakeLists.txt
Normal file
|
@ -0,0 +1,62 @@
|
|||
# Copyright 2020 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
|
||||
#
|
||||
# http://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.
|
||||
|
||||
# Minimum supported: LLVM 9
|
||||
find_package(LLVM REQUIRED)
|
||||
find_package(Clang REQUIRED)
|
||||
|
||||
add_library(sapi_generator
|
||||
diagnostics.cc
|
||||
diagnostics.h
|
||||
emitter.h
|
||||
emitter.cc
|
||||
generator.h
|
||||
generator.cc
|
||||
types.h
|
||||
types.cc
|
||||
)
|
||||
add_library(sapi::generator ALIAS sapi_generator)
|
||||
target_compile_definitions(sapi_generator PUBLIC
|
||||
${LLVM_DEFINITIONS}
|
||||
)
|
||||
target_include_directories(sapi_generator PUBLIC
|
||||
${LLVM_INCLUDE_DIRS}
|
||||
)
|
||||
llvm_map_components_to_libnames(_sapi_generator_llvm_libs
|
||||
core
|
||||
)
|
||||
target_link_libraries(sapi_generator PUBLIC
|
||||
sapi::base
|
||||
absl::flat_hash_set
|
||||
absl::memory
|
||||
absl::random_random
|
||||
absl::status
|
||||
absl::strings
|
||||
clangFormat
|
||||
clangFrontendTool
|
||||
clangTooling
|
||||
sandbox2::fileops
|
||||
sapi::status
|
||||
${_sapi_generator_llvm_libs}
|
||||
)
|
||||
|
||||
add_executable(sapi_generator_tool
|
||||
generator_tool.cc
|
||||
)
|
||||
target_link_libraries(sapi_generator_tool PRIVATE
|
||||
sapi::base
|
||||
sandbox2::fileops
|
||||
sapi::generator
|
||||
)
|
||||
|
56
sandboxed_api/tools/clang_generator/diagnostics.cc
Normal file
56
sandboxed_api/tools/clang_generator/diagnostics.cc
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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/diagnostics.h"
|
||||
|
||||
#include "absl/strings/cord.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
constexpr absl::string_view kSapiStatusPayload =
|
||||
"https://github.com/google/sandboxed-api";
|
||||
|
||||
absl::Status MakeStatusWithDiagnostic(clang::SourceLocation loc,
|
||||
absl::string_view message) {
|
||||
absl::Status status = absl::UnknownError(message);
|
||||
absl::Cord payload;
|
||||
uint64_t raw_loc = loc.getRawEncoding();
|
||||
payload.Append(
|
||||
absl::string_view(reinterpret_cast<char*>(&raw_loc), sizeof(raw_loc)));
|
||||
status.SetPayload(kSapiStatusPayload, std::move(payload));
|
||||
return status;
|
||||
}
|
||||
|
||||
absl::optional<clang::SourceLocation> GetDiagnosticLocationFromStatus(
|
||||
const absl::Status& status) {
|
||||
if (auto payload =
|
||||
status.GetPayload(kSapiStatusPayload).value_or(absl::Cord());
|
||||
payload.size() == sizeof(uint64_t)) {
|
||||
return clang::SourceLocation::getFromRawEncoding(
|
||||
*reinterpret_cast<const uint64_t*>(payload.Flatten().data()));
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
|
||||
clang::DiagnosticBuilder ReportFatalError(clang::DiagnosticsEngine& de,
|
||||
clang::SourceLocation loc,
|
||||
absl::string_view message) {
|
||||
clang::DiagnosticBuilder builder =
|
||||
de.Report(loc, de.getCustomDiagID(clang::DiagnosticsEngine::Fatal,
|
||||
"header generation failed: %0"));
|
||||
builder.AddString(llvm::StringRef(message.data(), message.size()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
} // namespace sapi
|
41
sandboxed_api/tools/clang_generator/diagnostics.h
Normal file
41
sandboxed_api/tools/clang_generator/diagnostics.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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_DIAGNOSTICS_H_
|
||||
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_DIAGNOSTICS_H_
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
// Returns a new UNKNOWN status with a payload that encodes the specified Clang
|
||||
// source location.
|
||||
absl::Status MakeStatusWithDiagnostic(clang::SourceLocation loc,
|
||||
absl::string_view message);
|
||||
|
||||
// Extracts the Clang source location encoded in a status payload.
|
||||
absl::optional<clang::SourceLocation> GetDiagnosticLocationFromStatus(
|
||||
const absl::Status& status);
|
||||
|
||||
clang::DiagnosticBuilder ReportFatalError(clang::DiagnosticsEngine& de,
|
||||
clang::SourceLocation loc,
|
||||
absl::string_view message);
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_DIAGNOSTICS_H_
|
343
sandboxed_api/tools/clang_generator/emitter.cc
Normal file
343
sandboxed_api/tools/clang_generator/emitter.cc
Normal file
|
@ -0,0 +1,343 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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.h"
|
||||
|
||||
#include "absl/random/random.h"
|
||||
#include "absl/strings/ascii.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/match.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "absl/strings/str_replace.h"
|
||||
#include "absl/strings/strip.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
// Common 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 <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "absl/base/macros.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "sandboxed_api/sandbox.h"
|
||||
#include "sandboxed_api/vars.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
)";
|
||||
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 {
|
||||
|
||||
)";
|
||||
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(%2$s_embed_create()) {}
|
||||
};
|
||||
|
||||
)";
|
||||
|
||||
// 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_; }
|
||||
)";
|
||||
|
||||
constexpr absl::string_view kClassFooterTemplate = R"(
|
||||
private:
|
||||
::sapi::Sandbox* sandbox_;
|
||||
};
|
||||
)";
|
||||
|
||||
std::string GetIncludeGuard(absl::string_view filename) {
|
||||
if (filename.empty()) {
|
||||
static auto* bit_gen = new absl::BitGen();
|
||||
return absl::StrCat(
|
||||
// Copybara will transform the string. This is intentional.
|
||||
"SANDBOXED_API_GENERATED_HEADER_",
|
||||
absl::Hex(absl::Uniform<uint64_t>(*bit_gen), absl::kZeroPad16), "_");
|
||||
}
|
||||
|
||||
constexpr absl::string_view kUnderscorePrefix = "SAPI";
|
||||
std::string guard;
|
||||
guard.reserve(filename.size() + kUnderscorePrefix.size() + 1);
|
||||
for (auto c : filename) {
|
||||
if (absl::ascii_isalpha(c)) {
|
||||
guard += c;
|
||||
continue;
|
||||
}
|
||||
if (guard.empty()) {
|
||||
guard = kUnderscorePrefix;
|
||||
}
|
||||
if (absl::ascii_isdigit(c)) {
|
||||
guard += c;
|
||||
} else if (guard.back() != '_') {
|
||||
guard += '_';
|
||||
}
|
||||
}
|
||||
if (!absl::EndsWith(guard, "_")) {
|
||||
guard += '_';
|
||||
}
|
||||
return guard;
|
||||
}
|
||||
|
||||
// Returns the namespace components of a declaration's qualified name.
|
||||
std::vector<std::string> GetNamespacePath(const clang::TypeDecl* decl) {
|
||||
std::vector<std::string> comps;
|
||||
for (const auto* ctx = decl->getDeclContext(); ctx; ctx = ctx->getParent()) {
|
||||
if (const auto* nd = llvm::dyn_cast<clang::NamespaceDecl>(ctx)) {
|
||||
comps.push_back(nd->getNameAsString());
|
||||
}
|
||||
}
|
||||
std::reverse(comps.begin(), comps.end());
|
||||
return comps;
|
||||
}
|
||||
|
||||
// Serializes the given Clang AST declaration back into compilable source code.
|
||||
std::string PrintAstDecl(const clang::Decl* decl) {
|
||||
// TODO(cblichmann): Make types nicer
|
||||
// - Rewrite typedef to using
|
||||
// - Rewrite function pointers using std::add_pointer_t<>;
|
||||
|
||||
if (const auto* record = llvm::dyn_cast<clang::CXXRecordDecl>(decl)) {
|
||||
// For C++ classes/structs, only emit a forward declaration.
|
||||
return absl::StrCat(record->isClass() ? "class " : "struct ",
|
||||
std::string(record->getName()));
|
||||
}
|
||||
std::string pretty;
|
||||
llvm::raw_string_ostream os(pretty);
|
||||
decl->print(os);
|
||||
return os.str();
|
||||
}
|
||||
|
||||
std::string GetParamName(const clang::ParmVarDecl* decl, int index) {
|
||||
if (std::string name = decl->getName(); !name.empty()) {
|
||||
return absl::StrCat(name, "_"); // Suffix to avoid collisions
|
||||
}
|
||||
return absl::StrCat("unnamed", index, "_");
|
||||
}
|
||||
|
||||
std::string PrintFunctionPrototype(const clang::FunctionDecl* decl) {
|
||||
// TODO(cblichmann): Fix function pointers and anonymous namespace formatting
|
||||
std::string out =
|
||||
absl::StrCat(decl->getDeclaredReturnType().getAsString(), " ",
|
||||
std::string(decl->getQualifiedNameAsString()), "(");
|
||||
|
||||
std::string print_separator;
|
||||
for (int i = 0; i < decl->getNumParams(); ++i) {
|
||||
const clang::ParmVarDecl* param = decl->getParamDecl(i);
|
||||
|
||||
absl::StrAppend(&out, print_separator);
|
||||
print_separator = ", ";
|
||||
absl::StrAppend(&out, param->getType().getAsString());
|
||||
if (std::string name = param->getName(); !name.empty()) {
|
||||
absl::StrAppend(&out, " ", name);
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&out, ")");
|
||||
return out;
|
||||
}
|
||||
|
||||
sapi::StatusOr<std::string> EmitFunction(const clang::FunctionDecl* decl) {
|
||||
std::string out;
|
||||
absl::StrAppend(&out, "\n// ", PrintFunctionPrototype(decl), "\n");
|
||||
const std::string function_name = decl->getNameAsString();
|
||||
const clang::QualType return_type = decl->getDeclaredReturnType();
|
||||
const bool returns_void = return_type->isVoidType();
|
||||
|
||||
const clang::ASTContext& context = decl->getASTContext();
|
||||
|
||||
// "Status<OptionalReturn> FunctionName("
|
||||
absl::StrAppend(&out, MapQualTypeReturn(context, return_type), " ",
|
||||
function_name, "(");
|
||||
|
||||
struct ParameterInfo {
|
||||
clang::QualType qual;
|
||||
std::string name;
|
||||
};
|
||||
std::vector<ParameterInfo> params;
|
||||
|
||||
std::string print_separator;
|
||||
for (int i = 0; i < decl->getNumParams(); ++i) {
|
||||
const clang::ParmVarDecl* param = decl->getParamDecl(i);
|
||||
|
||||
ParameterInfo& param_info = params.emplace_back();
|
||||
param_info.qual = param->getType();
|
||||
param_info.name = GetParamName(param, i);
|
||||
|
||||
absl::StrAppend(&out, print_separator);
|
||||
print_separator = ", ";
|
||||
absl::StrAppend(&out, MapQualTypeParameter(context, param_info.qual), " ",
|
||||
param_info.name);
|
||||
}
|
||||
|
||||
absl::StrAppend(&out, ") {\n");
|
||||
absl::StrAppend(&out, MapQualType(context, return_type), " v_ret_;\n");
|
||||
for (const auto& [qual, name] : params) {
|
||||
if (!IsPointerOrReference(qual)) {
|
||||
absl::StrAppend(&out, MapQualType(context, qual), " v_", name, "(", name,
|
||||
");\n");
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&out, "\nSAPI_RETURN_IF_ERROR(sandbox_->Call(\"", function_name,
|
||||
"\", &v_ret_");
|
||||
for (const auto& [qual, name] : params) {
|
||||
absl::StrAppend(&out, ", ", IsPointerOrReference(qual) ? "" : "&v_", name);
|
||||
}
|
||||
absl::StrAppend(&out, "));\nreturn ",
|
||||
(returns_void ? "absl::OkStatus()" : "v_ret_.GetValue()"),
|
||||
";\n}\n");
|
||||
return out;
|
||||
}
|
||||
|
||||
sapi::StatusOr<std::string> EmitHeader(
|
||||
std::vector<clang::FunctionDecl*> functions, const QualTypeSet& types,
|
||||
const GeneratorOptions& options) {
|
||||
std::string out;
|
||||
const std::string include_guard = GetIncludeGuard(options.out_file);
|
||||
absl::StrAppendFormat(&out, kHeaderProlog, include_guard);
|
||||
|
||||
// When embedding the sandboxee, add embed header include
|
||||
if (!options.embed_name.empty()) {
|
||||
// Not using JoinPath() because even on Windows include paths use plain
|
||||
// slashes.
|
||||
std::string include_file(absl::StripSuffix(
|
||||
absl::StrReplaceAll(options.embed_dir, {{"\\", "/"}}), "/"));
|
||||
if (!include_file.empty()) {
|
||||
absl::StrAppend(&include_file, "/");
|
||||
}
|
||||
absl::StrAppend(&include_file, options.embed_name);
|
||||
absl::StrAppendFormat(&out, kEmbedInclude, include_file);
|
||||
}
|
||||
|
||||
// If specified, wrap the generated API in a namespace
|
||||
if (options.has_namespace()) {
|
||||
absl::StrAppendFormat(&out, kNamespaceBeginTemplate,
|
||||
options.namespace_name);
|
||||
}
|
||||
|
||||
// Emit type dependencies
|
||||
// TODO(cblichmann): Coalesce namespaces
|
||||
std::string out_types = "// Types this API depends on\n";
|
||||
bool added_types = false;
|
||||
for (const clang::QualType& qual : types) {
|
||||
clang::TypeDecl* decl = nullptr;
|
||||
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
|
||||
decl = typedef_type->getDecl();
|
||||
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
decl = enum_type->getDecl();
|
||||
} else {
|
||||
decl = qual->getAsRecordDecl();
|
||||
}
|
||||
if (!decl) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::vector<std::string> ns_path = GetNamespacePath(decl);
|
||||
std::string nested_ns_name;
|
||||
if (!ns_path.empty()) {
|
||||
if (const auto& ns_root = ns_path.front();
|
||||
ns_root == "std" || ns_root == "sapi" || ns_root == "__gnu_cxx") {
|
||||
// Filter out any and all declarations from the C++ standard library,
|
||||
// from SAPI itself and from other well-known namespaces. This avoids
|
||||
// re-declaring things like standard integer types, for example.
|
||||
continue;
|
||||
}
|
||||
nested_ns_name = absl::StrCat(ns_path[0].empty() ? "" : " ",
|
||||
absl::StrJoin(ns_path, "::"));
|
||||
absl::StrAppend(&out_types, "namespace", nested_ns_name, " {\n");
|
||||
}
|
||||
absl::StrAppend(&out_types, PrintAstDecl(decl), ";");
|
||||
if (!ns_path.empty()) {
|
||||
absl::StrAppend(&out_types, "\n} // namespace", nested_ns_name);
|
||||
}
|
||||
absl::StrAppend(&out_types, "\n");
|
||||
added_types = true;
|
||||
}
|
||||
if (added_types) {
|
||||
absl::StrAppend(&out, out_types);
|
||||
}
|
||||
|
||||
// Optionally emit a default sandbox that instantiates an embedded sandboxee
|
||||
if (!options.embed_name.empty()) {
|
||||
// TODO(cblichmann): Make the "Sandbox" suffix configurable.
|
||||
absl::StrAppendFormat(
|
||||
&out, kEmbedClassTemplate, absl::StrCat(options.name, "Sandbox"),
|
||||
absl::StrReplaceAll(options.embed_name, {{"-", "_"}}));
|
||||
}
|
||||
|
||||
// Emit the actual Sandboxed API
|
||||
// TODO(cblichmann): Make the "Api" suffix configurable or at least optional.
|
||||
absl::StrAppendFormat(&out, kClassHeaderTemplate,
|
||||
absl::StrCat(options.name, "Api"));
|
||||
std::string out_func;
|
||||
for (const clang::FunctionDecl* decl : functions) {
|
||||
SAPI_ASSIGN_OR_RETURN(out_func, EmitFunction(decl));
|
||||
absl::StrAppend(&out, out_func);
|
||||
}
|
||||
absl::StrAppend(&out, kClassFooterTemplate);
|
||||
|
||||
// Close out the header: close namespace (if needed) and end include guard
|
||||
if (options.has_namespace()) {
|
||||
absl::StrAppendFormat(&out, kNamespaceEndTemplate, options.namespace_name);
|
||||
}
|
||||
absl::StrAppendFormat(&out, kHeaderEpilog, include_guard);
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace sapi
|
45
sandboxed_api/tools/clang_generator/emitter.h
Normal file
45
sandboxed_api/tools/clang_generator/emitter.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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_H_
|
||||
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "sandboxed_api/tools/clang_generator/generator.h"
|
||||
#include "sandboxed_api/tools/clang_generator/types.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
// Constructs an include guard name for the given filename. The name is of the
|
||||
// same for as the include guards in this project.
|
||||
// For example,
|
||||
// sandboxed_api/examples/zlib/zlib-sapi.sapi.h
|
||||
// will be mapped to
|
||||
// SANDBOXED_API_EXAMPLES_ZLIB_ZLIB_SAPI_SAPI_H_
|
||||
std::string GetIncludeGuard(absl::string_view filename);
|
||||
|
||||
// Outputs a formatted header for a list of functions and their related types.
|
||||
sapi::StatusOr<std::string> EmitHeader(
|
||||
std::vector<clang::FunctionDecl*> functions, const QualTypeSet& types,
|
||||
const GeneratorOptions& options);
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_EMITTER_H_
|
127
sandboxed_api/tools/clang_generator/generator.cc
Normal file
127
sandboxed_api/tools/clang_generator/generator.cc
Normal file
|
@ -0,0 +1,127 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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/generator.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "absl/status/status.h"
|
||||
#include "clang/Format/Format.h"
|
||||
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||
#include "sandboxed_api/tools/clang_generator/diagnostics.h"
|
||||
#include "sandboxed_api/tools/clang_generator/emitter.h"
|
||||
#include "sandboxed_api/util/status_macros.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
namespace sapi {
|
||||
namespace {
|
||||
|
||||
// Replaces the file extension of a path name.
|
||||
std::string ReplaceFileExtension(absl::string_view path,
|
||||
absl::string_view new_extension) {
|
||||
auto last_slash = path.rfind('/');
|
||||
auto pos = path.rfind('.', last_slash);
|
||||
if (pos != absl::string_view::npos && last_slash != absl::string_view::npos) {
|
||||
pos += last_slash;
|
||||
}
|
||||
return absl::StrCat(path.substr(0, pos), new_extension);
|
||||
}
|
||||
|
||||
std::string GetOutputFilename(absl::string_view source_file) {
|
||||
return ReplaceFileExtension(source_file, ".sapi.h");
|
||||
}
|
||||
|
||||
inline absl::string_view ToStringView(llvm::StringRef ref) {
|
||||
return absl::string_view(ref.data(), ref.size());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool GeneratorASTVisitor::VisitFunctionDecl(clang::FunctionDecl* decl) {
|
||||
if (!decl->isCXXClassMember() && // Skip classes
|
||||
decl->isExternC() && // Skip non external functions
|
||||
!decl->isTemplated() && // Skip function templates
|
||||
// Process either all function or just the requested ones
|
||||
(options_->function_names.empty() ||
|
||||
options_->function_names.count(ToStringView(decl->getName())) > 0)) {
|
||||
functions_.push_back(decl);
|
||||
GatherRelatedTypes(decl->getDeclaredReturnType(), &types_);
|
||||
for (const clang::ParmVarDecl* param : decl->parameters()) {
|
||||
GatherRelatedTypes(param->getType(), &types_);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
sapi::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
|
||||
const std::string& code) {
|
||||
// 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;
|
||||
|
||||
clang::tooling::Replacements replacements = clang::format::reformat(
|
||||
style, code, llvm::makeArrayRef(clang::tooling::Range(0, code.size())),
|
||||
filename);
|
||||
|
||||
llvm::Expected<std::string> formatted_header =
|
||||
clang::tooling::applyAllReplacements(code, replacements);
|
||||
if (!formatted_header) {
|
||||
return absl::InternalError(llvm::toString(formatted_header.takeError()));
|
||||
}
|
||||
return *formatted_header;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
absl::Status GeneratorASTConsumer::GenerateAndSaveHeader() {
|
||||
const std::string out_file =
|
||||
options_->out_file.empty() ? GetOutputFilename(in_file_)
|
||||
: sandbox2::file_util::fileops::MakeAbsolute(
|
||||
options_->out_file, options_->work_dir);
|
||||
|
||||
SAPI_ASSIGN_OR_RETURN(const std::string header,
|
||||
EmitHeader(visitor_.functions_, visitor_.types_, *options_));
|
||||
SAPI_ASSIGN_OR_RETURN(const std::string formatted_header,
|
||||
internal::ReformatGoogleStyle(in_file_, header));
|
||||
|
||||
std::ofstream os(out_file, std::ios::out | std::ios::trunc);
|
||||
os << formatted_header;
|
||||
if (!os) {
|
||||
return absl::UnknownError("I/O error");
|
||||
}
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
void GeneratorASTConsumer::HandleTranslationUnit(clang::ASTContext& context) {
|
||||
absl::Status status;
|
||||
if (!visitor_.TraverseDecl(context.getTranslationUnitDecl())) {
|
||||
status = absl::InternalError("AST traversal exited early");
|
||||
} else {
|
||||
status = GenerateAndSaveHeader();
|
||||
}
|
||||
|
||||
if (!status.ok()) {
|
||||
ReportFatalError(context.getDiagnostics(),
|
||||
GetDiagnosticLocationFromStatus(status).value_or(
|
||||
context.getTranslationUnitDecl()->getBeginLoc()),
|
||||
status.message());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sapi
|
123
sandboxed_api/tools/clang_generator/generator.h
Normal file
123
sandboxed_api/tools/clang_generator/generator.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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_GENERATOR_H_
|
||||
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_GENERATOR_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/status/status.h"
|
||||
#include "clang/AST/ASTConsumer.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendAction.h"
|
||||
#include "clang/Tooling/Tooling.h"
|
||||
#include "sandboxed_api/tools/clang_generator/types.h"
|
||||
#include "sandboxed_api/util/statusor.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
struct GeneratorOptions {
|
||||
template <typename ContainerT>
|
||||
GeneratorOptions& set_function_names(const ContainerT& value) {
|
||||
function_names.clear();
|
||||
function_names.insert(std::begin(value), std::end(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool has_namespace() const { return !namespace_name.empty(); }
|
||||
|
||||
absl::flat_hash_set<std::string> function_names;
|
||||
|
||||
// Output options
|
||||
std::string work_dir;
|
||||
std::string name; // Name of the Sandboxed API
|
||||
std::string namespace_name; // Namespace to wrap the SAPI in
|
||||
std::string out_file; // Output path of the generated header
|
||||
std::string embed_dir; // Directory with embedded includes
|
||||
std::string embed_name; // Identifier of the embed object
|
||||
};
|
||||
|
||||
class GeneratorASTVisitor
|
||||
: public clang::RecursiveASTVisitor<GeneratorASTVisitor> {
|
||||
public:
|
||||
bool VisitFunctionDecl(clang::FunctionDecl* decl);
|
||||
|
||||
private:
|
||||
friend class GeneratorASTConsumer;
|
||||
const GeneratorOptions* options_ = nullptr;
|
||||
|
||||
std::vector<clang::FunctionDecl*> functions_;
|
||||
QualTypeSet types_;
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
sapi::StatusOr<std::string> ReformatGoogleStyle(const std::string& filename,
|
||||
const std::string& code);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
class GeneratorASTConsumer : public clang::ASTConsumer {
|
||||
public:
|
||||
GeneratorASTConsumer(std::string in_file, const GeneratorOptions* options)
|
||||
: in_file_(std::move(in_file)), options_(options) {
|
||||
visitor_.options_ = options_;
|
||||
}
|
||||
|
||||
private:
|
||||
void HandleTranslationUnit(clang::ASTContext& context) override;
|
||||
|
||||
absl::Status GenerateAndSaveHeader();
|
||||
|
||||
std::string in_file_;
|
||||
const GeneratorOptions* options_;
|
||||
|
||||
GeneratorASTVisitor visitor_;
|
||||
};
|
||||
|
||||
class GeneratorAction : public clang::ASTFrontendAction {
|
||||
public:
|
||||
explicit GeneratorAction(const GeneratorOptions* options)
|
||||
: options_(options) {}
|
||||
|
||||
private:
|
||||
std::unique_ptr<clang::ASTConsumer> CreateASTConsumer(
|
||||
clang::CompilerInstance&, llvm::StringRef in_file) override {
|
||||
return absl::make_unique<GeneratorASTConsumer>(std::string(in_file),
|
||||
options_);
|
||||
}
|
||||
|
||||
bool hasCodeCompletionSupport() const override { return false; }
|
||||
|
||||
const GeneratorOptions* options_;
|
||||
};
|
||||
|
||||
class GeneratorFactory : public clang::tooling::FrontendActionFactory {
|
||||
public:
|
||||
explicit GeneratorFactory(GeneratorOptions options = {})
|
||||
: options_(std::move(options)) {}
|
||||
|
||||
private:
|
||||
clang::FrontendAction* create() override {
|
||||
return new GeneratorAction(&options_);
|
||||
}
|
||||
|
||||
GeneratorOptions options_;
|
||||
};
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_GENERATOR_H_
|
106
sandboxed_api/tools/clang_generator/generator_tool.cc
Normal file
106
sandboxed_api/tools/clang_generator/generator_tool.cc
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/Frontend/FrontendActions.h"
|
||||
#include "clang/Tooling/CommonOptionsParser.h"
|
||||
#include "llvm/Support/CommandLine.h"
|
||||
#include "sandboxed_api/sandbox2/util/fileops.h"
|
||||
#include "sandboxed_api/tools/clang_generator/generator.h"
|
||||
|
||||
namespace sapi {
|
||||
namespace {
|
||||
|
||||
static auto* g_tool_category =
|
||||
new llvm::cl::OptionCategory("Sandboxed API Options");
|
||||
|
||||
static auto* g_common_help =
|
||||
new llvm::cl::extrahelp(clang::tooling::CommonOptionsParser::HelpMessage);
|
||||
static auto* g_extra_help = new llvm::cl::extrahelp(
|
||||
"Full documentation at: <https://developers.google.com/sandboxed-api/>\n"
|
||||
"Report bugs to <https://github.com/google/sandboxed-api/issues>\n");
|
||||
|
||||
// Command line options
|
||||
static auto* g_sapi_embed_dir = new llvm::cl::opt<std::string>(
|
||||
"sapi_embed_dir", llvm::cl::desc("Directory with embedded includes"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_embed_name = new llvm::cl::opt<std::string>(
|
||||
"sapi_embed_name", llvm::cl::desc("Identifier of the embed object"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_functions = new llvm::cl::list<std::string>(
|
||||
"sapi_functions", llvm::cl::CommaSeparated,
|
||||
llvm::cl::desc("List of functions to generate a Sandboxed API for. If "
|
||||
"empty, generates a SAPI for all functions found."),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_in = new llvm::cl::list<std::string>(
|
||||
"sapi_in", llvm::cl::CommaSeparated,
|
||||
llvm::cl::desc("List of input files to analyze. Deprecated, use positional "
|
||||
"arguments instead."),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_isystem = new llvm::cl::opt<std::string>(
|
||||
"sapi_isystem", llvm::cl::desc("Extra system include paths"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_limit_scan_depth = new llvm::cl::opt<bool>(
|
||||
"sapi_limit_scan_depth",
|
||||
llvm::cl::desc(
|
||||
"Whether to only scan for functions in the top-most translation unit"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_name = new llvm::cl::opt<std::string>(
|
||||
"sapi_name", llvm::cl::desc("Name of the Sandboxed API library"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_ns = new llvm::cl::opt<std::string>(
|
||||
"sapi_ns", llvm::cl::desc("C++ namespace to wrap Sandboxed API class in"),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
static auto* g_sapi_out = new llvm::cl::opt<std::string>(
|
||||
"sapi_out",
|
||||
llvm::cl::desc(
|
||||
"Ouput path of the generated header. If empty, simply appends .sapi.h "
|
||||
"to the basename of the first source file specified."),
|
||||
llvm::cl::cat(*g_tool_category));
|
||||
|
||||
} // namespace
|
||||
|
||||
GeneratorOptions GeneratorOptionsFromFlags() {
|
||||
GeneratorOptions options;
|
||||
options.function_names.insert(g_sapi_functions->begin(),
|
||||
g_sapi_functions->end());
|
||||
options.work_dir = sandbox2::file_util::fileops::GetCWD();
|
||||
options.name = *g_sapi_name;
|
||||
options.namespace_name = *g_sapi_ns;
|
||||
options.out_file = *g_sapi_out;
|
||||
options.embed_dir = *g_sapi_embed_dir;
|
||||
options.embed_name = *g_sapi_embed_name;
|
||||
return options;
|
||||
}
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
clang::tooling::CommonOptionsParser opt_parser(
|
||||
argc, argv, *sapi::g_tool_category, llvm::cl::ZeroOrMore,
|
||||
"Generates a Sandboxed API header for C/C++ translation units.");
|
||||
std::vector<std::string> sources = opt_parser.getSourcePathList();
|
||||
for (const auto& sapi_in : *sapi::g_sapi_in) {
|
||||
sources.push_back(sapi_in);
|
||||
}
|
||||
|
||||
clang::tooling::ClangTool tool(opt_parser.getCompilations(), sources);
|
||||
return tool.run(absl::make_unique<sapi::GeneratorFactory>(
|
||||
sapi::GeneratorOptionsFromFlags())
|
||||
.get());
|
||||
}
|
203
sandboxed_api/tools/clang_generator/types.cc
Normal file
203
sandboxed_api/tools/clang_generator/types.cc
Normal file
|
@ -0,0 +1,203 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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/types.h"
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types) {
|
||||
if (const auto* typedef_type = qual->getAs<clang::TypedefType>()) {
|
||||
GatherRelatedTypes(typedef_type->getDecl()->getUnderlyingType(), types);
|
||||
types->insert(qual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (qual->isFunctionPointerType() || qual->isFunctionReferenceType() ||
|
||||
qual->isMemberFunctionPointerType()) {
|
||||
if (const auto* function_type = qual->getPointeeOrArrayElementType()
|
||||
->getAs<clang::FunctionProtoType>()) {
|
||||
// Note: Do not add the function type itself, as this will always be a
|
||||
// pointer argument. We only need to collect all its related types.
|
||||
GatherRelatedTypes(function_type->getReturnType(), types);
|
||||
for (const clang::QualType& param : function_type->getParamTypes()) {
|
||||
GatherRelatedTypes(param, types);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPointerOrReference(qual)) {
|
||||
clang::QualType pointee = qual->getPointeeType();
|
||||
while (IsPointerOrReference(pointee)) {
|
||||
pointee = pointee->getPointeeType();
|
||||
}
|
||||
GatherRelatedTypes(pointee, types);
|
||||
return;
|
||||
}
|
||||
|
||||
// C array with specified constant size (i.e. int a[42])?
|
||||
if (const clang::ArrayType* array_type = qual->getAsArrayTypeUnsafe()) {
|
||||
GatherRelatedTypes(array_type->getElementType(), types);
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsSimple(qual) || qual->isEnumeralType()) {
|
||||
if (const clang::EnumType* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
// Collect the underlying integer type of enum classes as well, as it may
|
||||
// be a typedef.
|
||||
if (const clang::EnumDecl* decl = enum_type->getDecl(); decl->isFixed()) {
|
||||
GatherRelatedTypes(decl->getIntegerType(), types);
|
||||
}
|
||||
}
|
||||
types->insert(qual);
|
||||
return;
|
||||
}
|
||||
|
||||
if (const auto* record_type = qual->getAs<clang::RecordType>()) {
|
||||
const clang::RecordDecl* decl = record_type->getDecl();
|
||||
for (const clang::FieldDecl* field : decl->fields()) {
|
||||
GatherRelatedTypes(field->getType(), types);
|
||||
}
|
||||
types->insert(qual);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Removes "const" from a qualified type if it denotes a pointer or reference
|
||||
// type.
|
||||
clang::QualType MaybeRemoveConst(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (IsPointerOrReference(qual)) {
|
||||
clang::QualType pointee_qual = qual->getPointeeType();
|
||||
pointee_qual.removeLocalConst();
|
||||
qual = context.getPointerType(pointee_qual);
|
||||
}
|
||||
return qual;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string MapQualType(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (const auto* builtin = qual->getAs<clang::BuiltinType>()) {
|
||||
switch (builtin->getKind()) {
|
||||
case clang::BuiltinType::Void:
|
||||
case clang::BuiltinType::NullPtr:
|
||||
return "::sapi::v::Void";
|
||||
|
||||
/*
|
||||
* Unsigned types
|
||||
*/
|
||||
case clang::BuiltinType::Bool:
|
||||
return "::sapi::v::Bool";
|
||||
// Unsigned character types
|
||||
case clang::BuiltinType::Char_U:
|
||||
case clang::BuiltinType::UChar:
|
||||
return "::sapi::v::UChar";
|
||||
case clang::BuiltinType::WChar_U:
|
||||
return "::sapi::v::ULong"; // 32-bit, correct for Linux and UTF-32
|
||||
// Added in C++20
|
||||
case clang::BuiltinType::Char8: // Underlying type: unsigned char
|
||||
return "::sapi::v::UChar";
|
||||
case clang::BuiltinType::Char16: // Underlying type: uint_least16_t
|
||||
return "::sapi::v::UShort";
|
||||
case clang::BuiltinType::Char32: // Underlying type: uint_least32_t
|
||||
return "::sapi::v::ULong";
|
||||
// Standard unsigned types
|
||||
case clang::BuiltinType::UShort:
|
||||
return "::sapi::v::UShort";
|
||||
case clang::BuiltinType::UInt:
|
||||
return "::sapi::v::UInt";
|
||||
case clang::BuiltinType::ULong:
|
||||
return "::sapi::v::ULong";
|
||||
case clang::BuiltinType::ULongLong:
|
||||
return "::sapi::v::ULLong";
|
||||
// TODO(cblichmann): Add 128-bit integer support
|
||||
// case clang::BuiltinType::UInt128:
|
||||
// return "::sapi::v::UInt128";
|
||||
|
||||
/*
|
||||
* Signed types
|
||||
*/
|
||||
// Signed character types
|
||||
case clang::BuiltinType::Char_S:
|
||||
case clang::BuiltinType::SChar:
|
||||
return "::sapi::v::Char";
|
||||
case clang::BuiltinType::WChar_S:
|
||||
return "::sapi::v::Long"; // 32-bit, correct for Linux and UTF-32
|
||||
|
||||
// Standard signed types
|
||||
case clang::BuiltinType::Short:
|
||||
return "::sapi::v::Short";
|
||||
case clang::BuiltinType::Int:
|
||||
return "::sapi::v::Int";
|
||||
case clang::BuiltinType::Long:
|
||||
return "::sapi::v::Long";
|
||||
case clang::BuiltinType::LongLong:
|
||||
return "::sapi::v::LLong";
|
||||
// TODO(cblichmann): Add 128-bit integer support
|
||||
// case clang::BuiltinType::Int128:
|
||||
// return "::sapi::v::Int128";
|
||||
|
||||
/*
|
||||
* Floating-point types
|
||||
*/
|
||||
// TODO(cblichmann): Map half/__fp16, _Float16 and __float128 types
|
||||
case clang::BuiltinType::Float:
|
||||
return "::sapi::v::Reg<float>";
|
||||
case clang::BuiltinType::Double:
|
||||
return "::sapi::v::Reg<double>";
|
||||
case clang::BuiltinType::LongDouble:
|
||||
return "::sapi::v::Reg<long double>";
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (const auto* enum_type = qual->getAs<clang::EnumType>()) {
|
||||
return absl::StrCat("::sapi::v::IntBase<",
|
||||
enum_type->getDecl()->getQualifiedNameAsString(), ">");
|
||||
} else if (IsPointerOrReference(qual)) {
|
||||
// Remove "const" qualifier from a pointer or reference type's pointee, as
|
||||
// e.g. const pointers do not work well with SAPI.
|
||||
return absl::StrCat("::sapi::v::Reg<",
|
||||
MaybeRemoveConst(context, qual).getAsString(), ">");
|
||||
}
|
||||
// Best-effort mapping to "int", leave a comment.
|
||||
return absl::StrCat("::sapi::v::Int /* aka '", qual.getAsString(), "' */");
|
||||
}
|
||||
|
||||
std::string MapQualTypeParameter(const clang::ASTContext& /*context*/,
|
||||
clang::QualType qual) {
|
||||
// TODO(cblichmann): Define additional mappings, as appropriate
|
||||
// _Bool -> bool
|
||||
// unsigned long long -> uint64_t (where applicable)
|
||||
// ...
|
||||
return IsPointerOrReference(qual) ? "::sapi::v::Ptr*" : qual.getAsString();
|
||||
}
|
||||
|
||||
std::string MapQualTypeReturn(const clang::ASTContext& context,
|
||||
clang::QualType qual) {
|
||||
if (qual->isVoidType()) {
|
||||
return "absl::Status";
|
||||
}
|
||||
// Remove const qualifier like in MapQualType().
|
||||
return absl::StrCat("::sapi::StatusOr<",
|
||||
MaybeRemoveConst(context, qual).getAsString(), ">");
|
||||
}
|
||||
|
||||
} // namespace sapi
|
78
sandboxed_api/tools/clang_generator/types.h
Normal file
78
sandboxed_api/tools/clang_generator/types.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2020 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
|
||||
//
|
||||
// http://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_TYPES_H_
|
||||
#define SANDBOXED_API_TOOLS_CLANG_GENERATOR_TYPES_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Type.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
|
||||
namespace sapi {
|
||||
|
||||
using QualTypeSet =
|
||||
llvm::SetVector<clang::QualType, std::vector<clang::QualType>,
|
||||
llvm::SmallPtrSet<clang::QualType, 8>>;
|
||||
|
||||
// Returns whether a type is "simple". Simple types are arithemtic types,
|
||||
// i.e. signed and unsigned integer, character and bool types, as well as
|
||||
// "void".
|
||||
inline bool IsSimple(clang::QualType qual) {
|
||||
return qual->isArithmeticType() || qual->isVoidType();
|
||||
}
|
||||
|
||||
inline bool IsPointerOrReference(clang::QualType qual) {
|
||||
return qual->isPointerType() || qual->isMemberPointerType() ||
|
||||
qual->isLValueReferenceType() || qual->isRValueReferenceType();
|
||||
}
|
||||
|
||||
// Computes the transitive closure of all types that a type depends on. Those
|
||||
// are types that need to be declared before a declaration of the type denoted
|
||||
// by the qual parameter is valid. For example, given
|
||||
// struct SubStruct { bool truth_value; };
|
||||
// struct AggregateStruct {
|
||||
// int int_member;
|
||||
// SubStruct struct_member;
|
||||
// };
|
||||
//
|
||||
// calling this function on the type "AggregateStruct" yields these types:
|
||||
// int
|
||||
// SubStruct
|
||||
// bool
|
||||
void GatherRelatedTypes(clang::QualType qual, QualTypeSet* types);
|
||||
|
||||
// Maps a qualified type to a fully qualified SAPI-compatible type name. This
|
||||
// is used for the generated code that invokes the actual function call RPC.
|
||||
// If no mapping can be found, "int" is assumed.
|
||||
std::string MapQualType(const clang::ASTContext& context, clang::QualType qual);
|
||||
|
||||
// Maps a qualified type used as a function parameter to a type name compatible
|
||||
// with the generated Sandboxed API.
|
||||
std::string MapQualTypeParameter(const clang::ASTContext& context,
|
||||
clang::QualType qual);
|
||||
|
||||
// Maps a qualified type used as a function return type to a type name
|
||||
// compatible with the generated Sandboxed API. Uses MapQualTypeParameter() and
|
||||
// wraps the type in a sapi::StatusOr<> if qual is non-void. Otherwise returns
|
||||
// absl::Status.
|
||||
std::string MapQualTypeReturn(const clang::ASTContext& context,
|
||||
clang::QualType qual);
|
||||
|
||||
} // namespace sapi
|
||||
|
||||
#endif // SANDBOXED_API_TOOLS_CLANG_GENERATOR_TYPES_H_
|
Loading…
Reference in New Issue
Block a user