/* * generator.cpp * * Created on: Aug 23, 2016 * Author: gregor */ #include #include #include #include #include "annotation.h" #include "generator.h" void generateMethodWrapper(std::ofstream &output, gltb::RefPtr thisClass, gltb::RefPtr method) { // do not wrap protected and private functions if(method->getAccess() != CX_CXXPublic) { return; } // get sorted list of parameters std::vector> parameters; for(auto iter = method->getChildren().begin(); iter != method->getChildren().end(); ++iter) { gltb::RefPtr parameter = dynamic_cast(iter->second.getNakedPointer()); if(parameter != nullptr) { parameters.push_back(parameter); } } std::sort(parameters.begin(), parameters.end(), [] (gltb::RefPtr a, gltb::RefPtr b) { return a->getIndex() < b->getIndex(); }); output << "// method wrapper for " << thisClass->getName() << "::" << method->getName() << std::endl; output << " gltb::refl::Variant " << thisClass->getName() << "_" << method->getName() << "(gltb::refl::ReflectedType *instance, const std::vector &arguments)" << std::endl; output << " {" << std::endl; // unpack parameters for(auto parameter : parameters) { output << " " << parameter->getTypeString() << " " << parameter->getName() << " = arguments[" << parameter->getIndex() << "];" << std::endl;; } // build argument list for function call std::string arguments; int i = 0; for(auto parameter : parameters) { if(i != 0) { arguments += ", "; } arguments += parameter->getName(); i++; } // call method and return result if(method->getReturnTypeString() == "void") { output << " (("+ thisClass->getName() + " *)instance)->" + method->getName() + " (" + arguments + ");" << std::endl; output << " return gltb::refl::Variant();" << std::endl; } else { output << " " + method->getReturnTypeString() + " _returnValue = (("+ thisClass->getName() + " *)instance)->" + method->getName() + " (" + arguments + ");" << std::endl; output << " return gltb::refl::Variant(_returnValue);" << std::endl; } output << " }" << std::endl; } void generateMethodWrappersForClass(std::ofstream &output, CXFile inputFile, gltb::RefPtr thisClass) { if(!thisClass->residesInFile(inputFile)) { return; } if(!thisClass->hasBaseClass("gltb::refl::ReflectedType")) { return; } auto children = thisClass->getChildren(); for(auto iter = children.begin(); iter != children.end(); ++iter) { TypeTreeNode *node = iter->second.getNakedPointer(); if(dynamic_cast(node) != nullptr) { generateMethodWrapper(output, thisClass, dynamic_cast(node)); } } } void generateMethodWrappersForNamespace(std::ofstream &output, CXFile inputFile, gltb::RefPtr thisNamespace) { output << " // " << thisNamespace->getDescription() << std::endl; auto children = thisNamespace->getChildren(); for(auto iter = children.begin(); iter != children.end(); ++iter) { TypeTreeNode *node = iter->second.getNakedPointer(); if(dynamic_cast(node) != nullptr) { generateMethodWrappersForNamespace(output, inputFile, dynamic_cast(node)); } else if(dynamic_cast(node) != nullptr) { generateMethodWrappersForClass(output, inputFile, dynamic_cast(node)); } } } void generateVariableRegistration(std::ofstream &output, gltb::RefPtr variable) { // TODO // skip global variables for now return; output << "// skipped variable " << variable->getDescription() << std::endl; } void generateFunctionRegistration(std::ofstream &output, gltb::RefPtr function) { // TODO // skip global functions for now return; output << "// skipped function " << function->getDescription() << std::endl; } std::string visibilityString(CX_CXXAccessSpecifier access) { switch(access) { case CX_CXXPublic: return "gltb::refl::Public"; case CX_CXXProtected: return "gltb::refl::Protected"; case CX_CXXPrivate: return "gltb::refl::Private"; default: return "gltb::refl::Public"; } } void generateFieldRegistration(std::ofstream &output, gltb::RefPtr thisClass, gltb::RefPtr field) { std::string readFunctionName = "nullptr"; std::string writeFunctionName = "nullptr"; bool noReflection = false; std::cout << "field " << field->getName() << " has " << field->getChildren().size() << " children" << std::endl; for(auto iter = field->getChildren().begin(); iter != field->getChildren().end(); ++iter) { std::cout << "processing child " << iter->second->getDescription() << std::endl; gltb::RefPtr annotationString = dynamic_cast(iter->second.getNakedPointer()); if(annotationString != nullptr) { for(gltb::RefPtr annotation : annotationString->getAnnotations()) { if(annotation != nullptr) { std::cout << "found annotation on field" << field->getName() << std::endl; if(annotation->isBuiltInAnnotation()) { if(annotation->getBuiltinAnnotationType() == BuiltinAnnotationType::ReadProperty) { readFunctionName = thisClass->getName() + "_" + annotation->getArguments()[0]; } else if(annotation->getBuiltinAnnotationType() == BuiltinAnnotationType::WriteProperty) { writeFunctionName = thisClass->getName() + "_" + annotation->getArguments()[0]; } else if(annotation->getBuiltinAnnotationType() == BuiltinAnnotationType::ReadWriteProperty) { readFunctionName = thisClass->getName() + "_" + annotation->getArguments()[0]; writeFunctionName = thisClass->getName() + "_" + annotation->getArguments()[1]; } else if(annotation->getBuiltinAnnotationType() == BuiltinAnnotationType::NoReflection) { noReflection = true; } } } } } } if(noReflection) { std::cout << "// field " << field->getName() << " is annotated with NoReflection" << std::endl; } output << " new gltb::refl::Field(\"" << field->getTypeString() << "\", \"" << field->getName() << "\", " << visibilityString(field->getAccess()) << ", " << readFunctionName << ", " << writeFunctionName << ")," << std::endl; } std::string parametersString(gltb::RefPtr method) { std::string parameters = "{ "; int i = 0; for(auto iter = method->getChildren().begin(); iter != method->getChildren().end(); ++iter) { gltb::RefPtr parameter = dynamic_cast(iter->second.getNakedPointer()); if(parameter != nullptr) { // if(i != 0) { // parameters += ", "; // } parameters += "gltb::refl::Parameter(\"" + parameter->getName() + "\", \"" + parameter->getTypeString() + "\"), "; } i++; } parameters += " }"; return parameters; } void generateMethodRegistration(std::ofstream &output, gltb::RefPtr thisClass, gltb::RefPtr method) { // TODO check if method has NoReflection annotation and omit it if required std::string wrapper = ""; // do not wrap protected and private functions if(method->getAccess() == CX_CXXPublic) { wrapper = ", " + thisClass->getName() + "_" + method->getName(); } output << " new gltb::refl::Method(\"" + method->getName() << + "\", " + parametersString(method) + ", " + visibilityString(method->getAccess()) + wrapper + ")," << std::endl; } void generateClassOrStructBodyRegistration(std::ofstream &output, gltb::RefPtr thisClass) { auto children = thisClass->getChildren(); output << " std::vector fields = {" << std::endl; for(auto iter = children.begin(); iter != children.end(); ++iter) { TypeTreeNode *node = iter->second.getNakedPointer(); if(dynamic_cast(node) != nullptr) { generateFieldRegistration(output, thisClass, dynamic_cast(node)); } } output << " };" << std::endl;; output << std::endl; output << " std::vector methods = {" << std::endl; for(auto iter = children.begin(); iter != children.end(); ++iter) { TypeTreeNode *node = iter->second.getNakedPointer(); if(dynamic_cast(node) != nullptr) { generateMethodRegistration(output, thisClass, dynamic_cast(node)); } } output << " };" << std::endl; } void generateClassRegistration(std::ofstream &output, CXFile inputFile, gltb::RefPtr thisClass) { // only generate data for that class if it is derived from ReflectedType if(!thisClass->hasBaseClass("gltb::refl::ReflectedType")) { return; } if(!thisClass->residesInFile(inputFile)) { return; } output << std::endl; output << " // " << thisClass->getDescription() << std::endl; output << " {" << std::endl; generateClassOrStructBodyRegistration(output, thisClass); output << std::endl; output << " gltb::refl::Class *thisClass = new gltb::refl::Class(\"" << thisClass->getName() << "\", fields, methods);" << std::endl; output << " module.registerType(thisClass);" << std::endl; output << " }" << std::endl; // TODO inner classes } void generateStructRegistration(std::ofstream &output, CXFile inputFile, gltb::RefPtr thisStruct) { // skip structs for now return; if(!thisStruct->residesInFile(inputFile)) { return; } output << std::endl; output << " // " << thisStruct->getDescription() << std::endl; output << " {" << std::endl; generateClassOrStructBodyRegistration(output, thisStruct); output << std::endl; output << " gltb::refl::Struct *thisStruct = new gltb::refl::Struct(\"" << thisStruct->getName() << "\", fields, methods);" << std::endl; output << " module.registerType(thisStruct);" << std::endl; output << " }" << std::endl; // TODO inner classes } void generateNamespaceRegistration(std::ofstream &output, CXFile inputFile, gltb::RefPtr thisNamespace) { output << " // " << thisNamespace->getDescription() << std::endl; auto children = thisNamespace->getChildren(); for(auto iter = children.begin(); iter != children.end(); ++iter) { TypeTreeNode *node = iter->second.getNakedPointer(); if(dynamic_cast(node) != nullptr) { generateNamespaceRegistration(output, inputFile, dynamic_cast(node)); } else if(dynamic_cast(node) != nullptr) { generateVariableRegistration(output, dynamic_cast(node)); } else if(dynamic_cast(node) != nullptr) { generateFunctionRegistration(output, dynamic_cast(node)); } else if(dynamic_cast(node) != nullptr) { generateClassRegistration(output, inputFile, dynamic_cast(node)); } else if(dynamic_cast(node) != nullptr) { generateStructRegistration(output, inputFile, dynamic_cast(node)); } } } void generateReflectionInformation(gltb::RefPtr globalNamespace, CXFile inputFile, std::string filename) { std::ofstream outputFile(filename); if(!outputFile) { throw new gltb::Exception("unable to open output file " + filename, "generateReflectionInformation()"); } outputFile << "//" << std::endl; outputFile << "// GENERATED FILE -- DO NOT EDIT!" << std::endl; outputFile << "//" << std::endl; outputFile << std::endl; outputFile << "#include " << std::endl; outputFile << std::endl; CXString inputFilename = clang_getFileName(inputFile); outputFile << "#include \"" << clang_getCString(inputFilename) << "\"" << std::endl;; outputFile << std::endl; outputFile << "namespace {" << std::endl; outputFile << " gltb::refl::Module module;" << std::endl; outputFile << std::endl; generateMethodWrappersForNamespace(outputFile, inputFile, globalNamespace); outputFile << std::endl; outputFile << " void registerModule()" << std::endl; outputFile << " {" << std::endl; generateNamespaceRegistration(outputFile, inputFile, globalNamespace); outputFile << " }" << std::endl; outputFile << std::endl; outputFile << " gltb::refl::ModuleInitializer moduleInitializer(&module, registerModule);" << std::endl; outputFile << "}" << std::endl; // end of namespace // TODO generate all GetType functions }