2017-10-28 16:31:07 +02:00
|
|
|
#include "./jsonserializationcodegenerator.h"
|
|
|
|
|
|
|
|
#include "../lib/json/serializable.h"
|
|
|
|
|
|
|
|
#include <clang/AST/DeclCXX.h>
|
2017-11-09 01:03:34 +01:00
|
|
|
#include <clang/AST/DeclFriend.h>
|
2017-11-06 21:25:45 +01:00
|
|
|
#include <clang/AST/DeclTemplate.h>
|
2017-10-28 16:31:07 +02:00
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
using namespace std;
|
2017-11-06 15:31:21 +01:00
|
|
|
using namespace ApplicationUtilities;
|
2017-10-28 16:31:07 +02:00
|
|
|
|
|
|
|
namespace ReflectiveRapidJSON {
|
|
|
|
|
2017-11-06 15:31:21 +01:00
|
|
|
JsonSerializationCodeGenerator::Options::Options()
|
|
|
|
: additionalClassesArg("json-classes", '\0', "specifies additional classes to consider for JSON serialization")
|
|
|
|
{
|
|
|
|
additionalClassesArg.setCombinable(true);
|
|
|
|
additionalClassesArg.setValueNames({ "class-name" });
|
|
|
|
additionalClassesArg.setRequiredValueCount(Argument::varValueCount);
|
|
|
|
}
|
|
|
|
|
2017-10-28 16:31:07 +02:00
|
|
|
/*!
|
|
|
|
* \brief Prints an LLVM string reference without instantiating a std::string first.
|
|
|
|
*/
|
|
|
|
ostream &operator<<(ostream &os, llvm::StringRef str)
|
|
|
|
{
|
|
|
|
os.write(str.data(), static_cast<streamsize>(str.size()));
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonSerializationCodeGenerator::addDeclaration(clang::Decl *decl)
|
|
|
|
{
|
|
|
|
switch (decl->getKind()) {
|
2017-11-06 21:25:45 +01:00
|
|
|
case clang::Decl::Kind::CXXRecord:
|
|
|
|
case clang::Decl::Kind::ClassTemplateSpecialization: {
|
2017-10-28 16:31:07 +02:00
|
|
|
auto *const record = static_cast<clang::CXXRecordDecl *>(decl);
|
|
|
|
// skip forward declarations
|
|
|
|
if (!record->hasDefinition()) {
|
|
|
|
return;
|
|
|
|
}
|
2017-11-06 19:11:02 +01:00
|
|
|
|
2017-11-06 21:25:45 +01:00
|
|
|
// check for template specializations to adapt a 3rd party class/struct
|
|
|
|
if (decl->getKind() == clang::Decl::Kind::ClassTemplateSpecialization) {
|
|
|
|
auto *const templRecord = static_cast<clang::ClassTemplateSpecializationDecl *>(decl);
|
2017-11-09 01:03:34 +01:00
|
|
|
if (templRecord->getQualifiedNameAsString() == AdaptedJsonSerializable<void>::qualifiedName) {
|
2017-11-06 21:25:45 +01:00
|
|
|
const clang::TemplateArgumentList &templateArgs = templRecord->getTemplateArgs();
|
|
|
|
if (templateArgs.size() != 1 || templateArgs.get(0).getKind() != clang::TemplateArgument::Type) {
|
|
|
|
return; // FIXME: use Clang diagnostics to print warning
|
|
|
|
}
|
|
|
|
const clang::CXXRecordDecl *templateRecord = templateArgs.get(0).getAsType()->getAsCXXRecordDecl();
|
|
|
|
if (!templateRecord) {
|
|
|
|
return; // FIXME: use Clang diagnostics to print warning
|
|
|
|
}
|
|
|
|
m_adaptionRecords.emplace_back(templateRecord->getNameAsString());
|
|
|
|
return;
|
|
|
|
}
|
2017-10-28 16:31:07 +02:00
|
|
|
}
|
2017-11-06 21:25:45 +01:00
|
|
|
|
|
|
|
// add any other records
|
|
|
|
m_records.emplace_back(record);
|
2017-11-09 01:03:34 +01:00
|
|
|
} break;
|
2017-10-28 16:31:07 +02:00
|
|
|
case clang::Decl::Kind::Enum:
|
|
|
|
// TODO: add enums
|
|
|
|
break;
|
|
|
|
default:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void JsonSerializationCodeGenerator::generate(ostream &os) const
|
|
|
|
{
|
2017-11-06 21:25:45 +01:00
|
|
|
// find relevant classes
|
|
|
|
std::vector<RelevantClass> relevantClasses;
|
|
|
|
for (clang::CXXRecordDecl *record : m_records) {
|
|
|
|
string qualifiedName(qualifiedNameIfRelevant(record));
|
|
|
|
if (!qualifiedName.empty()) {
|
|
|
|
relevantClasses.emplace_back(move(qualifiedName), record);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (relevantClasses.empty()) {
|
2017-10-28 16:31:07 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-11-02 23:35:56 +01:00
|
|
|
// put everything into namespace ReflectiveRapidJSON::JsonReflector
|
2017-10-28 16:31:07 +02:00
|
|
|
os << "namespace ReflectiveRapidJSON {\n"
|
2017-11-02 23:35:56 +01:00
|
|
|
"namespace JsonReflector {\n\n";
|
2017-10-28 16:31:07 +02:00
|
|
|
|
|
|
|
// add push and pull functions for each class, for an example of the resulting
|
|
|
|
// output, see ../lib/tests/jsonserializable.cpp (code under comment "pretend serialization code...")
|
2017-11-06 21:25:45 +01:00
|
|
|
for (const RelevantClass &relevantClass : relevantClasses) {
|
2017-11-09 01:03:34 +01:00
|
|
|
bool pushPrivateMembers = false;
|
|
|
|
bool pullPrivateMembers = false;
|
|
|
|
for (const clang::FriendDecl *const friendDecl : relevantClass.record->friends()) {
|
|
|
|
const clang::NamedDecl *const actualFriendDecl = friendDecl->getFriendDecl();
|
|
|
|
if (!actualFriendDecl /* && decl->getKind() != clang::Decl::Kind::FunctionTemplate */) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
const string friendName(actualFriendDecl->getQualifiedNameAsString());
|
|
|
|
if (friendName == "ReflectiveRapidJSON::JsonReflector::push") {
|
|
|
|
pushPrivateMembers = true;
|
|
|
|
}
|
|
|
|
if (friendName == "ReflectiveRapidJSON::JsonReflector::pull") {
|
|
|
|
pullPrivateMembers = true;
|
|
|
|
}
|
|
|
|
cout << "friend-kind: " << actualFriendDecl->getDeclKindName() << endl;
|
|
|
|
cout << "friend: " << actualFriendDecl->getQualifiedNameAsString() << endl;
|
|
|
|
if (pushPrivateMembers && pullPrivateMembers) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-28 16:31:07 +02:00
|
|
|
// write comment
|
|
|
|
os << "// define code for (de)serializing " << relevantClass.qualifiedName << " objects\n";
|
|
|
|
|
|
|
|
// find relevant base classes
|
2017-11-06 21:25:45 +01:00
|
|
|
const vector<const RelevantClass *> relevantBases = findRelevantBaseClasses(relevantClass, relevantClasses);
|
2017-10-28 16:31:07 +02:00
|
|
|
|
|
|
|
// print push method
|
|
|
|
os << "template <> inline void push<::" << relevantClass.qualifiedName << ">(const ::" << relevantClass.qualifiedName
|
|
|
|
<< " &reflectable, ::RAPIDJSON_NAMESPACE::Value::Object &value, ::RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)\n{\n"
|
|
|
|
" // push base classes\n";
|
|
|
|
for (const RelevantClass *baseClass : relevantBases) {
|
|
|
|
os << " push(static_cast<const ::" << baseClass->qualifiedName << " &>(reflectable), value, allocator);\n";
|
|
|
|
}
|
|
|
|
os << " // push members\n";
|
|
|
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
2017-11-09 01:03:34 +01:00
|
|
|
if (pushPrivateMembers || field->getAccess() == clang::AS_public) {
|
2017-11-09 00:07:40 +01:00
|
|
|
os << " push(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, allocator);\n";
|
|
|
|
}
|
2017-10-28 16:31:07 +02:00
|
|
|
}
|
|
|
|
os << "}\n";
|
|
|
|
|
|
|
|
// print pull method
|
|
|
|
os << "template <> inline void pull<::" << relevantClass.qualifiedName << ">(::" << relevantClass.qualifiedName
|
|
|
|
<< " &reflectable, const ::RAPIDJSON_NAMESPACE::GenericValue<::RAPIDJSON_NAMESPACE::UTF8<char>>::ConstObject &value, "
|
|
|
|
"JsonDeserializationErrors "
|
|
|
|
"*errors)\n{\n"
|
|
|
|
" // pull base classes\n";
|
|
|
|
for (const RelevantClass *baseClass : relevantBases) {
|
|
|
|
os << " pull(static_cast<::" << baseClass->qualifiedName << " &>(reflectable), value, errors);\n";
|
|
|
|
}
|
|
|
|
os << " // set error context for current record\n"
|
|
|
|
" const char *previousRecord;\n"
|
|
|
|
" if (errors) {\n"
|
|
|
|
" previousRecord = errors->currentRecord;\n"
|
|
|
|
" errors->currentRecord = \""
|
|
|
|
<< relevantClass.qualifiedName
|
|
|
|
<< "\";\n"
|
|
|
|
" }\n"
|
|
|
|
" // pull members\n";
|
|
|
|
for (const clang::FieldDecl *field : relevantClass.record->fields()) {
|
2017-11-09 01:03:34 +01:00
|
|
|
if (pullPrivateMembers || field->getAccess() == clang::AS_public) {
|
2017-11-09 00:07:40 +01:00
|
|
|
os << " pull(reflectable." << field->getName() << ", \"" << field->getName() << "\", value, errors);\n";
|
|
|
|
}
|
2017-10-28 16:31:07 +02:00
|
|
|
}
|
|
|
|
os << " // restore error context for previous record\n"
|
|
|
|
" if (errors) {\n"
|
|
|
|
" errors->currentRecord = previousRecord;\n"
|
|
|
|
" }\n";
|
|
|
|
os << "}\n\n";
|
|
|
|
}
|
|
|
|
|
2017-11-02 23:35:56 +01:00
|
|
|
// close namespace ReflectiveRapidJSON::JsonReflector
|
|
|
|
os << "} // namespace JsonReflector\n"
|
2017-10-28 16:31:07 +02:00
|
|
|
"} // namespace ReflectiveRapidJSON\n";
|
|
|
|
}
|
|
|
|
|
2017-11-06 15:31:21 +01:00
|
|
|
/*!
|
|
|
|
* \brief Returns the qualified name of the specified \a record if it is considered relevant.
|
|
|
|
*/
|
|
|
|
string JsonSerializationCodeGenerator::qualifiedNameIfRelevant(clang::CXXRecordDecl *record) const
|
|
|
|
{
|
|
|
|
// consider all classes inheriting from an instantiation of "JsonSerializable" relevant
|
|
|
|
if (inheritsFromInstantiationOf(record, JsonSerializable<void>::qualifiedName)) {
|
|
|
|
return record->getQualifiedNameAsString();
|
|
|
|
}
|
|
|
|
|
2017-11-06 21:25:45 +01:00
|
|
|
// consider all classes for which a specialization of the "AdaptedJsonSerializable" struct is available
|
|
|
|
const string qualifiedName(record->getQualifiedNameAsString());
|
|
|
|
for (const string &adaptionRecord : m_adaptionRecords) {
|
|
|
|
if (adaptionRecord == qualifiedName) {
|
|
|
|
return qualifiedName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 15:31:21 +01:00
|
|
|
// consider all classes specified via "--additional-classes" argument relevant
|
|
|
|
if (!m_options.additionalClassesArg.isPresent()) {
|
|
|
|
return string();
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const char *className : m_options.additionalClassesArg.values()) {
|
|
|
|
if (className == qualifiedName) {
|
|
|
|
return qualifiedName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return string();
|
|
|
|
}
|
|
|
|
|
2017-10-28 16:31:07 +02:00
|
|
|
std::vector<const JsonSerializationCodeGenerator::RelevantClass *> JsonSerializationCodeGenerator::findRelevantBaseClasses(
|
2017-11-06 21:25:45 +01:00
|
|
|
const RelevantClass &relevantClass, const std::vector<RelevantClass> &relevantBases)
|
2017-10-28 16:31:07 +02:00
|
|
|
{
|
|
|
|
vector<const RelevantClass *> relevantBaseClasses;
|
2017-11-06 21:25:45 +01:00
|
|
|
for (const RelevantClass &otherClass : relevantBases) {
|
2017-10-28 16:31:07 +02:00
|
|
|
if (relevantClass.record != otherClass.record && relevantClass.record->isDerivedFrom(otherClass.record)) {
|
|
|
|
relevantBaseClasses.push_back(&otherClass);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return relevantBaseClasses;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace ReflectiveRapidJSON
|