Skip to content

Commit

Permalink
Support negative values
Browse files Browse the repository at this point in the history
  • Loading branch information
kornilova203 committed Jun 19, 2018
1 parent c8c7543 commit e288ac9
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 85 deletions.
175 changes: 105 additions & 70 deletions bindgen/defines/DefineFinder.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#include "DefineFinder.h"

#include <clang/Basic/IdentifierTable.h>
#include <clang/Lex/LiteralSupport.h>
#include <clang/Lex/MacroInfo.h>
#include <llvm/ADT/APInt.h>
#include <llvm/ADT/APSInt.h>

DefineFinder::DefineFinder(IR &ir, const clang::CompilerInstance &compiler,
clang::Preprocessor &pp)
Expand All @@ -20,55 +16,80 @@ void DefineFinder::MacroDefined(const clang::Token &macroNameTok,
* Ignore function-like definitions because they usually are meaningful
* only inside C functions. */

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

if (token->isLiteral()) {
/* might be converted directly to Scala code */
std::string literal(token->getLiteralData(), token->getLength());
if (token->getKind() == clang::tok::numeric_constant) {
addNumericConstantDefine(macroName, literal, token);
} else if (token->getKind() == clang::tok::string_literal) {
ir.addLiteralDefine(macroName, "c" + literal, "native.CString");
} else {
llvm::errs() << "Warning: type of literal " << token->getName()
<< " is unsupported\n";
llvm::errs().flush();
}
} else if (token->isAnyIdentifier()) {
// TODO: save identifier and get its type in ScalaFrontendAction
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);
}
}

/**
* Follows simple chain of defines.
*
* @return token that is not a define
* @return array of tokens. Non of the returned tokens is a macro.
*/
const clang::Token *
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();
if (tokens.size() != 1) {
/* process only simple definitions that contain 1 token */
return nullptr;
}
clang::Token token = tokens[0];
if (!(token.isAnyIdentifier() || token.isLiteral())) {
/* unsupported */
return nullptr;
}
if (token.isLiteral() || !token.getIdentifierInfo()->hasMacroDefinition()) {
return &(tokens[0]);
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 expandDefine(*pp.getLocalMacroDirective(token.getIdentifierInfo()));
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::MacroDefinition &md,
const clang::MacroDirective *undef) {
clang::SourceManager &sm = compiler.getSourceManager();
if (sm.isWrittenInMainFile(macroNameTok.getLocation()) &&
!md.getMacroInfo()->isFunctionLike()) {
Expand All @@ -79,8 +100,9 @@ void DefineFinder::MacroUndefined(const clang::Token &macroNameTok,

void DefineFinder::addNumericConstantDefine(const std::string &macroName,
std::string literal,
const clang::Token *token) {
clang::NumericLiteralParser parser(literal, token->getLocation(), pp);
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');
Expand All @@ -98,50 +120,63 @@ void DefineFinder::addNumericConstantDefine(const std::string &macroName,
* is a long value.
* Therefore we need to check that value fits into certain number
* of bits. */
getTypeOfIntLiteralWithoutEnding(parser, literal, type);
getTypeOfIntLiteralWithoutEnding(parser, literal, type, positive);
}
} else {
if (parser.isFloat) {
type = "native.CFloat";
if (parser.isFloatingLiteral()) {
type = "native.CDouble";
// TODO: distinguish between float and double
}
}
if (!type.empty()) {
if (!positive) {
literal = "-" + literal;
}
ir.addLiteralDefine(macroName, literal, type);
}
}

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

llvm::APInt intValue(4 * 8 - 1, 0, false);
/* check if abs value fits into 4 * 8 - 1 bits */
if (!parser.GetIntegerValue(intValue)) {
if (fitsIntoType<int, uint>(parser, positive)) {
type = "native.CInt";
} else if (fitsIntoType<long, ulong>(parser, positive)) {
type = "native.CLong";
literal = literal + "L";
} else {
llvm::APInt longValue(8 * 8 - 1, 0, false);
if (!parser.GetIntegerValue(longValue)) {
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.
*/
}
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());
}
}
31 changes: 24 additions & 7 deletions bindgen/defines/DefineFinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "../ir/IR.h"
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/LiteralSupport.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/Lex/Preprocessor.h>

class DefineFinder : public clang::PPCallbacks {
Expand All @@ -15,8 +14,9 @@ class DefineFinder : public clang::PPCallbacks {
void MacroDefined(const clang::Token &macroNameTok,
const clang::MacroDirective *md) override;

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

private:
IR &ir;
Expand All @@ -25,13 +25,30 @@ class DefineFinder : public clang::PPCallbacks {

void addNumericConstantDefine(const std::string &macroName,
std::string literal,
const clang::Token *finalToken);

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);
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);

const clang::Token *expandDefine(const clang::MacroDirective &md);
/**
* @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
1 change: 0 additions & 1 deletion bindgen/defines/DefineFinderAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

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

class DefineFinderAction : public clang::PreprocessOnlyAction {
public:
Expand Down
2 changes: 1 addition & 1 deletion bindgen/ir/Define.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#ifndef SCALA_NATIVE_BINDGEN_DEFINE_H
#define SCALA_NATIVE_BINDGEN_DEFINE_H

#include "TypeAndName.h"
#include <string>

class Define {
public:
Expand Down
9 changes: 4 additions & 5 deletions tests/samples/Define.h → tests/samples/LiteralDefine.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,15 @@
#define MAXIMUM_UNSIGNED_LONG 18446744073709551615
#define MAXIMUM_SIGNED_LONG 9223372036854775807 // OK

#define NEGATIVE_NUMBER -1 // unsupported

/* negative values are currently ignored because there are 2 tokens in the
* representation */
// #define MINIMUM_SIGNED_LONG -9223372036854775808
#define MINIMUM_SIGNED_LONG -9223372036854775808 // OK
#define LESS_THEN_MINIMUM_SIGNED_LONG -9223372036854775809 // excluded

#define FLOAT 5.6

#define INT 42
#define MAXIMUM_INT 2147483647
#define NEW_INT INT
#define NEG_INT -INT

extern int a;
#define MY_A a // unsupported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ package org.scalanative.bindgen.samples
import scala.scalanative._
import scala.scalanative.native._

object DefineDefines {
object LiteralDefineDefines {
val STRING: native.CString = c"Hello, World!"
val LONG: native.CLong = 1000000000000L
val LONG_WITHOUT_ENDING: native.CLong = 1000000000000L
val LONG_LONG: native.CLongLong = 1000000000000L
val MAXIMUM_SIGNED_LONG: native.CLong = 9223372036854775807L
val MINIMUM_SIGNED_LONG: native.CLong = -9223372036854775808L
val FLOAT: native.CDouble = 5.6
val INT: native.CInt = 42
val MAXIMUM_INT: native.CInt = 2147483647
val NEW_INT: native.CInt = 42
val NEG_INT: native.CInt = -42
val SHOULD_BE_DEFINED: native.CString = c"Because INT is not equal to 0"
}

0 comments on commit e288ac9

Please sign in to comment.