void ASTConsumer::AddMethodDecl(clang::NamedDecl* decl, const std::string& name, const std::string& parent_name) { // Cast to a method clang::CXXMethodDecl* method_decl = llvm::dyn_cast<clang::CXXMethodDecl>(decl); assert(method_decl != 0 && "Failed to cast to C++ method declaration"); // Ignore overloaded operators for now if (method_decl->isOverloadedOperator()) return; std::vector<cldb::Field> parameters; if (method_decl->isInstance()) { // Parse the 'this' type, treating it as the first parameter to the method cldb::Field this_param; Status status = MakeField(*this, method_decl->getThisType(m_ASTContext), "this", name, 0, this_param, MF_CHECK_TYPE_IS_REFLECTED); if (status.HasWarnings()) { status.Print(method_decl->getLocation(), m_ASTContext.getSourceManager(), va("Failed to reflect method '%s' due to invalid 'this' type", name.c_str())); return; } parameters.push_back(this_param); } // Parse and add the method MakeFunction(decl, name, parent_name, parameters); }
void ASTConsumer::AddFieldDecl(clang::NamedDecl* decl, const std::string& name, const std::string& parent_name, const clang::ASTRecordLayout* layout) { // Cast to a field clang::FieldDecl* field_decl = llvm::dyn_cast<clang::FieldDecl>(decl); assert(field_decl != 0 && "Failed to cast to field declaration"); // These are implicitly generated by clang so skip them if (field_decl->isAnonymousStructOrUnion()) return; // Parse and add the field cldb::Field field; cldb::u32 offset = layout->getFieldOffset(field_decl->getFieldIndex()) / 8; std::string field_name = field_decl->getName().str(); Status status = MakeField(*this, field_decl->getType(), field_name.c_str(), parent_name, offset, field, MF_CHECK_TYPE_IS_REFLECTED); if (status.HasWarnings()) { status.Print(field_decl->getLocation(), m_ASTContext.getSourceManager(), va("Failed to reflect field in '%s'", parent_name.c_str())); return; } LOG(ast, INFO, "Field: %s%s%s %s\n", field.qualifier.is_const ? "const " : "", field.type.text.c_str(), field.qualifier.op == cldb::Qualifier::POINTER ? "*" : field.qualifier.op == cldb::Qualifier::REFERENCE ? "&" : "", field.name.text.c_str()); m_DB.AddPrimitive(field); }
void ASTConsumer::MakeFunction(clang::NamedDecl* decl, const std::string& function_name, const std::string& parent_name, std::vector<cldb::Field>& parameters) { // Cast to a function clang::FunctionDecl* function_decl = llvm::dyn_cast<clang::FunctionDecl>(decl); assert(function_decl != 0 && "Failed to cast to function declaration"); // Only add the function once if (!function_decl->isFirstDeclaration()) return; // Parse the return type - named as a reserved keyword so it won't clash with user symbols cldb::Field return_parameter; Status status = MakeField(*this, function_decl->getResultType(), "return", function_name, -1, return_parameter, 0); if (status.HasWarnings()) { status.Print(function_decl->getLocation(), m_ASTContext.getSourceManager(), va("Failed to reflect function '%s' due to invalid return type", function_name.c_str())); return; } // Try to gather every parameter successfully before adding the function int index = parameters.size(); for (clang::FunctionDecl::param_iterator i = function_decl->param_begin(); i != function_decl->param_end(); ++i) { clang::ParmVarDecl* param_decl = *i; // Check for unnamed parameters llvm::StringRef param_name = param_decl->getName(); if (param_name.empty()) { Status().Print(function_decl->getLocation(), m_ASTContext.getSourceManager(), va("Unnamed function parameters not supported - skipping reflection of '%s'", function_name.c_str())); return; } // Collect a list of constructed parameters in case evaluating one of them fails cldb::Field parameter; std::string param_name_str = param_name.str(); status = MakeField(*this, param_decl->getType(), param_name_str.c_str(), function_name, index++, parameter, 0); if (status.HasWarnings()) { status.Print(function_decl->getLocation(), m_ASTContext.getSourceManager(), va("Failed to reflection function '%s'", function_name.c_str())); return; } parameters.push_back(parameter); } // Generate a hash unique to this function among other functions of the same name // This is so that its parameters can re-parent themselves correctly cldb::u32 unique_id = cldb::CalculateFunctionUniqueID(parameters); // Parent each parameter to the function return_parameter.parent_unique_id = unique_id; for (size_t i = 0; i < parameters.size(); i++) parameters[i].parent_unique_id = unique_id; // Add the function LOG(ast, INFO, "function %s\n", function_name.c_str()); m_DB.AddPrimitive(cldb::Function( m_DB.GetName(function_name.c_str()), m_DB.GetName(parent_name.c_str()), unique_id)); LOG_PUSH_INDENT(ast); // Only add the return parameter if it's non-void if (return_parameter.type.text != "void") { LOG(ast, INFO, "Returns: %s%s%s\n", return_parameter.qualifier.is_const ? "const " : "", return_parameter.type.text.c_str(), return_parameter.qualifier.op == cldb::Qualifier::POINTER ? "*" : return_parameter.qualifier.op == cldb::Qualifier::REFERENCE ? "&" : ""); m_DB.AddPrimitive(return_parameter); } else { LOG(ast, INFO, "Returns: void (not added)\n"); } // Add the parameters for (std::vector<cldb::Field>::iterator i = parameters.begin(); i != parameters.end(); ++i) { LOG(ast, INFO, "%s%s%s %s\n", i->qualifier.is_const ? "const " : "", i->type.text.c_str(), i->qualifier.op == cldb::Qualifier::POINTER ? "*" : i->qualifier.op == cldb::Qualifier::REFERENCE ? "&" : "", i->name.text.c_str()); m_DB.AddPrimitive(*i); } LOG_POP_INDENT(ast); }
void ASTConsumer::AddClassDecl(clang::NamedDecl* decl, const std::string& name, const std::string& parent_name) { // Cast to a record (NOTE: CXXRecord is a temporary clang type and will change in future revisions) clang::CXXRecordDecl* record_decl = llvm::dyn_cast<clang::CXXRecordDecl>(decl); assert(record_decl != 0 && "Failed to cast to record declaration"); // Check for forward-declared types bool forward_decl = false; if (record_decl->isThisDeclarationADefinition() == clang::VarDecl::DeclarationOnly) { // // This classification of CXXRecord also comes through the AST like so: // // namespace ns // { // class ClassName { }; // } // // CXXRecord ns::ClassName // CXXRecord ns::ClassName::ClassName // CXXConstructor ns::ClassName::ClassName // // So before every constructor of a class, there's a superfluous CXX declaration of the same name. // Not sure why it's here, however the "free standing" flag is documented in the code to mark // these cases: // // namespace ns // { // class ClassName; // } // // CXXRecord ns::ClassName // // And these are the exact cases that represent a reflected forward-declaration! // if (!record_decl->isFreeStanding()) return; forward_decl = true; } // Ignore classes with virtual bases if (!forward_decl && record_decl->getNumVBases()) { Status().Print(record_decl->getLocation(), m_ASTContext.getSourceManager(), va("Class '%s' has an unsupported virtual base class", name.c_str())); return; } // Name gets added to the database if it's not already there cldb::Name type_name = m_DB.GetName(name.c_str()); // Parse base classes std::vector<cldb::Name> base_names; if (!forward_decl && record_decl->getNumBases()) { for (clang::CXXRecordDecl::base_class_const_iterator base_it = record_decl->bases_begin(); base_it != record_decl->bases_end(); base_it++) { cldb::Name base_name; Status status = ParseBaseClass(*this, type_name, *base_it, base_name); if (status.HasWarnings()) { status.Print(record_decl->getLocation(), m_ASTContext.getSourceManager(), va("Failed to reflect class '%s'", name.c_str())); return; } // If the base class is valid, then add the inheritance relationship m_DB.AddTypeInheritance(type_name, base_name); base_names.push_back(base_name); } } if (record_decl->isAnonymousStructOrUnion()) { // Add declarations to the parent const clang::ASTRecordLayout& layout = m_ASTContext.getASTRecordLayout(record_decl); AddContainedDecls(decl, parent_name, &layout); } else { LOG(ast, INFO, "class %s", name.c_str()); // Ensure there's at least an empty class definition in the database for this name cldb::Class* class_ptr = m_DB.GetFirstPrimitive<cldb::Class>(name.c_str()); if (class_ptr == nullptr) { bool is_class = record_decl->getTagKind() == clang::TTK_Class; m_DB.AddPrimitive(cldb::Class(m_DB.GetName(name.c_str()), m_DB.GetName(parent_name.c_str()), is_class)); class_ptr = m_DB.GetFirstPrimitive<cldb::Class>(name.c_str()); } if (!forward_decl) { // Fill in the missing class size const clang::ASTRecordLayout& layout = m_ASTContext.getASTRecordLayout(record_decl); class_ptr->size = layout.getSize().getQuantity(); for (size_t i = 0; i < base_names.size(); i++) LOG_APPEND(ast, INFO, (i == 0) ? " : %s" : ", %s", base_names[i].text.c_str()); LOG_NEWLINE(ast); // Populate class contents AddContainedDecls(decl, name, &layout); } else { // Forward-declaration log LOG_NEWLINE(ast); } } }