]> git.saurik.com Git - cycript.git/blame - Analyze.cpp
Print NULL instead of crashing for CString.toCYON.
[cycript.git] / Analyze.cpp
CommitLineData
2e43a0b0
JF
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
3935b9e5 22#include <cmath>
8d20f0f1
JF
23#include <cstring>
24#include <iostream>
25#include <map>
26#include <sstream>
27#include <string>
28
29#include <clang-c/Index.h>
30
2e43a0b0
JF
31#include "Functor.hpp"
32#include "Replace.hpp"
33#include "Syntax.hpp"
34
35static CXChildVisitResult CYVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
36 (*reinterpret_cast<const Functor<void (CXCursor)> *>(arg))(cursor);
37 return CXChildVisit_Continue;
38}
39
40static 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
44static 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
8d20f0f1
JF
54struct CYCXString {
55 CXString value_;
56
57 CYCXString(CXString value) :
58 value_(value)
59 {
60 }
61
2e43a0b0
JF
62 CYCXString(CXCursor cursor) :
63 value_(clang_getCursorSpelling(cursor))
64 {
65 }
66
67 CYCXString(CXCursorKind kind) :
68 value_(clang_getCursorKindSpelling(kind))
69 {
70 }
71
3935b9e5
JF
72 CYCXString(CXFile file) :
73 value_(clang_getFileName(file))
74 {
75 }
76
2e43a0b0
JF
77 CYCXString(CXTranslationUnit unit, CXToken token) :
78 value_(clang_getTokenSpelling(unit, token))
79 {
80 }
81
8d20f0f1
JF
82 ~CYCXString() {
83 clang_disposeString(value_);
84 }
85
86 operator const char *() const {
87 return clang_getCString(value_);
88 }
8d20f0f1 89
2e43a0b0
JF
90 const char *Pool(CYPool &pool) const {
91 return pool.strdup(*this);
8d20f0f1 92 }
7752205a
JF
93
94 bool operator ==(const char *rhs) const {
95 const char *lhs(*this);
96 return lhs == rhs || strcmp(lhs, rhs) == 0;
97 }
b7854baa
JF
98};
99
3935b9e5
JF
100template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation>
101struct 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
7752205a
JF
111 CYCXPosition(CXTranslationUnit unit, CXToken token) :
112 CYCXPosition(clang_getTokenLocation(unit, token))
113 {
114 }
115
3935b9e5
JF
116 CXSourceLocation Get(CXTranslationUnit unit) const {
117 return clang_getLocation(unit, file_, line_, column_);
118 }
119};
120
121template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *)>
122std::ostream &operator <<(std::ostream &out, const CYCXPosition<clang_get_Location> &position) {
123 if (position.file_ != NULL)
124 out << "[" << CYCXString(position.file_) << "]:";
7752205a
JF
125 out << position.line_ << ":" << position.column_ << "@" << position.offset_;
126 return out;
3935b9e5
JF
127}
128
83e1cbb8
JF
129struct CYKey {
130 std::string code_;
131 unsigned flags_;
132};
133
134typedef std::map<std::string, CYKey> CYKeyMap;
8d20f0f1
JF
135
136struct CYChildBaton {
137 CXTranslationUnit unit;
138 CYKeyMap &keys;
139
140 CYChildBaton(CXTranslationUnit unit, CYKeyMap &keys) :
141 unit(unit),
142 keys(keys)
143 {
144 }
145};
146
147struct CYTokens {
7752205a 148 private:
3935b9e5
JF
149 CXTranslationUnit unit_;
150 CXToken *tokens_;
151 unsigned count_;
7752205a 152 unsigned valid_;
3935b9e5 153
7752205a 154 public:
3935b9e5
JF
155 CYTokens(CXTranslationUnit unit, CXSourceRange range) :
156 unit_(unit)
157 {
158 clang_tokenize(unit_, range, &tokens_, &count_);
7752205a
JF
159
160
161 // libclang's tokenizer is horribly broken and returns "extra" tokens.
162 // this code goes back through the tokens and filters for good ones :/
163
164 CYCXPosition<> end(clang_getRangeEnd(range));
165 CYCXString file(end.file_);
166
167 for (valid_ = 0; valid_ != count_; ++valid_) {
168 CYCXPosition<> position(unit, tokens_[valid_]);
169 _assert(CYCXString(position.file_) == file);
170 if (position.offset_ >= end.offset_)
171 break;
172 }
3935b9e5 173 }
8d20f0f1
JF
174
175 CYTokens(CXTranslationUnit unit, CXCursor cursor) :
3935b9e5 176 CYTokens(unit, clang_getCursorExtent(cursor))
8d20f0f1 177 {
8d20f0f1
JF
178 }
179
180 ~CYTokens() {
3935b9e5 181 clang_disposeTokens(unit_, tokens_, count_);
8d20f0f1
JF
182 }
183
184 operator CXToken *() const {
3935b9e5 185 return tokens_;
8d20f0f1 186 }
7752205a
JF
187
188 size_t size() const {
189 return valid_;
190 }
8d20f0f1
JF
191};
192
38c824bf
JF
193static CYUTF8String CYCXPoolUTF8Range(CYPool &pool, CXSourceRange range) {
194 CYCXPosition<> start(clang_getRangeStart(range));
195 CYCXPosition<> end(clang_getRangeEnd(range));
196 CYCXString file(start.file_);
197 _assert(file == CYCXString(end.file_));
198
199 CYPool temp;
200 size_t size;
201 char *data(static_cast<char *>(CYPoolFile(temp, file, &size)));
202 _assert(start.offset_ <= size && end.offset_ <= size && start.offset_ <= end.offset_);
203
204 CYUTF8String code;
205 code.size = end.offset_ - start.offset_;
206 code.data = pool.strndup(data + start.offset_, code.size);
207 return code;
208}
209
2e43a0b0
JF
210static CYExpression *CYTranslateExpression(CXTranslationUnit unit, CXCursor cursor) {
211 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
212 case CXCursor_CallExpr: {
213 CYExpression *function(NULL);
214 CYList<CYArgument> arguments;
215 CYForChild(cursor, fun([&](CXCursor child) {
216 CYExpression *expression(CYTranslateExpression(unit, child));
217 if (function == NULL)
218 function = expression;
219 else
220 arguments->*$C_(expression);
221 }));
222 return $C(function, arguments);
223 } break;
224
225 case CXCursor_DeclRefExpr: {
226 return $V(CYCXString(cursor).Pool($pool));
227 } break;
228
229 case CXCursor_IntegerLiteral: {
3935b9e5
JF
230 // libclang doesn't provide any reasonable way to do this
231 // note: clang_tokenize doesn't work if this is a macro
232 // the token range starts inside the macro but ends after it
233 // the tokenizer freaks out and either fails with 0 tokens
234 // or returns some massive number of tokens ending here :/
235
38c824bf 236 CYUTF8String token(CYCXPoolUTF8Range($pool, clang_getCursorExtent(cursor)));
3935b9e5 237 double value(CYCastDouble(token));
38c824bf
JF
238 if (std::isnan(value))
239 return $V(token.data);
240 return $ CYNumber(value);
2e43a0b0
JF
241 } break;
242
243 case CXCursor_CStyleCastExpr:
244 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
245
246 case CXCursor_UnexposedExpr:
247 // there is a very high probability that this is actually an "ImplicitCastExpr"
248 // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one
249 // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html
250
251 case CXCursor_ParenExpr: {
252 CYExpression *pass(NULL);
253 CYOneChild(cursor, fun([&](CXCursor child) {
254 pass = CYTranslateExpression(unit, child);
255 }));
256 return pass;
257 } break;
258
259 default:
260 //std::cerr << "E:" << CYCXString(kind) << std::endl;
261 _assert(false);
262 }
263}
264
265static CYStatement *CYTranslateStatement(CXTranslationUnit unit, CXCursor cursor) {
266 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
267 case CXCursor_ReturnStmt: {
268 CYExpression *value(NULL);
269 CYOneChild(cursor, fun([&](CXCursor child) {
270 value = CYTranslateExpression(unit, child);
271 }));
272 return $ CYReturn(value);
273 } break;
274
275 default:
276 //std::cerr << "S:" << CYCXString(kind) << std::endl;
277 _assert(false);
278 }
279}
280
281static CYStatement *CYTranslateBlock(CXTranslationUnit unit, CXCursor cursor) {
282 CYList<CYStatement> statements;
283 CYForChild(cursor, fun([&](CXCursor child) {
284 statements->*CYTranslateStatement(unit, child);
285 }));
286 return $ CYBlock(statements);
287}
288
8d20f0f1
JF
289static CXChildVisitResult CYChildVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
290 CYChildBaton &baton(*static_cast<CYChildBaton *>(arg));
291 CXTranslationUnit &unit(baton.unit);
292
2e43a0b0 293 CYCXString spelling(cursor);
8d20f0f1
JF
294 std::string name(spelling);
295 std::ostringstream value;
83e1cbb8 296 unsigned flags(0);
8d20f0f1
JF
297
298 /*CXSourceLocation location(clang_getCursorLocation(cursor));
3935b9e5
JF
299 CYCXPosition<> position(location);
300 std::cout << spelling << " " << position << std::endl;*/
8d20f0f1 301
2e43a0b0 302 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
8d20f0f1
JF
303 case CXCursor_EnumConstantDecl: {
304 value << clang_getEnumConstantDeclValue(cursor);
305 } break;
306
7752205a
JF
307 case CXCursor_MacroDefinition: try {
308 CXSourceRange range(clang_getCursorExtent(cursor));
309 CYTokens tokens(unit, range);
310 _assert(tokens.size() != 0);
8d20f0f1 311
7752205a
JF
312 CXCursor cursors[tokens.size()];
313 clang_annotateTokens(unit, tokens, tokens.size(), cursors);
8d20f0f1 314
dee38f6c
JF
315 CYLocalPool local;
316 CYList<CYFunctionParameter> parameters;
317 unsigned offset(1);
318
319 if (tokens.size() != 1) {
320 CYCXPosition<> start(clang_getRangeStart(range));
321 CYCXString first(unit, tokens[offset]);
322 if (first == "(") {
323 CYCXPosition<> paren(unit, tokens[offset]);
324 if (start.offset_ + strlen(spelling) == paren.offset_) {
325 for (;;) {
326 _assert(++offset != tokens.size());
327 CYCXString token(unit, tokens[offset]);
328 parameters->*$P($B($I(token.Pool($pool))));
329 _assert(++offset != tokens.size());
330 CYCXString comma(unit, tokens[offset]);
331 if (comma == ")")
332 break;
333 _assert(comma == ",");
334 }
335 ++offset;
336 }
337 }
7752205a
JF
338 }
339
dee38f6c
JF
340 std::ostringstream body;
341 for (unsigned i(offset); i != tokens.size(); ++i) {
2e43a0b0 342 CYCXString token(unit, tokens[i]);
dee38f6c
JF
343 if (i != offset)
344 body << " ";
345 body << token;
346 }
347
348 if (!parameters)
349 value << body.str();
350 else {
351 CYOptions options;
352 CYOutput out(*value.rdbuf(), options);
353 out << '(' << "function" << '(';
354 out << parameters;
355 out << ')' << '{';
356 out << "return" << ' ';
357 value << body.str();
358 out << ';' << '}' << ')';
8d20f0f1 359 }
7752205a
JF
360 } catch (const CYException &error) {
361 CYPool pool;
362 //std::cerr << error.PoolCString(pool) << std::endl;
363 goto skip;
8d20f0f1
JF
364 } break;
365
366 case CXCursor_StructDecl: {
367 if (!clang_isCursorDefinition(cursor))
368 goto skip;
369 if (spelling[0] == '\0')
370 goto skip;
371
2e43a0b0
JF
372 std::ostringstream types;
373 std::ostringstream names;
374
375 CYForChild(cursor, fun([&](CXCursor child) {
376 if (clang_getCursorKind(child) == CXCursor_FieldDecl) {
377 CXType type(clang_getCursorType(child));
378 types << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << "),";
379 names << "'" << CYCXString(child) << "',";
380 }
381 }));
8d20f0f1
JF
382
383 name += "$cy";
2e43a0b0 384 value << "new Type([" << types.str() << "],[" << names.str() << "])";
8d20f0f1
JF
385 } break;
386
387 case CXCursor_TypedefDecl: {
388 CXType type(clang_getTypedefDeclUnderlyingType(cursor));
389 value << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << ")";
390 } break;
391
392 case CXCursor_FunctionDecl:
2e43a0b0
JF
393 case CXCursor_VarDecl: try {
394 std::string label;
395
396 CYList<CYFunctionParameter> parameters;
397 CYStatement *code(NULL);
398
399 CYLocalPool local;
400
401 CYForChild(cursor, fun([&](CXCursor child) {
402 switch (CXCursorKind kind = clang_getCursorKind(child)) {
403 case CXCursor_AsmLabelAttr:
404 label = CYCXString(child);
405 break;
406
407 case CXCursor_CompoundStmt:
408 code = CYTranslateBlock(unit, child);
409 break;
410
411 case CXCursor_ParmDecl:
412 parameters->*$P($B($I(CYCXString(child).Pool($pool))));
413 break;
414
415 case CXCursor_IntegerLiteral:
416 case CXCursor_ObjCClassRef:
417 case CXCursor_TypeRef:
418 case CXCursor_UnexposedAttr:
419 break;
420
421 default:
3935b9e5 422 //std::cerr << "A:" << CYCXString(child) << std::endl;
2e43a0b0
JF
423 break;
424 }
425 }));
426
427 if (label.empty()) {
428 label = spelling;
429 label = '_' + label;
430 } else if (label[0] != '_')
b7854baa
JF
431 goto skip;
432
2e43a0b0
JF
433 if (code == NULL) {
434 CXType type(clang_getCursorType(cursor));
435 value << "*(typedef " << CYCXString(clang_getTypeSpelling(type)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label.substr(1) << "'))";
436 } else {
437 CYOptions options;
438 CYOutput out(*value.rdbuf(), options);
439 CYFunctionExpression *function($ CYFunctionExpression(NULL, parameters, code));
440 function->Output(out, CYNoBFC);
441 //std::cerr << value.str() << std::endl;
442 }
443 } catch (const CYException &error) {
444 CYPool pool;
445 //std::cerr << error.PoolCString(pool) << std::endl;
446 goto skip;
8d20f0f1
JF
447 } break;
448
449 default: {
450 return CXChildVisit_Recurse;
451 } break;
452 }
453
83e1cbb8
JF
454 {
455 CYKey &key(baton.keys[name]);
456 key.code_ = value.str();
457 key.flags_ = flags;
458 }
8d20f0f1
JF
459
460 skip:
461 return CXChildVisit_Continue;
462}
463
464int main(int argc, const char *argv[]) {
465 CXIndex index(clang_createIndex(0, 0));
466
467 const char *file(argv[1]);
468
469 unsigned offset(3);
470#if CY_OBJECTIVEC
471 argv[--offset] = "-ObjC++";
472#endif
473
2e43a0b0 474 CXTranslationUnit unit(clang_parseTranslationUnit(index, file, argv + offset, argc - offset, NULL, 0, CXTranslationUnit_DetailedPreprocessingRecord));
8d20f0f1
JF
475
476 for (unsigned i(0), e(clang_getNumDiagnostics(unit)); i != e; ++i) {
477 CXDiagnostic diagnostic(clang_getDiagnostic(unit, i));
478 CYCXString spelling(clang_getDiagnosticSpelling(diagnostic));
479 std::cerr << spelling << std::endl;
480 }
481
482 CYKeyMap keys;
483 CYChildBaton baton(unit, keys);
484 clang_visitChildren(clang_getTranslationUnitCursor(unit), &CYChildVisit, &baton);
485
486 for (CYKeyMap::const_iterator key(keys.begin()); key != keys.end(); ++key) {
83e1cbb8
JF
487 std::string code(key->second.code_);
488 for (size_t i(0), e(code.size()); i != e; ++i)
489 if (code[i] <= 0 || code[i] >= 0x7f || code[i] == '\n')
8d20f0f1 490 goto skip;
83e1cbb8 491 std::cout << key->first << "|" << key->second.flags_ << "\"" << code << "\"" << std::endl;
8d20f0f1
JF
492 skip:; }
493
494 clang_disposeTranslationUnit(unit);
495 clang_disposeIndex(index);
496
497 return 0;
498}