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> 
  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_
; 
 130     unsigned priority_ 
= 0; 
 136 typedef std::map
<std::string
, CYKey
> CYKeyMap
; 
 138 struct CYChildBaton 
{ 
 139     CXTranslationUnit unit
; 
 142     CYChildBaton(CXTranslationUnit unit
, CYKeyMap 
&keys
) : 
 151     CXTranslationUnit unit_
; 
 157     CYTokens(CXTranslationUnit unit
, CXSourceRange range
) : 
 160         clang_tokenize(unit_
, range
, &tokens_
, &count_
); 
 163         // libclang's tokenizer is horribly broken and returns "extra" tokens. 
 164         // this code goes back through the tokens and filters for good ones :/ 
 166         CYCXPosition
<> end(clang_getRangeEnd(range
)); 
 167         CYCXString 
file(end
.file_
); 
 169         for (valid_ 
= 0; valid_ 
!= count_
; ++valid_
) { 
 170             CYCXPosition
<> position(unit
, tokens_
[valid_
]); 
 171             _assert(CYCXString(position
.file_
) == file
); 
 172             if (position
.offset_ 
>= end
.offset_
) 
 177     CYTokens(CXTranslationUnit unit
, CXCursor cursor
) : 
 178         CYTokens(unit
, clang_getCursorExtent(cursor
)) 
 183         clang_disposeTokens(unit_
, tokens_
, count_
); 
 186     operator CXToken 
*() const { 
 190     size_t size() const { 
 195 static CYUTF8String 
CYCXPoolUTF8Range(CYPool 
&pool
, CXSourceRange range
) { 
 196     CYCXPosition
<> start(clang_getRangeStart(range
)); 
 197     CYCXPosition
<> end(clang_getRangeEnd(range
)); 
 198     CYCXString 
file(start
.file_
); 
 199     _assert(file 
== CYCXString(end
.file_
)); 
 203     char *data(static_cast<char *>(CYPoolFile(temp
, file
, &size
))); 
 204     _assert(start
.offset_ 
<= size 
&& end
.offset_ 
<= size 
&& start
.offset_ 
<= end
.offset_
); 
 207     code
.size 
= end
.offset_ 
- start
.offset_
; 
 208     code
.data 
= pool
.strndup(data 
+ start
.offset_
, code
.size
); 
 212 static CYExpression 
*CYTranslateExpression(CXTranslationUnit unit
, CXCursor cursor
) { 
 213     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 214         case CXCursor_CallExpr
: { 
 215             CYExpression 
*function(NULL
); 
 216             CYList
<CYArgument
> arguments
; 
 217             CYForChild(cursor
, fun([&](CXCursor child
) { 
 218                 CYExpression 
*expression(CYTranslateExpression(unit
, child
)); 
 219                 if (function 
== NULL
) 
 220                     function 
= expression
; 
 222                     arguments
->*$
C_(expression
); 
 224             return $
C(function
, arguments
); 
 227         case CXCursor_DeclRefExpr
: { 
 228             return $
V(CYCXString(cursor
).Pool($pool
)); 
 231         case CXCursor_IntegerLiteral
: { 
 232             // libclang doesn't provide any reasonable way to do this 
 233             // note: clang_tokenize doesn't work if this is a macro 
 234             // the token range starts inside the macro but ends after it 
 235             // the tokenizer freaks out and either fails with 0 tokens 
 236             // or returns some massive number of tokens ending here :/ 
 238             CYUTF8String 
token(CYCXPoolUTF8Range($pool
, clang_getCursorExtent(cursor
))); 
 239             double value(CYCastDouble(token
)); 
 240             if (std::isnan(value
)) 
 241                 return $
V(token
.data
); 
 242             return $ 
CYNumber(value
); 
 245         case CXCursor_CStyleCastExpr
: 
 246             // XXX: most of the time, this is a "NoOp" integer cast; but we should check it 
 248         case CXCursor_UnexposedExpr
: 
 249             // there is a very high probability that this is actually an "ImplicitCastExpr" 
 250             // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one 
 251             // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html 
 253         case CXCursor_ParenExpr
: { 
 254             CYExpression 
*pass(NULL
); 
 255             CYOneChild(cursor
, fun([&](CXCursor child
) { 
 256                 pass 
= CYTranslateExpression(unit
, child
); 
 262             //std::cerr << "E:" << CYCXString(kind) << std::endl; 
 267 static CYStatement 
*CYTranslateStatement(CXTranslationUnit unit
, CXCursor cursor
) { 
 268     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 269         case CXCursor_ReturnStmt
: { 
 270             CYExpression 
*value(NULL
); 
 271             CYOneChild(cursor
, fun([&](CXCursor child
) { 
 272                 value 
= CYTranslateExpression(unit
, child
); 
 274             return $ 
CYReturn(value
); 
 278             //std::cerr << "S:" << CYCXString(kind) << std::endl; 
 283 static CYStatement 
*CYTranslateBlock(CXTranslationUnit unit
, CXCursor cursor
) { 
 284     CYList
<CYStatement
> statements
; 
 285     CYForChild(cursor
, fun([&](CXCursor child
) { 
 286         statements
->*CYTranslateStatement(unit
, child
); 
 288     return $ 
CYBlock(statements
); 
 291 static CXChildVisitResult 
CYChildVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) { 
 292     CYChildBaton 
&baton(*static_cast<CYChildBaton 
*>(arg
)); 
 293     CXTranslationUnit 
&unit(baton
.unit
); 
 295     CYCXString 
spelling(cursor
); 
 296     std::string 
name(spelling
); 
 297     std::ostringstream value
; 
 298     unsigned priority(2); 
 301     /*CXSourceLocation location(clang_getCursorLocation(cursor)); 
 302     CYCXPosition<> position(location); 
 303     std::cout << spelling << " " << position << std::endl;*/ 
 305     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 306         case CXCursor_EnumConstantDecl
: { 
 307             value 
<< clang_getEnumConstantDeclValue(cursor
); 
 310         case CXCursor_MacroDefinition
: try { 
 311             CXSourceRange 
range(clang_getCursorExtent(cursor
)); 
 312             CYTokens 
tokens(unit
, range
); 
 313             _assert(tokens
.size() != 0); 
 315             CXCursor cursors
[tokens
.size()]; 
 316             clang_annotateTokens(unit
, tokens
, tokens
.size(), cursors
); 
 319             CYList
<CYFunctionParameter
> parameters
; 
 322             if (tokens
.size() != 1) { 
 323                 CYCXPosition
<> start(clang_getRangeStart(range
)); 
 324                 CYCXString 
first(unit
, tokens
[offset
]); 
 326                     CYCXPosition
<> paren(unit
, tokens
[offset
]); 
 327                     if (start
.offset_ 
+ strlen(spelling
) == paren
.offset_
) { 
 329                             _assert(++offset 
!= tokens
.size()); 
 330                             CYCXString 
token(unit
, tokens
[offset
]); 
 331                             parameters
->*$
P($
B($
I(token
.Pool($pool
)))); 
 332                             _assert(++offset 
!= tokens
.size()); 
 333                             CYCXString 
comma(unit
, tokens
[offset
]); 
 336                             _assert(comma 
== ","); 
 343             std::ostringstream body
; 
 344             for (unsigned i(offset
); i 
!= tokens
.size(); ++i
) { 
 345                 CYCXString 
token(unit
, tokens
[i
]); 
 355                 CYOutput 
out(*value
.rdbuf(), options
); 
 356                 out 
<< '(' << "function" << '('; 
 359                 out 
<< "return" << ' '; 
 361                 out 
<< ';' << '}' << ')'; 
 363         } catch (const CYException 
&error
) { 
 365             //std::cerr << error.PoolCString(pool) << std::endl; 
 369         case CXCursor_StructDecl
: { 
 370             if (spelling
[0] == '\0') 
 372             if (!clang_isCursorDefinition(cursor
)) 
 375             std::ostringstream types
; 
 376             std::ostringstream names
; 
 378             CYForChild(cursor
, fun([&](CXCursor child
) { 
 379                 if (clang_getCursorKind(child
) == CXCursor_FieldDecl
) { 
 380                     CXType 
type(clang_getCursorType(child
)); 
 381                     types 
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << "),"; 
 382                     names 
<< "'" << CYCXString(child
) << "',"; 
 387             value 
<< "new Type([" << types
.str() << "],[" << names
.str() << "])"; 
 390         case CXCursor_TypedefDecl
: { 
 391             CXType 
type(clang_getTypedefDeclUnderlyingType(cursor
)); 
 392             value 
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ")"; 
 395         case CXCursor_FunctionDecl
: 
 396         case CXCursor_VarDecl
: try { 
 399             CYList
<CYFunctionParameter
> parameters
; 
 400             CYStatement 
*code(NULL
); 
 404             CYForChild(cursor
, fun([&](CXCursor child
) { 
 405                 switch (CXCursorKind kind 
= clang_getCursorKind(child
)) { 
 406                     case CXCursor_AsmLabelAttr
: 
 407                         label 
= CYCXString(child
); 
 410                     case CXCursor_CompoundStmt
: 
 411                         code 
= CYTranslateBlock(unit
, child
); 
 414                     case CXCursor_ParmDecl
: 
 415                         parameters
->*$
P($
B($
I(CYCXString(child
).Pool($pool
)))); 
 418                     case CXCursor_IntegerLiteral
: 
 419                     case CXCursor_ObjCClassRef
: 
 420                     case CXCursor_TypeRef
: 
 421                     case CXCursor_UnexposedAttr
: 
 425                         //std::cerr << "A:" << CYCXString(child) << std::endl; 
 433             } else if (label
[0] != '_') 
 437                 CXType 
type(clang_getCursorType(cursor
)); 
 438                 value 
<< "*(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label
.substr(1) << "'))"; 
 441                 CYOutput 
out(*value
.rdbuf(), options
); 
 442                 CYFunctionExpression 
*function($ 
CYFunctionExpression(NULL
, parameters
, code
)); 
 443                 function
->Output(out
, CYNoBFC
); 
 444                 //std::cerr << value.str() << std::endl; 
 446         } catch (const CYException 
&error
) { 
 448             //std::cerr << error.PoolCString(pool) << std::endl; 
 453             return CXChildVisit_Recurse
; 
 458         CYKey 
&key(baton
.keys
[name
]); 
 459         if (key
.priority_ 
< priority
) { 
 460             key
.priority_ 
= priority
; 
 461             key
.code_ 
= value
.str(); 
 467     return CXChildVisit_Continue
; 
 470 int main(int argc
, const char *argv
[]) { 
 471     CXIndex 
index(clang_createIndex(0, 0)); 
 473     const char *file(argv
[1]); 
 477     argv
[--offset
] = "-ObjC++"; 
 480     CXTranslationUnit 
unit(clang_parseTranslationUnit(index
, file
, argv 
+ offset
, argc 
- offset
, NULL
, 0, CXTranslationUnit_DetailedPreprocessingRecord
)); 
 482     for (unsigned i(0), e(clang_getNumDiagnostics(unit
)); i 
!= e
; ++i
) { 
 483         CXDiagnostic 
diagnostic(clang_getDiagnostic(unit
, i
)); 
 484         CYCXString 
spelling(clang_getDiagnosticSpelling(diagnostic
)); 
 485         std::cerr 
<< spelling 
<< std::endl
; 
 489     CYChildBaton 
baton(unit
, keys
); 
 490     clang_visitChildren(clang_getTranslationUnitCursor(unit
), &CYChildVisit
, &baton
); 
 492     for (CYKeyMap::const_iterator 
key(keys
.begin()); key 
!= keys
.end(); ++key
) { 
 493         std::string 
code(key
->second
.code_
); 
 494         for (size_t i(0), e(code
.size()); i 
!= e
; ++i
) 
 495             if (code
[i
] <= 0 || code
[i
] >= 0x7f || code
[i
] == '\n') 
 497         std::cout 
<< key
->first 
<< "|" << key
->second
.flags_ 
<< "\"" << code 
<< "\"" << std::endl
; 
 500     clang_disposeTranslationUnit(unit
); 
 501     clang_disposeIndex(index
);