Skip to content

Commit e288ac9

Browse files
committed
Support negative values
1 parent c8c7543 commit e288ac9

File tree

6 files changed

+139
-85
lines changed

6 files changed

+139
-85
lines changed

bindgen/defines/DefineFinder.cpp

Lines changed: 105 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
#include "DefineFinder.h"
22

3-
#include <clang/Basic/IdentifierTable.h>
4-
#include <clang/Lex/LiteralSupport.h>
5-
#include <clang/Lex/MacroInfo.h>
63
#include <llvm/ADT/APInt.h>
7-
#include <llvm/ADT/APSInt.h>
84

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

23-
const clang::Token *token = expandDefine(*md);
24-
if (!token) {
19+
std::vector<clang::Token> *tokens = expandDefine(*md);
20+
if (!tokens) { // there was function-like macro
2521
return;
2622
}
2723
std::string macroName = macroNameTok.getIdentifierInfo()->getName();
2824

29-
if (token->isLiteral()) {
30-
/* might be converted directly to Scala code */
31-
std::string literal(token->getLiteralData(), token->getLength());
32-
if (token->getKind() == clang::tok::numeric_constant) {
33-
addNumericConstantDefine(macroName, literal, token);
34-
} else if (token->getKind() == clang::tok::string_literal) {
35-
ir.addLiteralDefine(macroName, "c" + literal, "native.CString");
36-
} else {
37-
llvm::errs() << "Warning: type of literal " << token->getName()
38-
<< " is unsupported\n";
39-
llvm::errs().flush();
40-
}
41-
} else if (token->isAnyIdentifier()) {
42-
// TODO: save identifier and get its type in ScalaFrontendAction
25+
if (tokens->size() == 1 &&
26+
(*tokens)[0].getKind() == clang::tok::numeric_constant) {
27+
clang::Token numberToken = (*tokens)[0];
28+
std::string literal(numberToken.getLiteralData(),
29+
numberToken.getLength());
30+
addNumericConstantDefine(macroName, literal, numberToken);
31+
} else if (tokens->size() == 2 &&
32+
(*tokens)[0].getKind() == clang::tok::minus &&
33+
(*tokens)[1].getKind() == clang::tok::numeric_constant) {
34+
clang::Token numberToken = (*tokens)[1];
35+
std::string literal(numberToken.getLiteralData(),
36+
numberToken.getLength());
37+
addNumericConstantDefine(macroName, literal, numberToken, false);
38+
} else if (tokens->size() == 1 &&
39+
(*tokens)[0].getKind() == clang::tok::string_literal) {
40+
clang::Token stringToken = (*tokens)[0];
41+
std::string literal(stringToken.getLiteralData(),
42+
stringToken.getLength());
43+
ir.addLiteralDefine(macroName, "c" + literal, "native.CString");
4344
}
45+
std::free(tokens);
4446
}
4547
}
4648

4749
/**
48-
* Follows simple chain of defines.
49-
*
50-
* @return token that is not a define
50+
* @return array of tokens. Non of the returned tokens is a macro.
5151
*/
52-
const clang::Token *
52+
std::vector<clang::Token> *
5353
DefineFinder::expandDefine(const clang::MacroDirective &md) {
54+
auto *expandedTokens = new std::vector<clang::Token>();
5455
clang::ArrayRef<clang::Token> tokens = md.getMacroInfo()->tokens();
55-
if (tokens.size() != 1) {
56-
/* process only simple definitions that contain 1 token */
57-
return nullptr;
58-
}
59-
clang::Token token = tokens[0];
60-
if (!(token.isAnyIdentifier() || token.isLiteral())) {
61-
/* unsupported */
62-
return nullptr;
63-
}
64-
if (token.isLiteral() || !token.getIdentifierInfo()->hasMacroDefinition()) {
65-
return &(tokens[0]);
56+
for (const auto &token : tokens) {
57+
if (isMacro(token)) {
58+
if (isFunctionLikeMacro(token)) {
59+
/* function-like macros are unsupported */
60+
return nullptr;
61+
}
62+
std::vector<clang::Token> *newTokens = expandDefine(
63+
*pp.getLocalMacroDirective(token.getIdentifierInfo()));
64+
if (!newTokens) {
65+
std::free(expandedTokens);
66+
return nullptr;
67+
}
68+
for (const auto &newToken : *newTokens) {
69+
(*expandedTokens).push_back(newToken);
70+
}
71+
std::free(newTokens);
72+
} else {
73+
(*expandedTokens).push_back(token);
74+
}
6675
}
67-
return expandDefine(*pp.getLocalMacroDirective(token.getIdentifierInfo()));
76+
return expandedTokens;
77+
}
78+
79+
bool DefineFinder::isMacro(const clang::Token &token) {
80+
return token.isAnyIdentifier() &&
81+
token.getIdentifierInfo()->hasMacroDefinition();
82+
}
83+
84+
bool DefineFinder::isFunctionLikeMacro(const clang::Token &token) {
85+
clang::MacroDirective *md =
86+
pp.getLocalMacroDirective(token.getIdentifierInfo());
87+
return md->getMacroInfo()->isFunctionLike();
6888
}
6989

7090
void DefineFinder::MacroUndefined(const clang::Token &macroNameTok,
71-
const clang::MacroDefinition &md) {
91+
const clang::MacroDefinition &md,
92+
const clang::MacroDirective *undef) {
7293
clang::SourceManager &sm = compiler.getSourceManager();
7394
if (sm.isWrittenInMainFile(macroNameTok.getLocation()) &&
7495
!md.getMacroInfo()->isFunctionLike()) {
@@ -79,8 +100,9 @@ void DefineFinder::MacroUndefined(const clang::Token &macroNameTok,
79100

80101
void DefineFinder::addNumericConstantDefine(const std::string &macroName,
81102
std::string literal,
82-
const clang::Token *token) {
83-
clang::NumericLiteralParser parser(literal, token->getLocation(), pp);
103+
const clang::Token &token,
104+
bool positive) {
105+
clang::NumericLiteralParser parser(literal, token.getLocation(), pp);
84106
std::string type;
85107
if (parser.isIntegerLiteral()) {
86108
std::replace(literal.begin(), literal.end(), 'l', 'L');
@@ -98,50 +120,63 @@ void DefineFinder::addNumericConstantDefine(const std::string &macroName,
98120
* is a long value.
99121
* Therefore we need to check that value fits into certain number
100122
* of bits. */
101-
getTypeOfIntLiteralWithoutEnding(parser, literal, type);
123+
getTypeOfIntLiteralWithoutEnding(parser, literal, type, positive);
102124
}
103125
} else {
104-
if (parser.isFloat) {
105-
type = "native.CFloat";
126+
if (parser.isFloatingLiteral()) {
127+
type = "native.CDouble";
106128
// TODO: distinguish between float and double
107129
}
108130
}
109131
if (!type.empty()) {
132+
if (!positive) {
133+
literal = "-" + literal;
134+
}
110135
ir.addLiteralDefine(macroName, literal, type);
111136
}
112137
}
113138

114-
/**
115-
* Check if literal without `l` or `ll` ending fits into int or long variable.
116-
* Supports only non-negative numbers
117-
*
118-
* Set `literal` and `type` parameters.
119-
*/
120139
void DefineFinder::getTypeOfIntLiteralWithoutEnding(
121-
clang::NumericLiteralParser parser, std::string &literal,
122-
std::string &type) {
140+
clang::NumericLiteralParser parser, std::string &literal, std::string &type,
141+
bool positive) {
123142

124-
llvm::APInt intValue(4 * 8 - 1, 0, false);
125-
/* check if abs value fits into 4 * 8 - 1 bits */
126-
if (!parser.GetIntegerValue(intValue)) {
143+
if (fitsIntoType<int, uint>(parser, positive)) {
127144
type = "native.CInt";
145+
} else if (fitsIntoType<long, ulong>(parser, positive)) {
146+
type = "native.CLong";
147+
literal = literal + "L";
128148
} else {
129-
llvm::APInt longValue(8 * 8 - 1, 0, false);
130-
if (!parser.GetIntegerValue(longValue)) {
131-
type = "native.CLong";
132-
literal = literal + "L";
133-
} else {
134-
llvm::errs() << "Waring: integer value does not fit into 8 bytes: "
135-
<< literal << "\n";
136-
llvm::errs().flush();
137-
/**
138-
* `long long` value has mostly the same size as `long`.
139-
* Moreover in Scala Native the type is represented as `Long`:
140-
* @code
141-
* type CLongLong = Long
142-
* @endcode
143-
* Therefore the case of `long long` is not considered here.
144-
*/
145-
}
149+
llvm::errs() << "Waring: integer value does not fit into 8 bytes: "
150+
<< literal << "\n";
151+
llvm::errs().flush();
152+
/**
153+
* `long long` value has mostly the same size as `long`.
154+
* Moreover in Scala Native the type is represented as `Long`:
155+
* @code
156+
* type CLongLong = Long
157+
* @endcode
158+
* Therefore the case of `long long` is not considered here.
159+
*/
160+
}
161+
}
162+
163+
template <typename signedT, typename unsignedT>
164+
bool DefineFinder::fitsIntoType(clang::NumericLiteralParser parser,
165+
bool positive) {
166+
/* absolute value of minimum negative number will not fit
167+
* into (sizeof(signedT) * 8 - 1) bits */
168+
llvm::APInt uintValue(sizeof(signedT) * 8, 0, false);
169+
if (parser.GetIntegerValue(uintValue)) {
170+
/* absolute value of the number does not fit into (sizeof(signedT) * 8)
171+
* bits */
172+
return false;
173+
}
174+
auto uval = static_cast<unsignedT>(uintValue.getZExtValue());
175+
if (positive) {
176+
return uval <=
177+
static_cast<unsignedT>(std::numeric_limits<signedT>::max());
178+
} else {
179+
return uval <=
180+
static_cast<unsignedT>(-std::numeric_limits<signedT>::min());
146181
}
147182
}

bindgen/defines/DefineFinder.h

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
#include "../ir/IR.h"
55
#include <clang/Frontend/CompilerInstance.h>
66
#include <clang/Lex/LiteralSupport.h>
7-
#include <clang/Lex/PPCallbacks.h>
87
#include <clang/Lex/Preprocessor.h>
98

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

18-
virtual void MacroUndefined(const clang::Token &macroNameTok,
19-
const clang::MacroDefinition &md);
17+
void MacroUndefined(const clang::Token &macroNameTok,
18+
const clang::MacroDefinition &md,
19+
const clang::MacroDirective *undef) override;
2020

2121
private:
2222
IR &ir;
@@ -25,13 +25,30 @@ class DefineFinder : public clang::PPCallbacks {
2525

2626
void addNumericConstantDefine(const std::string &macroName,
2727
std::string literal,
28-
const clang::Token *finalToken);
29-
28+
const clang::Token &token,
29+
bool positive = true);
30+
31+
/**
32+
* Check if a number without `l` or `ll` ending fits into int or long
33+
* variable. Supports only non-negative numbers
34+
*
35+
* Updates `literal` and `type` parameters.
36+
*/
3037
void getTypeOfIntLiteralWithoutEnding(clang::NumericLiteralParser parser,
3138
std::string &literal,
32-
std::string &type);
39+
std::string &type, bool positive);
40+
41+
std::vector<clang::Token> *expandDefine(const clang::MacroDirective &md);
42+
43+
bool isMacro(const clang::Token &token);
44+
45+
bool isFunctionLikeMacro(const clang::Token &token);
3346

34-
const clang::Token *expandDefine(const clang::MacroDirective &md);
47+
/**
48+
* @return true if number contained in parser fits into int type
49+
*/
50+
template <typename signedT, typename unsignedT>
51+
bool fitsIntoType(clang::NumericLiteralParser parser, bool positive);
3552
};
3653

3754
#endif // SCALA_NATIVE_BINDGEN_DEFINEFINDER_H

bindgen/defines/DefineFinderAction.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
#include "DefineFinder.h"
55
#include <clang/Frontend/FrontendActions.h>
6-
#include <clang/Lex/Preprocessor.h>
76

87
class DefineFinderAction : public clang::PreprocessOnlyAction {
98
public:

bindgen/ir/Define.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#ifndef SCALA_NATIVE_BINDGEN_DEFINE_H
22
#define SCALA_NATIVE_BINDGEN_DEFINE_H
33

4-
#include "TypeAndName.h"
4+
#include <string>
55

66
class Define {
77
public:

tests/samples/Define.h renamed to tests/samples/LiteralDefine.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@
99
#define MAXIMUM_UNSIGNED_LONG 18446744073709551615
1010
#define MAXIMUM_SIGNED_LONG 9223372036854775807 // OK
1111

12-
#define NEGATIVE_NUMBER -1 // unsupported
13-
14-
/* negative values are currently ignored because there are 2 tokens in the
15-
* representation */
16-
// #define MINIMUM_SIGNED_LONG -9223372036854775808
12+
#define MINIMUM_SIGNED_LONG -9223372036854775808 // OK
13+
#define LESS_THEN_MINIMUM_SIGNED_LONG -9223372036854775809 // excluded
1714

1815
#define FLOAT 5.6
1916

2017
#define INT 42
18+
#define MAXIMUM_INT 2147483647
2119
#define NEW_INT INT
20+
#define NEG_INT -INT
2221

2322
extern int a;
2423
#define MY_A a // unsupported

tests/samples/Define.scala renamed to tests/samples/LiteralDefine.scala

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@ package org.scalanative.bindgen.samples
33
import scala.scalanative._
44
import scala.scalanative.native._
55

6-
object DefineDefines {
6+
object LiteralDefineDefines {
77
val STRING: native.CString = c"Hello, World!"
88
val LONG: native.CLong = 1000000000000L
99
val LONG_WITHOUT_ENDING: native.CLong = 1000000000000L
1010
val LONG_LONG: native.CLongLong = 1000000000000L
1111
val MAXIMUM_SIGNED_LONG: native.CLong = 9223372036854775807L
12+
val MINIMUM_SIGNED_LONG: native.CLong = -9223372036854775808L
13+
val FLOAT: native.CDouble = 5.6
1214
val INT: native.CInt = 42
15+
val MAXIMUM_INT: native.CInt = 2147483647
1316
val NEW_INT: native.CInt = 42
17+
val NEG_INT: native.CInt = -42
1418
val SHOULD_BE_DEFINED: native.CString = c"Because INT is not equal to 0"
1519
}

0 commit comments

Comments
 (0)