From 4b9163578ac28d172a016d03640a734e0eced20a Mon Sep 17 00:00:00 2001 From: kornilova-l Date: Mon, 30 Jul 2018 13:54:59 +0800 Subject: [PATCH] Reuse generated bindings Use json format for config file Update documentation --- Dockerfile | 1 + bindgen/CustomNames.h | 10 ++ bindgen/Main.cpp | 9 ++ bindgen/Utils.h | 7 + bindgen/ir/Function.cpp | 23 ++-- bindgen/ir/Function.h | 3 +- bindgen/ir/IR.cpp | 68 +++++++--- bindgen/ir/IR.h | 22 ++- bindgen/ir/LiteralDefine.cpp | 9 +- bindgen/ir/LiteralDefine.h | 3 +- bindgen/ir/Location.h | 2 +- bindgen/ir/LocationManager.cpp | 128 +++++++++++++++++- bindgen/ir/LocationManager.h | 25 +++- bindgen/ir/Record.h | 3 +- bindgen/ir/Struct.cpp | 79 ++++++----- bindgen/ir/Struct.h | 25 ++-- bindgen/ir/TypeDef.cpp | 23 +++- bindgen/ir/TypeDef.h | 10 +- bindgen/ir/Union.cpp | 19 ++- bindgen/ir/Union.h | 9 +- bindgen/ir/VarDefine.cpp | 11 +- bindgen/ir/VarDefine.h | 2 + bindgen/ir/Variable.cpp | 8 +- bindgen/ir/Variable.h | 3 +- bindgen/ir/types/ArrayType.cpp | 4 +- bindgen/ir/types/ArrayType.h | 2 +- bindgen/ir/types/FunctionPointerType.cpp | 7 +- bindgen/ir/types/FunctionPointerType.h | 2 +- bindgen/ir/types/PointerType.cpp | 4 +- bindgen/ir/types/PointerType.h | 2 +- bindgen/ir/types/PrimitiveType.cpp | 4 +- bindgen/ir/types/PrimitiveType.h | 2 +- bindgen/ir/types/Type.h | 3 +- bindgen/visitor/TreeVisitor.cpp | 1 + docs/src/paradox/command-line-usage/index.md | 17 +-- docs/src/paradox/index.md | 1 + .../src/paradox/integrating-bindings/index.md | 25 ++++ docs/src/paradox/obtaining-bindgen/cmake.md | 2 + tests/samples/CustomNames.scala | 12 ++ tests/samples/ReuseBindings.h | 18 +++ tests/samples/ReuseBindings.json | 11 ++ tests/samples/ReuseBindings.scala | 29 ++++ tests/samples/include/CustomNames.h | 10 ++ .../org/scalanative/bindgen/BindgenSpec.scala | 7 +- .../org/scalanative/bindgen/Bindgen.scala | 1 + .../scalanative/bindgen/BindingOptions.scala | 14 +- 46 files changed, 531 insertions(+), 149 deletions(-) create mode 100644 bindgen/CustomNames.h create mode 100644 docs/src/paradox/integrating-bindings/index.md create mode 100644 tests/samples/CustomNames.scala create mode 100644 tests/samples/ReuseBindings.h create mode 100644 tests/samples/ReuseBindings.json create mode 100644 tests/samples/ReuseBindings.scala create mode 100644 tests/samples/include/CustomNames.h diff --git a/Dockerfile b/Dockerfile index 5028a2b..45d74cf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,6 +13,7 @@ RUN set -x \ g++ openjdk-8-jdk-headless sbt cmake make curl git \ zlib1g-dev \ libgc-dev libunwind8-dev libre2-dev \ + nlohmann-json-dev \ && rm -rf /var/lib/apt/lists/* ARG LLVM_VERSION=6.0 diff --git a/bindgen/CustomNames.h b/bindgen/CustomNames.h new file mode 100644 index 0000000..7955794 --- /dev/null +++ b/bindgen/CustomNames.h @@ -0,0 +1,10 @@ +struct page { + char *content; + struct page *nextStruct; +}; + +struct book { + struct page *firstPage; +}; + +typedef int myInt; diff --git a/bindgen/Main.cpp b/bindgen/Main.cpp index 8a2433d..349cace 100644 --- a/bindgen/Main.cpp +++ b/bindgen/Main.cpp @@ -26,6 +26,10 @@ int main(int argc, const char *argv[]) { llvm::cl::opt LinkName( "link", llvm::cl::cat(Category), llvm::cl::desc("Library to link with, e.g. -luv for libuv")); + llvm::cl::opt ReuseBindingsConfig( + "binding-config", llvm::cl::cat(Category), + llvm::cl::desc("Path to a config file that contains the information " + "about bindings that should be reused")); clang::tooling::CommonOptionsParser op(argc, argv, Category); clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList()); @@ -62,6 +66,11 @@ int main(int argc, const char *argv[]) { std::string resolved = getRealPath(op.getSourcePathList()[0].c_str()); LocationManager locationManager(resolved); + auto reuseBindingsConfig = ReuseBindingsConfig.getValue(); + if (!reuseBindingsConfig.empty()) { + locationManager.loadConfig(reuseBindingsConfig); + } + IR ir(libName, linkName, objectName, Package.getValue(), locationManager); DefineFinderActionFactory defineFinderActionFactory(ir); diff --git a/bindgen/Utils.h b/bindgen/Utils.h index 94596b4..7ba919c 100644 --- a/bindgen/Utils.h +++ b/bindgen/Utils.h @@ -49,6 +49,13 @@ static inline bool startsWith(const std::string &str, return str.substr(0, prefix.size()) == prefix; } +/** + * @return true if str ends with given prefix + */ +static inline bool endsWith(const std::string &str, const std::string &suffix) { + return str.substr(str.length() - suffix.size(), str.length()) == suffix; +} + template static inline bool isInstanceOf(PT *type) { auto *p = dynamic_cast(type); return p != nullptr; diff --git a/bindgen/ir/Function.cpp b/bindgen/ir/Function.cpp index 87b6f00..ace1235 100644 --- a/bindgen/ir/Function.cpp +++ b/bindgen/ir/Function.cpp @@ -2,6 +2,7 @@ #include "../Utils.h" #include "Struct.h" #include "Union.h" +#include Parameter::Parameter(std::string name, std::shared_ptr type) : TypeAndName(std::move(name), type) {} @@ -12,24 +13,26 @@ Function::Function(const std::string &name, : name(name), scalaName(name), parameters(std::move(parameters)), retType(std::move(retType)), isVariadic(isVariadic) {} -llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Function &func) { - if (func.scalaName != func.name) { - s << " @native.link(\"" << func.name << "\")\n"; +std::string +Function::getDefinition(const LocationManager &locationManager) const { + std::stringstream s; + if (scalaName != name) { + s << " @native.link(\"" << name << "\")\n"; } - s << " def " << handleReservedWords(func.scalaName) << "("; + s << " def " << handleReservedWords(scalaName) << "("; std::string sep = ""; - for (const auto ¶m : func.parameters) { + for (const auto ¶m : parameters) { s << sep << handleReservedWords(param->getName()) << ": " - << param->getType()->str(); + << param->getType()->str(locationManager); sep = ", "; } - if (func.isVariadic) { + if (isVariadic) { /* the C Iso require at least one argument in a variadic function, so * the comma is fine */ - s << ", " << func.getVarargsParameterName() << ": native.CVararg*"; + s << ", " << getVarargsParameterName() << ": native.CVararg*"; } - s << "): " << func.retType->str() << " = native.extern\n"; - return s; + s << "): " << retType->str(locationManager) << " = native.extern\n"; + return s.str(); } bool Function::usesType( diff --git a/bindgen/ir/Function.h b/bindgen/ir/Function.h index c4f4a01..edb45ef 100644 --- a/bindgen/ir/Function.h +++ b/bindgen/ir/Function.h @@ -18,8 +18,7 @@ class Function { std::vector> parameters, std::shared_ptr retType, bool isVariadic); - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const Function &func); + std::string getDefinition(const LocationManager &locationManager) const; bool usesType(std::shared_ptr type, bool stopOnTypeDefs, std::vector> &visitedTypes) const; diff --git a/bindgen/ir/IR.cpp b/bindgen/ir/IR.cpp index e3a69d7..9e03584 100644 --- a/bindgen/ir/IR.cpp +++ b/bindgen/ir/IR.cpp @@ -87,10 +87,9 @@ void IR::addVarDefine(std::string name, std::shared_ptr variable) { } bool IR::libObjEmpty() const { - return functions.empty() && !hasOutputtedDeclaration(typeDefs) && - !hasOutputtedDeclaration(structs) && - !hasOutputtedDeclaration(unions) && varDefines.empty() && - variables.empty(); + return functions.empty() && !shouldOutputType(typeDefs) && + !shouldOutputType(structs) && !shouldOutputType(unions) && + varDefines.empty() && variables.empty(); } llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { @@ -100,7 +99,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { s << "package " << ir.packageName << "\n\n"; } - if (!ir.libObjEmpty() || ir.hasOutputtedDeclaration(ir.enums) || + if (!ir.libObjEmpty() || ir.shouldOutputType(ir.enums) || !ir.literalDefines.empty()) { s << "import scala.scalanative._\n" << "import scala.scalanative.native._\n\n"; @@ -123,8 +122,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { for (const auto &typeDef : ir.typeDefs) { visitedTypes.clear(); - if (ir.shouldOutput(typeDef, visitedTypes)) { - s << *typeDef; + if (ir.shouldOutputTypeDef(typeDef, visitedTypes)) { + s << typeDef->getDefinition(ir.locationManager); } else if (typeDef->hasLocation() && isAliasForOpaqueType(typeDef.get()) && ir.locationManager.inMainFile(*typeDef->getLocation())) { @@ -138,7 +137,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { for (const auto &variable : ir.variables) { if (!variable->hasIllegalUsageOfOpaqueType()) { - s << *variable; + s << variable->getDefinition(ir.locationManager); } else { llvm::errs() << "Error: Variable " << variable->getName() << " is skipped because it has incomplete type.\n"; @@ -147,7 +146,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { for (const auto &varDefine : ir.varDefines) { if (!varDefine->hasIllegalUsageOfOpaqueType()) { - s << *varDefine; + s << varDefine->getDefinition(ir.locationManager); } else { llvm::errs() << "Error: Variable alias " << varDefine->getName() << " is skipped because it has incomplete type.\n"; @@ -162,7 +161,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { "passing structs and arrays by value.\n"; llvm::errs().flush(); } else { - s << *func; + s << func->getDefinition(ir.locationManager); } } @@ -173,16 +172,16 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { if (!ir.literalDefines.empty()) { s << "object " << ir.libName << "Defines {\n"; for (const auto &literalDefine : ir.literalDefines) { - s << *literalDefine; + s << literalDefine->getDefinition(ir.locationManager); } s << "}\n\n"; } - if (ir.hasOutputtedDeclaration(ir.enums) || ir.hasHelperMethods()) { + if (ir.shouldOutputType(ir.enums) || ir.hasHelperMethods()) { s << "import " << objectName << "._\n\n"; } - if (ir.hasOutputtedDeclaration(ir.enums)) { + if (ir.shouldOutputType(ir.enums)) { s << "object " << ir.libName << "Enums {\n"; std::string sep = ""; @@ -203,14 +202,14 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { for (const auto &st : ir.structs) { visitedTypes.clear(); if (ir.shouldOutput(st, visitedTypes) && st->hasHelperMethods()) { - s << "\n" << st->generateHelperClass(); + s << "\n" << st->generateHelperClass(ir.locationManager); } } for (const auto &u : ir.unions) { visitedTypes.clear(); if (ir.shouldOutput(u, visitedTypes) && u->hasHelperMethods()) { - s << "\n" << u->generateHelperClass(); + s << "\n" << u->generateHelperClass(ir.locationManager); } } @@ -327,7 +326,7 @@ bool IR::isTypeUsed( for (const auto &typeDef : typeDefs) { visitedTypesInner.clear(); if (typeDef->usesType(type, false, visitedTypesInner)) { - if (shouldOutput(typeDef, visitedTypes)) { + if (shouldOutputTypeDef(typeDef, visitedTypes)) { return true; } } @@ -462,13 +461,21 @@ IR::~IR() { } template -bool IR::hasOutputtedDeclaration( +bool IR::shouldOutputType( const std::vector> &declarations) const { std::vector> visitedTypes; for (const auto &declaration : declarations) { visitedTypes.clear(); - if (shouldOutput(declaration, visitedTypes)) { - return true; + auto typeDefPointer = + std::dynamic_pointer_cast(declaration); + if (typeDefPointer) { + if (shouldOutputTypeDef(typeDefPointer, visitedTypes)) { + return true; + } + } else { + if (shouldOutput(declaration, visitedTypes)) { + return true; + } } } return false; @@ -477,12 +484,31 @@ bool IR::hasOutputtedDeclaration( bool IR::shouldOutput( const std::shared_ptr &type, std::vector> &visitedTypes) const { + if (locationManager.isImported(*type->getLocation())) { + return false; + } if (isTypeUsed(type, visitedTypes)) { return true; } - if (isAliasForOpaqueType(type.get())) { + /* remove unused types from included files */ + return locationManager.inMainFile(*type->getLocation()); +} + +bool IR::shouldOutputTypeDef( + const std::shared_ptr &typeDef, + std::vector> &visitedTypes) const { + if (isTypeUsed(typeDef, visitedTypes)) { + if (typeDef->wrapperForOpaqueType()) { + /* it is not possible to get location of this typedef + * so the typedef cannot be imported from other bindings */ + return true; + } + return !locationManager.isImported(*typeDef->getLocation()); + } + if (isAliasForOpaqueType(typeDef.get())) { + /* it does not matter where unused alias for opaque type is located */ return false; } /* remove unused types from included files */ - return locationManager.inMainFile(*type->getLocation()); + return locationManager.inMainFile(*typeDef->getLocation()); } diff --git a/bindgen/ir/IR.h b/bindgen/ir/IR.h index 3d34812..9c00707 100644 --- a/bindgen/ir/IR.h +++ b/bindgen/ir/IR.h @@ -153,21 +153,31 @@ class IR { /** * @return true if the type will be printed. * Following types are not printed: + * - Types that should be imported from other bindings * - Unused types from included headers - * - Unused typedefs from main header if they reference an opaque - * type (if such typedef is used then true is returned but error - * message is printed when bindings are generated) */ bool shouldOutput(const std::shared_ptr &type, std::vector> &visitedTypes) const; /** - * @tparam T Struct or Union + * @return true if typedef will be printed. + * Following typedefs are not printed: + * - TypeDefs that should be imported from other bindings + * - Unused typedefs from included headers + * - Unused typedefs from main header if they reference an opaque + * type + */ + bool shouldOutputTypeDef( + const std::shared_ptr &typeDef, + std::vector> &visitedTypes) const; + + /** + * @tparam T one of LocatableType */ template - bool hasOutputtedDeclaration( - const std::vector> &declarations) const; + bool + shouldOutputType(const std::vector> &declarations) const; std::string libName; // name of the library std::string linkName; // name of the library to link with diff --git a/bindgen/ir/LiteralDefine.cpp b/bindgen/ir/LiteralDefine.cpp index 62a84b1..f410d7a 100644 --- a/bindgen/ir/LiteralDefine.cpp +++ b/bindgen/ir/LiteralDefine.cpp @@ -4,11 +4,10 @@ LiteralDefine::LiteralDefine(std::string name, std::string literal, std::shared_ptr type) : Define(std::move(name)), literal(std::move(literal)), type(type) {} -llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const LiteralDefine &literalDefine) { - s << " val " << literalDefine.name << ": " << literalDefine.type->str() - << " = " << literalDefine.literal << "\n"; - return s; +std::string +LiteralDefine::getDefinition(const LocationManager &locationManager) const { + return " val " + name + ": " + type->str(locationManager) + " = " + + literal + "\n"; } bool LiteralDefine::usesType( diff --git a/bindgen/ir/LiteralDefine.h b/bindgen/ir/LiteralDefine.h index 8a2ea05..831c2cd 100644 --- a/bindgen/ir/LiteralDefine.h +++ b/bindgen/ir/LiteralDefine.h @@ -10,8 +10,7 @@ class LiteralDefine : public Define { LiteralDefine(std::string name, std::string literal, std::shared_ptr type); - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const LiteralDefine &literalDefine); + std::string getDefinition(const LocationManager &locationManager) const; bool usesType(const std::shared_ptr &type, bool stopOnTypeDefs, std::vector> &visitedTypes) const; diff --git a/bindgen/ir/Location.h b/bindgen/ir/Location.h index 9383ee9..8cefc4b 100644 --- a/bindgen/ir/Location.h +++ b/bindgen/ir/Location.h @@ -12,7 +12,7 @@ class Location { int getLineNumber() const; private: - std::string path; + std::string path; // may be empty int lineNumber; }; diff --git a/bindgen/ir/LocationManager.cpp b/bindgen/ir/LocationManager.cpp index ed0bfee..dcd5af3 100644 --- a/bindgen/ir/LocationManager.cpp +++ b/bindgen/ir/LocationManager.cpp @@ -1,12 +1,138 @@ #include "LocationManager.h" +#include "../Utils.h" +#include "Enum.h" +#include "Struct.h" +#include +#include LocationManager::LocationManager(std::string mainHeaderPath) : mainHeaderPath(std::move(mainHeaderPath)) {} void LocationManager::loadConfig(const std::string &path) { - // TODO: implement + std::string realPath = getRealPath(path.c_str()); + + std::stringstream s; + std::ifstream input(realPath); + for (std::string line; getline(input, line);) { + s << line; + } + config = json::parse(s.str()); + validateConfig(config); +} + +void LocationManager::validateConfig(const json &config) const { + if (!config.is_object()) { + throw std::invalid_argument( + "Invalid configuration. Configuration should be an object."); + } + for (auto it = config.begin(); it != config.end(); ++it) { + std::string headerName = it.key(); + if (headerName.empty()) { + throw std::invalid_argument("Invalid configuration. Header name " + "should not be an empty string."); + } + json headerEntry = it.value(); + validateHeaderEntry(headerEntry); + } +} + +void LocationManager::validateHeaderEntry(const json &headerEntry) const { + if (headerEntry.is_string()) { + std::string object = headerEntry.get(); + if (object.empty()) { + throw std::invalid_argument("Invalid configuration. Each header " + "entry should contain non-empty " + "value."); + } + } else if (headerEntry.is_object()) { + std::unordered_set headerKeys = {"object", "names"}; + validateKeys(headerEntry, headerKeys); + if (headerEntry.find("object") == headerEntry.end()) { + throw std::invalid_argument("Invalid configuration. Header entry " + "that is represented as an object " + "should contain 'object' property."); + } + json object = headerEntry["object"]; + if (!object.is_string() || object.get().empty()) { + throw std::invalid_argument("Invalid configuration. 'object' " + "property should be a not empty " + "string."); + } + if (headerEntry.find("name") != headerEntry.end()) { + validateNames(headerEntry["names"]); + } + } else { + throw std::invalid_argument("Invalid configuration. Header entry " + "should be a string or an object."); + } +} + +void LocationManager::validateKeys( + const json &object, const std::unordered_set &keys) const { + for (auto it = object.begin(); it != object.end(); ++it) { + if (keys.find(it.key()) == keys.end()) { + throw std::invalid_argument( + "Invalid configuration. Unknown key: '" + it.key() + "'."); + } + } +} + +void LocationManager::validateNames(const json &names) const { + if (!names.is_object()) { + throw std::invalid_argument("Invalid configuration. Library property " + "'names' should be an object."); + } + for (auto it = names.begin(); it != names.end(); ++it) { + if (!it.value().is_string()) { + throw std::invalid_argument( + "Invalid configuration. property 'names'" + " should contain only string values."); + } + } } bool LocationManager::inMainFile(const Location &location) const { return location.getPath() == mainHeaderPath; } + +bool LocationManager::isImported(const Location &location) const { + if (location.getPath().empty()) { + return false; + } + json headerEntry = getHeaderEntry(location); + return !headerEntry.empty(); +} + +json LocationManager::getHeaderEntry(const Location &location) const { + for (auto it = config.begin(); it != config.end(); ++it) { + std::string pathToHeader = it.key(); + if (startsWith(pathToHeader, "/")) { + /* full path */ + if (location.getPath() == pathToHeader) { + return it.value(); + } + } else if (endsWith(location.getPath(), "/" + pathToHeader)) { + return it.value(); + } + } + return json::object(); +} + +std::string LocationManager::getImportedType(const Location &location, + const std::string &name) const { + json headerEntry = getHeaderEntry(location); + if (headerEntry.is_string()) { + return headerEntry.get() + "." + + handleReservedWords(replaceChar(name, " ", "_")); + } + std::string scalaObject = headerEntry["object"]; + + if (headerEntry.find("names") != headerEntry.end()) { + /* name mapping */ + json names = headerEntry["names"]; + if (names.find(name) != names.end()) { + return scalaObject + "." + names[name].get(); + } + } + return scalaObject + "." + handleReservedWords(replaceChar(name, " ", "_")); +} diff --git a/bindgen/ir/LocationManager.h b/bindgen/ir/LocationManager.h index 0071104..9d2c2e5 100644 --- a/bindgen/ir/LocationManager.h +++ b/bindgen/ir/LocationManager.h @@ -2,8 +2,12 @@ #define SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H #include "Location.h" +#include #include #include +#include + +using json = nlohmann::json; class LocationManager { public: @@ -13,9 +17,28 @@ class LocationManager { bool inMainFile(const Location &location) const; + /** + * @return true if given type is imported from another Scala object + */ + bool isImported(const Location &location) const; + + std::string getImportedType(const Location &location, + const std::string &name) const; + private: std::string mainHeaderPath; - std::unordered_map existingBindings; + json config; + + json getHeaderEntry(const Location &location) const; + + void validateConfig(const json &config) const; + + void validateHeaderEntry(const json &headerEntry) const; + + void validateNames(const json &names) const; + + void validateKeys(const json &object, + const std::unordered_set &keys) const; }; #endif // SCALA_NATIVE_BINDGEN_LOCATIONMANAGER_H diff --git a/bindgen/ir/Record.h b/bindgen/ir/Record.h index 53415a1..9384894 100644 --- a/bindgen/ir/Record.h +++ b/bindgen/ir/Record.h @@ -30,7 +30,8 @@ class Record : public LocatableType { virtual std::shared_ptr generateTypeDef() = 0; - virtual std::string generateHelperClass() const = 0; + virtual std::string + generateHelperClass(const LocationManager &locationManager) const = 0; std::string getName() const; diff --git a/bindgen/ir/Struct.cpp b/bindgen/ir/Struct.cpp index 7beba5d..f352c02 100644 --- a/bindgen/ir/Struct.cpp +++ b/bindgen/ir/Struct.cpp @@ -29,7 +29,8 @@ std::shared_ptr Struct::generateTypeDef() { } } -std::string Struct::generateHelperClass() const { +std::string +Struct::generateHelperClass(const LocationManager &locationManager) const { assert(hasHelperMethods()); std::stringstream s; std::string type = replaceChar(getTypeName(), " ", "_"); @@ -37,9 +38,9 @@ std::string Struct::generateHelperClass() const { << "])" << " extends AnyVal {\n"; if (isRepresentedAsStruct()) { - s << generateHelperClassMethodsForStructRepresentation(); + s << generateHelperClassMethodsForStructRepresentation(locationManager); } else { - s << generateHelperClassMethodsForArrayRepresentation(); + s << generateHelperClassMethodsForArrayRepresentation(locationManager); } s << " }\n\n"; @@ -61,23 +62,29 @@ bool Struct::hasHelperMethods() const { return !isPacked && !fields.empty(); } -std::string Struct::generateHelperClassMethodsForStructRepresentation() const { +std::string Struct::generateHelperClassMethodsForStructRepresentation( + const LocationManager &locationManager) const { std::stringstream s; for (unsigned fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) { if (!fields[fieldIndex]->getName().empty()) { - s << generateGetterForStructRepresentation(fieldIndex); - s << generateSetterForStructRepresentation(fieldIndex); + s << generateGetterForStructRepresentation(fieldIndex, + locationManager); + s << generateSetterForStructRepresentation(fieldIndex, + locationManager); } } return s.str(); } -std::string Struct::generateHelperClassMethodsForArrayRepresentation() const { +std::string Struct::generateHelperClassMethodsForArrayRepresentation( + const LocationManager &locationManager) const { std::stringstream s; for (unsigned fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++) { if (!fields[fieldIndex]->getName().empty()) { - s << generateGetterForArrayRepresentation(fieldIndex); - s << generateSetterForArrayRepresentation(fieldIndex); + s << generateGetterForArrayRepresentation(fieldIndex, + locationManager); + s << generateSetterForArrayRepresentation(fieldIndex, + locationManager); } } return s.str(); @@ -85,7 +92,7 @@ std::string Struct::generateHelperClassMethodsForArrayRepresentation() const { std::string Struct::getTypeName() const { return "struct " + name; } -std::string Struct::str() const { +std::string Struct::str(const LocationManager &locationManager) const { std::stringstream ss; ss << "native.CStruct" << std::to_string(fields.size()) << "["; @@ -95,12 +102,12 @@ std::string Struct::str() const { std::vector> structTypesThatShouldBeReplaced = shouldFieldBreakCycle(field); if (structTypesThatShouldBeReplaced.empty()) { - ss << field->getType()->str(); + ss << field->getType()->str(locationManager); } else { /* field type is changed to avoid cyclic types in generated code */ std::shared_ptr typeReplacement = getTypeReplacement( field->getType(), structTypesThatShouldBeReplaced); - ss << typeReplacement->str(); + ss << typeReplacement->str(locationManager); } sep = ", "; } @@ -121,11 +128,11 @@ bool Struct::operator==(const Type &other) const { return false; } -std::string -Struct::generateSetterForStructRepresentation(unsigned fieldIndex) const { +std::string Struct::generateSetterForStructRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const { std::shared_ptr field = fields[fieldIndex]; std::string setter = handleReservedWords(field->getName(), "_="); - std::string parameterType = field->getType()->str(); + std::string parameterType = field->getType()->str(locationManager); std::string value = "value"; std::vector> structTypesThatShouldBeReplaced = shouldFieldBreakCycle(field); @@ -133,7 +140,7 @@ Struct::generateSetterForStructRepresentation(unsigned fieldIndex) const { /* field type is changed to avoid cyclic types in generated code */ std::shared_ptr typeReplacement = getTypeReplacement( field->getType(), structTypesThatShouldBeReplaced); - value = value + ".cast[" + typeReplacement->str() + "]"; + value = value + ".cast[" + typeReplacement->str(locationManager) + "]"; } else if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { parameterType = "native.Ptr[" + parameterType + "]"; @@ -145,19 +152,19 @@ Struct::generateSetterForStructRepresentation(unsigned fieldIndex) const { return s.str(); } -std::string -Struct::generateGetterForStructRepresentation(unsigned fieldIndex) const { +std::string Struct::generateGetterForStructRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const { std::shared_ptr field = fields[fieldIndex]; std::string getter = handleReservedWords(field->getName()); - std::string returnType = field->getType()->str(); + std::string returnType = field->getType()->str(locationManager); std::string methodBody = "p._" + std::to_string(fieldIndex + 1); if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { returnType = "native.Ptr[" + returnType + "]"; } else if (!shouldFieldBreakCycle(field).empty()) { /* field type is changed to avoid cyclic types in generated code */ - methodBody = - "(!" + methodBody + ").cast[" + field->getType()->str() + "]"; + methodBody = "(!" + methodBody + ").cast[" + + field->getType()->str(locationManager) + "]"; } else { methodBody = "!" + methodBody; } @@ -171,11 +178,11 @@ bool Struct::isRepresentedAsStruct() const { return fields.size() <= SCALA_NATIVE_MAX_STRUCT_FIELDS && !hasBitField; } -std::string -Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const { +std::string Struct::generateSetterForArrayRepresentation( + unsigned int fieldIndex, const LocationManager &locationManager) const { std::shared_ptr field = fields[fieldIndex]; std::string setter = handleReservedWords(field->getName(), "_="); - std::string parameterType = field->getType()->str(); + std::string parameterType = field->getType()->str(locationManager); std::string value = "value"; std::string castedField = "p._1"; @@ -185,17 +192,18 @@ Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const { castedField = "(" + castedField + " + " + std::to_string(offsetInBytes) + ")"; } - castedField = "!" + castedField + ".cast[" + pointerToFieldType.str() + "]"; + castedField = "!" + castedField + ".cast[" + + pointerToFieldType.str(locationManager) + "]"; std::vector> structTypesThatShouldBeReplaced = shouldFieldBreakCycle(field); if (!structTypesThatShouldBeReplaced.empty()) { /* field type is changed to avoid cyclic types in generated code */ std::shared_ptr typeReplacement = getTypeReplacement( field->getType(), structTypesThatShouldBeReplaced); - value = value + ".cast[" + typeReplacement->str() + "]"; + value = value + ".cast[" + typeReplacement->str(locationManager) + "]"; } else if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { - parameterType = pointerToFieldType.str(); + parameterType = pointerToFieldType.str(locationManager); value = "!" + value; } std::stringstream s; @@ -205,8 +213,8 @@ Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const { return s.str(); } -std::string -Struct::generateGetterForArrayRepresentation(unsigned fieldIndex) const { +std::string Struct::generateGetterForArrayRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const { std::shared_ptr field = fields[fieldIndex]; std::string getter = handleReservedWords(field->getName()); std::string returnType; @@ -219,19 +227,20 @@ Struct::generateGetterForArrayRepresentation(unsigned fieldIndex) const { } else { methodBody = "p._1"; } - methodBody = methodBody + ".cast[" + pointerToFieldType.str() + "]"; + methodBody = + methodBody + ".cast[" + pointerToFieldType.str(locationManager) + "]"; if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { - returnType = pointerToFieldType.str(); + returnType = pointerToFieldType.str(locationManager); } else if (!shouldFieldBreakCycle(field).empty()) { /* field type is changed to avoid cyclic types in generated code */ - methodBody = - "(!" + methodBody + ").cast[" + field->getType()->str() + "]"; - returnType = field->getType()->str(); + methodBody = "(!" + methodBody + ").cast[" + + field->getType()->str(locationManager) + "]"; + returnType = field->getType()->str(locationManager); } else { methodBody = "!" + methodBody; - returnType = field->getType()->str(); + returnType = field->getType()->str(locationManager); } std::stringstream s; s << " def " << getter << ": " << returnType << " = " << methodBody diff --git a/bindgen/ir/Struct.h b/bindgen/ir/Struct.h index 61ee97b..22c8b4b 100644 --- a/bindgen/ir/Struct.h +++ b/bindgen/ir/Struct.h @@ -13,7 +13,8 @@ class Struct : public Record { std::shared_ptr generateTypeDef() override; - std::string generateHelperClass() const override; + std::string + generateHelperClass(const LocationManager &locationManager) const override; std::string getTypeName() const override; @@ -22,7 +23,7 @@ class Struct : public Record { */ bool hasHelperMethods() const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; @@ -52,22 +53,26 @@ class Struct : public Record { /** * @return helper class methods for struct that is represented as CStruct. */ - std::string generateHelperClassMethodsForStructRepresentation() const; + std::string generateHelperClassMethodsForStructRepresentation( + const LocationManager &locationManager) const; /** * @return helper class methods for struct that is represented as CArray. */ - std::string generateHelperClassMethodsForArrayRepresentation() const; + std::string generateHelperClassMethodsForArrayRepresentation( + const LocationManager &locationManager) const; - std::string - generateSetterForStructRepresentation(unsigned fieldIndex) const; + std::string generateSetterForStructRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const; - std::string - generateGetterForStructRepresentation(unsigned fieldIndex) const; + std::string generateGetterForStructRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const; - std::string generateSetterForArrayRepresentation(unsigned fieldIndex) const; + std::string generateSetterForArrayRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const; - std::string generateGetterForArrayRepresentation(unsigned fieldIndex) const; + std::string generateGetterForArrayRepresentation( + unsigned fieldIndex, const LocationManager &locationManager) const; /** * This function is used to get type replacement for a field that should diff --git a/bindgen/ir/TypeDef.cpp b/bindgen/ir/TypeDef.cpp index 22beb63..8b4040c 100644 --- a/bindgen/ir/TypeDef.cpp +++ b/bindgen/ir/TypeDef.cpp @@ -3,6 +3,7 @@ #include "Enum.h" #include "Struct.h" #include "Union.h" +#include #include TypeDef::TypeDef(std::string name, std::shared_ptr type, @@ -10,15 +11,17 @@ TypeDef::TypeDef(std::string name, std::shared_ptr type, : TypeAndName(std::move(name), std::move(type)), LocatableType(std::move(location)) {} -llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) { - s << " type " << typeDef.str() << " = "; - if (typeDef.type) { - s << typeDef.getType()->str(); +std::string +TypeDef::getDefinition(const LocationManager &locationManager) const { + std::stringstream s; + s << " type " << this->str(locationManager) << " = "; + if (type) { + s << type->str(locationManager); } else { s << "native.CStruct0 // incomplete type"; } s << "\n"; - return s; + return s.str(); } bool TypeDef::usesType( @@ -40,7 +43,13 @@ bool TypeDef::usesType( return result; } -std::string TypeDef::str() const { +std::string TypeDef::str(const LocationManager &locationManager) const { + if (hasLocation()) { + std::shared_ptr location = getLocation(); + if (locationManager.isImported(*location)) { + return locationManager.getImportedType(*location, name); + } + } return handleReservedWords(replaceChar(name, " ", "_")); } @@ -117,3 +126,5 @@ TypeDef::replaceType(const std::shared_ptr &type, return std::make_shared( name, this->type->replaceType(type, replacement), nullptr); } + +bool TypeDef::wrapperForOpaqueType() const { return !type; } diff --git a/bindgen/ir/TypeDef.h b/bindgen/ir/TypeDef.h index 755a9a3..f6808a7 100644 --- a/bindgen/ir/TypeDef.h +++ b/bindgen/ir/TypeDef.h @@ -15,14 +15,13 @@ class TypeDef : public TypeAndName, public LocatableType { TypeDef(std::string name, std::shared_ptr type, std::shared_ptr location); - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const TypeDef &type); + std::string getDefinition(const LocationManager &locationManager) const; bool usesType( const std::shared_ptr &type, bool stopOnTypeDefs, std::vector> &visitedTypes) const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; @@ -31,6 +30,11 @@ class TypeDef : public TypeAndName, public LocatableType { */ bool hasLocation() const; + /** + * @return true if typedef directly references an opaque type + */ + bool wrapperForOpaqueType() const; + bool findAllCycles( const std::shared_ptr &startStruct, CycleNode &cycleNode, std::vector> &visitedTypes) const override; diff --git a/bindgen/ir/Union.cpp b/bindgen/ir/Union.cpp index 1fd275a..3e5bf53 100644 --- a/bindgen/ir/Union.cpp +++ b/bindgen/ir/Union.cpp @@ -17,7 +17,8 @@ std::shared_ptr Union::generateTypeDef() { nullptr); } -std::string Union::generateHelperClass() const { +std::string +Union::generateHelperClass(const LocationManager &locationManager) const { assert(hasHelperMethods()); std::stringstream s; std::string type = replaceChar(getTypeName(), " ", "_"); @@ -25,8 +26,8 @@ std::string Union::generateHelperClass() const { << "(val p: native.Ptr[" << type << "]) extends AnyVal {\n"; for (const auto &field : fields) { if (!field->getName().empty()) { - s << generateGetter(field); - s << generateSetter(field); + s << generateGetter(field, locationManager); + s << generateSetter(field, locationManager); } } s << " }\n"; @@ -65,16 +66,20 @@ bool Union::usesType( return Record::usesType(type, stopOnTypeDefs, visitedTypes); } -std::string Union::generateGetter(const std::shared_ptr &field) const { +std::string +Union::generateGetter(const std::shared_ptr &field, + const LocationManager &locationManager) const { std::string getter = handleReservedWords(field->getName()); - std::string ftype = field->getType()->str(); + std::string ftype = field->getType()->str(locationManager); return " def " + getter + ": native.Ptr[" + ftype + "] = p.cast[native.Ptr[" + ftype + "]]\n"; } -std::string Union::generateSetter(const std::shared_ptr &field) const { +std::string +Union::generateSetter(const std::shared_ptr &field, + const LocationManager &locationManager) const { std::string setter = handleReservedWords(field->getName(), "_="); - std::string ftype = field->getType()->str(); + std::string ftype = field->getType()->str(locationManager); if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { return " def " + setter + "(value: native.Ptr[" + ftype + diff --git a/bindgen/ir/Union.h b/bindgen/ir/Union.h index fd778bc..6e1aba5 100644 --- a/bindgen/ir/Union.h +++ b/bindgen/ir/Union.h @@ -16,7 +16,8 @@ class Union : public Record, public ArrayType { std::shared_ptr generateTypeDef() override; - std::string generateHelperClass() const override; + std::string + generateHelperClass(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; @@ -27,9 +28,11 @@ class Union : public Record, public ArrayType { std::vector> &visitedTypes) const override; private: - std::string generateGetter(const std::shared_ptr &field) const; + std::string generateGetter(const std::shared_ptr &field, + const LocationManager &locationManager) const; - std::string generateSetter(const std::shared_ptr &field) const; + std::string generateSetter(const std::shared_ptr &field, + const LocationManager &locationManager) const; }; #endif // SCALA_NATIVE_BINDGEN_UNION_H diff --git a/bindgen/ir/VarDefine.cpp b/bindgen/ir/VarDefine.cpp index 807cb78..3c6a5a0 100644 --- a/bindgen/ir/VarDefine.cpp +++ b/bindgen/ir/VarDefine.cpp @@ -3,12 +3,11 @@ VarDefine::VarDefine(std::string name, std::shared_ptr variable) : Define(std::move(name)), variable(variable) {} -llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const VarDefine &varDefine) { - s << " @name(\"" << varDefine.variable->getName() << "\")\n" - << " val " << varDefine.getName() << ": " - << varDefine.variable->getType()->str() << " = native.extern\n"; - return s; +std::string +VarDefine::getDefinition(const LocationManager &locationManager) const { + return " @name(\"" + variable->getName() + "\")\n" + " val " + name + + ": " + variable->getType()->str(locationManager) + + " = native.extern\n"; } bool VarDefine::hasIllegalUsageOfOpaqueType() const { diff --git a/bindgen/ir/VarDefine.h b/bindgen/ir/VarDefine.h index 2c736a4..d64e966 100644 --- a/bindgen/ir/VarDefine.h +++ b/bindgen/ir/VarDefine.h @@ -11,6 +11,8 @@ class VarDefine : public Define { public: VarDefine(std::string name, std::shared_ptr variable); + std::string getDefinition(const LocationManager &locationManager) const; + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const VarDefine &varDefine); diff --git a/bindgen/ir/Variable.cpp b/bindgen/ir/Variable.cpp index 4827324..1dc2c95 100644 --- a/bindgen/ir/Variable.cpp +++ b/bindgen/ir/Variable.cpp @@ -4,10 +4,10 @@ Variable::Variable(const std::string &name, std::shared_ptr type) : TypeAndName(name, type) {} -llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Variable &variable) { - s << " val " << variable.getName() << ": " << variable.getType()->str() - << " = native.extern\n"; - return s; +std::string +Variable::getDefinition(const LocationManager &locationManager) const { + return " val " + name + ": " + type->str(locationManager) + + " = native.extern\n"; } bool Variable::hasIllegalUsageOfOpaqueType() const { diff --git a/bindgen/ir/Variable.h b/bindgen/ir/Variable.h index 08ddea7..7c8b45d 100644 --- a/bindgen/ir/Variable.h +++ b/bindgen/ir/Variable.h @@ -8,8 +8,7 @@ class Variable : public TypeAndName { public: Variable(const std::string &name, std::shared_ptr type); - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, - const Variable &variable); + std::string getDefinition(const LocationManager &locationManager) const; bool hasIllegalUsageOfOpaqueType() const; }; diff --git a/bindgen/ir/types/ArrayType.cpp b/bindgen/ir/types/ArrayType.cpp index 785770c..0fb27dd 100644 --- a/bindgen/ir/types/ArrayType.cpp +++ b/bindgen/ir/types/ArrayType.cpp @@ -5,8 +5,8 @@ ArrayType::ArrayType(std::shared_ptr elementsType, uint64_t size) : size(size), elementsType(std::move(elementsType)) {} -std::string ArrayType::str() const { - return "native.CArray[" + elementsType->str() + ", " + +std::string ArrayType::str(const LocationManager &locationManager) const { + return "native.CArray[" + elementsType->str(locationManager) + ", " + uint64ToScalaNat(size) + "]"; } diff --git a/bindgen/ir/types/ArrayType.h b/bindgen/ir/types/ArrayType.h index 187f9f3..b3e9aea 100644 --- a/bindgen/ir/types/ArrayType.h +++ b/bindgen/ir/types/ArrayType.h @@ -11,7 +11,7 @@ class ArrayType : public virtual Type { const std::shared_ptr &type, bool stopOnTypeDefs, std::vector> &visitedTypes) const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; diff --git a/bindgen/ir/types/FunctionPointerType.cpp b/bindgen/ir/types/FunctionPointerType.cpp index 3aa34f9..c5c7d5e 100644 --- a/bindgen/ir/types/FunctionPointerType.cpp +++ b/bindgen/ir/types/FunctionPointerType.cpp @@ -8,18 +8,19 @@ FunctionPointerType::FunctionPointerType( : returnType(std::move(returnType)), parametersTypes(parametersTypes), isVariadic(isVariadic) {} -std::string FunctionPointerType::str() const { +std::string +FunctionPointerType::str(const LocationManager &locationManager) const { std::stringstream ss; ss << "native.CFunctionPtr" << parametersTypes.size() << "["; for (const auto ¶meterType : parametersTypes) { - ss << parameterType->str() << ", "; + ss << parameterType->str(locationManager) << ", "; } if (isVariadic) { ss << "native.CVararg, "; } - ss << returnType->str() << "]"; + ss << returnType->str(locationManager) << "]"; return ss.str(); } diff --git a/bindgen/ir/types/FunctionPointerType.h b/bindgen/ir/types/FunctionPointerType.h index c92ed2b..bda19fb 100644 --- a/bindgen/ir/types/FunctionPointerType.h +++ b/bindgen/ir/types/FunctionPointerType.h @@ -19,7 +19,7 @@ class FunctionPointerType : public Type { const std::shared_ptr &startStruct, CycleNode &cycleNode, std::vector> &visitedTypes) const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; diff --git a/bindgen/ir/types/PointerType.cpp b/bindgen/ir/types/PointerType.cpp index 5a9bf1e..e93d7ca 100644 --- a/bindgen/ir/types/PointerType.cpp +++ b/bindgen/ir/types/PointerType.cpp @@ -4,8 +4,8 @@ PointerType::PointerType(std::shared_ptr type) : type(std::move(type)) {} -std::string PointerType::str() const { - return "native.Ptr[" + type->str() + "]"; +std::string PointerType::str(const LocationManager &locationManager) const { + return "native.Ptr[" + type->str(locationManager) + "]"; } bool PointerType::usesType( diff --git a/bindgen/ir/types/PointerType.h b/bindgen/ir/types/PointerType.h index 0263d4b..e9d6231 100644 --- a/bindgen/ir/types/PointerType.h +++ b/bindgen/ir/types/PointerType.h @@ -15,7 +15,7 @@ class PointerType : public Type { const std::shared_ptr &startStruct, CycleNode &cycleNode, std::vector> &visitedTypes) const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; diff --git a/bindgen/ir/types/PrimitiveType.cpp b/bindgen/ir/types/PrimitiveType.cpp index b4c8621..6969f85 100644 --- a/bindgen/ir/types/PrimitiveType.cpp +++ b/bindgen/ir/types/PrimitiveType.cpp @@ -4,7 +4,9 @@ PrimitiveType::PrimitiveType(std::string type) : type(std::move(type)) {} -std::string PrimitiveType::str() const { return handleReservedWords(type); } +std::string PrimitiveType::str(const LocationManager &locationManager) const { + return handleReservedWords(type); +} std::string PrimitiveType::getType() const { return type; } diff --git a/bindgen/ir/types/PrimitiveType.h b/bindgen/ir/types/PrimitiveType.h index 31fbae3..36754d1 100644 --- a/bindgen/ir/types/PrimitiveType.h +++ b/bindgen/ir/types/PrimitiveType.h @@ -17,7 +17,7 @@ class PrimitiveType : virtual public Type { const std::shared_ptr &type, bool stopOnTypeDefs, std::vector> &visitedTypes) const override; - std::string str() const override; + std::string str(const LocationManager &locationManager) const override; bool operator==(const Type &other) const override; diff --git a/bindgen/ir/types/Type.h b/bindgen/ir/types/Type.h index 9d3dcc4..409ddb2 100644 --- a/bindgen/ir/types/Type.h +++ b/bindgen/ir/types/Type.h @@ -1,6 +1,7 @@ #ifndef SCALA_NATIVE_BINDGEN_TYPE_H #define SCALA_NATIVE_BINDGEN_TYPE_H +#include "../LocationManager.h" #include #include #include @@ -21,7 +22,7 @@ struct CycleNode { */ class Type : public std::enable_shared_from_this { public: - virtual std::string str() const = 0; + virtual std::string str(const LocationManager &locationManager) const = 0; /** * @param type search type. diff --git a/bindgen/visitor/TreeVisitor.cpp b/bindgen/visitor/TreeVisitor.cpp index 2da3f66..9d4420b 100644 --- a/bindgen/visitor/TreeVisitor.cpp +++ b/bindgen/visitor/TreeVisitor.cpp @@ -1,4 +1,5 @@ #include "TreeVisitor.h" +#include "../Utils.h" bool TreeVisitor::VisitFunctionDecl(clang::FunctionDecl *func) { if (!astContext->getSourceManager().isInMainFile(func->getLocation())) { diff --git a/docs/src/paradox/command-line-usage/index.md b/docs/src/paradox/command-line-usage/index.md index 52ddcd9..0e8d9d9 100644 --- a/docs/src/paradox/command-line-usage/index.md +++ b/docs/src/paradox/command-line-usage/index.md @@ -14,12 +14,13 @@ scala-native-bindgen --name uv /usr/include/uv.h -- > uv.scala ## Bindgen Options -| Option | Description | +| Option | Description |----------------------|---------------------------------------------------------------------------------| -| `--link` | Library to link with, e.g. `--link` uv for libuv. | -| `--no-link` | Library does not require linking. | -| `--name` | Scala object name that contains bindings. Default value set to library name. | -| `--package` | Package name of generated Scala file. | -| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have given prefix. | -| `--extra-arg` | Additional argument to append to the compiler command line. | -| `--extra-arg-before` | Additional argument to prepend to the compiler command line. | +| `--link` | Library to link with, e.g. `--link` uv for libuv. +| `--no-link` | Library does not require linking. +| `--name` | Scala object name that contains bindings. Default value set to library name. +| `--package` | Package name of generated Scala file. +| `--exclude-prefix` | Functions and unused typedefs will be removed if their names have given prefix. +| `--binding-config` | Path to a config file that contains the information about bindings that should be reused. See @ref:[Integrating Bindings](../integrating-bindings/index.md) for more information. +| `--extra-arg` | Additional argument to append to the compiler command line. +| `--extra-arg-before` | Additional argument to prepend to the compiler command line. diff --git a/docs/src/paradox/index.md b/docs/src/paradox/index.md index 609e22b..2609094 100644 --- a/docs/src/paradox/index.md +++ b/docs/src/paradox/index.md @@ -4,6 +4,7 @@ * [Obtaining Bindgen](obtaining-bindgen/index.md) * [Command Line Usage](command-line-usage/index.md) +* [Integrating Bindings](integrating-bindings/index.md) * [Limitations](limitations/index.md) * [Using Generated Bindings](using-generated-bindings/README.md) diff --git a/docs/src/paradox/integrating-bindings/index.md b/docs/src/paradox/integrating-bindings/index.md new file mode 100644 index 0000000..9ee8e24 --- /dev/null +++ b/docs/src/paradox/integrating-bindings/index.md @@ -0,0 +1,25 @@ +# Integrating Bindings + +To reuse already generated bindings create a `config.json` file that defines which headers correspond to which Scala objects: +```json +{ + "_stdio.h": "scala.scalanative.native.stdio", + "/full/path/to/regexp4.h": "bindings.regexp4" +} +``` + +Bindgen assumes that type names in header files match type names in generated binding (spaces in struct, union and enum +names are replaces with underscores), but it is possible to specify custom names mapping: +```json +{ + "hiredis.h": { + "object": "bindings.hiredis.hiredis", + "names": { + "redisContext": "Context" + } + } +} +``` + +Provide a path to `config.json` to bindgen using `--binding-config` command-line option or `NativeBinding.bindingConfig` +sbt plugin option (see @ref:[Using the sbt plugin](../obtaining-bindgen/sbt-plugin.md)). diff --git a/docs/src/paradox/obtaining-bindgen/cmake.md b/docs/src/paradox/obtaining-bindgen/cmake.md index 27384b3..fd5e17d 100644 --- a/docs/src/paradox/obtaining-bindgen/cmake.md +++ b/docs/src/paradox/obtaining-bindgen/cmake.md @@ -4,6 +4,7 @@ Building `scala-native-bindgen` requires the following tools and libraries: - [CMake] version 3.9 or higher - [LLVM] and [Clang] version 5.0 or 6.0. + - [nlohmann/json] version 3.1.2 or higher ```sh # On apt-based systems @@ -52,3 +53,4 @@ sbt test [LLVM]: https://llvm.org/ [Clang]: https://clang.llvm.org/ [Scala Native setup guide]: http://www.scala-native.org/en/latest/user/setup.html + [nlohmann/json]: https://github.com/nlohmann/json diff --git a/tests/samples/CustomNames.scala b/tests/samples/CustomNames.scala new file mode 100644 index 0000000..421d1f1 --- /dev/null +++ b/tests/samples/CustomNames.scala @@ -0,0 +1,12 @@ +package org.scalanative.bindgen.samples + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("bindgentests") +@native.extern +object CustomNames { + type page = native.CStruct2[native.CString, native.Ptr[Byte]] + type book = native.CStruct1[native.Ptr[page]] + type MY_INT = native.CInt +} diff --git a/tests/samples/ReuseBindings.h b/tests/samples/ReuseBindings.h new file mode 100644 index 0000000..1278216 --- /dev/null +++ b/tests/samples/ReuseBindings.h @@ -0,0 +1,18 @@ +#include "Struct.h" +#include "include/CustomNames.h" + +void useStruct(struct point *); + +point_s returnTypedef_point_s(); // struct point will be references instead of + // point_s because clang replaces the typedef +point *returnTypedef_point(); + +typedef struct bigStruct aliasForBigStruct; + +struct usesImportedEnum { + enum pointIndex index; +}; + +void readBook(struct book *book); + +myInt getMyInt(); diff --git a/tests/samples/ReuseBindings.json b/tests/samples/ReuseBindings.json new file mode 100644 index 0000000..2c166f5 --- /dev/null +++ b/tests/samples/ReuseBindings.json @@ -0,0 +1,11 @@ +{ + "Struct.h": "org.scalanative.bindgen.samples.Struct", + "CustomNames.h": { + "object": "org.scalanative.bindgen.samples.CustomNames", + "names": { + "struct book": "book", + "struct page": "page", + "myInt": "MY_INT" + } + } +} diff --git a/tests/samples/ReuseBindings.scala b/tests/samples/ReuseBindings.scala new file mode 100644 index 0000000..ceee2c4 --- /dev/null +++ b/tests/samples/ReuseBindings.scala @@ -0,0 +1,29 @@ +package org.scalanative.bindgen.samples + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("bindgentests") +@native.extern +object ReuseBindings { + type aliasForBigStruct = org.scalanative.bindgen.samples.Struct.struct_bigStruct + type struct_usesImportedEnum = native.CStruct1[org.scalanative.bindgen.samples.Struct.enum_pointIndex] + def useStruct(anonymous0: native.Ptr[org.scalanative.bindgen.samples.Struct.struct_point]): Unit = native.extern + def returnTypedef_point_s(): native.Ptr[org.scalanative.bindgen.samples.Struct.struct_point] = native.extern + def returnTypedef_point(): native.Ptr[org.scalanative.bindgen.samples.Struct.point] = native.extern + def readBook(book: native.Ptr[org.scalanative.bindgen.samples.CustomNames.book]): Unit = native.extern + def getMyInt(): org.scalanative.bindgen.samples.CustomNames.MY_INT = native.extern +} + +import ReuseBindings._ + +object ReuseBindingsHelpers { + + implicit class struct_usesImportedEnum_ops(val p: native.Ptr[struct_usesImportedEnum]) extends AnyVal { + def index: org.scalanative.bindgen.samples.Struct.enum_pointIndex = !p._1 + def index_=(value: org.scalanative.bindgen.samples.Struct.enum_pointIndex): Unit = !p._1 = value + } + + def struct_usesImportedEnum()(implicit z: native.Zone): native.Ptr[struct_usesImportedEnum] = native.alloc[struct_usesImportedEnum] +} + diff --git a/tests/samples/include/CustomNames.h b/tests/samples/include/CustomNames.h new file mode 100644 index 0000000..7955794 --- /dev/null +++ b/tests/samples/include/CustomNames.h @@ -0,0 +1,10 @@ +struct page { + char *content; + struct page *nextStruct; +}; + +struct book { + struct page *firstPage; +}; + +typedef int myInt; diff --git a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala index 22ef245..3ed5163 100644 --- a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala +++ b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala @@ -24,12 +24,17 @@ class BindgenSpec extends FunSpec { it(s"should generate bindings for ${input.getName}") { val testName = input.getName.replace(".h", "") val expected = new File(inputDirectory, testName + ".scala") - val options = BindingOptions(input) + val config = new File(inputDirectory, testName + ".json") + var options = BindingOptions(input) .name(testName) .link("bindgentests") .packageName("org.scalanative.bindgen.samples") .excludePrefix("__") + if (config.exists()) { + options = options.bindingConfig(config) + } + bindgen.generate(options) match { case Right(binding) => assert(binding.source.trim() == contentOf(expected)) diff --git a/tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala b/tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala index bdff89b..4cead66 100644 --- a/tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala +++ b/tools/src/main/scala/org/scalanative/bindgen/Bindgen.scala @@ -34,6 +34,7 @@ class Bindgen(val executable: File) { withArgs("--exclude-prefix", excludePrefix) ++ withArgs("--extra-arg", extraArgs) ++ withArgs("--extra-arg-before", extraArgsBefore) ++ + withArgs("--binding-config", bindingConfig.map(_.getAbsolutePath)) ++ Seq(header.getAbsolutePath, "--") val cmd = Seq(executable.getAbsolutePath) ++ options diff --git a/tools/src/main/scala/org/scalanative/bindgen/BindingOptions.scala b/tools/src/main/scala/org/scalanative/bindgen/BindingOptions.scala index c6e8d54..93ae07e 100644 --- a/tools/src/main/scala/org/scalanative/bindgen/BindingOptions.scala +++ b/tools/src/main/scala/org/scalanative/bindgen/BindingOptions.scala @@ -36,6 +36,12 @@ sealed trait BindingOptions { */ def extraArgsBefore(args: String*): BindingOptions + /** + * Reuse types from already generated bindings. + * @param config file that contains information about generated bindings. + */ + def bindingConfig(config: File): BindingOptions + } object BindingOptions { @@ -51,7 +57,8 @@ object BindingOptions { excludePrefix: Option[String] = None, extraArgs: Seq[String] = Seq.empty, extraArgsBefore: Seq[String] = - Seq.empty) + Seq.empty, + bindingConfig: Option[File] = None) extends BindingOptions { override def link(library: String): BindingOptions = { @@ -85,5 +92,10 @@ object BindingOptions { copy(extraArgsBefore = extraArgsBefore ++ args) } + override def bindingConfig(config: File): BindingOptions = { + require(config.exists(), s"Config file must exist: $config") + copy(bindingConfig = Some(config)) + } + } }