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 CYUTF8String 
CYCXPoolUTF8Range(CYPool 
&pool
, CXSourceRange range
) { 
 189     CYCXPosition
<> start(clang_getRangeStart(range
)); 
 190     CYCXPosition
<> end(clang_getRangeEnd(range
)); 
 191     CYCXString 
file(start
.file_
); 
 192     _assert(file 
== CYCXString(end
.file_
)); 
 196     char *data(static_cast<char *>(CYPoolFile(temp
, file
, &size
))); 
 197     _assert(start
.offset_ 
<= size 
&& end
.offset_ 
<= size 
&& start
.offset_ 
<= end
.offset_
); 
 200     code
.size 
= end
.offset_ 
- start
.offset_
; 
 201     code
.data 
= pool
.strndup(data 
+ start
.offset_
, code
.size
); 
 205 static CYExpression 
*CYTranslateExpression(CXTranslationUnit unit
, CXCursor cursor
) { 
 206     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 207         case CXCursor_CallExpr
: { 
 208             CYExpression 
*function(NULL
); 
 209             CYList
<CYArgument
> arguments
; 
 210             CYForChild(cursor
, fun([&](CXCursor child
) { 
 211                 CYExpression 
*expression(CYTranslateExpression(unit
, child
)); 
 212                 if (function 
== NULL
) 
 213                     function 
= expression
; 
 215                     arguments
->*$
C_(expression
); 
 217             return $
C(function
, arguments
); 
 220         case CXCursor_DeclRefExpr
: { 
 221             return $
V(CYCXString(cursor
).Pool($pool
)); 
 224         case CXCursor_IntegerLiteral
: { 
 225             // libclang doesn't provide any reasonable way to do this 
 226             // note: clang_tokenize doesn't work if this is a macro 
 227             // the token range starts inside the macro but ends after it 
 228             // the tokenizer freaks out and either fails with 0 tokens 
 229             // or returns some massive number of tokens ending here :/ 
 231             CYUTF8String 
token(CYCXPoolUTF8Range($pool
, clang_getCursorExtent(cursor
))); 
 232             double value(CYCastDouble(token
)); 
 233             if (std::isnan(value
)) 
 234                 return $
V(token
.data
); 
 235             return $ 
CYNumber(value
); 
 238         case CXCursor_CStyleCastExpr
: 
 239             // XXX: most of the time, this is a "NoOp" integer cast; but we should check it 
 241         case CXCursor_UnexposedExpr
: 
 242             // there is a very high probability that this is actually an "ImplicitCastExpr" 
 243             // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one 
 244             // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html 
 246         case CXCursor_ParenExpr
: { 
 247             CYExpression 
*pass(NULL
); 
 248             CYOneChild(cursor
, fun([&](CXCursor child
) { 
 249                 pass 
= CYTranslateExpression(unit
, child
); 
 255             //std::cerr << "E:" << CYCXString(kind) << std::endl; 
 260 static CYStatement 
*CYTranslateStatement(CXTranslationUnit unit
, CXCursor cursor
) { 
 261     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 262         case CXCursor_ReturnStmt
: { 
 263             CYExpression 
*value(NULL
); 
 264             CYOneChild(cursor
, fun([&](CXCursor child
) { 
 265                 value 
= CYTranslateExpression(unit
, child
); 
 267             return $ 
CYReturn(value
); 
 271             //std::cerr << "S:" << CYCXString(kind) << std::endl; 
 276 static CYStatement 
*CYTranslateBlock(CXTranslationUnit unit
, CXCursor cursor
) { 
 277     CYList
<CYStatement
> statements
; 
 278     CYForChild(cursor
, fun([&](CXCursor child
) { 
 279         statements
->*CYTranslateStatement(unit
, child
); 
 281     return $ 
CYBlock(statements
); 
 284 static CXChildVisitResult 
CYChildVisit(CXCursor cursor
, CXCursor parent
, CXClientData arg
) { 
 285     CYChildBaton 
&baton(*static_cast<CYChildBaton 
*>(arg
)); 
 286     CXTranslationUnit 
&unit(baton
.unit
); 
 288     CYCXString 
spelling(cursor
); 
 289     std::string 
name(spelling
); 
 290     std::ostringstream value
; 
 292     /*CXSourceLocation location(clang_getCursorLocation(cursor)); 
 293     CYCXPosition<> position(location); 
 294     std::cout << spelling << " " << position << std::endl;*/ 
 296     switch (CXCursorKind kind 
= clang_getCursorKind(cursor
)) { 
 297         case CXCursor_EnumConstantDecl
: { 
 298             value 
<< clang_getEnumConstantDeclValue(cursor
); 
 301         case CXCursor_MacroDefinition
: try { 
 302             CXSourceRange 
range(clang_getCursorExtent(cursor
)); 
 303             CYTokens 
tokens(unit
, range
); 
 304             _assert(tokens
.size() != 0); 
 306             CXCursor cursors
[tokens
.size()]; 
 307             clang_annotateTokens(unit
, tokens
, tokens
.size(), cursors
); 
 310             CYList
<CYFunctionParameter
> parameters
; 
 313             if (tokens
.size() != 1) { 
 314                 CYCXPosition
<> start(clang_getRangeStart(range
)); 
 315                 CYCXString 
first(unit
, tokens
[offset
]); 
 317                     CYCXPosition
<> paren(unit
, tokens
[offset
]); 
 318                     if (start
.offset_ 
+ strlen(spelling
) == paren
.offset_
) { 
 320                             _assert(++offset 
!= tokens
.size()); 
 321                             CYCXString 
token(unit
, tokens
[offset
]); 
 322                             parameters
->*$
P($
B($
I(token
.Pool($pool
)))); 
 323                             _assert(++offset 
!= tokens
.size()); 
 324                             CYCXString 
comma(unit
, tokens
[offset
]); 
 327                             _assert(comma 
== ","); 
 334             std::ostringstream body
; 
 335             for (unsigned i(offset
); i 
!= tokens
.size(); ++i
) { 
 336                 CYCXString 
token(unit
, tokens
[i
]); 
 346                 CYOutput 
out(*value
.rdbuf(), options
); 
 347                 out 
<< '(' << "function" << '('; 
 350                 out 
<< "return" << ' '; 
 352                 out 
<< ';' << '}' << ')'; 
 354         } catch (const CYException 
&error
) { 
 356             //std::cerr << error.PoolCString(pool) << std::endl; 
 360         case CXCursor_StructDecl
: { 
 361             if (!clang_isCursorDefinition(cursor
)) 
 363             if (spelling
[0] == '\0') 
 366             std::ostringstream types
; 
 367             std::ostringstream names
; 
 369             CYForChild(cursor
, fun([&](CXCursor child
) { 
 370                 if (clang_getCursorKind(child
) == CXCursor_FieldDecl
) { 
 371                     CXType 
type(clang_getCursorType(child
)); 
 372                     types 
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << "),"; 
 373                     names 
<< "'" << CYCXString(child
) << "',"; 
 378             value 
<< "new Type([" << types
.str() << "],[" << names
.str() << "])"; 
 381         case CXCursor_TypedefDecl
: { 
 382             CXType 
type(clang_getTypedefDeclUnderlyingType(cursor
)); 
 383             value 
<< "(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ")"; 
 386         case CXCursor_FunctionDecl
: 
 387         case CXCursor_VarDecl
: try { 
 390             CYList
<CYFunctionParameter
> parameters
; 
 391             CYStatement 
*code(NULL
); 
 395             CYForChild(cursor
, fun([&](CXCursor child
) { 
 396                 switch (CXCursorKind kind 
= clang_getCursorKind(child
)) { 
 397                     case CXCursor_AsmLabelAttr
: 
 398                         label 
= CYCXString(child
); 
 401                     case CXCursor_CompoundStmt
: 
 402                         code 
= CYTranslateBlock(unit
, child
); 
 405                     case CXCursor_ParmDecl
: 
 406                         parameters
->*$
P($
B($
I(CYCXString(child
).Pool($pool
)))); 
 409                     case CXCursor_IntegerLiteral
: 
 410                     case CXCursor_ObjCClassRef
: 
 411                     case CXCursor_TypeRef
: 
 412                     case CXCursor_UnexposedAttr
: 
 416                         //std::cerr << "A:" << CYCXString(child) << std::endl; 
 424             } else if (label
[0] != '_') 
 428                 CXType 
type(clang_getCursorType(cursor
)); 
 429                 value 
<< "*(typedef " << CYCXString(clang_getTypeSpelling(type
)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label
.substr(1) << "'))"; 
 432                 CYOutput 
out(*value
.rdbuf(), options
); 
 433                 CYFunctionExpression 
*function($ 
CYFunctionExpression(NULL
, parameters
, code
)); 
 434                 function
->Output(out
, CYNoBFC
); 
 435                 //std::cerr << value.str() << std::endl; 
 437         } catch (const CYException 
&error
) { 
 439             //std::cerr << error.PoolCString(pool) << std::endl; 
 444             return CXChildVisit_Recurse
; 
 448     baton
.keys
[name
] = value
.str(); 
 451     return CXChildVisit_Continue
; 
 454 int main(int argc
, const char *argv
[]) { 
 455     CXIndex 
index(clang_createIndex(0, 0)); 
 457     const char *file(argv
[1]); 
 461     argv
[--offset
] = "-ObjC++"; 
 464     CXTranslationUnit 
unit(clang_parseTranslationUnit(index
, file
, argv 
+ offset
, argc 
- offset
, NULL
, 0, CXTranslationUnit_DetailedPreprocessingRecord
)); 
 466     for (unsigned i(0), e(clang_getNumDiagnostics(unit
)); i 
!= e
; ++i
) { 
 467         CXDiagnostic 
diagnostic(clang_getDiagnostic(unit
, i
)); 
 468         CYCXString 
spelling(clang_getDiagnosticSpelling(diagnostic
)); 
 469         std::cerr 
<< spelling 
<< std::endl
; 
 473     CYChildBaton 
baton(unit
, keys
); 
 474     clang_visitChildren(clang_getTranslationUnitCursor(unit
), &CYChildVisit
, &baton
); 
 476     for (CYKeyMap::const_iterator 
key(keys
.begin()); key 
!= keys
.end(); ++key
) { 
 477         std::string 
value(key
->second
); 
 478         for (size_t i(0), e(value
.size()); i 
!= e
; ++i
) 
 479             if (value
[i
] <= 0 || value
[i
] >= 0x7f || value
[i
] == '\n') 
 481         std::cout 
<< key
->first 
<< "|\"" << value 
<< "\"" << std::endl
; 
 484     clang_disposeTranslationUnit(unit
); 
 485     clang_disposeIndex(index
);