From a56b84245e90ca480dc93450b41387de9b1523d3 Mon Sep 17 00:00:00 2001 From: Jonathan Lifflander Date: Wed, 9 Dec 2020 17:42:27 -0800 Subject: [PATCH] #4: sanitizer: major rewrite and cleanup --- src/sanitizer/common.h | 50 ++++++ src/sanitizer/generator.cc | 220 +++++++++++++++++++++++++ src/sanitizer/generator.h | 136 +++++++++++++++ src/sanitizer/member_list.h | 75 +++++++++ src/sanitizer/qualified_name.h | 2 + src/sanitizer/sanitizer.cc | 292 +++------------------------------ src/sanitizer/walk_record.cc | 195 ++++++++++++++++++++++ src/sanitizer/walk_record.h | 85 ++++++++++ 8 files changed, 783 insertions(+), 272 deletions(-) create mode 100644 src/sanitizer/common.h create mode 100644 src/sanitizer/generator.cc create mode 100644 src/sanitizer/generator.h create mode 100644 src/sanitizer/member_list.h create mode 100644 src/sanitizer/walk_record.cc create mode 100644 src/sanitizer/walk_record.h diff --git a/src/sanitizer/common.h b/src/sanitizer/common.h new file mode 100644 index 0000000..3478702 --- /dev/null +++ b/src/sanitizer/common.h @@ -0,0 +1,50 @@ +/* +//@HEADER +// ***************************************************************************** +// +// common.h +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SANITIZER_COMMON_H +#define INCLUDED_SANITIZER_COMMON_H + +#define SANITIZER_DEBUG 0 + +#endif /*INCLUDED_SANITIZER_COMMON_H*/ diff --git a/src/sanitizer/generator.cc b/src/sanitizer/generator.cc new file mode 100644 index 0000000..860a981 --- /dev/null +++ b/src/sanitizer/generator.cc @@ -0,0 +1,220 @@ +/* +//@HEADER +// ***************************************************************************** +// +// generator.cc +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#include "common.h" +#include "generator.h" + +#include "qualified_name.h" + +#include + +#include +#include + +namespace sanitizer { + +void InlineGenerator::run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members +) { + // No members to generate + if (members.size() == 0) { + return; + } + + // If the class is lacking a serialize body, code can't be generated + if (not fn->hasBody()) { + fmt::print( + stderr, + "{}: {} members exist, but no serialize body found!\n", + rd->getQualifiedNameAsString(), members.size() + ); + return; + } + + #if SANITIZER_DEBUG + fmt::print("Inserting checks for {}\n", rd->getQualifiedNameAsString()); + #endif + + auto body = fn->getBody(); + auto start = body->getLocEnd(); + rw_.InsertText(start, " /* begin generated sanitizer code */\n", true, true); + for (auto&& m : members) { + auto str = fmt::format(" s.check({}, \"{}\");\n", m.unqual(), m.qual()); + rw_.InsertText(start, str, true, true); + } + rw_.InsertText(start, " /* end generated sanitizer code */\n", true, true); +} + +static constexpr char const* sanitizer = "checkpoint::serializers::Sanitizer"; +static constexpr char const* begin = "{"; +static constexpr char const* end = "}"; + +void PartialSpecializationGenerator::run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members +) { + #if SANITIZER_DEBUG + fmt::print("Gen specialization for {}\n", rd->getQualifiedNameAsString()); + #endif + + using clang::TemplateSpecializationKind; + + TemplateSpecializationKind kind = rd->getTemplateSpecializationKind(); + + if (kind == TemplateSpecializationKind::TSK_Undeclared) { + auto qual_name = rd->getQualifiedNameAsString(); + + fmt::print(out_, "template <>\n"); + fmt::print( + out_, "void {}::serialize<{}>({}& s) {}\n", + qual_name, sanitizer, sanitizer, begin + ); + for (auto&& m : members) { + fmt::print(out_, " s.check({}, \"{}\");\n", m.unqual(), m.qual()); + } + fmt::print(out_, "{}\n", end); + } else if (kind == TemplateSpecializationKind::TSK_ImplicitInstantiation) { + if (clang::isa(rd)) { + auto ctsd = clang::cast(rd); + + clang::PrintingPolicy policy(ctsd->getASTContext().getPrintingPolicy()); + policy.SuppressScope = false; + policy.AnonymousTagLocations = false; + policy.PolishForDeclaration = true; + policy.SuppressUnwrittenScope = true; + + auto qt = clang::TypeName2::getFullyQualifiedType( + clang::QualType(ctsd->getTypeForDecl(),0), ctsd->getASTContext(), false + ); + + std::string qualified_type_outer = qt.getAsString(policy); + + fmt::print(out_, "template <>\n"); + fmt::print(out_, "template <>\n"); + fmt::print( + out_, "void {}::serialize<{}>({}& s) {}\n", qualified_type_outer, + sanitizer, sanitizer, begin + ); + for (auto&& m : members) { + fmt::print(out_," s.check({}, \"{}\");\n", m.unqual(), m.qual()); + } + fmt::print(out_,"{}\n", end); + } + } +} + +void SeperateGenerator::run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members +) { + std::list templates_decl; + std::list templates_def; + + auto cls_templ = rd->getDescribedClassTemplate(); + if (cls_templ) { + auto tparams = cls_templ->getTemplateParameters(); + for (auto iter = tparams->begin(); iter != tparams->end(); ++iter) { + auto tparam = *iter; + if (clang::isa(tparam)) { + auto type_param = clang::cast(tparam); + auto pack_str = type_param->isParameterPack() ? "..." : ""; + fmt::print("TYPE TPARAM: {}\n", tparam->getNameAsString()); + templates_decl.push_back(std::string("typename ") + pack_str + type_param->getName().str()); + templates_def.push_back(type_param->getName().str()); + tparam->dump(); + } else if (clang::isa(tparam)) { + auto non_type_param = clang::cast(tparam); + + auto qualified_type = clang::TypeName2::getFullyQualifiedName( + non_type_param->getType(), + non_type_param->getASTContext(), + true + ); + + templates_decl.push_back( + qualified_type + " " + non_type_param->getName().str() + ); + templates_def.push_back(non_type_param->getName().str()); + tparam->dump(); + } + + } + } + + //fmt::print("TEMPLATE: {}\n", template_str) + + std::string template_decl_context = ""; + for (auto&& elm : templates_decl) { + template_decl_context += ", " + elm; + } + + std::string template_def_context = ""; + if (templates_def.size() > 0) { + template_def_context += "<"; + std::size_t cur = 0; + for (auto&& elm : templates_def) { + template_def_context += elm; + if (cur != templates_def.size() - 1) { + template_def_context += ","; + } + cur++; + } + template_def_context += ">"; + } + + auto qual_name = rd->getQualifiedNameAsString(); + + fmt::print("template \n", template_decl_context); + fmt::print( + "void serializeCheck(SerializerT& s, {}{}& obj) {\n", + qual_name, template_def_context + ); + for (auto&& m : members) { + fmt::print("\ts.check(obj.{}, \"{}\");\n", m.unqual(), m.qual()); + } + fmt::print("}\n\n"); +} + +} /* end namespace sanitizer */ diff --git a/src/sanitizer/generator.h b/src/sanitizer/generator.h new file mode 100644 index 0000000..ed1ba4d --- /dev/null +++ b/src/sanitizer/generator.h @@ -0,0 +1,136 @@ +/* +//@HEADER +// ***************************************************************************** +// +// generator.h +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SANITIZER_GENERATOR_H +#define INCLUDED_SANITIZER_GENERATOR_H + +#include "common.h" +#include "member_list.h" + +#include "clang/AST/ExprCXX.h" +#include "clang/Rewrite/Core/Rewriter.h" + +namespace sanitizer { + +/** + * \struct Generator + * + * \brief Abstract code generator for sanitizer + */ +struct Generator { + + virtual ~Generator() = default; + + /** + * \brief Run the generator on a specific class with the fields + * + * \param[in] rd the class + * \param[in] fn the serialize method + * \param[in] members the list of fields in the class + */ + virtual void run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members + ) = 0; + +}; + +/** + * \struct InlineGenerator + * + * \brief Generates checks in the serialize method directly. Requires + * modification of source files. + */ +struct InlineGenerator : Generator { + + explicit InlineGenerator(clang::Rewriter& in_rw) + : rw_(in_rw) + { } + + void run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members + ) override; + +private: + clang::Rewriter& rw_; +}; + +/** + * \struct PartialSpecializationGenerator + * + * \brief Generates checks in a partial specialization of the serialize method. + */ +struct PartialSpecializationGenerator : Generator { + + explicit PartialSpecializationGenerator(FILE* in_out) + : out_(in_out) + { } + + void run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members + ) override; + +private: + FILE* out_ = nullptr; +}; + +/** + * \struct SeperateGenerator + * + * \brief Generates checks as a completely separate method (experimental). + */ +struct SeperateGenerator : Generator { + + void run( + clang::CXXRecordDecl const* rd, clang::FunctionDecl* fn, + MemberListType members + ) override; + +}; + +} /* end namespace sanitizer */ + +#endif /*INCLUDED_SANITIZER_GENERATOR_H*/ diff --git a/src/sanitizer/member_list.h b/src/sanitizer/member_list.h new file mode 100644 index 0000000..ce060b4 --- /dev/null +++ b/src/sanitizer/member_list.h @@ -0,0 +1,75 @@ +/* +//@HEADER +// ***************************************************************************** +// +// member_list.h +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SANITIZER_MEMBER_LIST_H +#define INCLUDED_SANITIZER_MEMBER_LIST_H + +#include +#include + +namespace sanitizer { + +struct Member { + + Member( + std::string const& in_unqualified_member, + std::string const& in_qualified_member + ) : unqualified_member_(in_unqualified_member), + qualified_member_(in_qualified_member) + { } + + std::string const& unqual() const { return unqualified_member_; } + + std::string const& qual() const { return qualified_member_; } + +private: + std::string unqualified_member_ = ""; + std::string qualified_member_ = ""; +}; + +using MemberListType = std::vector; + +} /* end namespace sanitizer */ + +#endif /*INCLUDED_SANITIZER_MEMBER_LIST_H*/ diff --git a/src/sanitizer/qualified_name.h b/src/sanitizer/qualified_name.h index c6ce5b4..01a3ab0 100644 --- a/src/sanitizer/qualified_name.h +++ b/src/sanitizer/qualified_name.h @@ -45,6 +45,8 @@ #if !defined INCLUDED_SANITIZER_QUALIFIED_NAME_H #define INCLUDED_SANITIZER_QUALIFIED_NAME_H +#include "clang/ASTMatchers/ASTMatchers.h" + namespace clang { namespace TypeName2 { diff --git a/src/sanitizer/sanitizer.cc b/src/sanitizer/sanitizer.cc index 305d301..0be9de5 100644 --- a/src/sanitizer/sanitizer.cc +++ b/src/sanitizer/sanitizer.cc @@ -54,301 +54,48 @@ #include "clang/Rewrite/Core/Rewriter.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/AST/ExprCXX.h" #include -#include -#include -#include #include -#include "qualified_name.h" +#include "generator.h" +#include "walk_record.h" using namespace clang; +using namespace llvm; using namespace clang::tooling; using namespace clang::ast_matchers; -using namespace llvm; static FILE* out = nullptr; static cl::opt Filename("o", cl::desc("Filename to output generated code")); - static cl::list Includes("I", cl::desc("Include directories"), cl::ZeroOrMore); - static cl::opt GenerateInline("inline", cl::desc("Generate code inline and modify files")); - static cl::opt OutputMainFile("main", cl::desc("Output main file with generated code")); - static cl::opt IncludeVTHeader("Ivt", cl::desc("Include VT headers in generated code")); DeclarationMatcher RecordMatcher = cxxRecordDecl().bind("recordDecl"); -static constexpr char const* sanitizer = "checkpoint::serializers::Sanitizer"; - struct ClassFuncDeclRewriter : MatchFinder::MatchCallback { - using MemberListType = std::list>; - using CXXDeclType = CXXRecordDecl const*; - explicit ClassFuncDeclRewriter(Rewriter& in_rw) : rw(in_rw) { } - virtual void run(const MatchFinder::MatchResult &Result) { - if (CXXRecordDecl const *rd = Result.Nodes.getNodeAs("recordDecl")) { - // fmt::print("Traversing class {}\n", rd->getQualifiedNameAsString()); - - // We are in a template instantiation--skip! - if (GenerateInline) { - if (rd->getTemplateInstantiationPattern() != nullptr) { - return; - } - } else { - // Skip template classes, walk instantiations instead - if (rd->getDescribedClassTemplate()) { - return; - } - } - - //rd->dump(); - - FunctionDecl* fndecl = nullptr; - bool has_serialize = false; - std::unordered_set existing_checks_; - - // Go through the declarations for this struct - for (auto&& m : rd->decls()) { - //fmt::print("DECL {}\n", m->getDeclKindName()); - - // Look for template decls, named serialize, with exactly one parameter - // (intrusive) serialize - if (m->isTemplateDecl()) { - auto cur_fndecl = m->getAsFunction(); - if (cur_fndecl) { - if (cur_fndecl->getNameAsString() == "serialize" and cur_fndecl->param_size() == 1) { - // Save this function decl so we can insert code later - fndecl = cur_fndecl; - has_serialize = true; - - // If we have a body the serialize member function, walk it - if (fndecl->hasBody()) { - auto body = fndecl->getBody(); - for (auto&& child : body->children()) { - // Look for CallExpr - if (isa(child)) { - auto ce = cast(child); - if (ce && ce->getCallee()->isTypeDependent() and ce->getNumArgs() == 2) { - auto expr_iter = ce->child_begin(); - if (expr_iter != ce->child_end()) { - if (isa(*expr_iter)) { - auto cxx = cast(*expr_iter); - if (cxx->getMemberNameInfo().getName().getAsString() == "check") { - //ce->dump(); - expr_iter++; - - // Store a list of checks that already exist to - // compare to members to see if the existing checks - // are valid! - if (isa(*expr_iter)) { - auto member = cast(*expr_iter); - existing_checks_.insert( - member->getMemberDecl()->getNameAsString() - ); - } - } - } - } - } - } - } - } - } - } - } - } - - if (not has_serialize) { - return; - } - - MemberListType members_to_serialize; - - auto record = rd->getQualifiedNameAsString(); - //fmt::print("Finding fields for CXX record {}\n", record); - for (auto&& f : rd->fields()) { - auto member = f->getQualifiedNameAsString(); - auto unqualified_member = f->getNameAsString(); - - auto existing_iter = existing_checks_.find(unqualified_member); - if (existing_iter == existing_checks_.end()) { - //fmt::print("{}: {}\n", record, member); - //f->dumpColor(); - members_to_serialize.push_back(std::make_tuple(unqualified_member, member)); - } - } - - if (GenerateInline) { - if (not fndecl->hasBody() and members_to_serialize.size() > 0) { - fmt::print( - stderr, - "{}: {} members exist, but no serialize body found!\n", - record, members_to_serialize.size() - ); - return; - } - } - - if (members_to_serialize.size() > 0) { - if (GenerateInline) { - generateChecksInline(rd, fndecl, members_to_serialize); - } else { - generateChecksSeperateInstance(rd, fndecl, members_to_serialize); - } - } - } - } + virtual void run(MatchFinder::MatchResult const& result) { + std::unique_ptr gen = nullptr; - void generateChecksInline( - CXXDeclType rd, FunctionDecl* fn, MemberListType const& members - ) { - if (fn->hasBody()) { - auto record = rd->getQualifiedNameAsString(); - fmt::print("Inserting new checks for: %s\n", record.c_str()); - auto body = fn->getBody(); - - auto start = body->getLocEnd(); - rw.InsertText(start, " /* begin generated serialize check code */\n", true, true); - for (auto&& elm : members) { - rw.InsertText( - start, - " s.check(" + std::get<0>(elm) + "," "\"" + std::get<1>(elm) + "\"" ");\n", - true, true - ); - } - rw.InsertText(start, " /* end generated serialize check code */\n", true, true); + if (GenerateInline) { + gen = std::make_unique(rw); + } else { + gen = std::make_unique(out); } - } - void generateChecksSeperateInstance( - CXXDeclType rd, FunctionDecl* fn, MemberListType const& members - ) { - //rd->dump(); - //fmt::print("{}: SPECIALIZATION: {}\n", rd->getQualifiedNameAsString(), rd->getTemplateSpecializationKind()); - - TemplateSpecializationKind kind = rd->getTemplateSpecializationKind(); - - if (kind == TemplateSpecializationKind::TSK_Undeclared) { - auto qual_name = rd->getQualifiedNameAsString(); - - fmt::print(out,"template <>\n"); - fmt::print(out,"void {}::serialize<{}>({}& s) {\n", qual_name, sanitizer, sanitizer); - for (auto&& m : members) { - fmt::print(out,"\ts.check({}, \"{}\");\n", std::get<0>(m), std::get<1>(m)); - } - fmt::print(out,"}\n"); - } else if (kind == TemplateSpecializationKind::TSK_ImplicitInstantiation) { - if (isa(rd)) { - auto ctsd = cast(rd); - - PrintingPolicy policy(ctsd->getASTContext().getPrintingPolicy()); - policy.SuppressScope = false; - policy.AnonymousTagLocations = false; - policy.PolishForDeclaration = true; - policy.SuppressUnwrittenScope = true; - - auto qt = clang::TypeName2::getFullyQualifiedType( - QualType(ctsd->getTypeForDecl(),0), ctsd->getASTContext(), false - ); - - std::string qualified_type_outer = qt.getAsString(policy); - - fmt::print(out,"template <>\n"); - fmt::print(out,"template <>\n"); - fmt::print( - out, "void {}::serialize<{}>({}& s) {\n", qualified_type_outer, - sanitizer, sanitizer - ); - for (auto&& m : members) { - fmt::print(out,"\ts.check({}, \"{}\");\n", std::get<0>(m), std::get<1>(m)); - } - fmt::print(out,"}\n"); - } - } - } - - void generateChecksSeperate( - CXXDeclType rd, FunctionDecl* fn, MemberListType const& members - ) { - std::list templates_decl; - std::list templates_def; - - auto cls_templ = rd->getDescribedClassTemplate(); - if (cls_templ) { - auto tparams = cls_templ->getTemplateParameters(); - for (auto iter = tparams->begin(); iter != tparams->end(); ++iter) { - auto tparam = *iter; - if (isa(tparam)) { - auto type_param = cast(tparam); - auto pack_str = type_param->isParameterPack() ? "..." : ""; - fmt::print("TYPE TPARAM: {}\n", tparam->getNameAsString()); - templates_decl.push_back(std::string("typename ") + pack_str + type_param->getName().str()); - templates_def.push_back(type_param->getName().str()); - tparam->dump(); - } else if (isa(tparam)) { - auto non_type_param = cast(tparam); - - auto qualified_type = clang::TypeName2::getFullyQualifiedName( - non_type_param->getType(), - non_type_param->getASTContext(), - true - ); - - templates_decl.push_back( - qualified_type + " " + non_type_param->getName().str() - ); - templates_def.push_back(non_type_param->getName().str()); - tparam->dump(); - } - - } - } - - //fmt::print("TEMPLATE: {}\n", template_str) - - std::string template_decl_context = ""; - for (auto&& elm : templates_decl) { - template_decl_context += ", " + elm; - } - - std::string template_def_context = ""; - if (templates_def.size() > 0) { - template_def_context += "<"; - std::size_t cur = 0; - for (auto&& elm : templates_def) { - template_def_context += elm; - if (cur != templates_def.size() - 1) { - template_def_context += ","; - } - cur++; - } - template_def_context += ">"; - } - - //rd->dumpColor(); - - auto qual_name = rd->getQualifiedNameAsString(); - - fmt::print("template \n", template_decl_context); - fmt::print( - "void serializeCheck(SerializerT& s, {}{}& obj) {\n", - qual_name, template_def_context - ); - for (auto&& m : members) { - fmt::print("\ts.check(obj.{}, \"{}\");\n", std::get<0>(m), std::get<1>(m)); - } - fmt::print("}\n\n"); + auto t = std::make_unique(GenerateInline, std::move(gen)); + t->walk(result); } +private: Rewriter& rw; }; @@ -376,8 +123,8 @@ struct MyFrontendAction : ASTFrontendAction { auto& sm = rw_.getSourceMgr(); for (auto iter = rw_.buffer_begin(); iter != rw_.buffer_end(); ++iter) { fmt::print( - stderr, "Modified file %s\n", - sm.getFileEntryForID(iter->first)->getName().str().c_str() + stderr, "Modified file {}\n", + sm.getFileEntryForID(iter->first)->getName().str() ); } @@ -402,7 +149,7 @@ struct MyFrontendAction : ASTFrontendAction { // Apply a custom category to all command-line options so that they are the // only ones displayed. -static cl::OptionCategory SerializeCheckerCategory("Serialize checker"); +static cl::OptionCategory SerializeCheckerCategory("Serialize sanitizer"); // CommonOptionsParser declares HelpMessage with a description of the common // command-line options related to the compilation database and input files. @@ -410,13 +157,14 @@ static cl::OptionCategory SerializeCheckerCategory("Serialize checker"); static cl::extrahelp CommonHelp(CommonOptionsParser::HelpMessage); // A help message for this specific tool can be added afterwards. -static cl::extrahelp MoreHelp("\nGenerates static checking code for serialize methods\n"); +static cl::extrahelp MoreHelp("\nGenerates sanitizer code for serializers\n"); int main(int argc, const char **argv) { CommonOptionsParser OptionsParser(argc, argv, SerializeCheckerCategory); - ClangTool Tool(OptionsParser.getCompilations(), - OptionsParser.getSourcePathList()); + ClangTool Tool( + OptionsParser.getCompilations(), OptionsParser.getSourcePathList() + ); if (Filename == "") { out = stdout; @@ -432,7 +180,7 @@ int main(int argc, const char **argv) { auto str = std::string("-I") + e; ArgumentsAdjuster ad1 = getInsertArgumentAdjuster(str.c_str()); Tool.appendArgumentsAdjuster(ad1); - fmt::print(stderr, "Including %s\n", e.c_str()); + fmt::print(stderr, "Including {}\n", e); } Tool.run(newFrontendActionFactory().get()); diff --git a/src/sanitizer/walk_record.cc b/src/sanitizer/walk_record.cc new file mode 100644 index 0000000..0ae504b --- /dev/null +++ b/src/sanitizer/walk_record.cc @@ -0,0 +1,195 @@ +/* +//@HEADER +// ***************************************************************************** +// +// walk_record.cc +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#include "common.h" +#include "walk_record.h" +#include "member_list.h" + +#include + +namespace sanitizer { + +void WalkRecord::walk(MatchResult const& result) { + using clang::isa; + using clang::cast; + using clang::CXXRecordDecl; + using clang::TemplateTypeParmDecl; + + auto const *rd = result.Nodes.getNodeAs("recordDecl"); + if (rd) { + #if SANITIZER_DEBUG + fmt::print("Traversing class {}\n", rd->getQualifiedNameAsString()); + #endif + + bool temp_instantiation = rd->getTemplateInstantiationPattern() != nullptr; + if (gen_inline_ && temp_instantiation) { + // skip template instantiation when generating inline + return; + } else if (rd->getDescribedClassTemplate()) { + // skip template classes when generating out-of-line + return; + } + + // Walk declarations for this struct + for (auto&& m : rd->decls()) { + // Skip non-templated functions + if (not m->isTemplateDecl()) { + continue; + } + + // Skip functions not called serialize that have exactly one parameter + // Matches intrusive pattern + auto fn = m->getAsFunction(); + if (!fn || fn->getNameAsString() != "serialize" || fn->param_size() != 1) { + continue; + } + + // Examine the template parameters to the serialize function + auto ft = fn->getDescribedFunctionTemplate(); + auto tp = ft->getTemplateParameters(); + + // If we are more than one, we might have a enable_if (Footprinting?) + if (tp->size() != 1) { + #if SANITIZER_DEBUG + for (unsigned int i = 0; i < tp->size(); i++) { + auto elm = tp->getParam(i); + if (isa(elm)) { + auto ttpd = cast(elm); + if (ttpd->hasDefaultArgument()) { + fmt::print("Template parameter with default argument\n"); + ttpd->getDefaultArgument()->dump(); + } else { + fmt::print("Template parameter without default argument\n"); + ttpd->dumpColor(); + } + } + } + #endif + + continue; + } + + // After all these checks, we have a valid serialize! + found_serialize_ = true; + + // Look for any existing checks in the body + findExistingChecks(fn); + + // Gather the member fields in the class + gatherMembers(rd); + + // Invoke the code generator + if (gen_ != nullptr) { + gen_->run(rd, fn, members_); + } + + break; + } + } +} + +void WalkRecord::gatherMembers(clang::CXXRecordDecl const* rd) { + #if SANITIZER_DEBUG + fmt::print("Gather members of class {}\n", rd->getQualifiedNameAsString()); + #endif + + // Walk all the fields in the class + for (auto&& f : rd->fields()) { + auto qual = f->getQualifiedNameAsString(); + auto unqual = f->getNameAsString(); + + // Skip members that already have checks + auto iter = existing_checks_.find(unqual); + if (iter == existing_checks_.end()) { + members_.emplace_back(Member{unqual, qual}); + } + } +} + +void WalkRecord::findExistingChecks(clang::FunctionDecl* fn) { + using clang::isa; + using clang::cast; + using clang::CallExpr; + using clang::CXXDependentScopeMemberExpr; + using clang::MemberExpr; + + // Skip if the function body is missing + if (not fn->hasBody()) { + return; + } + + for (auto&& elm : fn->getBody()->children()) { + // Skip any non-call expressions + if (not isa(elm)) { + continue; + } + + // Only look for a specific expression form + auto ce = cast(elm); + if (!ce or not ce->getCallee()->isTypeDependent() or ce->getNumArgs() != 2) { + continue; + } + + // Inspect further to see if this is a "check" invocation + auto iter = ce->child_begin(); + if (iter != ce->child_end()) { + if (isa(*iter)) { + auto cxx = cast(*iter); + if (cxx->getMemberNameInfo().getName().getAsString() == "check") { + iter++; + + // Save the existing check to a list so we know it exists + if (isa(*iter)) { + auto member = clang::cast(*iter); + existing_checks_.insert(member->getMemberDecl()->getNameAsString()); + } + } + } + + } + } +} + + +} /* end namespace sanitizer */ diff --git a/src/sanitizer/walk_record.h b/src/sanitizer/walk_record.h new file mode 100644 index 0000000..3dabca2 --- /dev/null +++ b/src/sanitizer/walk_record.h @@ -0,0 +1,85 @@ +/* +//@HEADER +// ***************************************************************************** +// +// walk_record.h +// DARMA Toolkit v. 1.0.0 +// DARMA/Serialization Sanitizer +// +// Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC +// (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. +// Government retains certain rights in this software. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from this +// software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// Questions? Contact darma@sandia.gov +// +// ***************************************************************************** +//@HEADER +*/ + +#if !defined INCLUDED_SANITIZER_WALK_RECORD_H +#define INCLUDED_SANITIZER_WALK_RECORD_H + +#include "member_list.h" +#include "generator.h" + +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#include +#include +#include + +namespace sanitizer { + +struct WalkRecord { + using MatchResult = clang::ast_matchers::MatchFinder::MatchResult; + + WalkRecord(bool in_gen_inline, std::unique_ptr in_gen) + : gen_inline_(in_gen_inline), + gen_(std::move(in_gen)) + { } + + void walk(MatchResult const& result); + + void findExistingChecks(clang::FunctionDecl* fn); + + void gatherMembers(clang::CXXRecordDecl const* rd); + +protected: + bool found_serialize_ = false; + MemberListType members_; + std::unordered_set existing_checks_; + +private: + bool gen_inline_ = false; + std::unique_ptr gen_ = nullptr; +}; + +} /* end namespace sanitizer */ + +#endif /*INCLUDED_SANITIZER_WALK_RECORD_H*/