2017-10-18 23:07:17 +02:00
|
|
|
#include "./generator.h"
|
2017-10-21 00:32:42 +02:00
|
|
|
#include "./frontendaction.h"
|
2017-10-18 23:07:17 +02:00
|
|
|
|
2017-10-23 00:41:10 +02:00
|
|
|
#include "../lib/jsonserializable.h"
|
|
|
|
|
2017-10-18 23:07:17 +02:00
|
|
|
#include <c++utilities/application/global.h>
|
|
|
|
#include <c++utilities/conversion/stringbuilder.h>
|
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
#include <clang/AST/DeclCXX.h>
|
|
|
|
#include <clang/Basic/FileManager.h>
|
|
|
|
#include <clang/Frontend/FrontendActions.h>
|
|
|
|
#include <clang/Tooling/Tooling.h>
|
2017-10-18 23:07:17 +02:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
#include <memory>
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
using namespace ConversionUtilities;
|
|
|
|
|
|
|
|
namespace ReflectiveRapidJSON {
|
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
/*!
|
|
|
|
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
|
|
|
*/
|
|
|
|
ostream &operator<<(ostream &os, llvm::StringRef str)
|
2017-10-18 23:07:17 +02:00
|
|
|
{
|
2017-10-21 00:32:42 +02:00
|
|
|
os.write(str.data(), static_cast<streamsize>(str.size()));
|
|
|
|
return os;
|
2017-10-18 23:07:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
CodeGenerator::~CodeGenerator()
|
|
|
|
{
|
|
|
|
}
|
2017-10-18 23:07:17 +02:00
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
/*!
|
|
|
|
* \brief Adds the specified \a decl to the code generator. The generator might ignore irrelevant declarations.
|
|
|
|
*/
|
|
|
|
void CodeGenerator::addDeclaration(clang::Decl *decl)
|
2017-10-18 23:07:17 +02:00
|
|
|
{
|
2017-10-21 00:32:42 +02:00
|
|
|
VAR_UNUSED(decl)
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Returns whether the specified \a record inherits from an instantiation of the specified \a templateClass.
|
|
|
|
* \remarks The specified \a record must be defined (not only forward-declared).
|
|
|
|
*/
|
|
|
|
bool CodeGenerator::inheritsFromInstantiationOf(clang::CXXRecordDecl *const record, const char *const templateClass)
|
|
|
|
{
|
|
|
|
for (const clang::CXXBaseSpecifier &base : record->bases()) {
|
|
|
|
const clang::CXXRecordDecl *const baseDecl = base.getType()->getAsCXXRecordDecl();
|
|
|
|
if (baseDecl && baseDecl->getQualifiedNameAsString() == templateClass) {
|
|
|
|
return true;
|
2017-10-18 23:07:17 +02:00
|
|
|
}
|
2017-10-21 00:32:42 +02:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
2017-10-18 23:07:17 +02:00
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
void JSONSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|
|
|
{
|
|
|
|
switch (decl->getKind()) {
|
|
|
|
case clang::Decl::Kind::CXXRecord: {
|
|
|
|
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
|
|
|
// skip forward declarations
|
|
|
|
if (!record->hasDefinition()) {
|
|
|
|
return;
|
|
|
|
}
|
2017-10-23 00:41:10 +02:00
|
|
|
// add classes derived from any instantiation of "ReflectiveRapidJSON::JSONSerializable"
|
|
|
|
if (inheritsFromInstantiationOf(record, JSONSerializable<void>::qualifiedName)) {
|
2017-10-21 00:32:42 +02:00
|
|
|
m_relevantClasses.emplace_back(record->getQualifiedNameAsString(), record);
|
2017-10-18 23:07:17 +02:00
|
|
|
}
|
2017-10-21 00:32:42 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case clang::Decl::Kind::Enum:
|
|
|
|
// TODO: add enums
|
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JSONSerializationCodeGenerator::generate(ostream &os) const
|
|
|
|
{
|
|
|
|
if (m_relevantClasses.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2017-10-18 23:07:17 +02:00
|
|
|
|
2017-10-24 01:00:40 +02:00
|
|
|
// put everything into namespace ReflectiveRapidJSON::Reflector
|
2017-10-21 00:32:42 +02:00
|
|
|
os << "namespace ReflectiveRapidJSON {\n"
|
2017-10-24 01:00:40 +02:00
|
|
|
"namespace Reflector {\n\n";
|
2017-10-21 00:32:42 +02:00
|
|
|
|
|
|
|
// add push and pull functions for each class, for an example of the resulting
|
2017-10-23 00:41:10 +02:00
|
|
|
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
2017-10-21 00:32:42 +02:00
|
|
|
for (const RelevantClass &relevantClass : m_relevantClasses) {
|
|
|
|
// print push method
|
2017-10-24 01:00:40 +02:00
|
|
|
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
|
|
|
|
<< " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n";
|
2017-10-21 00:32:42 +02:00
|
|
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
|
|
|
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
|
|
|
}
|
|
|
|
os << "}\n";
|
|
|
|
|
|
|
|
// print pull method
|
2017-10-24 01:00:40 +02:00
|
|
|
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
|
|
|
|
<< " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value)\n{\n";
|
2017-10-21 00:32:42 +02:00
|
|
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
|
|
|
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value);\n";
|
|
|
|
}
|
|
|
|
os << "}\n\n";
|
2017-10-18 23:07:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-21 00:32:42 +02:00
|
|
|
// close namespace ReflectiveRapidJSON::Reflector
|
|
|
|
os << "} // namespace Reflector\n"
|
|
|
|
"} // namespace ReflectiveRapidJSON\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
struct CodeFactory::ToolInvocation {
|
|
|
|
ToolInvocation(CodeFactory &factory);
|
|
|
|
|
|
|
|
clang::FileManager fileManager;
|
|
|
|
clang::tooling::ToolInvocation invocation;
|
|
|
|
};
|
|
|
|
|
|
|
|
CodeFactory::ToolInvocation::ToolInvocation(CodeFactory &factory)
|
|
|
|
: fileManager({ "." })
|
|
|
|
, invocation(factory.makeClangArgs(), new FrontendAction(factory), &fileManager)
|
|
|
|
{
|
|
|
|
fileManager.Retain();
|
|
|
|
}
|
|
|
|
|
2017-10-24 01:00:40 +02:00
|
|
|
CodeFactory::CodeFactory(
|
|
|
|
const char *applicationPath, const std::vector<const char *> &sourceFiles, const std::vector<const char *> &clangOptions, std::ostream &os)
|
2017-10-21 00:32:42 +02:00
|
|
|
: m_applicationPath(applicationPath)
|
|
|
|
, m_sourceFiles(sourceFiles)
|
2017-10-24 01:00:40 +02:00
|
|
|
, m_clangOptions(clangOptions)
|
2017-10-21 00:32:42 +02:00
|
|
|
, m_os(os)
|
2017-10-24 01:00:40 +02:00
|
|
|
, m_compilerInstance(nullptr)
|
2017-10-21 00:32:42 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
CodeFactory::~CodeFactory()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Constructs arguments for the Clang tool invocation.
|
|
|
|
*/
|
|
|
|
std::vector<string> CodeFactory::makeClangArgs() const
|
|
|
|
{
|
|
|
|
static const initializer_list<const char *> flags
|
|
|
|
= { m_applicationPath, "-x", "c++", "-fPIE", "-fPIC", "-Wno-microsoft", "-Wno-pragma-once-outside-header", "-std=c++14", "-fsyntax-only" };
|
|
|
|
vector<string> clangArgs;
|
2017-10-24 01:00:40 +02:00
|
|
|
clangArgs.reserve(flags.size() + m_clangOptions.size() + m_sourceFiles.size());
|
2017-10-21 00:32:42 +02:00
|
|
|
clangArgs.insert(clangArgs.end(), flags.begin(), flags.end());
|
2017-10-24 01:00:40 +02:00
|
|
|
clangArgs.insert(clangArgs.end(), m_clangOptions.cbegin(), m_clangOptions.cend());
|
2017-10-21 00:32:42 +02:00
|
|
|
clangArgs.insert(clangArgs.end(), m_sourceFiles.cbegin(), m_sourceFiles.cend());
|
|
|
|
return clangArgs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Adds the specified \a decl to all underlying code generators. The generators might ignore irrelevant declarations.
|
|
|
|
* \remarks Supposed to be called by assigned generators inside readAST().
|
|
|
|
*/
|
|
|
|
void CodeFactory::addDeclaration(clang::Decl *decl)
|
|
|
|
{
|
|
|
|
for (const auto &generator : m_generators) {
|
|
|
|
generator->addDeclaration(decl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Reads (relevent) AST elements using Clang.
|
|
|
|
*/
|
|
|
|
bool CodeFactory::readAST()
|
|
|
|
{
|
|
|
|
// lazy initialize Clang tool invocation
|
|
|
|
if (!m_toolInvocation) {
|
|
|
|
m_toolInvocation = make_unique<ToolInvocation>(*this);
|
|
|
|
}
|
|
|
|
// run Clang
|
|
|
|
return m_toolInvocation->invocation.run();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \brief Generates code based on the AST elements which have been read by invoking readAST().
|
|
|
|
*/
|
|
|
|
bool CodeFactory::generate() const
|
|
|
|
{
|
|
|
|
for (const auto &generator : m_generators) {
|
|
|
|
generator->generate(m_os);
|
|
|
|
}
|
|
|
|
return true;
|
2017-10-18 23:07:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ReflectiveRapidJSON
|