From e698e7fb569acb5b23958cc42f1ada396265dbc6 Mon Sep 17 00:00:00 2001 From: kornilova-l Date: Wed, 11 Jul 2018 22:22:39 +0300 Subject: [PATCH] Fix detection of cyclic structs and unions Break cycle only in one place. Support breaking cycles on complex types. Remove `avoid` parameter in TypeTranslator methods. --- bindgen/CMakeLists.txt | 1 - bindgen/CycleDetection.h | 61 ------- bindgen/TypeTranslator.cpp | 39 ++--- bindgen/TypeTranslator.h | 14 +- bindgen/Utils.h | 10 ++ bindgen/ir/Function.cpp | 13 +- bindgen/ir/Function.h | 3 +- bindgen/ir/IR.cpp | 105 +++++++----- bindgen/ir/IR.h | 13 +- bindgen/ir/LiteralDefine.cpp | 12 +- bindgen/ir/LiteralDefine.h | 4 +- bindgen/ir/Struct.cpp | 202 ++++++++++++++++++++--- bindgen/ir/Struct.h | 74 ++++++++- bindgen/ir/TypeAndName.cpp | 12 +- bindgen/ir/TypeAndName.h | 4 +- bindgen/ir/TypeDef.cpp | 49 +++++- bindgen/ir/TypeDef.h | 15 +- bindgen/ir/types/ArrayType.cpp | 30 +++- bindgen/ir/types/ArrayType.h | 11 +- bindgen/ir/types/FunctionPointerType.cpp | 74 ++++++++- bindgen/ir/types/FunctionPointerType.h | 15 +- bindgen/ir/types/PointerType.cpp | 42 ++++- bindgen/ir/types/PointerType.h | 15 +- bindgen/ir/types/PrimitiveType.cpp | 15 +- bindgen/ir/types/PrimitiveType.h | 11 +- bindgen/ir/types/Type.cpp | 21 +++ bindgen/ir/types/Type.h | 62 ++++++- bindgen/visitor/TreeVisitor.cpp | 27 +-- bindgen/visitor/TreeVisitor.h | 4 +- tests/samples/Cycles.h | 85 ++++++++++ tests/samples/Cycles.scala | 126 ++++++++++++++ 31 files changed, 921 insertions(+), 248 deletions(-) delete mode 100644 bindgen/CycleDetection.h create mode 100644 tests/samples/Cycles.h create mode 100644 tests/samples/Cycles.scala diff --git a/bindgen/CMakeLists.txt b/bindgen/CMakeLists.txt index d6db8aa..8a7a0fe 100644 --- a/bindgen/CMakeLists.txt +++ b/bindgen/CMakeLists.txt @@ -36,7 +36,6 @@ add_executable(bindgen defines/DefineFinderActionFactory.h TypeTranslator.h TypeTranslator.cpp - CycleDetection.h Utils.h ir/IR.h ir/IR.cpp diff --git a/bindgen/CycleDetection.h b/bindgen/CycleDetection.h deleted file mode 100644 index 62349f5..0000000 --- a/bindgen/CycleDetection.h +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -#include "TypeTranslator.h" - -#include -#include -#include - -class CycleDetection { - private: - TypeTranslator &tpeTransl; - - bool contains(const std::string &k) const { - return dependencies.count(k) != 0; - } - - public: - std::map> dependencies; - - explicit CycleDetection(TypeTranslator &tpeTransl_) - : tpeTransl(tpeTransl_), dependencies{} {} - - void AddDependency(const std::string &name, const clang::QualType &qtpe) { - - if (qtpe->isFunctionPointerType()) { - // TODO: function pointer - /* type translator cannot translate function type */ - return; - } - - if (qtpe->isPointerType()) { - const auto *ptr = qtpe.getTypePtr()->getAs(); - AddDependency(name, ptr->getPointeeType()); - return; - } - - std::shared_ptr type = tpeTransl.translate(qtpe); - if (type == nullptr) { - return; - } - - std::string qtpeString = type->str(); - - // Add the dependence of qtpe - if (contains(qtpeString)) { - dependencies[name].insert(dependencies[qtpeString].begin(), - dependencies[qtpeString].end()); - } - - dependencies[name].insert(qtpeString); - } - - bool isCyclic(const std::string &name) { - if (contains(name)) { - if (dependencies[name].count(name) != 0) { - return true; - } - } - return false; - } -}; diff --git a/bindgen/TypeTranslator.cpp b/bindgen/TypeTranslator.cpp index c7d334f..c01dea4 100644 --- a/bindgen/TypeTranslator.cpp +++ b/bindgen/TypeTranslator.cpp @@ -34,19 +34,17 @@ TypeTranslator::TypeTranslator(clang::ASTContext *ctx_, IR &ir) } std::shared_ptr -TypeTranslator::translateFunctionPointer(const clang::QualType &qtpe, - const std::string *avoid) { +TypeTranslator::translateFunctionPointer(const clang::QualType &qtpe) { const auto *ptr = qtpe.getTypePtr()->getAs(); const clang::QualType &inner = ptr->getPointeeType(); if (inner->isFunctionProtoType()) { const auto *fc = inner->getAs(); - std::shared_ptr returnType = - translate(fc->getReturnType(), avoid); + std::shared_ptr returnType = translate(fc->getReturnType()); std::vector> parametersTypes; for (const clang::QualType ¶m : fc->param_types()) { - parametersTypes.push_back(translate(param, avoid)); + parametersTypes.push_back(translate(param)); } return std::make_shared( @@ -61,8 +59,7 @@ TypeTranslator::translateFunctionPointer(const clang::QualType &qtpe, } std::shared_ptr -TypeTranslator::translatePointer(const clang::QualType &pte, - const std::string *avoid) { +TypeTranslator::translatePointer(const clang::QualType &pte) { if (pte->isBuiltinType()) { const clang::BuiltinType *as = pte->getAs(); @@ -81,7 +78,7 @@ TypeTranslator::translatePointer(const clang::QualType &pte, } } - return std::make_shared(translate(pte, avoid)); + return std::make_shared(translate(pte)); } std::shared_ptr @@ -120,10 +117,9 @@ TypeTranslator::translateStructOrUnion(const clang::QualType &qtpe) { } std::shared_ptr -TypeTranslator::translateConstantArray(const clang::ConstantArrayType *ar, - const std::string *avoid) { +TypeTranslator::translateConstantArray(const clang::ConstantArrayType *ar) { const uint64_t size = ar->getSize().getZExtValue(); - std::shared_ptr elementType = translate(ar->getElementType(), avoid); + std::shared_ptr elementType = translate(ar->getElementType()); if (elementType == nullptr) { llvm::errs() << "Failed to translate array type " << ar->getElementType().getAsString() << "\n"; @@ -133,30 +129,20 @@ TypeTranslator::translateConstantArray(const clang::ConstantArrayType *ar, return std::make_shared(elementType, size); } -std::shared_ptr TypeTranslator::translate(const clang::QualType &qtpe, - const std::string *avoid) { +std::shared_ptr TypeTranslator::translate(const clang::QualType &qtpe) { const clang::Type *tpe = qtpe.getTypePtr(); - if (typeEquals(tpe, avoid)) { - // This is a type that we want to avoid the usage. - // Êxample: A struct that has a pointer to itself - uint64_t sizeInBits = ctx->getTypeSize(tpe); - assert(sizeInBits % 8 == 0); - return std::make_shared( - std::make_shared("Byte"), sizeInBits / 8); - } - if (tpe->isFunctionType()) { return nullptr; } if (tpe->isFunctionPointerType()) { - return translateFunctionPointer(qtpe, avoid); + return translateFunctionPointer(qtpe); } else if (tpe->isPointerType()) { return translatePointer( - tpe->getAs()->getPointeeType(), avoid); + tpe->getAs()->getPointeeType()); } else if (qtpe->isStructureType()) { return translateStructOrUnion(qtpe); @@ -168,10 +154,9 @@ std::shared_ptr TypeTranslator::translate(const clang::QualType &qtpe, return translateStructOrUnionOrEnum(qtpe); } else if (qtpe->isConstantArrayType()) { - return translateConstantArray(ctx->getAsConstantArrayType(qtpe), avoid); + return translateConstantArray(ctx->getAsConstantArrayType(qtpe)); } else if (qtpe->isArrayType()) { - return translatePointer(ctx->getAsArrayType(qtpe)->getElementType(), - avoid); + return translatePointer(ctx->getAsArrayType(qtpe)->getElementType()); } else { auto found = typeMap.find(qtpe.getUnqualifiedType().getAsString()); diff --git a/bindgen/TypeTranslator.h b/bindgen/TypeTranslator.h index 797be08..358ebe9 100644 --- a/bindgen/TypeTranslator.h +++ b/bindgen/TypeTranslator.h @@ -10,12 +10,9 @@ class TypeTranslator { /** * @brief Translate the qualified type from c to a scala type * @param tpe The type to translate - * @param avoid A type to avoid, useful to avoid cyclic definitions inside - * structs, unions, ... * @return the type translated or nullptr if type is function type. */ - std::shared_ptr translate(const clang::QualType &tpe, - const std::string *avoid = nullptr); + std::shared_ptr translate(const clang::QualType &tpe); std::string getTypeFromTypeMap(std::string cType); @@ -33,13 +30,10 @@ class TypeTranslator { std::shared_ptr translateStructOrUnion(const clang::QualType &qtpe); - std::shared_ptr translateFunctionPointer(const clang::QualType &qtpe, - const std::string *avoid); + std::shared_ptr translateFunctionPointer(const clang::QualType &qtpe); - std::shared_ptr translatePointer(const clang::QualType &pointee, - const std::string *avoid); + std::shared_ptr translatePointer(const clang::QualType &pointee); std::shared_ptr - translateConstantArray(const clang::ConstantArrayType *ar, - const std::string *avoid); + translateConstantArray(const clang::ConstantArrayType *ar); }; diff --git a/bindgen/Utils.h b/bindgen/Utils.h index 5f39e32..f6028bd 100644 --- a/bindgen/Utils.h +++ b/bindgen/Utils.h @@ -109,4 +109,14 @@ static inline bool isAliasForOpaqueType(const Type *type) { return false; } +static inline bool contains(const Type *type, + std::vector> &types) { + for (const auto &t : types) { + if (*type == *t) { + return true; + } + } + return false; +} + #endif // UTILS_H diff --git a/bindgen/ir/Function.cpp b/bindgen/ir/Function.cpp index 4191f5a..388fff1 100644 --- a/bindgen/ir/Function.cpp +++ b/bindgen/ir/Function.cpp @@ -31,14 +31,19 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Function &func) { return s; } -bool Function::usesType(std::shared_ptr type, - bool stopOnTypeDefs) const { - if (*retType == *type || retType->usesType(type, stopOnTypeDefs)) { +bool Function::usesType( + std::shared_ptr type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + visitedTypes.clear(); + if (*retType == *type || + retType.get()->usesType(type, stopOnTypeDefs, visitedTypes)) { return true; } for (const auto ¶meter : parameters) { + visitedTypes.clear(); if (*parameter->getType() == *type || - parameter->getType()->usesType(type, stopOnTypeDefs)) { + parameter->getType().get()->usesType(type, stopOnTypeDefs, + visitedTypes)) { return true; } } diff --git a/bindgen/ir/Function.h b/bindgen/ir/Function.h index 6b13dae..c4f4a01 100644 --- a/bindgen/ir/Function.h +++ b/bindgen/ir/Function.h @@ -21,7 +21,8 @@ class Function { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Function &func); - bool usesType(std::shared_ptr type, bool stopOnTypeDefs) const; + bool usesType(std::shared_ptr type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const; std::string getName() const; diff --git a/bindgen/ir/IR.cpp b/bindgen/ir/IR.cpp index cff5349..7402731 100644 --- a/bindgen/ir/IR.cpp +++ b/bindgen/ir/IR.cpp @@ -113,8 +113,11 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { << "object " << objectName << " {\n"; } + std::vector> visitedTypes; + for (const auto &typeDef : ir.typeDefs) { - if (ir.shouldOutput(typeDef)) { + visitedTypes.clear(); + if (ir.shouldOutput(typeDef, visitedTypes)) { s << *typeDef; } else if (typeDef->hasLocation() && isAliasForOpaqueType(typeDef.get()) && @@ -178,7 +181,8 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir) { std::string sep = ""; for (const auto &e : ir.enums) { - if (ir.shouldOutput(e)) { + visitedTypes.clear(); + if (ir.shouldOutput(e, visitedTypes)) { s << sep << *e; sep = "\n"; } @@ -191,13 +195,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 (ir.shouldOutput(st) && st->hasHelperMethods()) { + visitedTypes.clear(); + if (ir.shouldOutput(st, visitedTypes) && st->hasHelperMethods()) { s << "\n" << st->generateHelperClass(); } } for (const auto &u : ir.unions) { - if (ir.shouldOutput(u) && u->hasHelperMethods()) { + visitedTypes.clear(); + if (ir.shouldOutput(u, visitedTypes) && u->hasHelperMethods()) { s << "\n" << u->generateHelperClass(); } } @@ -217,14 +223,17 @@ void IR::generate(const std::string &excludePrefix) { } bool IR::hasHelperMethods() const { + std::vector> visitedTypes; for (const auto &u : unions) { - if (shouldOutput(u) && u->hasHelperMethods()) { + visitedTypes.clear(); + if (shouldOutput(u, visitedTypes) && u->hasHelperMethods()) { return true; } } for (const auto &s : structs) { - if (shouldOutput(s) && s->hasHelperMethods()) { + visitedTypes.clear(); + if (shouldOutput(s, visitedTypes) && s->hasHelperMethods()) { return true; } } @@ -274,8 +283,10 @@ template bool IR::isTypeUsed(const std::vector &declarations, std::shared_ptr type, bool stopOnTypeDefs) const { + std::vector> visitedTypes; for (const auto &decl : declarations) { - if (decl->usesType(type, stopOnTypeDefs)) { + visitedTypes.clear(); + if (decl->usesType(type, stopOnTypeDefs, visitedTypes)) { return true; } } @@ -292,46 +303,50 @@ bool IR::typeIsUsedOnlyInTypeDefs( 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; - } +bool IR::isTypeUsed( + const std::shared_ptr &type, + std::vector> &visitedTypes) const { + if (contains(type.get(), visitedTypes)) { + return false; + } + visitedTypes.push_back(type); + 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 */ + std::vector> visitedTypesInner; + for (const auto &typeDef : typeDefs) { + visitedTypesInner.clear(); + if (typeDef->usesType(type, false, visitedTypesInner)) { + if (shouldOutput(typeDef, visitedTypes)) { + 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 &s : structs) { + /* stopOnTypeDefs parameter is true because because typedefs were + * checked */ + visitedTypesInner.clear(); + if (s->usesType(type, true, visitedTypesInner)) { + if (shouldOutput(s, visitedTypes)) { + 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; - } + } + for (const auto &u : unions) { + /* stopOnTypeDefs parameter is true because because typedefs were + * checked */ + visitedTypesInner.clear(); + if (u->usesType(type, true, visitedTypesInner)) { + if (shouldOutput(u, visitedTypes)) { + return true; } } - return false; - } else { - return !(typeIsUsedOnlyInTypeDefs(type) && - !isTypeUsed(typeDefs, type, false)); } + return false; } void IR::setScalaNames() { @@ -443,16 +458,20 @@ IR::~IR() { template bool IR::hasOutputtedDeclaration( const std::vector> &declarations) const { + std::vector> visitedTypes; for (const auto &declaration : declarations) { - if (shouldOutput(declaration)) { + visitedTypes.clear(); + if (shouldOutput(declaration, visitedTypes)) { return true; } } return false; } -bool IR::shouldOutput(const std::shared_ptr &type) const { - if (isTypeUsed(type, true)) { +bool IR::shouldOutput( + const std::shared_ptr &type, + std::vector> &visitedTypes) const { + if (isTypeUsed(type, visitedTypes)) { return true; } if (isAliasForOpaqueType(type.get())) { diff --git a/bindgen/ir/IR.h b/bindgen/ir/IR.h index cf4940f..d473c16 100644 --- a/bindgen/ir/IR.h +++ b/bindgen/ir/IR.h @@ -118,12 +118,11 @@ class IR { 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 + * @return true if type is used in one of declarations. */ - bool isTypeUsed(const std::shared_ptr &type, - bool checkRecursively = false) const; + bool + isTypeUsed(const std::shared_ptr &type, + std::vector> &visitedTypes) const; /** * @return true if type is used in one of given declarations. @@ -156,7 +155,9 @@ class IR { * 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) const; + bool + shouldOutput(const std::shared_ptr &type, + std::vector> &visitedTypes) const; /** * @tparam T Struct or Union diff --git a/bindgen/ir/LiteralDefine.cpp b/bindgen/ir/LiteralDefine.cpp index 18e925e..62a84b1 100644 --- a/bindgen/ir/LiteralDefine.cpp +++ b/bindgen/ir/LiteralDefine.cpp @@ -11,8 +11,12 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, return s; } -bool LiteralDefine::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - return *this->type == *type || - this->type.get()->usesType(type, stopOnTypeDefs); +bool LiteralDefine::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + if (*this->type == *type) { + return true; + } + visitedTypes.clear(); + return this->type->usesType(type, stopOnTypeDefs, visitedTypes); } diff --git a/bindgen/ir/LiteralDefine.h b/bindgen/ir/LiteralDefine.h index 5e46628..8a2ea05 100644 --- a/bindgen/ir/LiteralDefine.h +++ b/bindgen/ir/LiteralDefine.h @@ -13,8 +13,8 @@ class LiteralDefine : public Define { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const LiteralDefine &literalDefine); - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const; + bool usesType(const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const; private: std::string literal; diff --git a/bindgen/ir/Struct.cpp b/bindgen/ir/Struct.cpp index dc8efe9..2485053 100644 --- a/bindgen/ir/Struct.cpp +++ b/bindgen/ir/Struct.cpp @@ -1,6 +1,7 @@ #include "Struct.h" #include "../Utils.h" #include "types/ArrayType.h" +#include "types/FunctionPointerType.h" #include "types/PointerType.h" #include "types/PrimitiveType.h" #include @@ -31,6 +32,26 @@ Struct::Struct(std::string name, std::vector> fields, : StructOrUnion(std::move(name), std::move(fields), std::move(location)), typeSize(typeSize), isPacked(isPacked), hasBitField(isBitField) {} +bool StructOrUnion::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + + for (const auto &field : fields) { + if (*field->getType() == *type || + field->getType()->usesType(type, stopOnTypeDefs, visitedTypes)) { + visitedTypes.pop_back(); + return true; + } + } + visitedTypes.pop_back(); + return false; +} + std::shared_ptr Struct::generateTypeDef() { if (isRepresentedAsStruct()) { return std::make_shared(getTypeAlias(), shared_from_this(), @@ -109,7 +130,17 @@ std::string Struct::str() const { std::string sep = ""; for (const auto &field : fields) { - ss << sep << field->getType()->str(); + ss << sep; + std::vector> + structTypesThatShouldBeReplaced = shouldFieldBreakCycle(field); + if (structTypesThatShouldBeReplaced.empty()) { + ss << field->getType()->str(); + } else { + /* field type is changed to avoid cyclic types in generated code */ + std::shared_ptr typeReplacement = getTypeReplacement( + field->getType(), structTypesThatShouldBeReplaced); + ss << typeReplacement->str(); + } sep = ", "; } @@ -117,17 +148,6 @@ std::string Struct::str() const { return ss.str(); } -bool Struct::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - for (const auto &field : fields) { - if (*field->getType() == *type || - field->getType()->usesType(type, stopOnTypeDefs)) { - return true; - } - } - return false; -} - bool Struct::operator==(const Type &other) const { if (this == &other) { return true; @@ -146,8 +166,15 @@ Struct::generateSetterForStructRepresentation(unsigned fieldIndex) const { std::string setter = handleReservedWords(field->getName(), "_="); std::string parameterType = field->getType()->str(); std::string value = "value"; - if (isAliasForType(field->getType().get()) || - isAliasForType(field->getType().get())) { + 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() + "]"; + } else if (isAliasForType(field->getType().get()) || + isAliasForType(field->getType().get())) { parameterType = "native.Ptr[" + parameterType + "]"; value = "!" + value; } @@ -162,13 +189,16 @@ Struct::generateGetterForStructRepresentation(unsigned fieldIndex) const { std::shared_ptr field = fields[fieldIndex]; std::string getter = handleReservedWords(field->getName()); std::string returnType = field->getType()->str(); - std::string methodBody; + std::string methodBody = "p._" + std::to_string(fieldIndex + 1); if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { returnType = "native.Ptr[" + returnType + "]"; - methodBody = "p._" + std::to_string(fieldIndex + 1); + } else if (!shouldFieldBreakCycle(field).empty()) { + /* field type is changed to avoid cyclic types in generated code */ + methodBody = + "(!" + methodBody + ").cast[" + field->getType()->str() + "]"; } else { - methodBody = "!p._" + std::to_string(fieldIndex + 1); + methodBody = "!" + methodBody; } std::stringstream s; s << " def " << getter << ": " << returnType << " = " << methodBody @@ -184,7 +214,7 @@ std::string Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const { std::shared_ptr field = fields[fieldIndex]; std::string setter = handleReservedWords(field->getName(), "_="); - std::string parameterType; + std::string parameterType = field->getType()->str(); std::string value = "value"; std::string castedField = "p._1"; @@ -195,12 +225,17 @@ Struct::generateSetterForArrayRepresentation(unsigned int fieldIndex) const { "(" + castedField + " + " + std::to_string(offsetInBytes) + ")"; } castedField = "!" + castedField + ".cast[" + pointerToFieldType.str() + "]"; - if (isAliasForType(field->getType().get()) || - isAliasForType(field->getType().get())) { + 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() + "]"; + } else if (isAliasForType(field->getType().get()) || + isAliasForType(field->getType().get())) { parameterType = pointerToFieldType.str(); value = "!" + value; - } else { - parameterType = field->getType()->str(); } std::stringstream s; s << " def " << setter @@ -228,6 +263,11 @@ Struct::generateGetterForArrayRepresentation(unsigned fieldIndex) const { if (isAliasForType(field->getType().get()) || isAliasForType(field->getType().get())) { returnType = pointerToFieldType.str(); + } 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(); } else { methodBody = "!" + methodBody; returnType = field->getType()->str(); @@ -238,6 +278,106 @@ Struct::generateGetterForArrayRepresentation(unsigned fieldIndex) const { return s.str(); } +std::shared_ptr +Struct::getTypeReplacement(std::shared_ptr type, + std::vector> + structTypesThatShouldBeReplaced) const { + std::shared_ptr replacementType = type->unrollTypedefs(); + std::shared_ptr pointerToByte = + std::make_shared(std::make_shared("Byte")); + for (const auto &recordType : structTypesThatShouldBeReplaced) { + std::shared_ptr recordTypeDef = std::make_shared( + recordType->getTypeAlias(), recordType, nullptr); + std::shared_ptr pointerToRecord = + std::make_shared(recordTypeDef); + if (*replacementType == *pointerToRecord) { + replacementType = pointerToByte; + } else { + replacementType = + replacementType->replaceType(pointerToRecord, pointerToByte); + } + std::vector> visitedTypes; + if (replacementType->usesType(recordType, false, visitedTypes)) { + assert(isInstanceOf(replacementType.get())); + /* function pointer types may have return value or a parameter of + * value type */ + replacementType = replacementType->replaceType( + recordTypeDef, + std::make_shared("native.CStruct0")); + } + } + return replacementType; +} + +std::vector> +Struct::shouldFieldBreakCycle(const std::shared_ptr &field) const { + std::vector> structTypesThatShouldBeReplaced; + if (isAliasForType(field->getType().get()) || + isAliasForType(field->getType().get())) { + /* cycle should be broken on pointer type */ + return structTypesThatShouldBeReplaced; + } + CycleNode baseNode(shared_from_base(), field.get()); + std::vector> visitedTypes; + if (field->getType()->findAllCycles(shared_from_base(), baseNode, + visitedTypes)) { + /* one or more cycles were found but type of the filed should be + * changed if this struct has the biggest name compared to other structs + * in cycle that have fields of non-value type. + */ + for (const auto &nextCycleNode : baseNode.cycleNodes) { + std::vector namesInCycle; + if (hasBiggestName(nextCycleNode, namesInCycle)) { + structTypesThatShouldBeReplaced.push_back(nextCycleNode.s); + } + } + } + return structTypesThatShouldBeReplaced; +} + +bool Struct::findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const { + if (this == startStruct.get()) { + return true; + } + /* visitedTypes check is ignored because it is not necessary to save Struct + * and Union types in visitedTypes if it is done for TypeDefs (Struct and + * Union types can be references only through TypeDefs) */ + bool belongsToCycle = false; + for (const auto &field : fields) { + CycleNode newCycleNode(shared_from_base(), field.get()); + if (field->getType()->findAllCycles(startStruct, newCycleNode, + visitedTypes)) { + if (isAliasForType(field->getType().get()) || + isAliasForType(field->getType().get())) { + /* cycles cannot be broken on value type fields */ + newCycleNode.isValueType = true; + } + belongsToCycle = true; + cycleNode.cycleNodes.push_back(newCycleNode); + } + } + return belongsToCycle; +} + +bool Struct::hasBiggestName(const CycleNode &node, + std::vector namesInCycle) const { + if (!node.isValueType) { + namesInCycle.push_back(node.s->getTypeAlias()); + } + if (node.cycleNodes.empty()) { + std::sort(namesInCycle.begin(), namesInCycle.end()); + return getTypeAlias() >= namesInCycle.back(); + } + for (const auto &cycleNode : node.cycleNodes) { + if (hasBiggestName(cycleNode, namesInCycle)) { + return true; + } + } + 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)), @@ -278,6 +418,24 @@ bool Union::operator==(const Type &other) const { return false; } +bool Union::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + + if (ArrayType::usesType(type, stopOnTypeDefs, visitedTypes)) { + visitedTypes.pop_back(); + return true; + } + visitedTypes.pop_back(); + + return StructOrUnion::usesType(type, stopOnTypeDefs, visitedTypes); +} + std::string Union::generateGetter(const std::shared_ptr &field) const { std::string getter = handleReservedWords(field->getName()); std::string ftype = field->getType()->str(); diff --git a/bindgen/ir/Struct.h b/bindgen/ir/Struct.h index e0d1b43..805e267 100644 --- a/bindgen/ir/Struct.h +++ b/bindgen/ir/Struct.h @@ -5,6 +5,7 @@ #include "TypeAndName.h" #include "TypeDef.h" #include "types/ArrayType.h" +#include "types/PointerType.h" #include #include @@ -41,6 +42,10 @@ class StructOrUnion : public LocatableType { virtual bool hasHelperMethods() const; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; + protected: std::string name; std::vector> fields; @@ -63,13 +68,24 @@ class Struct : public StructOrUnion { */ bool hasHelperMethods() const override; - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; - std::string str() const override; bool operator==(const Type &other) const override; + /** + * If the struct belongs to one or more cycles that contain + * startStruct then field(s) that causes cycle(s) will be added to + * cycleNode.cycleNodes (as well as all other fields further in this cycle). + * @param startStruct struct that should belong to found cycles. + * @param visitedTypes is used to avoid endless cycle of function calls in + * the case of cyclic types. + * @return true if the struct belongs to one or more cycles that + * contain startStruct. + */ + bool findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const override; + private: /** type size is needed if number of fields is bigger than 22 */ uint64_t typeSize; @@ -98,6 +114,54 @@ class Struct : public StructOrUnion { std::string generateSetterForArrayRepresentation(unsigned fieldIndex) const; std::string generateGetterForArrayRepresentation(unsigned fieldIndex) const; + + /** + * This function is used to get type replacement for a field that should + * break Scala Native types cycle. + * @return copy of type where: + * - all typedefs are omitted + * - pointers to structTypesThatShouldBeReplaced are replaced with + * pointers to Byte + * - structTypesThatShouldBeReplaced value type is replaced with + * CStruct0 (it may happen in function pointer types) + */ + std::shared_ptr + getTypeReplacement(std::shared_ptr type, + std::vector> + structTypesThatShouldBeReplaced) const; + + /** + * Scala Native does not support cyclic types. + * Cycles may contain structs, typedefs, pointer types and function + * pointer types. + * Unions are represented as arrays therefore they cannot belong to cycle. + * Cycle cannot be broken on value type. There exist at least one field in + * cycle of pointer type / function pointer type (or typedef alias to one of + * these types) because otherwise cycle produces structs of + * infinite size. + * + * Type of the field may contain a pointer to a struct (or typedef + * alias to this type). + * The pointer should be replaced with a pointer to Byte in following case: + * - field belongs to a cycle + * - name of the struct is the biggest among all structs in the + * cycle that also have fields of non-value type. + * + * Note: a field may belong to one or more cycles. It is not enough to check + * only one of cycles. + * + * @return vector of structs that should be replaced by Byte type in order + * to break cycle. + */ + std::vector> + shouldFieldBreakCycle(const std::shared_ptr &field) const; + + /** + * @return true if current struct has the biggest name among all structs in + * the cycle that also have fields of non-value type. + */ + bool hasBiggestName(const CycleNode &node, + std::vector namesInCycle) const; }; class Union : public StructOrUnion, public ArrayType { @@ -113,6 +177,10 @@ class Union : public StructOrUnion, public ArrayType { std::string getTypeAlias() const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; + private: std::string generateGetter(const std::shared_ptr &field) const; diff --git a/bindgen/ir/TypeAndName.cpp b/bindgen/ir/TypeAndName.cpp index 7597bcc..4005b6b 100644 --- a/bindgen/ir/TypeAndName.cpp +++ b/bindgen/ir/TypeAndName.cpp @@ -23,8 +23,12 @@ bool TypeAndName::operator!=(const TypeAndName &other) const { return !(*this == other); } -bool TypeAndName::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - return *this->type == *type || - this->type.get()->usesType(type, stopOnTypeDefs); +bool TypeAndName::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + if (*this->type == *type) { + return true; + } + visitedTypes.clear(); + return this->type->usesType(type, stopOnTypeDefs, visitedTypes); } diff --git a/bindgen/ir/TypeAndName.h b/bindgen/ir/TypeAndName.h index 6a20964..ca120f3 100644 --- a/bindgen/ir/TypeAndName.h +++ b/bindgen/ir/TypeAndName.h @@ -23,8 +23,8 @@ class TypeAndName { bool operator!=(const TypeAndName &other) const; - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const; + bool usesType(const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const; protected: std::string name; diff --git a/bindgen/ir/TypeDef.cpp b/bindgen/ir/TypeDef.cpp index 3532b76..77595a7 100644 --- a/bindgen/ir/TypeDef.cpp +++ b/bindgen/ir/TypeDef.cpp @@ -19,15 +19,23 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) { return s; } -bool TypeDef::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { +bool TypeDef::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { if (stopOnTypeDefs) { return false; } if (!this->type) { return false; } - return *this->type == *type || this->type->usesType(type, stopOnTypeDefs); + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + bool result = *this->type == *type || + this->type->usesType(type, stopOnTypeDefs, visitedTypes); + visitedTypes.pop_back(); + return result; } std::string TypeDef::str() const { return handleReservedWords(name); } @@ -70,3 +78,38 @@ std::shared_ptr TypeDef::getLocation() const { } bool TypeDef::hasLocation() const { return location || type; } + +bool TypeDef::findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const { + if (contains(this, visitedTypes) || !type) { + return false; + } + visitedTypes.push_back(shared_from_this()); + bool result = type->findAllCycles(startStruct, cycleNode, visitedTypes); + visitedTypes.pop_back(); + return result; +} + +std::shared_ptr TypeDef::unrollTypedefs() const { + if (!type || isInstanceOf(type.get()) || + isInstanceOf(type.get()) || isInstanceOf(type.get())) { + return std::make_shared(name, type, nullptr); + } + return type->unrollTypedefs(); +} + +std::shared_ptr +TypeDef::replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + if (!this->type || isInstanceOf(this->type.get()) || + isInstanceOf(this->type.get()) || + isInstanceOf(this->type.get())) { + return std::make_shared(name, this->type, nullptr); + } + if (*this->type == *type) { + return std::make_shared(name, replacement, nullptr); + } + return std::make_shared( + name, this->type->replaceType(type, replacement), nullptr); +} diff --git a/bindgen/ir/TypeDef.h b/bindgen/ir/TypeDef.h index a3b54e5..755a9a3 100644 --- a/bindgen/ir/TypeDef.h +++ b/bindgen/ir/TypeDef.h @@ -18,8 +18,9 @@ class TypeDef : public TypeAndName, public LocatableType { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &type); - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; std::string str() const override; @@ -30,6 +31,16 @@ class TypeDef : public TypeAndName, public LocatableType { */ bool hasLocation() const; + bool findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const override; + + std::shared_ptr unrollTypedefs() const override; + + std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const override; + /** * @return location of the typedef is it is not generated otherwise * location of referenced type (struct, union or enum). diff --git a/bindgen/ir/types/ArrayType.cpp b/bindgen/ir/types/ArrayType.cpp index aa55195..fc98920 100644 --- a/bindgen/ir/types/ArrayType.cpp +++ b/bindgen/ir/types/ArrayType.cpp @@ -10,10 +10,17 @@ std::string ArrayType::str() const { uint64ToScalaNat(size) + "]"; } -bool ArrayType::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - return *elementsType == *type || - elementsType->usesType(type, stopOnTypeDefs); +bool ArrayType::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + bool result = *elementsType == *type || + elementsType->usesType(type, stopOnTypeDefs, visitedTypes); + visitedTypes.pop_back(); + return result; } bool ArrayType::operator==(const Type &other) const { @@ -29,3 +36,18 @@ bool ArrayType::operator==(const Type &other) const { } return false; } + +std::shared_ptr ArrayType::unrollTypedefs() const { + return std::make_shared(elementsType->unrollTypedefs(), size); +} + +std::shared_ptr +ArrayType::replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + + if (*elementsType == *replacement) { + return std::make_shared(replacement, size); + } + return std::make_shared( + elementsType->replaceType(type, replacement), size); +} diff --git a/bindgen/ir/types/ArrayType.h b/bindgen/ir/types/ArrayType.h index e95db04..187f9f3 100644 --- a/bindgen/ir/types/ArrayType.h +++ b/bindgen/ir/types/ArrayType.h @@ -7,13 +7,20 @@ class ArrayType : public virtual Type { public: ArrayType(std::shared_ptr elementsType, uint64_t size); - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; std::string str() const override; bool operator==(const Type &other) const override; + std::shared_ptr unrollTypedefs() const override; + + std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const override; + private: const uint64_t size; std::shared_ptr elementsType; diff --git a/bindgen/ir/types/FunctionPointerType.cpp b/bindgen/ir/types/FunctionPointerType.cpp index e7aefb8..3aa34f9 100644 --- a/bindgen/ir/types/FunctionPointerType.cpp +++ b/bindgen/ir/types/FunctionPointerType.cpp @@ -23,18 +23,28 @@ std::string FunctionPointerType::str() const { return ss.str(); } -bool FunctionPointerType::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - if (*returnType == *type || returnType->usesType(type, stopOnTypeDefs)) { +bool FunctionPointerType::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + + if (*returnType == *type || + returnType->usesType(type, stopOnTypeDefs, visitedTypes)) { + visitedTypes.pop_back(); return true; } for (const auto ¶meterType : parametersTypes) { if (*parameterType == *type || - parameterType->usesType(type, stopOnTypeDefs)) { + parameterType->usesType(type, stopOnTypeDefs, visitedTypes)) { + visitedTypes.pop_back(); return true; } } + visitedTypes.pop_back(); return false; } @@ -42,7 +52,7 @@ bool FunctionPointerType::operator==(const Type &other) const { if (this == &other) { return true; } - if (isInstanceOf(&other)) { + if (isInstanceOf(&other)) { auto *functionPointerType = dynamic_cast(&other); if (isVariadic != functionPointerType->isVariadic) { @@ -65,3 +75,57 @@ bool FunctionPointerType::operator==(const Type &other) const { } return false; } + +bool FunctionPointerType::findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const { + + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + + bool foundCycle = false; + + if (returnType->findAllCycles(startStruct, cycleNode, visitedTypes)) { + foundCycle = true; + } + + for (const auto ¶meterType : parametersTypes) { + if (parameterType->findAllCycles(startStruct, cycleNode, + visitedTypes)) { + foundCycle = true; + } + } + visitedTypes.pop_back(); + return foundCycle; +} + +std::shared_ptr FunctionPointerType::unrollTypedefs() const { + std::vector> unrolledParameterTypes; + for (const auto ¶meterType : parametersTypes) { + unrolledParameterTypes.push_back(parameterType->unrollTypedefs()); + } + return std::make_shared( + returnType->unrollTypedefs(), unrolledParameterTypes, isVariadic); +} + +std::shared_ptr FunctionPointerType::replaceType( + const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + std::shared_ptr newReturnType = returnType; + if (*returnType == *type) { + newReturnType = replacement; + } + std::vector> newParametersTypes; + for (const auto ¶meterType : parametersTypes) { + if (*parameterType == *type) { + newParametersTypes.push_back(replacement); + } else { + newParametersTypes.push_back(parameterType); + } + } + + return std::make_shared( + newReturnType, newParametersTypes, isVariadic); +} diff --git a/bindgen/ir/types/FunctionPointerType.h b/bindgen/ir/types/FunctionPointerType.h index 4aaef6d..c92ed2b 100644 --- a/bindgen/ir/types/FunctionPointerType.h +++ b/bindgen/ir/types/FunctionPointerType.h @@ -11,13 +11,24 @@ class FunctionPointerType : public Type { std::vector> ¶metersTypes, bool isVariadic); - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; + + bool findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const override; std::string str() const override; bool operator==(const Type &other) const override; + std::shared_ptr unrollTypedefs() const override; + + std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const override; + private: std::shared_ptr returnType; std::vector> parametersTypes; diff --git a/bindgen/ir/types/PointerType.cpp b/bindgen/ir/types/PointerType.cpp index 6338c36..5a9bf1e 100644 --- a/bindgen/ir/types/PointerType.cpp +++ b/bindgen/ir/types/PointerType.cpp @@ -8,10 +8,17 @@ std::string PointerType::str() const { return "native.Ptr[" + type->str() + "]"; } -bool PointerType::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { - return *this->type == *type || - this->type.get()->usesType(type, stopOnTypeDefs); +bool PointerType::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + bool result = *this->type == *type || + this->type->usesType(type, stopOnTypeDefs, visitedTypes); + visitedTypes.pop_back(); + return result; } bool PointerType::operator==(const Type &other) const { @@ -24,3 +31,30 @@ bool PointerType::operator==(const Type &other) const { } return false; } + +bool PointerType::findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const { + if (contains(this, visitedTypes)) { + return false; + } + visitedTypes.push_back(shared_from_this()); + bool result = + this->type->findAllCycles(startStruct, cycleNode, visitedTypes); + visitedTypes.pop_back(); + return result; +} + +std::shared_ptr PointerType::unrollTypedefs() const { + return std::make_shared(type->unrollTypedefs()); +} + +std::shared_ptr +PointerType::replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + if (*this->type == *type) { + return std::make_shared(replacement); + } + return std::make_shared( + this->type->replaceType(type, replacement)); +} diff --git a/bindgen/ir/types/PointerType.h b/bindgen/ir/types/PointerType.h index 32d0cfe..0263d4b 100644 --- a/bindgen/ir/types/PointerType.h +++ b/bindgen/ir/types/PointerType.h @@ -7,13 +7,24 @@ class PointerType : public Type { public: explicit PointerType(std::shared_ptr type); - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; + + bool findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const override; std::string str() const override; bool operator==(const Type &other) const override; + std::shared_ptr unrollTypedefs() const override; + + std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const override; + private: std::shared_ptr type; }; diff --git a/bindgen/ir/types/PrimitiveType.cpp b/bindgen/ir/types/PrimitiveType.cpp index b8db4a7..b4c8621 100644 --- a/bindgen/ir/types/PrimitiveType.cpp +++ b/bindgen/ir/types/PrimitiveType.cpp @@ -8,8 +8,9 @@ std::string PrimitiveType::str() const { return handleReservedWords(type); } std::string PrimitiveType::getType() const { return type; } -bool PrimitiveType::usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const { +bool PrimitiveType::usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const { return false; } @@ -23,3 +24,13 @@ bool PrimitiveType::operator==(const Type &other) const { } return false; } + +std::shared_ptr PrimitiveType::unrollTypedefs() const { + return std::make_shared(type); +} + +std::shared_ptr PrimitiveType::replaceType( + const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + return shared_from_this(); +} diff --git a/bindgen/ir/types/PrimitiveType.h b/bindgen/ir/types/PrimitiveType.h index dd539e5..31fbae3 100644 --- a/bindgen/ir/types/PrimitiveType.h +++ b/bindgen/ir/types/PrimitiveType.h @@ -13,13 +13,20 @@ class PrimitiveType : virtual public Type { std::string getType() const; - bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const override; + bool usesType( + const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const override; std::string str() const override; bool operator==(const Type &other) const override; + std::shared_ptr unrollTypedefs() const override; + + std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const override; + private: std::string type; }; diff --git a/bindgen/ir/types/Type.cpp b/bindgen/ir/types/Type.cpp index 0872bc4..1a9652c 100644 --- a/bindgen/ir/types/Type.cpp +++ b/bindgen/ir/types/Type.cpp @@ -1,3 +1,24 @@ #include "Type.h" +#include + +CycleNode::CycleNode(std::shared_ptr s, const Field *field) + : s(std::move(s)), field(field) {} bool Type::operator!=(const Type &other) const { return !(*this == other); } + +bool Type::findAllCycles( + const std::shared_ptr &startStruct, CycleNode &cycleNode, + std::vector> &visitedTypes) const { + return false; +} + +std::shared_ptr Type::unrollTypedefs() const { return nullptr; } + +std::shared_ptr +Type::replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const { + assert( + false && + "Base class implementation of replaceType method must not be executed"); + return nullptr; +} diff --git a/bindgen/ir/types/Type.h b/bindgen/ir/types/Type.h index 6f1a808..da80fce 100644 --- a/bindgen/ir/types/Type.h +++ b/bindgen/ir/types/Type.h @@ -3,6 +3,18 @@ #include #include +#include + +class Struct; +class Field; + +struct CycleNode { + CycleNode(std::shared_ptr s, const Field *field); + std::shared_ptr s; + const Field *field; + std::vector cycleNodes; + bool isValueType = false; +}; /** * Base class for types. @@ -12,18 +24,64 @@ class Type : public std::enable_shared_from_this { virtual std::string str() const = 0; /** + * @param type search type. * @param stopOnTypeDefs if this parameter is true then TypeDefs instances * will not be checked. This parameter is needed when * usages of TypeDefs are checked, it helps to avoid * false positives when usages if aliases for the * typedef are found. + * + * @return true if type uses search type. */ - virtual bool usesType(const std::shared_ptr &type, - bool stopOnTypeDefs) const = 0; + virtual bool + usesType(const std::shared_ptr &type, bool stopOnTypeDefs, + std::vector> &visitedTypes) const = 0; virtual bool operator==(const Type &other) const = 0; virtual bool operator!=(const Type &other) const; + + /** + * @param startStruct struct that should belong to found + * cycles. + * @param visitedTypes is used to avoid endless cycle of function calls in + * the case of cyclic types. + * @return true if current type belongs to one or more cycles that contain + * startStruct. If current type is struct or union then + * cycleNode is updated (see StructOrUnion::findAllCycles) + */ + virtual bool + findAllCycles(const std::shared_ptr &startStruct, + CycleNode &cycleNode, + std::vector> &visitedTypes) const; + + /** + * Execution stops at typedefs of structs and unions therefore it cannot + * stuck in infinite recursion. + * @return copy of current type in which all typedefs are omitted except + * typedefs that wrap structs, unions, enums and opaque types. + */ + virtual std::shared_ptr unrollTypedefs() const; + + /** + * Execution stops at typedefs of structs and unions therefore it cannot + * stuck in infinite recursion. + * @return copy of current type in which given type is replaced with + * replacement type. + */ + virtual std::shared_ptr + replaceType(const std::shared_ptr &type, + const std::shared_ptr &replacement) const; + + protected: + template std::shared_ptr shared_from_base() { + return std::dynamic_pointer_cast(shared_from_this()); + } + + template + std::shared_ptr shared_from_base() const { + return std::dynamic_pointer_cast(shared_from_this()); + } }; #endif // SCALA_NATIVE_BINDGEN_TYPE_H diff --git a/bindgen/visitor/TreeVisitor.cpp b/bindgen/visitor/TreeVisitor.cpp index 0f99811..bdab6d7 100644 --- a/bindgen/visitor/TreeVisitor.cpp +++ b/bindgen/visitor/TreeVisitor.cpp @@ -34,17 +34,6 @@ bool TreeVisitor::VisitFunctionDecl(clang::FunctionDecl *func) { bool TreeVisitor::VisitTypedefDecl(clang::TypedefDecl *tpdef) { std::string name = tpdef->getName(); - cycleDetection.AddDependency(name, tpdef->getUnderlyingType()); - if (cycleDetection.isCyclic(name)) { - llvm::errs() << "Error: " << name << " is cyclic\n"; - llvm::errs() << name << "\n"; - for (auto &s : cycleDetection.dependencies[name]) { - llvm::errs() << "\t" << s << "\n"; - } - llvm::errs() << cycleDetection.isCyclic(name) << "\n"; - llvm::errs().flush(); - } - std::shared_ptr type = typeTranslator.translate(tpdef->getUnderlyingType()); if (type) { @@ -104,7 +93,7 @@ void TreeVisitor::handleUnion(clang::RecordDecl *record, std::string name) { for (const clang::FieldDecl *field : record->fields()) { std::string fname = field->getNameAsString(); std::shared_ptr ftype = - typeTranslator.translate(field->getType(), &name); + typeTranslator.translate(field->getType()); fields.push_back(std::make_shared(fname, ftype)); } @@ -135,23 +124,11 @@ void TreeVisitor::handleStruct(clang::RecordDecl *record, std::string name) { isBitFieldStruct = true; } std::shared_ptr ftype = - typeTranslator.translate(field->getType(), &name); + typeTranslator.translate(field->getType()); uint64_t recordOffsetInBits = recordLayout.getFieldOffset(field->getFieldIndex()); fields.push_back(std::make_shared(field->getNameAsString(), ftype, recordOffsetInBits)); - - cycleDetection.AddDependency(newName, field->getType()); - } - - if (cycleDetection.isCyclic(newName)) { - llvm::errs() << "Error: " << newName << " is cyclic\n"; - llvm::errs() << newName << "\n"; - for (auto &s : cycleDetection.dependencies[newName]) { - llvm::errs() << "\t" << s << "\n"; - } - llvm::errs() << cycleDetection.isCyclic(newName) << "\n"; - llvm::errs().flush(); } uint64_t sizeInBits = astContext->getTypeSize(record->getTypeForDecl()); diff --git a/bindgen/visitor/TreeVisitor.h b/bindgen/visitor/TreeVisitor.h index 71bda88..483f3e2 100644 --- a/bindgen/visitor/TreeVisitor.h +++ b/bindgen/visitor/TreeVisitor.h @@ -1,6 +1,5 @@ #pragma once -#include "../CycleDetection.h" #include "../TypeTranslator.h" #include "../ir/IR.h" #include @@ -10,7 +9,6 @@ class TreeVisitor : public clang::RecursiveASTVisitor { private: clang::ASTContext *astContext; TypeTranslator typeTranslator; - CycleDetection cycleDetection; IR &ir; void handleUnion(clang::RecordDecl *record, std::string name); @@ -22,7 +20,7 @@ class TreeVisitor : public clang::RecursiveASTVisitor { public: TreeVisitor(clang::CompilerInstance *CI, IR &ir) : astContext(&(CI->getASTContext())), typeTranslator(astContext, ir), - cycleDetection(typeTranslator), ir(ir) {} + ir(ir) {} virtual ~TreeVisitor() = default; diff --git a/tests/samples/Cycles.h b/tests/samples/Cycles.h new file mode 100644 index 0000000..eb8dab5 --- /dev/null +++ b/tests/samples/Cycles.h @@ -0,0 +1,85 @@ +struct b; +struct c; + +struct a { + struct b *bb; +}; + +struct b { + struct c **cc; // type will be replace with Ptr[Ptr[Byte]] +}; + +struct c { + struct a aa; +}; + +/* function pointer type */ + +struct FuncPointerCycle2; + +struct FuncPointerCycle1 { + struct FuncPointerCycle2 *s; +}; + +struct FuncPointerCycle2 { + struct FuncPointerCycle1 *(*memberFunction)( + void); // type will be replace with CFunctionPtr0[Ptr[Byte]] +}; + +/* type has typedef alias */ + +struct TypeWithTypedef1; + +typedef struct TypeWithTypedef1 TypeWithTypedef1; + +struct TypeWithTypedef2 { + TypeWithTypedef1 *s; // replaced with Ptr[Byte] +}; + +struct TypeWithTypedef1 { + struct TypeWithTypedef2 *s; +}; + +/* two types should be replaced */ + +struct TwoTypesReplaced1; +struct TwoTypesReplaced2; + +struct TwoTypesReplaced3 { + struct TwoTypesReplaced1 *(*memberFunction)(struct TwoTypesReplaced2 *); +}; + +struct TwoTypesReplaced1 { + struct TwoTypesReplaced2 *s; +}; + +struct TwoTypesReplaced2 { + struct TwoTypesReplaced3 *s; +}; + +/* cycle contains union. + * unions are represented as arrays therefore they cannot belong to Scala Native + * cycle of types */ + +union cycleWithUnionU; + +struct cycleWithUnionS { + union cycleWithUnionU *u; +}; + +union cycleWithUnionU { + struct cycleWithUnionS *s; +}; + +/* function pointer uses value type instead of pointer type */ + +struct FuncPointerWithValueType2; + +struct FuncPointerWithValueType1 { + struct FuncPointerWithValueType2 *s; +}; + +struct FuncPointerWithValueType2 { + struct FuncPointerWithValueType1 (*memberFunction)( + void); // return type will be replaced by CStruct0 +}; \ No newline at end of file diff --git a/tests/samples/Cycles.scala b/tests/samples/Cycles.scala new file mode 100644 index 0000000..eef565c --- /dev/null +++ b/tests/samples/Cycles.scala @@ -0,0 +1,126 @@ +package org.scalanative.bindgen.samples + +import scala.scalanative._ +import scala.scalanative.native._ + +@native.link("bindgentests") +@native.extern +object Cycles { + type struct_b = native.CStruct1[native.Ptr[native.Ptr[Byte]]] + type struct_a = native.CStruct1[native.Ptr[struct_b]] + type struct_c = native.CStruct1[struct_a] + type struct_FuncPointerCycle2 = native.CStruct1[native.CFunctionPtr0[native.Ptr[Byte]]] + type struct_FuncPointerCycle1 = native.CStruct1[native.Ptr[struct_FuncPointerCycle2]] + type struct_TypeWithTypedef1 = native.CStruct1[native.Ptr[struct_TypeWithTypedef2]] + type TypeWithTypedef1 = struct_TypeWithTypedef1 + type struct_TypeWithTypedef2 = native.CStruct1[native.Ptr[Byte]] + type struct_TwoTypesReplaced1 = native.CStruct1[native.Ptr[struct_TwoTypesReplaced2]] + type struct_TwoTypesReplaced2 = native.CStruct1[native.Ptr[struct_TwoTypesReplaced3]] + type struct_TwoTypesReplaced3 = native.CStruct1[native.CFunctionPtr1[native.Ptr[Byte], native.Ptr[Byte]]] + type union_cycleWithUnionU = native.CArray[Byte, native.Nat._8] + type struct_cycleWithUnionS = native.CStruct1[native.Ptr[union_cycleWithUnionU]] + type struct_FuncPointerWithValueType2 = native.CStruct1[native.CFunctionPtr0[native.CStruct0]] + type struct_FuncPointerWithValueType1 = native.CStruct1[native.Ptr[struct_FuncPointerWithValueType2]] +} + +import Cycles._ + +object CyclesHelpers { + + implicit class struct_a_ops(val p: native.Ptr[struct_a]) extends AnyVal { + def bb: native.Ptr[struct_b] = !p._1 + def bb_=(value: native.Ptr[struct_b]): Unit = !p._1 = value + } + + def struct_a()(implicit z: native.Zone): native.Ptr[struct_a] = native.alloc[struct_a] + + implicit class struct_b_ops(val p: native.Ptr[struct_b]) extends AnyVal { + def cc: native.Ptr[native.Ptr[struct_c]] = (!p._1).cast[native.Ptr[native.Ptr[struct_c]]] + def cc_=(value: native.Ptr[native.Ptr[struct_c]]): Unit = !p._1 = value.cast[native.Ptr[native.Ptr[Byte]]] + } + + def struct_b()(implicit z: native.Zone): native.Ptr[struct_b] = native.alloc[struct_b] + + implicit class struct_c_ops(val p: native.Ptr[struct_c]) extends AnyVal { + def aa: native.Ptr[struct_a] = p._1 + def aa_=(value: native.Ptr[struct_a]): Unit = !p._1 = !value + } + + def struct_c()(implicit z: native.Zone): native.Ptr[struct_c] = native.alloc[struct_c] + + implicit class struct_FuncPointerCycle1_ops(val p: native.Ptr[struct_FuncPointerCycle1]) extends AnyVal { + def s: native.Ptr[struct_FuncPointerCycle2] = !p._1 + def s_=(value: native.Ptr[struct_FuncPointerCycle2]): Unit = !p._1 = value + } + + def struct_FuncPointerCycle1()(implicit z: native.Zone): native.Ptr[struct_FuncPointerCycle1] = native.alloc[struct_FuncPointerCycle1] + + implicit class struct_FuncPointerCycle2_ops(val p: native.Ptr[struct_FuncPointerCycle2]) extends AnyVal { + def memberFunction: native.CFunctionPtr0[native.Ptr[struct_FuncPointerCycle1]] = (!p._1).cast[native.CFunctionPtr0[native.Ptr[struct_FuncPointerCycle1]]] + def memberFunction_=(value: native.CFunctionPtr0[native.Ptr[struct_FuncPointerCycle1]]): Unit = !p._1 = value.cast[native.CFunctionPtr0[native.Ptr[Byte]]] + } + + def struct_FuncPointerCycle2()(implicit z: native.Zone): native.Ptr[struct_FuncPointerCycle2] = native.alloc[struct_FuncPointerCycle2] + + implicit class struct_TypeWithTypedef2_ops(val p: native.Ptr[struct_TypeWithTypedef2]) extends AnyVal { + def s: native.Ptr[TypeWithTypedef1] = (!p._1).cast[native.Ptr[TypeWithTypedef1]] + def s_=(value: native.Ptr[TypeWithTypedef1]): Unit = !p._1 = value.cast[native.Ptr[Byte]] + } + + def struct_TypeWithTypedef2()(implicit z: native.Zone): native.Ptr[struct_TypeWithTypedef2] = native.alloc[struct_TypeWithTypedef2] + + implicit class struct_TypeWithTypedef1_ops(val p: native.Ptr[struct_TypeWithTypedef1]) extends AnyVal { + def s: native.Ptr[struct_TypeWithTypedef2] = !p._1 + def s_=(value: native.Ptr[struct_TypeWithTypedef2]): Unit = !p._1 = value + } + + def struct_TypeWithTypedef1()(implicit z: native.Zone): native.Ptr[struct_TypeWithTypedef1] = native.alloc[struct_TypeWithTypedef1] + + implicit class struct_TwoTypesReplaced3_ops(val p: native.Ptr[struct_TwoTypesReplaced3]) extends AnyVal { + def memberFunction: native.CFunctionPtr1[native.Ptr[struct_TwoTypesReplaced2], native.Ptr[struct_TwoTypesReplaced1]] = (!p._1).cast[native.CFunctionPtr1[native.Ptr[struct_TwoTypesReplaced2], native.Ptr[struct_TwoTypesReplaced1]]] + def memberFunction_=(value: native.CFunctionPtr1[native.Ptr[struct_TwoTypesReplaced2], native.Ptr[struct_TwoTypesReplaced1]]): Unit = !p._1 = value.cast[native.CFunctionPtr1[native.Ptr[Byte], native.Ptr[Byte]]] + } + + def struct_TwoTypesReplaced3()(implicit z: native.Zone): native.Ptr[struct_TwoTypesReplaced3] = native.alloc[struct_TwoTypesReplaced3] + + implicit class struct_TwoTypesReplaced1_ops(val p: native.Ptr[struct_TwoTypesReplaced1]) extends AnyVal { + def s: native.Ptr[struct_TwoTypesReplaced2] = !p._1 + def s_=(value: native.Ptr[struct_TwoTypesReplaced2]): Unit = !p._1 = value + } + + def struct_TwoTypesReplaced1()(implicit z: native.Zone): native.Ptr[struct_TwoTypesReplaced1] = native.alloc[struct_TwoTypesReplaced1] + + implicit class struct_TwoTypesReplaced2_ops(val p: native.Ptr[struct_TwoTypesReplaced2]) extends AnyVal { + def s: native.Ptr[struct_TwoTypesReplaced3] = !p._1 + def s_=(value: native.Ptr[struct_TwoTypesReplaced3]): Unit = !p._1 = value + } + + def struct_TwoTypesReplaced2()(implicit z: native.Zone): native.Ptr[struct_TwoTypesReplaced2] = native.alloc[struct_TwoTypesReplaced2] + + implicit class struct_cycleWithUnionS_ops(val p: native.Ptr[struct_cycleWithUnionS]) extends AnyVal { + def u: native.Ptr[union_cycleWithUnionU] = !p._1 + def u_=(value: native.Ptr[union_cycleWithUnionU]): Unit = !p._1 = value + } + + def struct_cycleWithUnionS()(implicit z: native.Zone): native.Ptr[struct_cycleWithUnionS] = native.alloc[struct_cycleWithUnionS] + + implicit class struct_FuncPointerWithValueType1_ops(val p: native.Ptr[struct_FuncPointerWithValueType1]) extends AnyVal { + def s: native.Ptr[struct_FuncPointerWithValueType2] = !p._1 + def s_=(value: native.Ptr[struct_FuncPointerWithValueType2]): Unit = !p._1 = value + } + + def struct_FuncPointerWithValueType1()(implicit z: native.Zone): native.Ptr[struct_FuncPointerWithValueType1] = native.alloc[struct_FuncPointerWithValueType1] + + implicit class struct_FuncPointerWithValueType2_ops(val p: native.Ptr[struct_FuncPointerWithValueType2]) extends AnyVal { + def memberFunction: native.CFunctionPtr0[struct_FuncPointerWithValueType1] = (!p._1).cast[native.CFunctionPtr0[struct_FuncPointerWithValueType1]] + def memberFunction_=(value: native.CFunctionPtr0[struct_FuncPointerWithValueType1]): Unit = !p._1 = value.cast[native.CFunctionPtr0[native.CStruct0]] + } + + def struct_FuncPointerWithValueType2()(implicit z: native.Zone): native.Ptr[struct_FuncPointerWithValueType2] = native.alloc[struct_FuncPointerWithValueType2] + + implicit class union_cycleWithUnionU_pos(val p: native.Ptr[union_cycleWithUnionU]) extends AnyVal { + def s: native.Ptr[native.Ptr[struct_cycleWithUnionS]] = p.cast[native.Ptr[native.Ptr[struct_cycleWithUnionS]]] + def s_=(value: native.Ptr[struct_cycleWithUnionS]): Unit = !p.cast[native.Ptr[native.Ptr[struct_cycleWithUnionS]]] = value + } +} +