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
219 changes: 219 additions & 0 deletions bindgen/defines/DefineFinder.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#include "DefineFinder.h"

#include <llvm/ADT/APInt.h>
#include <sstream>

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() && !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;
std::string scalaLiteral;
if (parser.isIntegerLiteral()) {
if (parser.isLongLong) {
/* literal has `LL` ending. `long long` is represented as `Long`
* in Scala Native */
type = "native.CLongLong";

/* must fit into Scala integer type */
if (getTypeOfIntegerLiteral(parser, literal, positive).empty()) {
type = "";
}
} else if (parser.isLong) {
/* literal has `L` ending */
type = "native.CLong";

/* must fit into Scala integer type */
if (getTypeOfIntegerLiteral(parser, literal, positive).empty()) {
type = "";
}
} else {
type = getTypeOfIntegerLiteral(parser, literal, positive);
}

if (!type.empty()) {
scalaLiteral = getDecimalLiteral(parser);
if (type == "native.CLong" || type == "native.CLongLong") {
scalaLiteral = scalaLiteral + "L";
}
}
} else if (parser.isFloatingLiteral()) {
if (fitsIntoDouble(parser)) {
type = "native.CDouble";
scalaLiteral = getDoubleLiteral(parser);
}
}

if (!type.empty()) {
if (!positive) {
scalaLiteral = "-" + scalaLiteral;
}
ir.addLiteralDefine(macroName, scalaLiteral, type);
}
}

std::string
DefineFinder::getTypeOfIntegerLiteral(const clang::NumericLiteralParser &parser,
const std::string &literal,
bool positive) {

if (integerFitsIntoType<int, uint>(parser, positive)) {
return "native.CInt";
} else if (integerFitsIntoType<long, ulong>(parser, positive)) {
return "native.CLong";
} 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.
*/
return "";
}
}

template <typename signedT, typename unsignedT>
bool DefineFinder::integerFitsIntoType(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());
}
}

std::string
DefineFinder::getDecimalLiteral(clang::NumericLiteralParser parser) {
llvm::APInt val(8 * 8, 0, false);
parser.GetIntegerValue(val);
return std::to_string(val.getZExtValue());
}

std::string DefineFinder::getDoubleLiteral(clang::NumericLiteralParser parser) {
llvm::APFloat val(.0); // double
parser.GetFloatValue(val);
std::ostringstream ss;
ss << val.convertToDouble();
return ss.str();
}

bool DefineFinder::fitsIntoDouble(clang::NumericLiteralParser parser) {
llvm::APFloat val(.0); // double
parser.GetFloatValue(val);
std::ostringstream ss;
ss << val.convertToDouble();
return ss.str() != "inf";
}
59 changes: 59 additions & 0 deletions bindgen/defines/DefineFinder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#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 the number fits into int or long variable.
*
* @return type of the number
*/
std::string
getTypeOfIntegerLiteral(const clang::NumericLiteralParser &parser,
const std::string &literal, 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 integerFitsIntoType(clang::NumericLiteralParser parser, bool positive);

std::string getDecimalLiteral(clang::NumericLiteralParser parser);

std::string getDoubleLiteral(clang::NumericLiteralParser parser);

bool fitsIntoDouble(clang::NumericLiteralParser parser);
};

#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; }
Loading