1 /* Cycript - Optimizing JavaScript Compiler/Runtime
2 * Copyright (C) 2009-2015 Jay Freeman (saurik)
5 /* GNU Affero General Public License, Version 3 {{{ */
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include <clang-c/Index.h>
31 #include "Functor.hpp"
32 #include "Replace.hpp"
35 static CXChildVisitResult
CYVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) {
36 (*reinterpret_cast<const Functor
<void (CXCursor
)> *>(arg
))(cursor
);
37 return CXChildVisit_Continue
;
40 static unsigned CYForChild(CXCursor cursor
, const Functor
<void (CXCursor
)> &visitor
) {
41 return clang_visitChildren(cursor
, &CYVisit
, const_cast<void *>(static_cast<const void *>(&visitor
)));
44 static bool CYOneChild(CXCursor cursor
, const Functor
<void (CXCursor
)> &visitor
) {
46 CYForChild(cursor
, fun([&](CXCursor child
) {
57 CYCXString(CXString value
) :
62 CYCXString(CXCursor cursor
) :
63 value_(clang_getCursorSpelling(cursor
))
67 CYCXString(CXCursorKind kind
) :
68 value_(clang_getCursorKindSpelling(kind
))
72 CYCXString(CXFile file
) :
73 value_(clang_getFileName(file
))
77 CYCXString(CXTranslationUnit unit
, CXToken token
) :
78 value_(clang_getTokenSpelling(unit
, token
))
83 clang_disposeString(value_
);
86 operator const char *() const {
87 return clang_getCString(value_
);
90 const char *Pool(CYPool
&pool
) const {
91 return pool
.strdup(*this);
94 bool operator ==(const char *rhs
) const {
95 const char *lhs(*this);
96 return lhs
== rhs
|| strcmp(lhs
, rhs
) == 0;
100 template <void (&clang_get_Location
)(CXSourceLocation
, CXFile
*, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation
>
101 struct CYCXPosition
{
107 CYCXPosition(CXSourceLocation location
) {
108 clang_get_Location(location
, &file_
, &line_
, &column_
, &offset_
);
111 CYCXPosition(CXTranslationUnit unit
, CXToken token
) :
112 CYCXPosition(clang_getTokenLocation(unit
, token
))
116 CXSourceLocation
Get(CXTranslationUnit unit
) const {
117 return clang_getLocation(unit
, file_
, line_
, column_
);
121 template <void (&clang_get_Location
)(CXSourceLocation
, CXFile
*, unsigned *, unsigned *, unsigned *)>
122 std::ostream
&operator <<(std::ostream
&out
, const CYCXPosition
<clang_get_Location
> &position
) {
123 if (position
.file_
!= NULL
)
124 out
<< "[" << CYCXString(position
.file_
) << "]:";
125 out
<< position
.line_
<< ":" << position
.column_
<< "@" << position
.offset_
;
129 typedef std::map
<std::string
, std::string
> CYKeyMap
;
131 struct CYChildBaton
{
132 CXTranslationUnit unit
;
135 CYChildBaton(CXTranslationUnit unit
, CYKeyMap
&keys
) :
144 CXTranslationUnit unit_
;
150 CYTokens(CXTranslationUnit unit
, CXSourceRange range
) :
153 clang_tokenize(unit_
, range
, &tokens_
, &count_
);
156 // libclang's tokenizer is horribly broken and returns "extra" tokens.
157 // this code goes back through the tokens and filters for good ones :/
159 CYCXPosition
<> end(clang_getRangeEnd(range
));
160 CYCXString
file(end
.file_
);
162 for (valid_
= 0; valid_
!= count_
; ++valid_
) {
163 CYCXPosition
<> position(unit
, tokens_
[valid_
]);
164 _assert(CYCXString(position
.file_
) == file
);
165 if (position
.offset_
>= end
.offset_
)
170 CYTokens(CXTranslationUnit unit
, CXCursor cursor
) :
171 CYTokens(unit
, clang_getCursorExtent(cursor
))
176 clang_disposeTokens(unit_
, tokens_
, count_
);
179 operator CXToken
*() const {
183 size_t size() const {
188 static CYExpression
*CYTranslateExpression(CXTranslationUnit unit
, CXCursor cursor
) {
189 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
190 case CXCursor_CallExpr
: {
191 CYExpression
*function(NULL
);
192 CYList
<CYArgument
> arguments
;
193 CYForChild(cursor
, fun([&](CXCursor child
) {
194 CYExpression
*expression(CYTranslateExpression(unit
, child
));
195 if (function
== NULL
)
196 function
= expression
;
198 arguments
->*$
C_(expression
);
200 return $
C(function
, arguments
);
203 case CXCursor_DeclRefExpr
: {
204 return $
V(CYCXString(cursor
).Pool($pool
));
207 case CXCursor_IntegerLiteral
: {
208 // libclang doesn't provide any reasonable way to do this
209 // note: clang_tokenize doesn't work if this is a macro
210 // the token range starts inside the macro but ends after it
211 // the tokenizer freaks out and either fails with 0 tokens
212 // or returns some massive number of tokens ending here :/
214 CXSourceRange
range(clang_getCursorExtent(cursor
));
215 CYCXPosition
<> start(clang_getRangeStart(range
));
216 CYCXPosition
<> end(clang_getRangeEnd(range
));
217 CYCXString
file(start
.file_
);
218 _assert(file
== CYCXString(end
.file_
));
222 char *data(static_cast<char *>(CYPoolFile(pool
, file
, &size
)));
223 _assert(start
.offset_
<= size
&& end
.offset_
<= size
&& start
.offset_
<= end
.offset_
);
225 const char *token($pool
.strndup(data
+ start
.offset_
, end
.offset_
- start
.offset_
));
226 double value(CYCastDouble(token
));
227 if (!std::isnan(value
))
228 return $
CYNumber(value
);
233 case CXCursor_CStyleCastExpr
:
234 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
236 case CXCursor_UnexposedExpr
:
237 // there is a very high probability that this is actually an "ImplicitCastExpr"
238 // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one
239 // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html
241 case CXCursor_ParenExpr
: {
242 CYExpression
*pass(NULL
);
243 CYOneChild(cursor
, fun([&](CXCursor child
) {
244 pass
= CYTranslateExpression(unit
, child
);
250 //std::cerr << "E:" << CYCXString(kind) << std::endl;
255 static CYStatement
*CYTranslateStatement(CXTranslationUnit unit
, CXCursor cursor
) {
256 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
257 case CXCursor_ReturnStmt
: {
258 CYExpression
*value(NULL
);
259 CYOneChild(cursor
, fun([&](CXCursor child
) {
260 value
= CYTranslateExpression(unit
, child
);
262 return $
CYReturn(value
);
266 //std::cerr << "S:" << CYCXString(kind) << std::endl;
271 static CYStatement
*CYTranslateBlock(CXTranslationUnit unit
, CXCursor cursor
) {
272 CYList
<CYStatement
> statements
;
273 CYForChild(cursor
, fun([&](CXCursor child
) {
274 statements
->*CYTranslateStatement(unit
, child
);
276 return $
CYBlock(statements
);
279 static CXChildVisitResult
CYChildVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) {
280 CYChildBaton
&baton(*static_cast<CYChildBaton
*>(arg
));
281 CXTranslationUnit
&unit(baton
.unit
);
283 CYCXString
spelling(cursor
);
284 std::string
name(spelling
);
285 std::ostringstream value
;
287 /*CXSourceLocation location(clang_getCursorLocation(cursor));
288 CYCXPosition<> position(location);
289 std::cout << spelling << " " << position << std::endl;*/
291 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
292 case CXCursor_EnumConstantDecl
: {
293 value
<< clang_getEnumConstantDeclValue(cursor
);
296 case CXCursor_MacroDefinition
: try {
297 CXSourceRange
range(clang_getCursorExtent(cursor
));
298 CYTokens
tokens(unit
, range
);
299 _assert(tokens
.size() != 0);
301 CXCursor cursors
[tokens
.size()];
302 clang_annotateTokens(unit
, tokens
, tokens
.size(), cursors
);
304 CYCXPosition
<> start(clang_getRangeStart(range
));
305 CYCXString
first(unit
, tokens
[1]);
307 CYCXPosition
<> paren(unit
, tokens
[1]);
308 if (start
.offset_
+ strlen(spelling
) == paren
.offset_
)
309 _assert(false); // XXX: support parameterized macros
312 for (unsigned i(1); i
!= tokens
.size(); ++i
) {
313 CYCXString
token(unit
, tokens
[i
]);
318 } catch (const CYException
&error
) {
320 //std::cerr << error.PoolCString(pool) << std::endl;
324 case CXCursor_StructDecl
: {
325 if (!clang_isCursorDefinition(cursor
))
327 if (spelling
[0] == '\0')
330 std::ostringstream types
;
331 std::ostringstream names
;
333 CYForChild(cursor
, fun([&](CXCursor child
) {
334 if (clang_getCursorKind(child
) == CXCursor_FieldDecl
) {
335 CXType
type(clang_getCursorType(child
));
336 types
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << "),";
337 names
<< "'" << CYCXString(child
) << "',";
342 value
<< "new Type([" << types
.str() << "],[" << names
.str() << "])";
345 case CXCursor_TypedefDecl
: {
346 CXType
type(clang_getTypedefDeclUnderlyingType(cursor
));
347 value
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ")";
350 case CXCursor_FunctionDecl
:
351 case CXCursor_VarDecl
: try {
354 CYList
<CYFunctionParameter
> parameters
;
355 CYStatement
*code(NULL
);
359 CYForChild(cursor
, fun([&](CXCursor child
) {
360 switch (CXCursorKind kind
= clang_getCursorKind(child
)) {
361 case CXCursor_AsmLabelAttr
:
362 label
= CYCXString(child
);
365 case CXCursor_CompoundStmt
:
366 code
= CYTranslateBlock(unit
, child
);
369 case CXCursor_ParmDecl
:
370 parameters
->*$
P($
B($
I(CYCXString(child
).Pool($pool
))));
373 case CXCursor_IntegerLiteral
:
374 case CXCursor_ObjCClassRef
:
375 case CXCursor_TypeRef
:
376 case CXCursor_UnexposedAttr
:
380 //std::cerr << "A:" << CYCXString(child) << std::endl;
388 } else if (label
[0] != '_')
392 CXType
type(clang_getCursorType(cursor
));
393 value
<< "*(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label
.substr(1) << "'))";
396 CYOutput
out(*value
.rdbuf(), options
);
397 CYFunctionExpression
*function($
CYFunctionExpression(NULL
, parameters
, code
));
398 function
->Output(out
, CYNoBFC
);
399 //std::cerr << value.str() << std::endl;
401 } catch (const CYException
&error
) {
403 //std::cerr << error.PoolCString(pool) << std::endl;
408 return CXChildVisit_Recurse
;
412 baton
.keys
[name
] = value
.str();
415 return CXChildVisit_Continue
;
418 int main(int argc
, const char *argv
[]) {
419 CXIndex
index(clang_createIndex(0, 0));
421 const char *file(argv
[1]);
425 argv
[--offset
] = "-ObjC++";
428 CXTranslationUnit
unit(clang_parseTranslationUnit(index
, file
, argv
+ offset
, argc
- offset
, NULL
, 0, CXTranslationUnit_DetailedPreprocessingRecord
));
430 for (unsigned i(0), e(clang_getNumDiagnostics(unit
)); i
!= e
; ++i
) {
431 CXDiagnostic
diagnostic(clang_getDiagnostic(unit
, i
));
432 CYCXString
spelling(clang_getDiagnosticSpelling(diagnostic
));
433 std::cerr
<< spelling
<< std::endl
;
437 CYChildBaton
baton(unit
, keys
);
438 clang_visitChildren(clang_getTranslationUnitCursor(unit
), &CYChildVisit
, &baton
);
440 for (CYKeyMap::const_iterator
key(keys
.begin()); key
!= keys
.end(); ++key
) {
441 std::string
value(key
->second
);
442 for (size_t i(0), e(value
.size()); i
!= e
; ++i
)
443 if (value
[i
] <= 0 || value
[i
] >= 0x7f || value
[i
] == '\n')
445 std::cout
<< key
->first
<< "|\"" << value
<< "\"" << std::endl
;
448 clang_disposeTranslationUnit(unit
);
449 clang_disposeIndex(index
);