Skip to content

Commit

Permalink
Merge pull request #140 from kornilova-l/reuse-bindings
Browse files Browse the repository at this point in the history
Reuse generated bindings
  • Loading branch information
kornilova203 authored Aug 8, 2018
2 parents 91b8136 + 4b91635 commit ab51928
Show file tree
Hide file tree
Showing 46 changed files with 531 additions and 149 deletions.
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions bindgen/CustomNames.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
struct page {
char *content;
struct page *nextStruct;
};

struct book {
struct page *firstPage;
};

typedef int myInt;
9 changes: 9 additions & 0 deletions bindgen/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ int main(int argc, const char *argv[]) {
llvm::cl::opt<std::string> LinkName(
"link", llvm::cl::cat(Category),
llvm::cl::desc("Library to link with, e.g. -luv for libuv"));
llvm::cl::opt<std::string> 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());
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions bindgen/Utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <typename T, typename PT> static inline bool isInstanceOf(PT *type) {
auto *p = dynamic_cast<const T *>(type);
return p != nullptr;
Expand Down
23 changes: 13 additions & 10 deletions bindgen/ir/Function.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include "../Utils.h"
#include "Struct.h"
#include "Union.h"
#include <sstream>

Parameter::Parameter(std::string name, std::shared_ptr<const Type> type)
: TypeAndName(std::move(name), type) {}
Expand All @@ -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 &param : func.parameters) {
for (const auto &param : 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(
Expand Down
3 changes: 1 addition & 2 deletions bindgen/ir/Function.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ class Function {
std::vector<std::shared_ptr<Parameter>> parameters,
std::shared_ptr<const Type> 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<const Type> type, bool stopOnTypeDefs,
std::vector<std::shared_ptr<const Type>> &visitedTypes) const;
Expand Down
68 changes: 47 additions & 21 deletions bindgen/ir/IR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,9 @@ void IR::addVarDefine(std::string name, std::shared_ptr<Variable> 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) {
Expand All @@ -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";
Expand All @@ -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())) {
Expand All @@ -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";
Expand All @@ -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";
Expand All @@ -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);
}
}

Expand All @@ -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 = "";
Expand All @@ -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);
}
}

Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -462,13 +461,21 @@ IR::~IR() {
}

template <typename T>
bool IR::hasOutputtedDeclaration(
bool IR::shouldOutputType(
const std::vector<std::shared_ptr<T>> &declarations) const {
std::vector<std::shared_ptr<const Type>> visitedTypes;
for (const auto &declaration : declarations) {
visitedTypes.clear();
if (shouldOutput(declaration, visitedTypes)) {
return true;
auto typeDefPointer =
std::dynamic_pointer_cast<const TypeDef>(declaration);
if (typeDefPointer) {
if (shouldOutputTypeDef(typeDefPointer, visitedTypes)) {
return true;
}
} else {
if (shouldOutput(declaration, visitedTypes)) {
return true;
}
}
}
return false;
Expand All @@ -477,12 +484,31 @@ bool IR::hasOutputtedDeclaration(
bool IR::shouldOutput(
const std::shared_ptr<const LocatableType> &type,
std::vector<std::shared_ptr<const Type>> &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<const TypeDef> &typeDef,
std::vector<std::shared_ptr<const Type>> &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());
}
22 changes: 16 additions & 6 deletions bindgen/ir/IR.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<const LocatableType> &type,
std::vector<std::shared_ptr<const Type>> &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<const TypeDef> &typeDef,
std::vector<std::shared_ptr<const Type>> &visitedTypes) const;

/**
* @tparam T one of LocatableType
*/
template <typename T>
bool hasOutputtedDeclaration(
const std::vector<std::shared_ptr<T>> &declarations) const;
bool
shouldOutputType(const std::vector<std::shared_ptr<T>> &declarations) const;

std::string libName; // name of the library
std::string linkName; // name of the library to link with
Expand Down
9 changes: 4 additions & 5 deletions bindgen/ir/LiteralDefine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ LiteralDefine::LiteralDefine(std::string name, std::string literal,
std::shared_ptr<const Type> 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(
Expand Down
3 changes: 1 addition & 2 deletions bindgen/ir/LiteralDefine.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ class LiteralDefine : public Define {
LiteralDefine(std::string name, std::string literal,
std::shared_ptr<const Type> 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<const Type> &type, bool stopOnTypeDefs,
std::vector<std::shared_ptr<const Type>> &visitedTypes) const;
Expand Down
2 changes: 1 addition & 1 deletion bindgen/ir/Location.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Location {
int getLineNumber() const;

private:
std::string path;
std::string path; // may be empty
int lineNumber;
};

Expand Down
Loading

0 comments on commit ab51928

Please sign in to comment.