-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #50 from kornilova-l/support-bindings-for-defines
Support bindings for literal defines
- Loading branch information
Showing
17 changed files
with
541 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ¯oNameTok, | ||
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 ¯oNameTok, | ||
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 ¯oName, | ||
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"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 ¯oNameTok, | ||
const clang::MacroDirective *md) override; | ||
|
||
void MacroUndefined(const clang::Token ¯oNameTok, | ||
const clang::MacroDefinition &md, | ||
const clang::MacroDirective *undef) override; | ||
|
||
private: | ||
IR &ir; | ||
const clang::CompilerInstance &compiler; | ||
clang::Preprocessor &pp; | ||
|
||
void addNumericConstantDefine(const std::string ¯oName, | ||
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; } |
Oops, something went wrong.