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
);