diff --git a/bindgen/CMakeLists.txt b/bindgen/CMakeLists.txt index d098acb..4f617da 100644 --- a/bindgen/CMakeLists.txt +++ b/bindgen/CMakeLists.txt @@ -36,8 +36,6 @@ add_executable(bindgen defines/DefineFinderActionFactory.h TypeTranslator.h TypeTranslator.cpp - HeaderManager.h - HeaderManager.cpp CycleDetection.h Utils.h ir/IR.h @@ -72,6 +70,10 @@ add_executable(bindgen ir/types/FunctionPointerType.h ir/types/ArrayType.cpp ir/types/ArrayType.h + ir/Location.h + ir/Location.cpp + ir/LocationManager.h + ir/LocationManager.cpp ) if (STATIC_LINKING) diff --git a/bindgen/HeaderManager.cpp b/bindgen/HeaderManager.cpp deleted file mode 100644 index fae6b0b..0000000 --- a/bindgen/HeaderManager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "HeaderManager.h" -#include "Utils.h" - -#include - -#include -#include - -HeaderManager::HeaderManager() : headers() {} - -void HeaderManager::LoadConfig(std::string path) { - trim(path); - std::ifstream input(path); - - for (std::string line; getline(input, line);) { - size_t f = line.find("="); - if (f != line.npos) { - std::string header = line.substr(0, f); - std::string import = line.substr(f + 1, line.npos); - headers[header] = import; - } - } -} - -bool HeaderManager::IsStandard(std::string path) { - trim(path); - return !!headers.count(path); -} - -std::string *HeaderManager::GetImport(const std::string &include) { - return nullptr; -} diff --git a/bindgen/HeaderManager.h b/bindgen/HeaderManager.h deleted file mode 100644 index 88402e5..0000000 --- a/bindgen/HeaderManager.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -class HeaderManager { - private: - std::map headers; - - public: - HeaderManager(); - void LoadConfig(std::string path); - bool IsStandard(std::string path); - std::string *GetImport(const std::string &include); -}; diff --git a/bindgen/Main.cpp b/bindgen/Main.cpp index 272c13f..4f877d6 100644 --- a/bindgen/Main.cpp +++ b/bindgen/Main.cpp @@ -1,4 +1,5 @@ #include "defines/DefineFinderActionFactory.h" +#include "ir/LocationManager.h" #include "visitor/ScalaFrontendActionFactory.h" #include @@ -11,19 +12,7 @@ int main(int argc, const char *argv[]) { llvm::cl::opt LibName("name", llvm::cl::cat(Category), llvm::cl::desc("Library name")); - llvm::cl::opt StdHeaders( - "std-headers", llvm::cl::cat(Category), - llvm::cl::desc("Path to a file with the list of headers for which " - "bindings\n" - "will not be generated. " - "The list contains header files names\n" - "and package names that contain bindings for these " - "headers.\nExample:\n" - "math.h=scala.scalanative.native.math\n" - "stdlib.h=scala.scalanative.native.stdlib")); - llvm::cl::opt PrintHeadersLocation( - "location", llvm::cl::cat(Category), - llvm::cl::desc("Print list of parsed headers")); + llvm::cl::opt ExcludePrefix( "exclude-prefix", llvm::cl::cat(Category), llvm::cl::desc("Functions and unused typedefs will be removed if their " @@ -41,6 +30,12 @@ int main(int argc, const char *argv[]) { clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList()); + if (op.getSourcePathList().size() != 1) { + llvm::errs() << "Error: Only one file may be processed at a time.\n"; + llvm::errs().flush(); + return -1; + } + auto libName = LibName.getValue(); if (libName.empty()) { llvm::errs() @@ -64,14 +59,10 @@ int main(int argc, const char *argv[]) { objectName = "nativeLib"; } - auto stdhead = StdHeaders.getValue(); - if (!stdhead.empty()) { - headerMan.LoadConfig(stdhead); - } - - locations.clear(); + char *resolved = realpath(op.getSourcePathList()[0].c_str(), nullptr); + LocationManager locationManager(resolved); - IR ir(libName, linkName, objectName, Package.getValue()); + IR ir(libName, linkName, objectName, Package.getValue(), locationManager); DefineFinderActionFactory defineFinderActionFactory(ir); int result = Tool.run(&defineFinderActionFactory); @@ -82,15 +73,8 @@ int main(int argc, const char *argv[]) { ScalaFrontendActionFactory actionFactory(ir); result = Tool.run(&actionFactory); - auto printLoc = PrintHeadersLocation.getValue(); - if (printLoc) { - for (const auto &location : locations) { - llvm::outs() << location.c_str(); - } - } else { - ir.generate(ExcludePrefix.getValue()); - llvm::outs() << ir; - } + ir.generate(ExcludePrefix.getValue()); + llvm::outs() << ir; llvm::outs().flush(); return result; } diff --git a/bindgen/TypeTranslator.cpp b/bindgen/TypeTranslator.cpp index 42bf295..73f48fb 100644 --- a/bindgen/TypeTranslator.cpp +++ b/bindgen/TypeTranslator.cpp @@ -102,7 +102,7 @@ TypeTranslator::translateStructOrUnionOrEnum(const clang::QualType &qtpe) { /* type is not yet defined. * TypeDef with nullptr will be created. * nullptr will be replaced by actual type when the type is declared. */ - typeDef = ir.addTypeDef(nameWithoutSpace, nullptr); + typeDef = ir.addTypeDef(nameWithoutSpace, nullptr, nullptr); return typeDef; } diff --git a/bindgen/defines/DefineFinder.cpp b/bindgen/defines/DefineFinder.cpp index 578e888..08cffd7 100644 --- a/bindgen/defines/DefineFinder.cpp +++ b/bindgen/defines/DefineFinder.cpp @@ -11,6 +11,10 @@ DefineFinder::DefineFinder(IR &ir, const clang::CompilerInstance &compiler, void DefineFinder::MacroDefined(const clang::Token ¯oNameTok, const clang::MacroDirective *md) { clang::SourceManager &sm = compiler.getSourceManager(); + if (!sm.isInMainFile(md->getLocation())) { + /* include defines only from the original header */ + return; + } if (sm.isWrittenInMainFile(macroNameTok.getLocation()) && md->isDefined() && !md->getMacroInfo()->isFunctionLike()) { /* save defines only from the given header. @@ -100,6 +104,9 @@ void DefineFinder::MacroUndefined(const clang::Token ¯oNameTok, const clang::MacroDefinition &md, const clang::MacroDirective *undef) { clang::SourceManager &sm = compiler.getSourceManager(); + if (!sm.isInMainFile(undef->getLocation())) { + return; + } if (sm.isWrittenInMainFile(macroNameTok.getLocation()) && md.getMacroInfo() && !md.getMacroInfo()->isFunctionLike()) { std::string macroName = macroNameTok.getIdentifierInfo()->getName(); diff --git a/bindgen/ir/Enum.cpp b/bindgen/ir/Enum.cpp index ea01407..f83904a 100644 --- a/bindgen/ir/Enum.cpp +++ b/bindgen/ir/Enum.cpp @@ -8,15 +8,16 @@ std::string Enumerator::getName() { return name; } int64_t Enumerator::getValue() { return value; } Enum::Enum(std::string name, std::string type, - std::vector enumerators) + std::vector enumerators, + std::shared_ptr location) : PrimitiveType(std::move(type)), name(std::move(name)), - enumerators(std::move(enumerators)) {} + enumerators(std::move(enumerators)), location(std::move(location)) {} bool Enum::isAnonymous() const { return name.empty(); } std::shared_ptr Enum::generateTypeDef() { - assert(!isAnonymous()); - return std::make_shared("enum_" + name, shared_from_this()); + return std::make_shared(getTypeAlias(), shared_from_this(), + nullptr); } llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Enum &e) { @@ -49,3 +50,10 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Enum &e) { } std::string Enum::getName() const { return name; } + +std::string Enum::getTypeAlias() const { + assert(!isAnonymous()); + return "enum_" + name; +} + +std::shared_ptr Enum::getLocation() const { return location; } diff --git a/bindgen/ir/Enum.h b/bindgen/ir/Enum.h index e6e469f..14dacf3 100644 --- a/bindgen/ir/Enum.h +++ b/bindgen/ir/Enum.h @@ -21,7 +21,8 @@ class Enumerator { class Enum : public PrimitiveType, public std::enable_shared_from_this { public: Enum(std::string name, std::string type, - std::vector enumerators); + std::vector enumerators, + std::shared_ptr location); bool isAnonymous() const; @@ -31,9 +32,14 @@ class Enum : public PrimitiveType, public std::enable_shared_from_this { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Enum &e); + std::string getTypeAlias() const; + + std::shared_ptr getLocation() const; + private: std::string name; // might be empty std::vector enumerators; + std::shared_ptr location; }; #endif // SCALA_NATIVE_BINDGEN_ENUM_H diff --git a/bindgen/ir/IR.cpp b/bindgen/ir/IR.cpp index b7e7510..481f39b 100644 --- a/bindgen/ir/IR.cpp +++ b/bindgen/ir/IR.cpp @@ -2,9 +2,10 @@ #include "../Utils.h" IR::IR(std::string libName, std::string linkName, std::string objectName, - std::string packageName) + std::string packageName, const LocationManager &locationManager) : libName(std::move(libName)), linkName(std::move(linkName)), - objectName(std::move(objectName)), packageName(std::move(packageName)) {} + objectName(std::move(objectName)), locationManager(locationManager), + packageName(std::move(packageName)) {} void IR::addFunction(std::string name, std::vector parameters, std::shared_ptr retType, bool isVariadic) { @@ -13,27 +14,28 @@ void IR::addFunction(std::string name, std::vector parameters, } std::shared_ptr IR::addTypeDef(std::string name, - std::shared_ptr type) { - typeDefs.push_back(std::make_shared(std::move(name), type)); + std::shared_ptr type, + std::shared_ptr location) { + typeDefs.push_back( + std::make_shared(std::move(name), type, std::move(location))); return typeDefs.back(); } -std::shared_ptr IR::addEnum(std::string name, const std::string &type, - std::vector enumerators) { - std::shared_ptr e = - std::make_shared(std::move(name), type, std::move(enumerators)); +void IR::addEnum(std::string name, const std::string &type, + std::vector enumerators, + std::shared_ptr location) { + std::shared_ptr e = std::make_shared( + std::move(name), type, std::move(enumerators), std::move(location)); enums.push_back(e); if (!e->isAnonymous()) { typeDefs.push_back(e->generateTypeDef()); - return typeDefs.back(); } - return nullptr; } void IR::addStruct(std::string name, std::vector fields, - uint64_t typeSize) { - std::shared_ptr s = - std::make_shared(name, std::move(fields), typeSize); + uint64_t typeSize, std::shared_ptr location) { + std::shared_ptr s = std::make_shared( + name, std::move(fields), typeSize, std::move(location)); structs.push_back(s); std::shared_ptr typeDef = getTypeDefWithName("struct_" + name); if (typeDef) { @@ -45,9 +47,9 @@ void IR::addStruct(std::string name, std::vector fields, } void IR::addUnion(std::string name, std::vector fields, - uint64_t maxSize) { - std::shared_ptr u = - std::make_shared(name, std::move(fields), maxSize); + uint64_t maxSize, std::shared_ptr location) { + std::shared_ptr u = std::make_shared( + name, std::move(fields), maxSize, std::move(location)); unions.push_back(u); std::shared_ptr typeDef = getTypeDefWithName("union_" + name); if (typeDef) { @@ -76,8 +78,10 @@ void IR::addVarDefine(std::string name, std::shared_ptr variable) { } bool IR::libObjEmpty() const { - return functions.empty() && typeDefs.empty() && structs.empty() && - unions.empty() && varDefines.empty() && variables.empty(); + return functions.empty() && !hasOutputtedDeclaration(typeDefs) && + !hasOutputtedDeclaration(structs) && + !hasOutputtedDeclaration(unions) && varDefines.empty() && + variables.empty(); } llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { @@ -87,7 +91,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { s << "package " << ir.packageName << "\n\n"; } - if (!ir.libObjEmpty() || !ir.enums.empty() || !ir.literalDefines.empty()) { + if (!ir.libObjEmpty() || ir.hasOutputtedDeclaration(ir.enums) || + !ir.literalDefines.empty()) { s << "import scala.scalanative._\n" << "import scala.scalanative.native._\n\n"; } @@ -103,7 +108,9 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { << "object " << objectName << " {\n"; for (const auto &typeDef : ir.typeDefs) { - s << *typeDef; + if (ir.shouldOutput(typeDef)) { + s << *typeDef; + } } for (const auto &variable : ir.variables) { @@ -129,19 +136,18 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { s << "}\n\n"; } - if (!ir.enums.empty() || ir.hasHelperMethods()) { + if (ir.hasOutputtedDeclaration(ir.enums) || ir.hasHelperMethods()) { s << "import " << objectName << "._\n\n"; } - if (!ir.enums.empty()) { + if (ir.hasOutputtedDeclaration(ir.enums)) { s << "object " << ir.libName << "Enums {\n"; - unsigned long enumeratorsCount = ir.enums.size(); - for (unsigned long i = 0; i < enumeratorsCount; i++) { - auto e = ir.enums[i]; - s << *e; - if (i < enumeratorsCount - 1) { - s << "\n"; // space between groups of enums + std::string sep = ""; + for (const auto &e : ir.enums) { + if (ir.shouldOutput(e)) { + s << sep << *e; + sep = "\n"; } } @@ -152,13 +158,15 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { s << "object " << ir.libName << "Helpers {\n"; for (const auto &st : ir.structs) { - if (st->hasHelperMethods()) { + if (ir.shouldOutput(st) && st->hasHelperMethods()) { s << "\n" << st->generateHelperClass(); } } for (const auto &u : ir.unions) { - s << "\n" << u->generateHelperClass(); + if (ir.shouldOutput(u)) { + s << "\n" << u->generateHelperClass(); + } } s << "}\n\n"; @@ -176,13 +184,13 @@ void IR::generate(const std::string &excludePrefix) { } bool IR::hasHelperMethods() const { - if (!unions.empty()) { + if (hasOutputtedDeclaration(unions)) { /* all unions have helper methods */ return true; } for (const auto &s : structs) { - if (s->hasHelperMethods()) { + if (shouldOutput(s) && s->hasHelperMethods()) { return true; } } @@ -230,7 +238,7 @@ void IR::replaceTypeInTypeDefs(std::shared_ptr oldType, template bool IR::isTypeUsed(const std::vector &declarations, - std::shared_ptr type, bool stopOnTypeDefs) { + std::shared_ptr type, bool stopOnTypeDefs) const { for (const auto &decl : declarations) { if (decl->usesType(type, stopOnTypeDefs)) { return true; @@ -239,7 +247,7 @@ bool IR::isTypeUsed(const std::vector &declarations, return false; } -bool IR::typeIsUsedOnlyInTypeDefs(std::shared_ptr type) { +bool IR::typeIsUsedOnlyInTypeDefs(const std::shared_ptr &type) const { /* varDefines are not checked here because they are simply * aliases for variables.*/ return !( @@ -248,6 +256,48 @@ bool IR::typeIsUsedOnlyInTypeDefs(std::shared_ptr type) { isTypeUsed(literalDefines, type, true)); } +bool IR::isTypeUsed(const std::shared_ptr &type, + bool checkRecursively) const { + if (checkRecursively) { + if (isTypeUsed(functions, type, true) || + isTypeUsed(variables, type, true) || + isTypeUsed(literalDefines, type, true)) { + return true; + } + /* type is used if there exists another type that is used and that + * references this type */ + for (const auto &typeDef : typeDefs) { + if (typeDef->usesType(type, false)) { + if (shouldOutput(typeDef)) { + return true; + } + } + } + for (const auto &s : structs) { + /* stopOnTypeDefs parameter is true because because typedefs were + * checked */ + if (s->usesType(type, true)) { + if (shouldOutput(s)) { + return true; + } + } + } + for (const auto &u : unions) { + /* stopOnTypeDefs parameter is true because because typedefs were + * checked */ + if (u->usesType(type, true)) { + if (shouldOutput(u)) { + return true; + } + } + } + return false; + } else { + return !(typeIsUsedOnlyInTypeDefs(type) && + !isTypeUsed(typeDefs, type, false)); + } +} + void IR::setScalaNames() { /* Renaming according to Scala naming conventions * should happen here */ @@ -320,7 +370,7 @@ std::shared_ptr IR::addVariable(const std::string &name, return variable; } -std::shared_ptr IR::getTypeDefWithName(const std::string &name) { +std::shared_ptr IR::getTypeDefWithName(const std::string &name) const { /* nullptr is returned in 2 cases: * 1. TypeTranslator translates opaque struct/union type for which TypeDef * was not created. @@ -330,8 +380,8 @@ std::shared_ptr IR::getTypeDefWithName(const std::string &name) { } template -T IR::getDeclarationWithName(std::vector &declarations, - const std::string &name) { +T IR::getDeclarationWithName(const std::vector &declarations, + const std::string &name) const { for (auto it = declarations.begin(), end = declarations.end(); it != end; ++it) { T declaration = (*it); @@ -353,3 +403,39 @@ IR::~IR() { variables.clear(); varDefines.clear(); } + +template bool IR::inMainFile(const T &type) const { + std::shared_ptr location = type.getLocation(); + if (!location) { + /* generated TypeDef */ + auto *typeDef = dynamic_cast(&type); + assert(typeDef); + Type *innerType = typeDef->getType().get(); + if (isInstanceOf(innerType)) { + return inMainFile(*dynamic_cast(innerType)); + } + if (isInstanceOf(innerType)) { + return inMainFile(*dynamic_cast(innerType)); + } + if (isInstanceOf(innerType)) { + return inMainFile(*dynamic_cast(innerType)); + } + } + return location && locationManager.inMainFile(*location); +} + +template +bool IR::hasOutputtedDeclaration( + const std::vector> &declarations) const { + for (const auto &declaration : declarations) { + if (shouldOutput(declaration)) { + return true; + } + } + return false; +} + +template +bool IR::shouldOutput(const std::shared_ptr &type) const { + return inMainFile(*type) || isTypeUsed(type, true); +} diff --git a/bindgen/ir/IR.h b/bindgen/ir/IR.h index 1cdd953..8df9859 100644 --- a/bindgen/ir/IR.h +++ b/bindgen/ir/IR.h @@ -4,6 +4,7 @@ #include "Enum.h" #include "Function.h" #include "LiteralDefine.h" +#include "LocationManager.h" #include "PossibleVarDefine.h" #include "Struct.h" #include "TypeDef.h" @@ -15,7 +16,7 @@ class IR { public: IR(std::string libName, std::string linkName, std::string objectName, - std::string packageName); + std::string packageName, const LocationManager &locationManager); ~IR(); @@ -23,19 +24,21 @@ class IR { std::shared_ptr retType, bool isVariadic); std::shared_ptr addTypeDef(std::string name, - std::shared_ptr type); + std::shared_ptr type, + std::shared_ptr location); /** * @return type alias for the enum */ - std::shared_ptr addEnum(std::string name, const std::string &type, - std::vector enumerators); + void addEnum(std::string name, const std::string &type, + std::vector enumerators, + std::shared_ptr location); void addStruct(std::string name, std::vector fields, - uint64_t typeSize); + uint64_t typeSize, std::shared_ptr location); void addUnion(std::string name, std::vector fields, - uint64_t maxSize); + uint64_t maxSize, std::shared_ptr location); void addLiteralDefine(std::string name, std::string literal, std::shared_ptr type); @@ -66,7 +69,7 @@ class IR { */ std::string getDefineForVar(const std::string &varName) const; - std::shared_ptr getTypeDefWithName(const std::string &name); + std::shared_ptr getTypeDefWithName(const std::string &name) const; private: /** @@ -109,14 +112,22 @@ class IR { /** * @return true if given type is used only in typedefs. */ - bool typeIsUsedOnlyInTypeDefs(std::shared_ptr type); + bool typeIsUsedOnlyInTypeDefs(const std::shared_ptr &type) const; + + /** + * @param checkRecursively if this parameter is true then the method will + * output false if the type is used only in unused types + * @return true if type is used in one of declarations + */ + bool isTypeUsed(const std::shared_ptr &type, + bool checkRecursively = false) const; /** * @return true if type is used in one of given declarations. */ template bool isTypeUsed(const std::vector &declarations, - std::shared_ptr type, bool stopOnTypeDefs); + std::shared_ptr type, bool stopOnTypeDefs) const; void setScalaNames(); @@ -130,12 +141,30 @@ class IR { void filterByName(std::vector &declarations, const std::string &name); template - T getDeclarationWithName(std::vector &declarations, - const std::string &name); + T getDeclarationWithName(const std::vector &declarations, + const std::string &name) const; + + template bool inMainFile(const T &type) const; + + /** + * @tparam T Type subclass + * @return true if type is in main file or it is used by declaration from + * main file. + */ + template + bool shouldOutput(const std::shared_ptr &type) const; + + /** + * @tparam T Struct or Union + */ + template + bool hasOutputtedDeclaration( + const std::vector> &declarations) const; std::string libName; // name of the library std::string linkName; // name of the library to link with std::string objectName; // name of Scala object + const LocationManager &locationManager; std::vector> functions; std::vector> typeDefs; std::vector> structs; diff --git a/bindgen/ir/Location.cpp b/bindgen/ir/Location.cpp new file mode 100644 index 0000000..71b1798 --- /dev/null +++ b/bindgen/ir/Location.cpp @@ -0,0 +1,8 @@ +#include "Location.h" + +Location::Location(std::string path, int lineNumber) + : path(std::move(path)), lineNumber(lineNumber) {} + +std::string Location::getPath() const { return path; } + +int Location::getLineNumber() const { return lineNumber; } diff --git a/bindgen/ir/Location.h b/bindgen/ir/Location.h new file mode 100644 index 0000000..9383ee9 --- /dev/null +++ b/bindgen/ir/Location.h @@ -0,0 +1,19 @@ +#ifndef SCALA_NATIVE_BINDGEN_LOCATION_H +#define SCALA_NATIVE_BINDGEN_LOCATION_H + +#include + +class Location { + public: + Location(std::string path, int lineNumber); + + std::string getPath() const; + + int getLineNumber() const; + + private: + std::string path; + int lineNumber; +}; + +#endif // SCALA_NATIVE_BINDGEN_LOCATION_H diff --git a/bindgen/ir/LocationManager.cpp b/bindgen/ir/LocationManager.cpp new file mode 100644 index 0000000..ed0bfee --- /dev/null +++ b/bindgen/ir/LocationManager.cpp @@ -0,0 +1,12 @@ +#include "LocationManager.h" + +LocationManager::LocationManager(std::string mainHeaderPath) + : mainHeaderPath(std::move(mainHeaderPath)) {} + +void LocationManager::loadConfig(const std::string &path) { + // TODO: implement +} + +bool LocationManager::inMainFile(const Location &location) const { + return location.getPath() == mainHeaderPath; +} diff --git a/bindgen/ir/LocationManager.h b/bindgen/ir/LocationManager.h new file mode 100644 index 0000000..0071104 --- /dev/null +++ b/bindgen/ir/LocationManager.h @@ -0,0 +1,21 @@ +#ifndef SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H +#define SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H + +#include "Location.h" +#include +#include + +class LocationManager { + public: + explicit LocationManager(std::string mainHeaderPath); + + void loadConfig(const std::string &path); + + bool inMainFile(const Location &location) const; + + private: + std::string mainHeaderPath; + std::unordered_map existingBindings; +}; + +#endif // SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H diff --git a/bindgen/ir/Struct.cpp b/bindgen/ir/Struct.cpp index 5837b24..3a5a3c3 100644 --- a/bindgen/ir/Struct.cpp +++ b/bindgen/ir/Struct.cpp @@ -38,8 +38,10 @@ std::string Field::generateGetter(int fieldIndex) { return s.str(); } -StructOrUnion::StructOrUnion(std::string name, std::vector fields) - : name(std::move(name)), fields(std::move(fields)) {} +StructOrUnion::StructOrUnion(std::string name, std::vector fields, + std::shared_ptr location) + : name(std::move(name)), fields(std::move(fields)), + location(std::move(location)) {} std::string StructOrUnion::getName() const { return name; } @@ -49,7 +51,7 @@ StructOrUnion::~StructOrUnion() { } } -bool StructOrUnion::operator==(const StructOrUnion &other) const { +bool StructOrUnion::equals(const StructOrUnion &other) const { if (this == &other) { return true; } @@ -71,20 +73,28 @@ bool StructOrUnion::operator==(const StructOrUnion &other) const { return false; } -Struct::Struct(std::string name, std::vector fields, uint64_t typeSize) - : StructOrUnion(std::move(name), std::move(fields)), typeSize(typeSize) {} +std::shared_ptr StructOrUnion::getLocation() const { + return location; +} + +Struct::Struct(std::string name, std::vector fields, uint64_t typeSize, + std::shared_ptr location) + : StructOrUnion(std::move(name), std::move(fields), std::move(location)), + typeSize(typeSize) {} std::shared_ptr Struct::generateTypeDef() { if (fields.size() < SCALA_NATIVE_MAX_STRUCT_FIELDS) { - return std::make_shared(getAliasType(), shared_from_this()); + return std::make_shared(getTypeAlias(), shared_from_this(), + nullptr); } else { // There is no easy way to represent it as a struct in scala native, // have to represent it as an array and then Add helpers to help with // its manipulation return std::make_shared( - getAliasType(), + getTypeAlias(), std::make_shared(std::make_shared("Byte"), - typeSize)); + typeSize), + location); } } @@ -92,7 +102,7 @@ std::string Struct::generateHelperClass() const { assert(hasHelperMethods()); /* struct is not empty and not represented as an array */ std::stringstream s; - std::string type = getAliasType(); + std::string type = getTypeAlias(); s << " implicit class " << type << "_ops(val p: native.Ptr[" << type << "])" << " extends AnyVal {\n"; @@ -118,7 +128,7 @@ bool Struct::hasHelperMethods() const { return !fields.empty() && fields.size() < SCALA_NATIVE_MAX_STRUCT_FIELDS; } -std::string Struct::getAliasType() const { return "struct_" + name; } +std::string Struct::getTypeAlias() const { return "struct_" + name; } std::string Struct::str() const { std::stringstream ss; @@ -145,12 +155,22 @@ bool Struct::usesType(const std::shared_ptr &type, return false; } -Union::Union(std::string name, std::vector fields, uint64_t maxSize) - : StructOrUnion(std::move(name), std::move(fields)), +bool Struct::operator==(const Type &other) const { + auto *s = dynamic_cast(&other); + if (s) { + return this->equals(*s); + } + return false; +} + +Union::Union(std::string name, std::vector fields, uint64_t maxSize, + std::shared_ptr location) + : StructOrUnion(std::move(name), std::move(fields), std::move(location)), ArrayType(std::make_shared("Byte"), maxSize) {} std::shared_ptr Union::generateTypeDef() { - return std::make_shared(getTypeAlias(), shared_from_this()); + return std::make_shared(getTypeAlias(), shared_from_this(), + nullptr); } std::string Union::generateHelperClass() const { @@ -176,3 +196,11 @@ std::string Union::generateHelperClass() const { } std::string Union::getTypeAlias() const { return "union_" + name; } + +bool Union::operator==(const Type &other) const { + auto *u = dynamic_cast(&other); + if (u) { + return this->equals(*u); + } + return false; +} diff --git a/bindgen/ir/Struct.h b/bindgen/ir/Struct.h index 990c6f3..5669946 100644 --- a/bindgen/ir/Struct.h +++ b/bindgen/ir/Struct.h @@ -20,7 +20,8 @@ class Field : public TypeAndName { class StructOrUnion { public: - StructOrUnion(std::string name, std::vector fields); + StructOrUnion(std::string name, std::vector fields, + std::shared_ptr location); ~StructOrUnion(); @@ -30,24 +31,30 @@ class StructOrUnion { std::string getName() const; - bool operator==(const StructOrUnion &other) const; + bool equals(const StructOrUnion &other) const; + + std::shared_ptr getLocation() const; + + virtual std::string getTypeAlias() const = 0; protected: std::string name; std::vector fields; + std::shared_ptr location; }; class Struct : public StructOrUnion, public Type, public std::enable_shared_from_this { public: - Struct(std::string name, std::vector fields, uint64_t typeSize); + Struct(std::string name, std::vector fields, uint64_t typeSize, + std::shared_ptr location); std::shared_ptr generateTypeDef() override; std::string generateHelperClass() const override; - std::string getAliasType() const; + std::string getTypeAlias() const override; /** * @return true if helper methods will be generated for this struct @@ -59,7 +66,7 @@ class Struct : public StructOrUnion, std::string str() const override; - using StructOrUnion::operator==; + bool operator==(const Type &other) const override; private: /* type size is needed if number of fields is bigger than 22 */ @@ -70,16 +77,16 @@ class Union : public StructOrUnion, public ArrayType, public std::enable_shared_from_this { public: - Union(std::string name, std::vector fields, uint64_t maxSize); + Union(std::string name, std::vector fields, uint64_t maxSize, + std::shared_ptr location); std::shared_ptr generateTypeDef() override; std::string generateHelperClass() const override; - using StructOrUnion::operator==; + bool operator==(const Type &other) const override; - private: - std::string getTypeAlias() const; + std::string getTypeAlias() const override; }; #endif // SCALA_NATIVE_BINDGEN_STRUCT_H diff --git a/bindgen/ir/TypeAndName.cpp b/bindgen/ir/TypeAndName.cpp index e3e7eb4..b6a0fe7 100644 --- a/bindgen/ir/TypeAndName.cpp +++ b/bindgen/ir/TypeAndName.cpp @@ -11,6 +11,9 @@ std::string TypeAndName::getName() const { return name; } void TypeAndName::setType(std::shared_ptr type) { this->type = type; } bool TypeAndName::operator==(const TypeAndName &other) const { + if (this == &other) { + return true; + } return name == other.name && *type == *other.type; } diff --git a/bindgen/ir/TypeDef.cpp b/bindgen/ir/TypeDef.cpp index 3d37203..6bbafe8 100644 --- a/bindgen/ir/TypeDef.cpp +++ b/bindgen/ir/TypeDef.cpp @@ -3,12 +3,14 @@ #include "Enum.h" #include "Struct.h" -TypeDef::TypeDef(std::string name, std::shared_ptr type) - : TypeAndName(std::move(name), std::move(type)) {} +TypeDef::TypeDef(std::string name, std::shared_ptr type, + std::shared_ptr location) + : TypeAndName(std::move(name), std::move(type)), + location(std::move(location)) {} llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) { if (!typeDef.getType()) { - llvm::errs() << "Error: type declaration for " << typeDef.getName() + llvm::errs() << "Error: type definition for " << typeDef.getName() << " was not found.\n"; llvm::errs().flush(); return s; @@ -23,8 +25,10 @@ bool TypeDef::usesType(const std::shared_ptr &type, if (stopOnTypeDefs) { return false; } - return *this->type == *type || - this->type.get()->usesType(type, stopOnTypeDefs); + if (!this->type) { + return false; + } + return *this->type == *type || this->type->usesType(type, stopOnTypeDefs); } std::string TypeDef::str() const { return handleReservedWords(name); } @@ -42,3 +46,5 @@ bool TypeDef::operator==(const Type &other) const { } return false; } + +std::shared_ptr TypeDef::getLocation() const { return location; } diff --git a/bindgen/ir/TypeDef.h b/bindgen/ir/TypeDef.h index d3767a2..2ecdf18 100644 --- a/bindgen/ir/TypeDef.h +++ b/bindgen/ir/TypeDef.h @@ -1,13 +1,15 @@ #ifndef SCALA_NATIVE_BINDGEN_TYPEDEF_H #define SCALA_NATIVE_BINDGEN_TYPEDEF_H +#include "Location.h" #include "TypeAndName.h" #include #include class TypeDef : public TypeAndName, public Type { public: - TypeDef(std::string name, std::shared_ptr type); + TypeDef(std::string name, std::shared_ptr type, + std::shared_ptr location); friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &type); @@ -18,6 +20,14 @@ class TypeDef : public TypeAndName, public Type { std::string str() const override; bool operator==(const Type &other) const override; + + std::shared_ptr getLocation() const; + + private: + /** + * nullptr if type is generated. + */ + std::shared_ptr location; }; #endif // SCALA_NATIVE_BINDGEN_TYPEDEF_H diff --git a/bindgen/ir/types/ArrayType.cpp b/bindgen/ir/types/ArrayType.cpp index fc729e7..6109042 100644 --- a/bindgen/ir/types/ArrayType.cpp +++ b/bindgen/ir/types/ArrayType.cpp @@ -1,5 +1,6 @@ #include "ArrayType.h" #include "../../Utils.h" +#include "../Struct.h" ArrayType::ArrayType(std::shared_ptr elementsType, uint64_t size) : size(size), elementsType(std::move(elementsType)) {} @@ -12,19 +13,20 @@ std::string ArrayType::str() const { bool ArrayType::usesType(const std::shared_ptr &type, bool stopOnTypeDefs) const { return *elementsType == *type || - elementsType.get()->usesType(type, stopOnTypeDefs); + elementsType->usesType(type, stopOnTypeDefs); } bool ArrayType::operator==(const Type &other) const { if (this == &other) { return true; } - if (isInstanceOf(&other)) { + if (isInstanceOf(&other) && + !isInstanceOf(&other)) { auto *arrayType = dynamic_cast(&other); if (size != arrayType->size) { return false; } - return *elementsType == *arrayType->elementsType.get(); + return *elementsType == *arrayType->elementsType; } return false; } diff --git a/bindgen/ir/types/PrimitiveType.cpp b/bindgen/ir/types/PrimitiveType.cpp index c9f8821..9cd300f 100644 --- a/bindgen/ir/types/PrimitiveType.cpp +++ b/bindgen/ir/types/PrimitiveType.cpp @@ -1,5 +1,6 @@ #include "PrimitiveType.h" #include "../../Utils.h" +#include "../Enum.h" PrimitiveType::PrimitiveType(std::string type) : type(std::move(type)) {} @@ -16,7 +17,8 @@ bool PrimitiveType::operator==(const Type &other) const { if (this == &other) { return true; } - if (isInstanceOf(&other)) { + if (isInstanceOf(&other) && + !isInstanceOf(&other)) { auto *primitiveType = dynamic_cast(&other); return type == primitiveType->type; } diff --git a/bindgen/visitor/ScalaFrontendAction.h b/bindgen/visitor/ScalaFrontendAction.h index e2b2041..3a87899 100644 --- a/bindgen/visitor/ScalaFrontendAction.h +++ b/bindgen/visitor/ScalaFrontendAction.h @@ -12,7 +12,7 @@ */ class ScalaFrontendAction : public clang::ASTFrontendAction { public: - explicit ScalaFrontendAction(IR &ir); + ScalaFrontendAction(IR &ir); std::unique_ptr CreateASTConsumer(clang::CompilerInstance &CI, diff --git a/bindgen/visitor/ScalaFrontendActionFactory.h b/bindgen/visitor/ScalaFrontendActionFactory.h index ea7fb65..7a31e50 100644 --- a/bindgen/visitor/ScalaFrontendActionFactory.h +++ b/bindgen/visitor/ScalaFrontendActionFactory.h @@ -13,7 +13,7 @@ class ScalaFrontendActionFactory : public clang::tooling::FrontendActionFactory { public: - explicit ScalaFrontendActionFactory(IR &ir); + ScalaFrontendActionFactory(IR &ir); clang::FrontendAction *create() override; diff --git a/bindgen/visitor/TreeConsumer.h b/bindgen/visitor/TreeConsumer.h index 380c4eb..92217f5 100644 --- a/bindgen/visitor/TreeConsumer.h +++ b/bindgen/visitor/TreeConsumer.h @@ -18,12 +18,7 @@ class TreeConsumer : public clang::ASTConsumer { // a DeclGroupRef may have multiple Decls, so we iterate through each // one for (auto D : DG) { - std::string fpath = smanager.getFilename(D->getLocation()).str(); - - if (!headerMan.IsStandard(basename(fpath)) && !fpath.empty()) { - locations.insert(basename(fpath) + "\n"); - visitor.TraverseDecl(D); - } + visitor.TraverseDecl(D); } return true; } diff --git a/bindgen/visitor/TreeVisitor.cpp b/bindgen/visitor/TreeVisitor.cpp index 500c398..06807c6 100644 --- a/bindgen/visitor/TreeVisitor.cpp +++ b/bindgen/visitor/TreeVisitor.cpp @@ -1,10 +1,11 @@ #include "TreeVisitor.h" - -HeaderManager headerMan; - -std::set locations; +#include bool TreeVisitor::VisitFunctionDecl(clang::FunctionDecl *func) { + if (!astContext->getSourceManager().isInMainFile(func->getLocation())) { + /* include functions only from the original header */ + return true; + } std::string funcName = func->getNameInfo().getName().getAsString(); std::shared_ptr retType = typeTranslator.translate(func->getReturnType()); @@ -47,7 +48,7 @@ bool TreeVisitor::VisitTypedefDecl(clang::TypedefDecl *tpdef) { std::shared_ptr type = typeTranslator.translate(tpdef->getUnderlyingType()); if (type) { - ir.addTypeDef(name, type); + ir.addTypeDef(name, type, getLocation(tpdef)); } return true; } @@ -69,8 +70,7 @@ bool TreeVisitor::VisitEnumDecl(clang::EnumDecl *enumdecl) { std::string scalaType = typeTranslator.getTypeFromTypeMap( enumdecl->getIntegerType().getUnqualifiedType().getAsString()); - std::shared_ptr alias = - ir.addEnum(name, scalaType, std::move(enumerators)); + ir.addEnum(name, scalaType, std::move(enumerators), getLocation(enumdecl)); return true; } @@ -114,7 +114,7 @@ void TreeVisitor::handleUnion(clang::RecordDecl *record, std::string name) { fields.push_back(new Field(fname, ftype)); } - ir.addUnion(name, std::move(fields), maxSize); + ir.addUnion(name, std::move(fields), maxSize, getLocation(record)); } void TreeVisitor::handleStruct(clang::RecordDecl *record, std::string name) { @@ -153,10 +153,14 @@ void TreeVisitor::handleStruct(clang::RecordDecl *record, std::string name) { uint64_t sizeInBits = astContext->getTypeSize(record->getTypeForDecl()); assert(sizeInBits % 8 == 0); - ir.addStruct(name, std::move(fields), sizeInBits / 8); + ir.addStruct(name, std::move(fields), sizeInBits / 8, getLocation(record)); } bool TreeVisitor::VisitVarDecl(clang::VarDecl *varDecl) { + if (!astContext->getSourceManager().isInMainFile(varDecl->getLocation())) { + /* include variables only from the original header */ + return true; + } if (!varDecl->isThisDeclarationADefinition()) { std::string variableName = varDecl->getName().str(); std::shared_ptr type = @@ -171,3 +175,12 @@ bool TreeVisitor::VisitVarDecl(clang::VarDecl *varDecl) { } return true; } + +std::shared_ptr TreeVisitor::getLocation(clang::Decl *decl) { + clang::SourceManager &sm = astContext->getSourceManager(); + std::string filename = std::string(sm.getFilename(decl->getLocation())); + std::string path = realpath(filename.c_str(), nullptr); + + unsigned lineNumber = sm.getSpellingLineNumber(decl->getLocation()); + return std::make_shared(path, lineNumber); +} diff --git a/bindgen/visitor/TreeVisitor.h b/bindgen/visitor/TreeVisitor.h index d0f4907..71bda88 100644 --- a/bindgen/visitor/TreeVisitor.h +++ b/bindgen/visitor/TreeVisitor.h @@ -1,16 +1,11 @@ #pragma once #include "../CycleDetection.h" -#include "../HeaderManager.h" #include "../TypeTranslator.h" #include "../ir/IR.h" #include #include -extern HeaderManager headerMan; - -extern std::set locations; - class TreeVisitor : public clang::RecursiveASTVisitor { private: clang::ASTContext *astContext; @@ -22,11 +17,14 @@ class TreeVisitor : public clang::RecursiveASTVisitor { void handleStruct(clang::RecordDecl *record, std::string name); + std::shared_ptr getLocation(clang::Decl *decl); + public: TreeVisitor(clang::CompilerInstance *CI, IR &ir) : astContext(&(CI->getASTContext())), typeTranslator(astContext, ir), cycleDetection(typeTranslator), ir(ir) {} - virtual ~TreeVisitor() {} + + virtual ~TreeVisitor() = default; virtual bool VisitFunctionDecl(clang::FunctionDecl *func); virtual bool VisitTypedefDecl(clang::TypedefDecl *tpdef); diff --git a/tests/samples/IncludesHeader.h b/tests/samples/IncludesHeader.h new file mode 100644 index 0000000..a7887eb --- /dev/null +++ b/tests/samples/IncludesHeader.h @@ -0,0 +1,8 @@ +#include "include/included.h" + +size getSize(struct document *d); + +struct courseInfo { + char *name; + enum semester s; +}; diff --git a/tests/samples/IncludesHeader.scala b/tests/samples/IncludesHeader.scala new file mode 100644 index 0000000..7873a50 --- /dev/null +++ b/tests/samples/IncludesHeader.scala @@ -0,0 +1,51 @@ +package org.scalanative.bindgen.samples + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("bindgentests") +@native.extern +object IncludesHeader { + type size = native.CInt + type struct_metadata = native.CStruct2[native.CUnsignedInt, native.CString] + type metadata = struct_metadata + type struct_document = native.CStruct1[metadata] + type enum_semester = native.CUnsignedInt + type struct_courseInfo = native.CStruct2[native.CString, enum_semester] + def getSize(d: native.Ptr[struct_document]): size = native.extern +} + +import IncludesHeader._ + +object IncludesHeaderEnums { + final val enum_semester_AUTUMN: enum_semester = 0.toUInt + final val enum_semester_SPRING: enum_semester = 1.toUInt +} + +object IncludesHeaderHelpers { + + implicit class struct_metadata_ops(val p: native.Ptr[struct_metadata]) extends AnyVal { + def year: native.CUnsignedInt = !p._1 + def year_=(value: native.CUnsignedInt): Unit = !p._1 = value + def publisher: native.CString = !p._2 + def publisher_=(value: native.CString): Unit = !p._2 = value + } + + def struct_metadata()(implicit z: native.Zone): native.Ptr[struct_metadata] = native.alloc[struct_metadata] + + implicit class struct_document_ops(val p: native.Ptr[struct_document]) extends AnyVal { + def m: native.Ptr[metadata] = p._1 + def m_=(value: native.Ptr[metadata]): Unit = !p._1 = !value + } + + def struct_document()(implicit z: native.Zone): native.Ptr[struct_document] = native.alloc[struct_document] + + implicit class struct_courseInfo_ops(val p: native.Ptr[struct_courseInfo]) extends AnyVal { + def name: native.CString = !p._1 + def name_=(value: native.CString): Unit = !p._1 = value + def s: enum_semester = !p._2 + def s_=(value: enum_semester): Unit = !p._2 = value + } + + def struct_courseInfo()(implicit z: native.Zone): native.Ptr[struct_courseInfo] = native.alloc[struct_courseInfo] +} diff --git a/tests/samples/include/included.h b/tests/samples/include/included.h new file mode 100644 index 0000000..073fbf2 --- /dev/null +++ b/tests/samples/include/included.h @@ -0,0 +1,37 @@ +void func(); // removed + +extern int mode; // removed + +typedef int size; + +struct metadata { + unsigned year; + char *publisher; +}; + +typedef struct metadata metadata; + +struct document { + metadata m; +}; + +typedef struct document unusedTypedef; // removed + +union data { + int i; + float f; +}; // removed + +union data getData(); // removed + +enum { ONE, TWO }; // anonymous enum will be removed + +enum unusedEnum { UNUSED }; // removed + +typedef enum unusedEnum unusedEnum; // removed + +struct unusedStruct { + enum unusedEnum e; +}; // removed + +enum semester { AUTUMN, SPRING };