Skip to content

Commit

Permalink
Merge pull request #50 from kornilova-l/support-bindings-for-defines
Browse files Browse the repository at this point in the history
Support bindings for literal defines
  • Loading branch information
kornilova203 authored Jun 22, 2018
2 parents e857065 + 471b06a commit 7b0bccb
Show file tree
Hide file tree
Showing 17 changed files with 541 additions and 26 deletions.
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

0 comments on commit 7b0bccb

Please sign in to comment.