1 /* Cycript - The Truly Universal Scripting Language
2 * Copyright (C) 2009-2016 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>
32 #include "Functor.hpp"
33 #include "Replace.hpp"
36 static CXChildVisitResult
CYVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) {
37 (*reinterpret_cast<const Functor
<void (CXCursor
)> *>(arg
))(cursor
);
38 return CXChildVisit_Continue
;
41 static unsigned CYForChild(CXCursor cursor
, const Functor
<void (CXCursor
)> &visitor
) {
42 return clang_visitChildren(cursor
, &CYVisit
, const_cast<void *>(static_cast<const void *>(&visitor
)));
45 static bool CYOneChild(CXCursor cursor
, const Functor
<void (CXCursor
)> &visitor
) {
47 CYForChild(cursor
, fun([&](CXCursor child
) {
58 CYCXString(CXString value
) :
63 CYCXString(CXCursor cursor
) :
64 value_(clang_getCursorSpelling(cursor
))
68 CYCXString(CXCursorKind kind
) :
69 value_(clang_getCursorKindSpelling(kind
))
73 CYCXString(CXFile file
) :
74 value_(clang_getFileName(file
))
78 CYCXString(CXTranslationUnit unit
, CXToken token
) :
79 value_(clang_getTokenSpelling(unit
, token
))
84 clang_disposeString(value_
);
87 operator const char *() const {
88 return clang_getCString(value_
);
91 const char *Pool(CYPool
&pool
) const {
92 return pool
.strdup(*this);
95 bool operator ==(const char *rhs
) const {
96 const char *lhs(*this);
97 return lhs
== rhs
|| strcmp(lhs
, rhs
) == 0;
101 template <void (&clang_get_Location
)(CXSourceLocation
, CXFile
*, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation
>
102 struct CYCXPosition
{
108 CYCXPosition(CXSourceLocation location
) {
109 clang_get_Location(location
, &file_
, &line_
, &column_
, &offset_
);
112 CYCXPosition(CXTranslationUnit unit
, CXToken token
) :
113 CYCXPosition(clang_getTokenLocation(unit
, token
))
117 CXSourceLocation
Get(CXTranslationUnit unit
) const {
118 return clang_getLocation(unit
, file_
, line_
, column_
);
122 template <void (&clang_get_Location
)(CXSourceLocation
, CXFile
*, unsigned *, unsigned *, unsigned *)>
123 std::ostream
&operator <<(std::ostream
&out
, const CYCXPosition
<clang_get_Location
> &position
) {
124 if (position
.file_
!= NULL
)
125 out
<< "[" << CYCXString(position
.file_
) << "]:";
126 out
<< position
.line_
<< ":" << position
.column_
<< "@" << position
.offset_
;
131 unsigned priority_
= 0;
137 typedef std::map
<std::string
, CYKey
> CYKeyMap
;
139 struct CYChildBaton
{
140 CXTranslationUnit unit
;
143 CYChildBaton(CXTranslationUnit unit
, CYKeyMap
&keys
) :
152 CXTranslationUnit unit_
;
158 CYTokens(CXTranslationUnit unit
, CXSourceRange range
) :
161 clang_tokenize(unit_
, range
, &tokens_
, &count_
);
164 // libclang's tokenizer is horribly broken and returns "extra" tokens.
165 // this code goes back through the tokens and filters for good ones :/
167 CYCXPosition
<> end(clang_getRangeEnd(range
));
168 CYCXString
file(end
.file_
);
170 for (valid_
= 0; valid_
!= count_
; ++valid_
) {
171 CYCXPosition
<> position(unit
, tokens_
[valid_
]);
172 _assert(CYCXString(position
.file_
) == file
);
173 if (position
.offset_
>= end
.offset_
)
178 CYTokens(CXTranslationUnit unit
, CXCursor cursor
) :
179 CYTokens(unit
, clang_getCursorExtent(cursor
))
184 clang_disposeTokens(unit_
, tokens_
, count_
);
187 operator CXToken
*() const {
191 size_t size() const {
196 static CYUTF8String
CYCXPoolUTF8Range(CYPool
&pool
, CXSourceRange range
) {
197 CYCXPosition
<> start(clang_getRangeStart(range
));
198 CYCXPosition
<> end(clang_getRangeEnd(range
));
199 CYCXString
file(start
.file_
);
200 _assert(file
== CYCXString(end
.file_
));
204 char *data(static_cast<char *>(CYPoolFile(temp
, file
, &size
)));
205 _assert(start
.offset_
<= size
&& end
.offset_
<= size
&& start
.offset_
<= end
.offset_
);
208 code
.size
= end
.offset_
- start
.offset_
;
209 code
.data
= pool
.strndup(data
+ start
.offset_
, code
.size
);
213 static CYExpression
*CYTranslateExpression(CXTranslationUnit unit
, CXCursor cursor
) {
214 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
215 case CXCursor_CallExpr
: {
216 CYExpression
*function(NULL
);
217 CYList
<CYArgument
> arguments
;
218 CYForChild(cursor
, fun([&](CXCursor child
) {
219 CYExpression
*expression(CYTranslateExpression(unit
, child
));
220 if (function
== NULL
)
221 function
= expression
;
223 arguments
->*$
C_(expression
);
225 return $
C(function
, arguments
);
228 case CXCursor_DeclRefExpr
: {
229 return $
V(CYCXString(cursor
).Pool($pool
));
232 case CXCursor_IntegerLiteral
: {
233 // libclang doesn't provide any reasonable way to do this
234 // note: clang_tokenize doesn't work if this is a macro
235 // the token range starts inside the macro but ends after it
236 // the tokenizer freaks out and either fails with 0 tokens
237 // or returns some massive number of tokens ending here :/
239 CYUTF8String
token(CYCXPoolUTF8Range($pool
, clang_getCursorExtent(cursor
)));
240 double value(CYCastDouble(token
));
241 if (std::isnan(value
))
242 return $
V(token
.data
);
243 return $
CYNumber(value
);
246 case CXCursor_CStyleCastExpr
:
247 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
249 case CXCursor_UnexposedExpr
:
250 // there is a very high probability that this is actually an "ImplicitCastExpr"
251 // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one
252 // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html
254 case CXCursor_ParenExpr
: {
255 CYExpression
*pass(NULL
);
256 CYOneChild(cursor
, fun([&](CXCursor child
) {
257 pass
= CYTranslateExpression(unit
, child
);
263 //std::cerr << "E:" << CYCXString(kind) << std::endl;
268 static CYStatement
*CYTranslateStatement(CXTranslationUnit unit
, CXCursor cursor
) {
269 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
270 case CXCursor_ReturnStmt
: {
271 CYExpression
*value(NULL
);
272 CYOneChild(cursor
, fun([&](CXCursor child
) {
273 value
= CYTranslateExpression(unit
, child
);
275 return $
CYReturn(value
);
279 //std::cerr << "S:" << CYCXString(kind) << std::endl;
284 static CYStatement
*CYTranslateBlock(CXTranslationUnit unit
, CXCursor cursor
) {
285 CYList
<CYStatement
> statements
;
286 CYForChild(cursor
, fun([&](CXCursor child
) {
287 statements
->*CYTranslateStatement(unit
, child
);
289 return $
CYBlock(statements
);
292 static CYTypedIdentifier
*CYDecodeType(CXType type
);
293 static void CYParseType(CXType type
, CYTypedIdentifier
*typed
);
295 static CYTypedIdentifier
*CYDecodeType(CXType type
, const CYCXString
&identifier
) {
296 CYTypedIdentifier
*typed(CYDecodeType(type
));
297 typed
->identifier_
= $
CYIdentifier(identifier
.Pool($pool
));
301 static void CYParseEnumeration(CXCursor cursor
, CYTypedIdentifier
*typed
) {
302 CYList
<CYEnumConstant
> constants
;
304 CYForChild(cursor
, fun([&](CXCursor child
) {
305 if (clang_getCursorKind(child
) == CXCursor_EnumConstantDecl
)
306 constants
->*$
CYEnumConstant($
I($pool
.strdup(CYCXString(child
))), $
D(clang_getEnumConstantDeclValue(child
)));
309 CYTypedIdentifier
*integer(CYDecodeType(clang_getEnumDeclIntegerType(cursor
)));
310 typed
->specifier_
= $
CYTypeEnum(NULL
, integer
->specifier_
, constants
);
313 static void CYParseStructure(CXCursor cursor
, CYTypedIdentifier
*typed
) {
314 CYList
<CYTypeStructField
> fields
;
315 CYForChild(cursor
, fun([&](CXCursor child
) {
316 if (clang_getCursorKind(child
) == CXCursor_FieldDecl
) {
317 CYTypedIdentifier
*field(CYDecodeType(clang_getCursorType(child
), child
));
318 fields
->*$
CYTypeStructField(field
);
322 typed
->specifier_
= $
CYTypeStruct(NULL
, $
CYStructTail(fields
));
325 static void CYParseCursor(CXType type
, CXCursor cursor
, CYTypedIdentifier
*typed
) {
326 CYCXString
spelling(cursor
);
328 switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
329 case CXCursor_EnumDecl
:
330 if (spelling
[0] != '\0')
331 typed
->specifier_
= $
CYTypeReference(CYTypeReferenceEnum
, $
I(spelling
.Pool($pool
)));
333 CYParseEnumeration(cursor
, typed
);
336 case CXCursor_StructDecl
: {
337 if (spelling
[0] != '\0')
338 typed
->specifier_
= $
CYTypeReference(CYTypeReferenceStruct
, $
I(spelling
.Pool($pool
)));
340 CYParseStructure(cursor
, typed
);
343 case CXCursor_UnionDecl
: {
348 std::cerr
<< "C:" << CYCXString(kind
) << std::endl
;
354 static CYTypedParameter
*CYParseSignature(CXType type
, CYTypedIdentifier
*typed
) {
355 CYParseType(clang_getResultType(type
), typed
);
356 CYList
<CYTypedParameter
> parameters
;
357 for (int i(0), e(clang_getNumArgTypes(type
)); i
!= e
; ++i
)
358 parameters
->*$
CYTypedParameter(CYDecodeType(clang_getArgType(type
, i
)));
362 static void CYParseFunction(CXType type
, CYTypedIdentifier
*typed
) {
363 typed
= typed
->Modify($
CYTypeFunctionWith(clang_isFunctionTypeVariadic(type
), CYParseSignature(type
, typed
)));
366 static void CYParseType(CXType type
, CYTypedIdentifier
*typed
) {
367 switch (CXTypeKind kind
= type
.kind
) {
368 case CXType_Unexposed
: {
369 CXType
result(clang_getResultType(type
));
370 if (result
.kind
== CXType_Invalid
)
371 CYParseCursor(type
, clang_getTypeDeclaration(type
), typed
);
373 // clang marks function pointers as Unexposed but still supports them
374 CYParseFunction(type
, typed
);
377 case CXType_Bool
: typed
->specifier_
= $
CYTypeVariable("bool"); break;
378 case CXType_Float
: typed
->specifier_
= $
CYTypeVariable("float"); break;
379 case CXType_Double
: typed
->specifier_
= $
CYTypeVariable("double"); break;
381 case CXType_Char_U
: typed
->specifier_
= $
CYTypeCharacter(CYTypeNeutral
); break;
382 case CXType_Char_S
: typed
->specifier_
= $
CYTypeCharacter(CYTypeNeutral
); break;
383 case CXType_SChar
: typed
->specifier_
= $
CYTypeCharacter(CYTypeSigned
); break;
384 case CXType_UChar
: typed
->specifier_
= $
CYTypeCharacter(CYTypeUnsigned
); break;
386 case CXType_Short
: typed
->specifier_
= $
CYTypeIntegral(CYTypeSigned
, 0); break;
387 case CXType_UShort
: typed
->specifier_
= $
CYTypeIntegral(CYTypeUnsigned
, 0); break;
389 case CXType_Int
: typed
->specifier_
= $
CYTypeIntegral(CYTypeSigned
, 1); break;
390 case CXType_UInt
: typed
->specifier_
= $
CYTypeIntegral(CYTypeUnsigned
, 1); break;
392 case CXType_Long
: typed
->specifier_
= $
CYTypeIntegral(CYTypeSigned
, 2); break;
393 case CXType_ULong
: typed
->specifier_
= $
CYTypeIntegral(CYTypeUnsigned
, 2); break;
395 case CXType_LongLong
: typed
->specifier_
= $
CYTypeIntegral(CYTypeSigned
, 3); break;
396 case CXType_ULongLong
: typed
->specifier_
= $
CYTypeIntegral(CYTypeUnsigned
, 3); break;
398 case CXType_Int128
: typed
->specifier_
= $
CYTypeInt128(CYTypeSigned
); break;
399 case CXType_UInt128
: typed
->specifier_
= $
CYTypeInt128(CYTypeUnsigned
); break;
401 case CXType_BlockPointer
: {
402 CXType
pointee(clang_getPointeeType(type
));
403 _assert(!clang_isFunctionTypeVariadic(pointee
));
404 typed
= typed
->Modify($
CYTypeBlockWith(CYParseSignature(pointee
, typed
)));
407 case CXType_ConstantArray
:
408 CYParseType(clang_getArrayElementType(type
), typed
);
409 typed
= typed
->Modify($
CYTypeArrayOf($
D(clang_getArraySize(type
))));
413 typed
->specifier_
= $
CYTypeVariable($pool
.strdup(CYCXString(clang_getTypeSpelling(type
))));
416 case CXType_FunctionProto
:
417 CYParseFunction(type
, typed
);
420 case CXType_IncompleteArray
:
421 // XXX: I probably should not decay to Pointer
422 CYParseType(clang_getArrayElementType(type
), typed
);
423 typed
= typed
->Modify($
CYTypePointerTo());
426 case CXType_ObjCClass
:
427 typed
->specifier_
= $
CYTypeVariable("Class");
431 typed
->specifier_
= $
CYTypeVariable("id");
434 case CXType_ObjCInterface
:
435 typed
->specifier_
= $
CYTypeVariable($pool
.strdup(CYCXString(clang_getTypeSpelling(type
))));
438 case CXType_ObjCObjectPointer
: {
439 CXType
pointee(clang_getPointeeType(type
));
440 if (pointee
.kind
!= CXType_Unexposed
) {
441 CYParseType(pointee
, typed
);
442 typed
= typed
->Modify($
CYTypePointerTo());
444 // Clang seems to have internal typedefs for id and Class that are awkward
449 typed
->specifier_
= $
CYTypeVariable("SEL");
453 CYParseType(clang_getPointeeType(type
), typed
);
454 typed
= typed
->Modify($
CYTypePointerTo());
458 typed
->specifier_
= $
CYTypeReference(CYTypeReferenceStruct
, $
I($pool
.strdup(CYCXString(clang_getTypeSpelling(type
)))));
462 // use the declaration in order to isolate the name of the typedef itself
463 typed
->specifier_
= $
CYTypeVariable($pool
.strdup(CYCXString(clang_getTypeDeclaration(type
))));
471 typed
->specifier_
= $
CYTypeVoid();
475 std::cerr
<< "T:" << CYCXString(clang_getTypeKindSpelling(kind
)) << std::endl
;
476 std::cerr
<< "_: " << CYCXString(clang_getTypeSpelling(type
)) << std::endl
;
480 if (clang_isConstQualifiedType(type
))
481 typed
= typed
->Modify($
CYTypeConstant());
484 static CYTypedIdentifier
*CYDecodeType(CXType type
) {
485 CYTypedIdentifier
*typed($
CYTypedIdentifier(NULL
));
486 CYParseType(type
, typed
);
490 static CXChildVisitResult
CYChildVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) {
491 CYChildBaton
&baton(*static_cast<CYChildBaton
*>(arg
));
492 CXTranslationUnit
&unit(baton
.unit
);
494 CXChildVisitResult
result(CXChildVisit_Continue
);
495 CYCXString
spelling(cursor
);
496 std::string
name(spelling
);
497 std::ostringstream value
;
498 unsigned priority(2);
499 unsigned flags(CYBridgeHold
);
501 /*CXSourceLocation location(clang_getCursorLocation(cursor));
502 CYCXPosition<> position(location);
503 std::cerr << spelling << " " << position << std::endl;*/
505 try { switch (CXCursorKind kind
= clang_getCursorKind(cursor
)) {
506 case CXCursor_EnumConstantDecl
: {
507 value
<< clang_getEnumConstantDeclValue(cursor
);
510 case CXCursor_EnumDecl
: {
511 if (spelling
[0] == '\0')
513 // XXX: this was blindly copied from StructDecl
514 if (!clang_isCursorDefinition(cursor
))
519 CYTypedIdentifier
typed(NULL
);
520 CYParseEnumeration(cursor
, &typed
);
523 CYOutput
out(*value
.rdbuf(), options
);
524 CYTypeExpression(&typed
).Output(out
, CYNoBFC
);
526 value
<< ".withName(\"" << name
<< "\")";
527 name
= "$cye" + name
;
528 flags
= CYBridgeType
;
530 // the enum constants are implemented separately *also*
531 // XXX: maybe move output logic to function we can call
532 result
= CXChildVisit_Recurse
;
535 case CXCursor_MacroDefinition
: {
536 CXSourceRange
range(clang_getCursorExtent(cursor
));
537 CYTokens
tokens(unit
, range
);
538 _assert(tokens
.size() != 0);
540 CXCursor cursors
[tokens
.size()];
541 clang_annotateTokens(unit
, tokens
, tokens
.size(), cursors
);
544 CYList
<CYFunctionParameter
> parameters
;
547 if (tokens
.size() != 1) {
548 CYCXPosition
<> start(clang_getRangeStart(range
));
549 CYCXString
first(unit
, tokens
[offset
]);
551 CYCXPosition
<> paren(unit
, tokens
[offset
]);
552 if (start
.offset_
+ strlen(spelling
) == paren
.offset_
) {
554 _assert(++offset
!= tokens
.size());
555 CYCXString
token(unit
, tokens
[offset
]);
556 parameters
->*$
P($
B($
I(token
.Pool($pool
))));
557 _assert(++offset
!= tokens
.size());
558 CYCXString
comma(unit
, tokens
[offset
]);
561 _assert(comma
== ",");
568 std::ostringstream body
;
569 for (unsigned i(offset
); i
!= tokens
.size(); ++i
) {
570 CYCXString
token(unit
, tokens
[i
]);
580 CYOutput
out(*value
.rdbuf(), options
);
581 out
<< '(' << "function" << '(';
584 out
<< "return" << ' ';
586 out
<< ';' << '}' << ')';
590 case CXCursor_StructDecl
: {
591 if (spelling
[0] == '\0')
593 if (!clang_isCursorDefinition(cursor
))
598 CYTypedIdentifier
typed(NULL
);
599 CYParseStructure(cursor
, &typed
);
602 CYOutput
out(*value
.rdbuf(), options
);
603 CYTypeExpression(&typed
).Output(out
, CYNoBFC
);
605 value
<< ".withName(\"" << name
<< "\")";
606 name
= "$cys" + name
;
607 flags
= CYBridgeType
;
610 case CXCursor_TypedefDecl
: {
613 CYTypedIdentifier
*typed(CYDecodeType(clang_getTypedefDeclUnderlyingType(cursor
)));
614 if (typed
->specifier_
== NULL
)
615 value
<< "(typedef " << CYCXString(clang_getTypeSpelling(clang_getTypedefDeclUnderlyingType(cursor
))) << ")";
618 CYOutput
out(*value
.rdbuf(), options
);
619 CYTypeExpression(typed
).Output(out
, CYNoBFC
);
623 case CXCursor_FunctionDecl
:
624 case CXCursor_VarDecl
: {
627 CYList
<CYFunctionParameter
> parameters
;
628 CYStatement
*code(NULL
);
632 CYForChild(cursor
, fun([&](CXCursor child
) {
633 switch (CXCursorKind kind
= clang_getCursorKind(child
)) {
634 case CXCursor_AsmLabelAttr
:
635 label
= CYCXString(child
);
638 case CXCursor_CompoundStmt
:
639 code
= CYTranslateBlock(unit
, child
);
642 case CXCursor_ParmDecl
:
643 parameters
->*$
P($
B($
I(CYCXString(child
).Pool($pool
))));
646 case CXCursor_IntegerLiteral
:
647 case CXCursor_ObjCClassRef
:
648 case CXCursor_TypeRef
:
649 case CXCursor_UnexposedAttr
:
653 //std::cerr << "A:" << CYCXString(child) << std::endl;
661 } else if (label
[0] != '_')
665 CXType
type(clang_getCursorType(cursor
));
666 value
<< "*(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label
.substr(1) << "'))";
669 CYOutput
out(*value
.rdbuf(), options
);
670 CYFunctionExpression
*function($
CYFunctionExpression(NULL
, parameters
, code
));
671 function
->Output(out
, CYNoBFC
);
672 //std::cerr << value.str() << std::endl;
677 result
= CXChildVisit_Recurse
;
681 CYKey
&key(baton
.keys
[name
]);
682 if (key
.priority_
<= priority
) {
683 key
.priority_
= priority
;
684 key
.code_
= value
.str();
687 } } catch (const CYException
&error
) {
689 //std::cerr << error.PoolCString(pool) << std::endl;
696 int main(int argc
, const char *argv
[]) {
697 CXIndex
index(clang_createIndex(0, 0));
699 const char *file(argv
[1]);
703 argv
[--offset
] = "-ObjC++";
706 CXTranslationUnit
unit(clang_parseTranslationUnit(index
, file
, argv
+ offset
, argc
- offset
, NULL
, 0, CXTranslationUnit_DetailedPreprocessingRecord
));
708 for (unsigned i(0), e(clang_getNumDiagnostics(unit
)); i
!= e
; ++i
) {
709 CXDiagnostic
diagnostic(clang_getDiagnostic(unit
, i
));
710 CYCXString
spelling(clang_getDiagnosticSpelling(diagnostic
));
711 std::cerr
<< spelling
<< std::endl
;
715 CYChildBaton
baton(unit
, keys
);
716 clang_visitChildren(clang_getTranslationUnitCursor(unit
), &CYChildVisit
, &baton
);
718 for (CYKeyMap::const_iterator
key(keys
.begin()); key
!= keys
.end(); ++key
) {
719 std::string
code(key
->second
.code_
);
720 for (size_t i(0), e(code
.size()); i
!= e
; ++i
)
721 if (code
[i
] <= 0 || code
[i
] >= 0x7f || code
[i
] == '\n')
723 std::cout
<< key
->first
<< "|" << key
->second
.flags_
<< "\"" << code
<< "\"" << std::endl
;
726 clang_disposeTranslationUnit(unit
);
727 clang_disposeIndex(index
);