Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support bindings for literal defines #50

Merged
merged 10 commits into from
Jun 22, 2018
10 changes: 10 additions & 0 deletions bindgen/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ add_executable(bindgen
visitor/TreeVisitor.h
visitor/TreeVisitor.cpp
visitor/TreeConsumer.h
defines/DefineFinderAction.h
defines/DefineFinderAction.cpp
defines/DefineFinder.cpp
defines/DefineFinder.h
defines/DefineFinderActionFactory.cpp
defines/DefineFinderActionFactory.h
TypeTranslator.h
TypeTranslator.cpp
HeaderManager.h
Expand All @@ -38,6 +44,10 @@ add_executable(bindgen
ir/Enum.h
ir/TypeAndName.cpp
ir/TypeAndName.h
ir/Define.h
ir/Define.cpp
ir/LiteralDefine.cpp
ir/LiteralDefine.h
)

set_target_properties(bindgen
Expand Down
11 changes: 9 additions & 2 deletions bindgen/Main.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "defines/DefineFinderActionFactory.h"
#include "visitor/ScalaFrontendActionFactory.h"
#include <clang/Tooling/CommonOptionsParser.h>

Expand Down Expand Up @@ -71,9 +72,15 @@ int main(int argc, char *argv[]) {
locations.clear();

IR ir(libName, linkName, objectName, Package.getValue());
ScalaFrontendActionFactory actionFactory(ir);

int result = Tool.run(&actionFactory);
DefineFinderActionFactory defineFinderActionFactory(ir);
int result = Tool.run(&defineFinderActionFactory);
if (result) {
return result;
}

ScalaFrontendActionFactory actionFactory(ir);
result = Tool.run(&actionFactory);

auto printLoc = PrintHeadersLocation.getValue();
if (printLoc) {
Expand Down
182 changes: 182 additions & 0 deletions bindgen/defines/DefineFinder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
#include "DefineFinder.h"

#include <llvm/ADT/APInt.h>

DefineFinder::DefineFinder(IR &ir, const clang::CompilerInstance &compiler,
clang::Preprocessor &pp)
: ir(ir), compiler(compiler), pp(pp) {}

void DefineFinder::MacroDefined(const clang::Token &macroNameTok,
const clang::MacroDirective *md) {
clang::SourceManager &sm = compiler.getSourceManager();
if (sm.isWrittenInMainFile(macroNameTok.getLocation()) && md->isDefined() &&
!md->getMacroInfo()->isFunctionLike()) {
/* save defines only from the given header.
*
* Ignore function-like definitions because they usually are meaningful
* only inside C functions. */

std::vector<clang::Token> *tokens = expandDefine(*md);
if (!tokens) { // there was function-like macro
return;
}
std::string macroName = macroNameTok.getIdentifierInfo()->getName();

if (tokens->size() == 1 &&
(*tokens)[0].getKind() == clang::tok::numeric_constant) {
clang::Token numberToken = (*tokens)[0];
std::string literal(numberToken.getLiteralData(),
numberToken.getLength());
addNumericConstantDefine(macroName, literal, numberToken);
} else if (tokens->size() == 2 &&
(*tokens)[0].getKind() == clang::tok::minus &&
(*tokens)[1].getKind() == clang::tok::numeric_constant) {
clang::Token numberToken = (*tokens)[1];
std::string literal(numberToken.getLiteralData(),
numberToken.getLength());
addNumericConstantDefine(macroName, literal, numberToken, false);
} else if (tokens->size() == 1 &&
(*tokens)[0].getKind() == clang::tok::string_literal) {
clang::Token stringToken = (*tokens)[0];
std::string literal(stringToken.getLiteralData(),
stringToken.getLength());
ir.addLiteralDefine(macroName, "c" + literal, "native.CString");
}
std::free(tokens);
}
}

/**
* @return array of tokens. Non of the returned tokens is a macro.
*/
std::vector<clang::Token> *
DefineFinder::expandDefine(const clang::MacroDirective &md) {
auto *expandedTokens = new std::vector<clang::Token>();
clang::ArrayRef<clang::Token> tokens = md.getMacroInfo()->tokens();
for (const auto &token : tokens) {
if (isMacro(token)) {
if (isFunctionLikeMacro(token)) {
/* function-like macros are unsupported */
return nullptr;
}
std::vector<clang::Token> *newTokens = expandDefine(
*pp.getLocalMacroDirective(token.getIdentifierInfo()));
if (!newTokens) {
std::free(expandedTokens);
return nullptr;
}
for (const auto &newToken : *newTokens) {
(*expandedTokens).push_back(newToken);
}
std::free(newTokens);
} else {
(*expandedTokens).push_back(token);
}
}
return expandedTokens;
}

bool DefineFinder::isMacro(const clang::Token &token) {
return token.isAnyIdentifier() &&
token.getIdentifierInfo()->hasMacroDefinition();
}

bool DefineFinder::isFunctionLikeMacro(const clang::Token &token) {
clang::MacroDirective *md =
pp.getLocalMacroDirective(token.getIdentifierInfo());
return md->getMacroInfo()->isFunctionLike();
}

void DefineFinder::MacroUndefined(const clang::Token &macroNameTok,
const clang::MacroDefinition &md,
const clang::MacroDirective *undef) {
clang::SourceManager &sm = compiler.getSourceManager();
if (sm.isWrittenInMainFile(macroNameTok.getLocation()) &&
!md.getMacroInfo()->isFunctionLike()) {
std::string macroName = macroNameTok.getIdentifierInfo()->getName();
ir.removeDefine(macroName);
}
}

void DefineFinder::addNumericConstantDefine(const std::string &macroName,
std::string literal,
const clang::Token &token,
bool positive) {
clang::NumericLiteralParser parser(literal, token.getLocation(), pp);
std::string type;
if (parser.isIntegerLiteral()) {
std::replace(literal.begin(), literal.end(), 'l', 'L');
if (parser.isLongLong) {
/* literal has `LL` ending but `long long` is represented as
* `Long` in Scala Native */
literal = literal.substr(0, literal.length() - 1); // remove last L
type = "native.CLongLong";
} else if (parser.isLong) {
/* literal already has `L` ending */
type = "native.CLong";
} else {
/* literal may not have `l` or `ll` ending but still be of long type
* NumericLiteralParser does not recognize that 10000000000
* is a long value.
* Therefore we need to check that value fits into certain number
* of bits. */
getTypeOfIntLiteralWithoutEnding(parser, literal, type, positive);
}
} else {
if (parser.isFloatingLiteral()) {
type = "native.CDouble";
// TODO: distinguish between float and double
}
}
if (!type.empty()) {
if (!positive) {
literal = "-" + literal;
}
ir.addLiteralDefine(macroName, literal, type);
}
}

void DefineFinder::getTypeOfIntLiteralWithoutEnding(
clang::NumericLiteralParser parser, std::string &literal, std::string &type,
bool positive) {

if (fitsIntoType<int, uint>(parser, positive)) {
type = "native.CInt";
} else if (fitsIntoType<long, ulong>(parser, positive)) {
type = "native.CLong";
literal = literal + "L";
} else {
llvm::errs() << "Waring: integer value does not fit into 8 bytes: "
<< literal << "\n";
llvm::errs().flush();
/**
* `long long` value has mostly the same size as `long`.
* Moreover in Scala Native the type is represented as `Long`:
* @code
* type CLongLong = Long
* @endcode
* Therefore the case of `long long` is not considered here.
*/
}
}

template <typename signedT, typename unsignedT>
bool DefineFinder::fitsIntoType(clang::NumericLiteralParser parser,
bool positive) {
/* absolute value of minimum negative number will not fit
* into (sizeof(signedT) * 8 - 1) bits */
llvm::APInt uintValue(sizeof(signedT) * 8, 0, false);
if (parser.GetIntegerValue(uintValue)) {
/* absolute value of the number does not fit into (sizeof(signedT) * 8)
* bits */
return false;
}
auto uval = static_cast<unsignedT>(uintValue.getZExtValue());
if (positive) {
return uval <=
static_cast<unsignedT>(std::numeric_limits<signedT>::max());
} else {
return uval <=
static_cast<unsignedT>(-std::numeric_limits<signedT>::min());
}
}
54 changes: 54 additions & 0 deletions bindgen/defines/DefineFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDER_H
#define SCALA_NATIVE_BINDGEN_DEFINEFINDER_H

#include "../ir/IR.h"
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/LiteralSupport.h>
#include <clang/Lex/Preprocessor.h>

class DefineFinder : public clang::PPCallbacks {
public:
DefineFinder(IR &ir, const clang::CompilerInstance &compiler,
clang::Preprocessor &pp);

void MacroDefined(const clang::Token &macroNameTok,
const clang::MacroDirective *md) override;

void MacroUndefined(const clang::Token &macroNameTok,
const clang::MacroDefinition &md,
const clang::MacroDirective *undef) override;

private:
IR &ir;
const clang::CompilerInstance &compiler;
clang::Preprocessor &pp;

void addNumericConstantDefine(const std::string &macroName,
std::string literal,
const clang::Token &token,
bool positive = true);

/**
* Check if a number without `l` or `ll` ending fits into int or long
* variable. Supports only non-negative numbers
*
* Updates `literal` and `type` parameters.
*/
void getTypeOfIntLiteralWithoutEnding(clang::NumericLiteralParser parser,
std::string &literal,
std::string &type, bool positive);

std::vector<clang::Token> *expandDefine(const clang::MacroDirective &md);

bool isMacro(const clang::Token &token);

bool isFunctionLikeMacro(const clang::Token &token);

/**
* @return true if number contained in parser fits into int type
*/
template <typename signedT, typename unsignedT>
bool fitsIntoType(clang::NumericLiteralParser parser, bool positive);
};

#endif // SCALA_NATIVE_BINDGEN_DEFINEFINDER_H
12 changes: 12 additions & 0 deletions bindgen/defines/DefineFinderAction.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "DefineFinderAction.h"

DefineFinderAction::DefineFinderAction(IR &ir) : ir(ir) {}

void DefineFinderAction::ExecuteAction() {
getCompilerInstance().getPreprocessor().addPPCallbacks(
std::unique_ptr<clang::PPCallbacks>(
new DefineFinder(ir, getCompilerInstance(),
getCompilerInstance().getPreprocessor())));

clang::PreprocessOnlyAction::ExecuteAction();
}
18 changes: 18 additions & 0 deletions bindgen/defines/DefineFinderAction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H
#define SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H

#include "DefineFinder.h"
#include <clang/Frontend/FrontendActions.h>

class DefineFinderAction : public clang::PreprocessOnlyAction {
public:
explicit DefineFinderAction(IR &ir);

protected:
void ExecuteAction() override;

private:
IR &ir;
};

#endif // SCALA_NATIVE_BINDGEN_DEFINEFINDERACTION_H
8 changes: 8 additions & 0 deletions bindgen/defines/DefineFinderActionFactory.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "DefineFinderActionFactory.h"
#include "DefineFinderAction.h"

DefineFinderActionFactory::DefineFinderActionFactory(IR &ir) : ir(ir) {}

clang::FrontendAction *DefineFinderActionFactory::create() {
return new DefineFinderAction(ir);
}
17 changes: 17 additions & 0 deletions bindgen/defines/DefineFinderActionFactory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H
#define SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H

#include "../ir/IR.h"
#include <clang/Tooling/Tooling.h>

class DefineFinderActionFactory : public clang::tooling::FrontendActionFactory {
public:
explicit DefineFinderActionFactory(IR &ir);

clang::FrontendAction *create() override;

private:
IR &ir;
};

#endif // SCALA_NATIVE_BINDGEN_DEFINEFINDERACTIONFACTORY_H
5 changes: 5 additions & 0 deletions bindgen/ir/Define.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#include "Define.h"

Define::Define(std::string name) : name(std::move(name)) {}

std::string Define::getName() { return name; }
16 changes: 16 additions & 0 deletions bindgen/ir/Define.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#ifndef SCALA_NATIVE_BINDGEN_DEFINE_H
#define SCALA_NATIVE_BINDGEN_DEFINE_H

#include <string>

class Define {
public:
explicit Define(std::string name);

std::string getName();

protected:
std::string name;
};

#endif // SCALA_NATIVE_BINDGEN_DEFINE_H
Loading