diff --git a/Main.cpp b/Main.cpp index 44a347d..f5a1463 100644 --- a/Main.cpp +++ b/Main.cpp @@ -2,15 +2,16 @@ #include -static llvm::cl::OptionCategory Category("Binding Generator"); -static llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); -static llvm::cl::extrahelp MoreHelp("\nProduce Bindings for scala native. Please specify lib name wit parameter name\n"); -static llvm::cl::opt LibName("name", llvm::cl::cat(Category)); -static llvm::cl::opt StdHeaders("stdHeaders", llvm::cl::cat(Category)); -static llvm::cl::opt PrintHeadersLocation ("location", llvm::cl::cat(Category)); - - int main(int argc, char *argv[]) { + llvm::cl::OptionCategory Category("Binding Generator"); + llvm::cl::extrahelp CommonHelp(clang::tooling::CommonOptionsParser::HelpMessage); + llvm::cl::extrahelp MoreHelp("\nProduce Bindings for scala native. Please specify lib name with parameter name\n"); + + llvm::cl::opt LibName("name", llvm::cl::cat(Category)); + llvm::cl::opt StdHeaders("std-headers", llvm::cl::cat(Category)); + llvm::cl::opt PrintHeadersLocation("location", llvm::cl::cat(Category)); + llvm::cl::opt ExcludePrefix("exclude-prefix", llvm::cl::cat(Category)); + clang::tooling::CommonOptionsParser op(argc, (const char**)argv, Category); clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList()); @@ -40,7 +41,7 @@ int main(int argc, char *argv[]) { llvm::outs() << location.c_str(); } } else { - ir.generate(); + ir.generate(ExcludePrefix.getValue()); llvm::outs() << ir; } llvm::outs().flush(); diff --git a/Utils.h b/Utils.h index 1d2f32f..8099b7d 100644 --- a/Utils.h +++ b/Utils.h @@ -79,5 +79,23 @@ static inline void trim(std::string &s) { rtrim(s); } +/** + * @return true if str starts with given prefix + */ +static inline bool startsWith(const std::string &str, const std::string &prefix) { + return str.substr(0, prefix.size()) == prefix; +} + +/** + * @return true if checkedType uses type + * example: checkedType = native.Ptr[struct_A], type = struct_A + */ +static inline bool typeUsesOtherType(const std::string &checkedType, const std::string &type) { + // TODO: find better way to check it + return checkedType == type || + checkedType == "native.Ptr[" + type + "]" || + startsWith(checkedType, "native.CArray[" + type + ", "); +} + #endif // UTILS_H diff --git a/ir/Function.cpp b/ir/Function.cpp index 1ae06b7..95092e2 100644 --- a/ir/Function.cpp +++ b/ir/Function.cpp @@ -30,3 +30,19 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Function &func) { << " = native.extern\n"; return s; } + +bool Function::usesType(const std::string &type) const { + if (typeUsesOtherType(retType, type)) { + return true; + } + for (const auto ¶meter : parameters) { + if (typeUsesOtherType(parameter.getType(), type)) { + return true; + } + } + return false; +} + +std::string Function::getName() const { + return name; +} diff --git a/ir/Function.h b/ir/Function.h index 57daa02..b6e4718 100644 --- a/ir/Function.h +++ b/ir/Function.h @@ -19,6 +19,10 @@ class Function { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const Function &func); + bool usesType(const std::string &type) const; + + std::string getName() const; + private: std::string name; std::vector parameters; diff --git a/ir/IR.cpp b/ir/IR.cpp index 8f55447..f5e8193 100644 --- a/ir/IR.cpp +++ b/ir/IR.cpp @@ -113,8 +113,9 @@ void IR::generateTypeDefs() { } } -void IR::generate() { +void IR::generate(const std::string &excludePrefix) { if (!generated) { + filterDeclarations(excludePrefix); generateTypeDefs(); generated = true; } @@ -137,3 +138,62 @@ bool IR::hasHelperMethods() const { bool IR::hasEnums() const { return !enums.empty(); } + +void IR::filterDeclarations(const std::string &excludePrefix) { + if (excludePrefix.empty()) { + return; + } + + filterTypeDefs(excludePrefix); + + filterFunctions(excludePrefix); +} + +void IR::filterTypeDefs(const std::string &excludePrefix) { + for (auto it = typeDefs.begin(); it != typeDefs.end();) { + TypeDef &typeDef = *it; + if (startsWith(typeDef.getName(), excludePrefix) && + typeIsUsedOnlyInTypeDefs(typeDef.getName())) { + /* remove this typedef and replace aliases with actual type */ + replaceTypeInTypeDefs(typeDef.getName(), typeDef.getType()); + it = typeDefs.erase(it); + } else { + ++it; + } + } +} + +void IR::replaceTypeInTypeDefs(const std::string &oldType, const std::string &newType) { + for (auto &typeDef : typeDefs) { + if (typeDef.getType() == oldType) { + typeDef.setType(newType); + } + } +} + +void IR::filterFunctions(const std::string &excludePrefix) { + for (auto it = functions.begin(); it != functions.end();) { + Function &function = *it; + if (startsWith(function.getName(), excludePrefix)) { + it = functions.erase(it); + } else { + it++; + } + } +} + +template +bool IR::isTypeUsed(const std::vector &declarations, const std::string &type) { + for (const auto &decl : declarations) { + if (decl.usesType(type)) { + return true; + } + } + return false; +} + +bool IR::typeIsUsedOnlyInTypeDefs(std::string type) { + return !(isTypeUsed(functions, type) || + isTypeUsed(structs, type) || + isTypeUsed(unions, type)); +} diff --git a/ir/IR.h b/ir/IR.h index 7463f30..aa15743 100644 --- a/ir/IR.h +++ b/ir/IR.h @@ -35,7 +35,7 @@ class IR { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const IR &ir); - void generate(); + void generate(const std::string &excludePrefix); private: @@ -49,6 +49,53 @@ class IR { */ bool hasHelperMethods() const; + /** + * Remove functions that start with given prefix. + * + * Replace typedef by underlying type if it starts with given prefix + * and it is used only in other typedefs. + * + * Example: + * @code + * type __int32_t = native.CInt + * type __darwin_pid_t = __int32_t + * type pid_t = __darwin_pid_t + * @endcode + * + * Becomes: + * @code + * type pid_t = native.CInt + * @endcode + * + */ + void filterDeclarations(const std::string &excludePrefix); + + /** + * Remove all typedefs that start with given prefix. + */ + void filterTypeDefs(const std::string &excludePrefix); + + /** + * Find all typedefs that use oldType and replace it with newType. + */ + void replaceTypeInTypeDefs(const std::string &oldType, const std::string &newType); + + /** + * Remove functions with names that start with excludePrefix. + */ + void filterFunctions(const std::string &excludePrefix); + + /** + * @return true if given type is used only in typedefs. + */ + bool typeIsUsedOnlyInTypeDefs(std::string type); + + /** + * @return true if type is used in one of given declarations. + */ + template + bool isTypeUsed(const std::vector &declarations, const std::string &type); + std::string libName; std::vector functions; std::vector typeDefs; diff --git a/ir/Struct.cpp b/ir/Struct.cpp index 8b858db..fdefa63 100644 --- a/ir/Struct.cpp +++ b/ir/Struct.cpp @@ -8,6 +8,19 @@ Field::Field(std::string name, std::string type) StructOrUnion::StructOrUnion(std::string name, std::vector fields) : name(std::move(name)), fields(std::move(fields)) {} +std::string StructOrUnion::getName() const { + return name; +} + +bool StructOrUnion::usesType(const std::string &type) const { + for (const auto &field : fields) { + if (typeUsesOtherType(field.getType(), type)) { + return true; + } + } + return false; +} + Struct::Struct(std::string name, std::vector fields, uint64_t typeSize) : StructOrUnion(std::move(name), std::move(fields)), typeSize(typeSize) {} @@ -38,8 +51,8 @@ std::string Struct::generateHelperClass() const { return ""; } std::stringstream s; - std::string newName = "struct_" + name; - s << " implicit class " << newName << "_ops(val p: native.Ptr[struct_" << name << "])" + std::string type = getType(); + s << " implicit class " << type << "_ops(val p: native.Ptr[" << type << "])" << " extends AnyVal {\n"; int fieldIndex = 0; for (const auto &field : fields) { @@ -56,8 +69,8 @@ std::string Struct::generateHelperClass() const { s << " }\n\n"; /* makes struct instantiation easier */ - s << " def " << newName + "()(implicit z: native.Zone): native.Ptr[" + newName + "]" - << " = native.alloc[" + newName + "]\n"; + s << " def " << type + "()(implicit z: native.Zone): native.Ptr[" + type + "]" + << " = native.alloc[" + type + "]\n"; return s.str(); } @@ -66,18 +79,23 @@ bool Struct::hasHelperMethods() const { return !fields.empty() && fields.size() < SCALA_NATIVE_MAX_STRUCT_FIELDS; } +std::string Struct::getType() const { + return "struct_" + name; +} + Union::Union(std::string name, std::vector members, uint64_t maxSize) : StructOrUnion(std::move(name), std::move(members)), maxSize(maxSize) {} TypeDef Union::generateTypeDef() const { - return TypeDef("union_" + name, "native.CArray[Byte, " + uint64ToScalaNat(maxSize) + "]"); + return TypeDef(getType(), "native.CArray[Byte, " + uint64ToScalaNat(maxSize) + "]"); } std::string Union::generateHelperClass() const { std::stringstream s; - s << " implicit class union_" << name << "_pos" - << "(val p: native.Ptr[union_" << name << "]) extends AnyVal {\n"; + std::string type = getType(); + s << " implicit class " << type << "_pos" + << "(val p: native.Ptr[" << type << "]) extends AnyVal {\n"; for (const auto &field : fields) { if (!field.getName().empty()) { std::string getter = handleReservedWords(field.getName()); @@ -93,3 +111,7 @@ std::string Union::generateHelperClass() const { s << " }\n"; return s.str(); } + +std::string Union::getType() const { + return "union_" + name; +} diff --git a/ir/Struct.h b/ir/Struct.h index ec3420f..0b08ea8 100644 --- a/ir/Struct.h +++ b/ir/Struct.h @@ -21,12 +21,21 @@ class StructOrUnion { virtual std::string generateHelperClass() const = 0; + std::string getName() const; + + virtual std::string getType() const = 0; + + /** + * @return true if at leas one field has given type + */ + bool usesType(const std::string &type) const; + protected: std::string name; // names of structs and unions are not empty std::vector fields; }; -class Struct : StructOrUnion { +class Struct : public StructOrUnion { public: Struct(std::string name, std::vector fields, uint64_t typeSize); @@ -34,6 +43,8 @@ class Struct : StructOrUnion { std::string generateHelperClass() const override; + std::string getType() const override; + /** * @return true if helper methods will be generated for this struct */ @@ -46,7 +57,7 @@ class Struct : StructOrUnion { uint64_t typeSize; }; -class Union : StructOrUnion { +class Union : public StructOrUnion { public: Union(std::string name, std::vector members, uint64_t maxSize); @@ -54,6 +65,8 @@ class Union : StructOrUnion { std::string generateHelperClass() const override; + std::string getType() const override; + private: uint64_t maxSize; }; diff --git a/ir/TypeAndName.cpp b/ir/TypeAndName.cpp index a3d89cf..91bf444 100644 --- a/ir/TypeAndName.cpp +++ b/ir/TypeAndName.cpp @@ -10,3 +10,7 @@ std::string TypeAndName::getType() const { std::string TypeAndName::getName() const { return name; } + +void TypeAndName::setType(std::string type) { + this->type = std::move(type); +} diff --git a/ir/TypeAndName.h b/ir/TypeAndName.h index 33e52a0..44138cc 100644 --- a/ir/TypeAndName.h +++ b/ir/TypeAndName.h @@ -14,6 +14,8 @@ class TypeAndName { std::string getType() const; + void setType(std::string name); + std::string getName() const; protected: diff --git a/ir/TypeDef.cpp b/ir/TypeDef.cpp index d457fd4..2f8b85c 100644 --- a/ir/TypeDef.cpp +++ b/ir/TypeDef.cpp @@ -8,3 +8,7 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &s, const TypeDef &typeDef) { s << " type " + handleReservedWords(typeDef.name) + " = " + typeDef.getType() + "\n"; return s; } + +bool TypeDef::usesType(const std::string &type) const { + return typeUsesOtherType(this->type, type); +} diff --git a/ir/TypeDef.h b/ir/TypeDef.h index 28a2ad8..537ca37 100644 --- a/ir/TypeDef.h +++ b/ir/TypeDef.h @@ -7,11 +7,13 @@ #include -class TypeDef : TypeAndName { +class TypeDef : public TypeAndName { public: TypeDef(std::string name, std::string type); friend llvm::raw_ostream &operator <<(llvm::raw_ostream &s, const TypeDef &type); + + bool usesType(const std::string &type) const; }; diff --git a/tests/samples/PrivateMembers.h b/tests/samples/PrivateMembers.h new file mode 100644 index 0000000..ea60fcc --- /dev/null +++ b/tests/samples/PrivateMembers.h @@ -0,0 +1,49 @@ +typedef int __int32_t; +typedef __int32_t __darwin_pid_t; +typedef __darwin_pid_t pid_t; // should associate pid_t with int + +typedef int __private_type; + +struct structWithPrivateType { + int field1; + __private_type field2; +}; // will not be removed + +union __unionWithPrivateName { + int a; +}; // will not be removed + +struct structWithPrivateStruct { + struct structWithPrivateType *s; +}; // will not be removed + +struct normalStruct { + int a; +}; + +enum __privateEnum { + A, B +}; // will not be removed + +enum enumWithPrivateMembers { + __C, D +}; // will not be removed + +enum { + __E, F +}; // will not be removed + +typedef struct { + __private_type *a; +} privateStructWithTypedef, *privateStructWithTypedefPtr; // will not be removed + +pid_t getTypeThatUsesPrivateTypes(); // will not be removed + +// functions that should be removed: +void __privateFunction(); + +// functions that should not be removed: +__private_type *getPrivateType(); +void usesPrivateUnion(union __unionWithPrivateName); +void usesPrivateStruct(struct structWithPrivateType *, struct normalStruct *); +void usesPrivateEnum(enum __privateEnum *); diff --git a/tests/samples/PrivateMembers.scala b/tests/samples/PrivateMembers.scala new file mode 100644 index 0000000..be0849e --- /dev/null +++ b/tests/samples/PrivateMembers.scala @@ -0,0 +1,75 @@ +import scala.scalanative._ +import scala.scalanative.native._ +import scala.scalanative.native.Nat._ + +@native.link("PrivateMembers") +@native.extern +object PrivateMembers { + type pid_t = native.CInt + type __private_type = native.CInt + type privateStructWithTypedef = struct_privateStructWithTypedef + type privateStructWithTypedefPtr = native.Ptr[struct_privateStructWithTypedef] + type enum___privateEnum = native.CUnsignedInt + type enum_enumWithPrivateMembers = native.CUnsignedInt + type struct_structWithPrivateType = native.CStruct2[native.CInt, __private_type] + type struct_structWithPrivateStruct = native.CStruct1[native.Ptr[struct_structWithPrivateType]] + type struct_normalStruct = native.CStruct1[native.CInt] + type struct_privateStructWithTypedef = native.CStruct1[native.Ptr[__private_type]] + type union___unionWithPrivateName = native.CArray[Byte, Digit[_3, _2]] + def getTypeThatUsesPrivateTypes(): pid_t = native.extern + def getPrivateType(): native.Ptr[__private_type] = native.extern + def usesPrivateUnion(anonymous0: union___unionWithPrivateName): Unit = native.extern + def usesPrivateStruct(anonymous0: native.Ptr[struct_structWithPrivateType], anonymous1: native.Ptr[struct_normalStruct]): Unit = native.extern + def usesPrivateEnum(anonymous0: native.Ptr[enum___privateEnum]): Unit = native.extern +} + +import PrivateMembers._ + +object PrivateMembersEnums { + final val enum___privateEnum_A: enum___privateEnum = 0.toUInt + final val enum___privateEnum_B: enum___privateEnum = 1.toUInt + + final val enum_enumWithPrivateMembers___C: enum_enumWithPrivateMembers = 0.toUInt + final val enum_enumWithPrivateMembers_D: enum_enumWithPrivateMembers = 1.toUInt + + final val enum___E: native.CUnsignedInt = 0.toUInt + final val enum_F: native.CUnsignedInt = 1.toUInt +} + +object PrivateMembersHelpers { + + implicit class struct_structWithPrivateType_ops(val p: native.Ptr[struct_structWithPrivateType]) extends AnyVal { + def field1: native.CInt = !p._1 + def field1_=(value: native.CInt):Unit = !p._1 = value + def field2: __private_type = !p._2 + def field2_=(value: __private_type):Unit = !p._2 = value + } + + def struct_structWithPrivateType()(implicit z: native.Zone): native.Ptr[struct_structWithPrivateType] = native.alloc[struct_structWithPrivateType] + + implicit class struct_structWithPrivateStruct_ops(val p: native.Ptr[struct_structWithPrivateStruct]) extends AnyVal { + def s: native.Ptr[struct_structWithPrivateType] = !p._1 + def s_=(value: native.Ptr[struct_structWithPrivateType]):Unit = !p._1 = value + } + + def struct_structWithPrivateStruct()(implicit z: native.Zone): native.Ptr[struct_structWithPrivateStruct] = native.alloc[struct_structWithPrivateStruct] + + implicit class struct_normalStruct_ops(val p: native.Ptr[struct_normalStruct]) extends AnyVal { + def a: native.CInt = !p._1 + def a_=(value: native.CInt):Unit = !p._1 = value + } + + def struct_normalStruct()(implicit z: native.Zone): native.Ptr[struct_normalStruct] = native.alloc[struct_normalStruct] + + implicit class struct_privateStructWithTypedef_ops(val p: native.Ptr[struct_privateStructWithTypedef]) extends AnyVal { + def a: native.Ptr[__private_type] = !p._1 + def a_=(value: native.Ptr[__private_type]):Unit = !p._1 = value + } + + def struct_privateStructWithTypedef()(implicit z: native.Zone): native.Ptr[struct_privateStructWithTypedef] = native.alloc[struct_privateStructWithTypedef] + + implicit class union___unionWithPrivateName_pos(val p: native.Ptr[union___unionWithPrivateName]) extends AnyVal { + def a: native.Ptr[native.CInt] = p.cast[native.Ptr[native.CInt]] + def a_=(value: native.CInt): Unit = !p.cast[native.Ptr[native.CInt]] = value + } +} diff --git a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala index 921e7da..04c52a4 100644 --- a/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala +++ b/tests/src/test/scala/org/scalanative/bindgen/BindgenSpec.scala @@ -22,8 +22,9 @@ class BindgenSpec extends FunSpec { val cmd = Seq( bindgenPath, inputFile.getAbsolutePath, - "-name", + "--name", name, + "--exclude-prefix=__", "--" ) val output = Process(cmd).lineStream.mkString("\n") diff --git a/visitor/TreeConsumer.h b/visitor/TreeConsumer.h index dbb821d..1fcaa1d 100644 --- a/visitor/TreeConsumer.h +++ b/visitor/TreeConsumer.h @@ -19,7 +19,7 @@ class TreeConsumer : public clang::ASTConsumer { public: - explicit TreeConsumer(clang::CompilerInstance *CI, IR *ir) + TreeConsumer(clang::CompilerInstance *CI, IR *ir) : visitor(CI, ir), smanager(CI->getASTContext().getSourceManager()) {}