]> git.saurik.com Git - cycript.git/blob - Analyze.cpp
Use variadic templates to better organize Classes.
[cycript.git] / Analyze.cpp
1 /* Cycript - Optimizing JavaScript Compiler/Runtime
2 * Copyright (C) 2009-2015 Jay Freeman (saurik)
3 */
4
5 /* GNU Affero General Public License, Version 3 {{{ */
6 /*
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.
11
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.
16
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/>.
19 **/
20 /* }}} */
21
22 #include <cmath>
23 #include <cstring>
24 #include <iostream>
25 #include <map>
26 #include <sstream>
27 #include <string>
28
29 #include <clang-c/Index.h>
30
31 #include "Functor.hpp"
32 #include "Replace.hpp"
33 #include "Syntax.hpp"
34
35 static CXChildVisitResult CYVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
36 (*reinterpret_cast<const Functor<void (CXCursor)> *>(arg))(cursor);
37 return CXChildVisit_Continue;
38 }
39
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)));
42 }
43
44 static bool CYOneChild(CXCursor cursor, const Functor<void (CXCursor)> &visitor) {
45 bool visited(false);
46 CYForChild(cursor, fun([&](CXCursor child) {
47 _assert(!visited);
48 visited = true;
49 visitor(child);
50 }));
51 return visited;
52 }
53
54 struct CYCXString {
55 CXString value_;
56
57 CYCXString(CXString value) :
58 value_(value)
59 {
60 }
61
62 CYCXString(CXCursor cursor) :
63 value_(clang_getCursorSpelling(cursor))
64 {
65 }
66
67 CYCXString(CXCursorKind kind) :
68 value_(clang_getCursorKindSpelling(kind))
69 {
70 }
71
72 CYCXString(CXFile file) :
73 value_(clang_getFileName(file))
74 {
75 }
76
77 CYCXString(CXTranslationUnit unit, CXToken token) :
78 value_(clang_getTokenSpelling(unit, token))
79 {
80 }
81
82 ~CYCXString() {
83 clang_disposeString(value_);
84 }
85
86 operator const char *() const {
87 return clang_getCString(value_);
88 }
89
90 const char *Pool(CYPool &pool) const {
91 return pool.strdup(*this);
92 }
93
94 bool operator ==(const char *rhs) const {
95 const char *lhs(*this);
96 return lhs == rhs || strcmp(lhs, rhs) == 0;
97 }
98 };
99
100 template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation>
101 struct CYCXPosition {
102 CXFile file_;
103 unsigned line_;
104 unsigned column_;
105 unsigned offset_;
106
107 CYCXPosition(CXSourceLocation location) {
108 clang_get_Location(location, &file_, &line_, &column_, &offset_);
109 }
110
111 CYCXPosition(CXTranslationUnit unit, CXToken token) :
112 CYCXPosition(clang_getTokenLocation(unit, token))
113 {
114 }
115
116 CXSourceLocation Get(CXTranslationUnit unit) const {
117 return clang_getLocation(unit, file_, line_, column_);
118 }
119 };
120
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_;
126 return out;
127 }
128
129 struct CYKey {
130 unsigned priority_ = 0;
131
132 std::string code_;
133 unsigned flags_;
134 };
135
136 typedef std::map<std::string, CYKey> CYKeyMap;
137
138 struct CYChildBaton {
139 CXTranslationUnit unit;
140 CYKeyMap &keys;
141
142 CYChildBaton(CXTranslationUnit unit, CYKeyMap &keys) :
143 unit(unit),
144 keys(keys)
145 {
146 }
147 };
148
149 struct CYTokens {
150 private:
151 CXTranslationUnit unit_;
152 CXToken *tokens_;
153 unsigned count_;
154 unsigned valid_;
155
156 public:
157 CYTokens(CXTranslationUnit unit, CXSourceRange range) :
158 unit_(unit)
159 {
160 clang_tokenize(unit_, range, &tokens_, &count_);
161
162
163 // libclang's tokenizer is horribly broken and returns "extra" tokens.
164 // this code goes back through the tokens and filters for good ones :/
165
166 CYCXPosition<> end(clang_getRangeEnd(range));
167 CYCXString file(end.file_);
168
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_)
173 break;
174 }
175 }
176
177 CYTokens(CXTranslationUnit unit, CXCursor cursor) :
178 CYTokens(unit, clang_getCursorExtent(cursor))
179 {
180 }
181
182 ~CYTokens() {
183 clang_disposeTokens(unit_, tokens_, count_);
184 }
185
186 operator CXToken *() const {
187 return tokens_;
188 }
189
190 size_t size() const {
191 return valid_;
192 }
193 };
194
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_));
200
201 CYPool temp;
202 size_t size;
203 char *data(static_cast<char *>(CYPoolFile(temp, file, &size)));
204 _assert(start.offset_ <= size && end.offset_ <= size && start.offset_ <= end.offset_);
205
206 CYUTF8String code;
207 code.size = end.offset_ - start.offset_;
208 code.data = pool.strndup(data + start.offset_, code.size);
209 return code;
210 }
211
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;
221 else
222 arguments->*$C_(expression);
223 }));
224 return $C(function, arguments);
225 } break;
226
227 case CXCursor_DeclRefExpr: {
228 return $V(CYCXString(cursor).Pool($pool));
229 } break;
230
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 :/
237
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);
243 } break;
244
245 case CXCursor_CStyleCastExpr:
246 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
247
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
252
253 case CXCursor_ParenExpr: {
254 CYExpression *pass(NULL);
255 CYOneChild(cursor, fun([&](CXCursor child) {
256 pass = CYTranslateExpression(unit, child);
257 }));
258 return pass;
259 } break;
260
261 default:
262 //std::cerr << "E:" << CYCXString(kind) << std::endl;
263 _assert(false);
264 }
265 }
266
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);
273 }));
274 return $ CYReturn(value);
275 } break;
276
277 default:
278 //std::cerr << "S:" << CYCXString(kind) << std::endl;
279 _assert(false);
280 }
281 }
282
283 static CYStatement *CYTranslateBlock(CXTranslationUnit unit, CXCursor cursor) {
284 CYList<CYStatement> statements;
285 CYForChild(cursor, fun([&](CXCursor child) {
286 statements->*CYTranslateStatement(unit, child);
287 }));
288 return $ CYBlock(statements);
289 }
290
291 static CXChildVisitResult CYChildVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
292 CYChildBaton &baton(*static_cast<CYChildBaton *>(arg));
293 CXTranslationUnit &unit(baton.unit);
294
295 CYCXString spelling(cursor);
296 std::string name(spelling);
297 std::ostringstream value;
298 unsigned priority(2);
299 unsigned flags(0);
300
301 /*CXSourceLocation location(clang_getCursorLocation(cursor));
302 CYCXPosition<> position(location);
303 std::cout << spelling << " " << position << std::endl;*/
304
305 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
306 case CXCursor_EnumConstantDecl: {
307 value << clang_getEnumConstantDeclValue(cursor);
308 } break;
309
310 case CXCursor_MacroDefinition: try {
311 CXSourceRange range(clang_getCursorExtent(cursor));
312 CYTokens tokens(unit, range);
313 _assert(tokens.size() != 0);
314
315 CXCursor cursors[tokens.size()];
316 clang_annotateTokens(unit, tokens, tokens.size(), cursors);
317
318 CYLocalPool local;
319 CYList<CYFunctionParameter> parameters;
320 unsigned offset(1);
321
322 if (tokens.size() != 1) {
323 CYCXPosition<> start(clang_getRangeStart(range));
324 CYCXString first(unit, tokens[offset]);
325 if (first == "(") {
326 CYCXPosition<> paren(unit, tokens[offset]);
327 if (start.offset_ + strlen(spelling) == paren.offset_) {
328 for (;;) {
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]);
334 if (comma == ")")
335 break;
336 _assert(comma == ",");
337 }
338 ++offset;
339 }
340 }
341 }
342
343 std::ostringstream body;
344 for (unsigned i(offset); i != tokens.size(); ++i) {
345 CYCXString token(unit, tokens[i]);
346 if (i != offset)
347 body << " ";
348 body << token;
349 }
350
351 if (!parameters)
352 value << body.str();
353 else {
354 CYOptions options;
355 CYOutput out(*value.rdbuf(), options);
356 out << '(' << "function" << '(';
357 out << parameters;
358 out << ')' << '{';
359 out << "return" << ' ';
360 value << body.str();
361 out << ';' << '}' << ')';
362 }
363 } catch (const CYException &error) {
364 CYPool pool;
365 //std::cerr << error.PoolCString(pool) << std::endl;
366 goto skip;
367 } break;
368
369 case CXCursor_StructDecl: {
370 if (spelling[0] == '\0')
371 goto skip;
372 if (!clang_isCursorDefinition(cursor))
373 priority = 1;
374
375 std::ostringstream types;
376 std::ostringstream names;
377
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) << "',";
383 }
384 }));
385
386 name += "$cy";
387 value << "new Type([" << types.str() << "],[" << names.str() << "])";
388 } break;
389
390 case CXCursor_TypedefDecl: {
391 CXType type(clang_getTypedefDeclUnderlyingType(cursor));
392 value << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << ")";
393 } break;
394
395 case CXCursor_FunctionDecl:
396 case CXCursor_VarDecl: try {
397 std::string label;
398
399 CYList<CYFunctionParameter> parameters;
400 CYStatement *code(NULL);
401
402 CYLocalPool local;
403
404 CYForChild(cursor, fun([&](CXCursor child) {
405 switch (CXCursorKind kind = clang_getCursorKind(child)) {
406 case CXCursor_AsmLabelAttr:
407 label = CYCXString(child);
408 break;
409
410 case CXCursor_CompoundStmt:
411 code = CYTranslateBlock(unit, child);
412 break;
413
414 case CXCursor_ParmDecl:
415 parameters->*$P($B($I(CYCXString(child).Pool($pool))));
416 break;
417
418 case CXCursor_IntegerLiteral:
419 case CXCursor_ObjCClassRef:
420 case CXCursor_TypeRef:
421 case CXCursor_UnexposedAttr:
422 break;
423
424 default:
425 //std::cerr << "A:" << CYCXString(child) << std::endl;
426 break;
427 }
428 }));
429
430 if (label.empty()) {
431 label = spelling;
432 label = '_' + label;
433 } else if (label[0] != '_')
434 goto skip;
435
436 if (code == NULL) {
437 CXType type(clang_getCursorType(cursor));
438 value << "*(typedef " << CYCXString(clang_getTypeSpelling(type)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label.substr(1) << "'))";
439 } else {
440 CYOptions options;
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;
445 }
446 } catch (const CYException &error) {
447 CYPool pool;
448 //std::cerr << error.PoolCString(pool) << std::endl;
449 goto skip;
450 } break;
451
452 default: {
453 return CXChildVisit_Recurse;
454 } break;
455 }
456
457 {
458 CYKey &key(baton.keys[name]);
459 if (key.priority_ < priority) {
460 key.priority_ = priority;
461 key.code_ = value.str();
462 key.flags_ = flags;
463 }
464 }
465
466 skip:
467 return CXChildVisit_Continue;
468 }
469
470 int main(int argc, const char *argv[]) {
471 CXIndex index(clang_createIndex(0, 0));
472
473 const char *file(argv[1]);
474
475 unsigned offset(3);
476 #if CY_OBJECTIVEC
477 argv[--offset] = "-ObjC++";
478 #endif
479
480 CXTranslationUnit unit(clang_parseTranslationUnit(index, file, argv + offset, argc - offset, NULL, 0, CXTranslationUnit_DetailedPreprocessingRecord));
481
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;
486 }
487
488 CYKeyMap keys;
489 CYChildBaton baton(unit, keys);
490 clang_visitChildren(clang_getTranslationUnitCursor(unit), &CYChildVisit, &baton);
491
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')
496 goto skip;
497 std::cout << key->first << "|" << key->second.flags_ << "\"" << code << "\"" << std::endl;
498 skip:; }
499
500 clang_disposeTranslationUnit(unit);
501 clang_disposeIndex(index);
502
503 return 0;
504 }