]> git.saurik.com Git - cycript.git/blob - Analyze.cpp
dd0c26ae0cb919fbbdd479c3af0171c95c2a527f
[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 typedef std::map<std::string, std::string> CYKeyMap;
130
131 struct CYChildBaton {
132 CXTranslationUnit unit;
133 CYKeyMap &keys;
134
135 CYChildBaton(CXTranslationUnit unit, CYKeyMap &keys) :
136 unit(unit),
137 keys(keys)
138 {
139 }
140 };
141
142 struct CYTokens {
143 private:
144 CXTranslationUnit unit_;
145 CXToken *tokens_;
146 unsigned count_;
147 unsigned valid_;
148
149 public:
150 CYTokens(CXTranslationUnit unit, CXSourceRange range) :
151 unit_(unit)
152 {
153 clang_tokenize(unit_, range, &tokens_, &count_);
154
155
156 // libclang's tokenizer is horribly broken and returns "extra" tokens.
157 // this code goes back through the tokens and filters for good ones :/
158
159 CYCXPosition<> end(clang_getRangeEnd(range));
160 CYCXString file(end.file_);
161
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_)
166 break;
167 }
168 }
169
170 CYTokens(CXTranslationUnit unit, CXCursor cursor) :
171 CYTokens(unit, clang_getCursorExtent(cursor))
172 {
173 }
174
175 ~CYTokens() {
176 clang_disposeTokens(unit_, tokens_, count_);
177 }
178
179 operator CXToken *() const {
180 return tokens_;
181 }
182
183 size_t size() const {
184 return valid_;
185 }
186 };
187
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;
197 else
198 arguments->*$C_(expression);
199 }));
200 return $C(function, arguments);
201 } break;
202
203 case CXCursor_DeclRefExpr: {
204 return $V(CYCXString(cursor).Pool($pool));
205 } break;
206
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 :/
213
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_));
219
220 CYPool pool;
221 size_t size;
222 char *data(static_cast<char *>(CYPoolFile(pool, file, &size)));
223 _assert(start.offset_ <= size && end.offset_ <= size && start.offset_ <= end.offset_);
224
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);
229
230 return $V(token);
231 } break;
232
233 case CXCursor_CStyleCastExpr:
234 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
235
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
240
241 case CXCursor_ParenExpr: {
242 CYExpression *pass(NULL);
243 CYOneChild(cursor, fun([&](CXCursor child) {
244 pass = CYTranslateExpression(unit, child);
245 }));
246 return pass;
247 } break;
248
249 default:
250 //std::cerr << "E:" << CYCXString(kind) << std::endl;
251 _assert(false);
252 }
253 }
254
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);
261 }));
262 return $ CYReturn(value);
263 } break;
264
265 default:
266 //std::cerr << "S:" << CYCXString(kind) << std::endl;
267 _assert(false);
268 }
269 }
270
271 static CYStatement *CYTranslateBlock(CXTranslationUnit unit, CXCursor cursor) {
272 CYList<CYStatement> statements;
273 CYForChild(cursor, fun([&](CXCursor child) {
274 statements->*CYTranslateStatement(unit, child);
275 }));
276 return $ CYBlock(statements);
277 }
278
279 static CXChildVisitResult CYChildVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
280 CYChildBaton &baton(*static_cast<CYChildBaton *>(arg));
281 CXTranslationUnit &unit(baton.unit);
282
283 CYCXString spelling(cursor);
284 std::string name(spelling);
285 std::ostringstream value;
286
287 /*CXSourceLocation location(clang_getCursorLocation(cursor));
288 CYCXPosition<> position(location);
289 std::cout << spelling << " " << position << std::endl;*/
290
291 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
292 case CXCursor_EnumConstantDecl: {
293 value << clang_getEnumConstantDeclValue(cursor);
294 } break;
295
296 case CXCursor_MacroDefinition: try {
297 CXSourceRange range(clang_getCursorExtent(cursor));
298 CYTokens tokens(unit, range);
299 _assert(tokens.size() != 0);
300
301 CXCursor cursors[tokens.size()];
302 clang_annotateTokens(unit, tokens, tokens.size(), cursors);
303
304 CYCXPosition<> start(clang_getRangeStart(range));
305 CYCXString first(unit, tokens[1]);
306 if (first == "(") {
307 CYCXPosition<> paren(unit, tokens[1]);
308 if (start.offset_ + strlen(spelling) == paren.offset_)
309 _assert(false); // XXX: support parameterized macros
310 }
311
312 for (unsigned i(1); i != tokens.size(); ++i) {
313 CYCXString token(unit, tokens[i]);
314 if (i != 1)
315 value << " ";
316 value << token;
317 }
318 } catch (const CYException &error) {
319 CYPool pool;
320 //std::cerr << error.PoolCString(pool) << std::endl;
321 goto skip;
322 } break;
323
324 case CXCursor_StructDecl: {
325 if (!clang_isCursorDefinition(cursor))
326 goto skip;
327 if (spelling[0] == '\0')
328 goto skip;
329
330 std::ostringstream types;
331 std::ostringstream names;
332
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) << "',";
338 }
339 }));
340
341 name += "$cy";
342 value << "new Type([" << types.str() << "],[" << names.str() << "])";
343 } break;
344
345 case CXCursor_TypedefDecl: {
346 CXType type(clang_getTypedefDeclUnderlyingType(cursor));
347 value << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << ")";
348 } break;
349
350 case CXCursor_FunctionDecl:
351 case CXCursor_VarDecl: try {
352 std::string label;
353
354 CYList<CYFunctionParameter> parameters;
355 CYStatement *code(NULL);
356
357 CYLocalPool local;
358
359 CYForChild(cursor, fun([&](CXCursor child) {
360 switch (CXCursorKind kind = clang_getCursorKind(child)) {
361 case CXCursor_AsmLabelAttr:
362 label = CYCXString(child);
363 break;
364
365 case CXCursor_CompoundStmt:
366 code = CYTranslateBlock(unit, child);
367 break;
368
369 case CXCursor_ParmDecl:
370 parameters->*$P($B($I(CYCXString(child).Pool($pool))));
371 break;
372
373 case CXCursor_IntegerLiteral:
374 case CXCursor_ObjCClassRef:
375 case CXCursor_TypeRef:
376 case CXCursor_UnexposedAttr:
377 break;
378
379 default:
380 //std::cerr << "A:" << CYCXString(child) << std::endl;
381 break;
382 }
383 }));
384
385 if (label.empty()) {
386 label = spelling;
387 label = '_' + label;
388 } else if (label[0] != '_')
389 goto skip;
390
391 if (code == NULL) {
392 CXType type(clang_getCursorType(cursor));
393 value << "*(typedef " << CYCXString(clang_getTypeSpelling(type)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label.substr(1) << "'))";
394 } else {
395 CYOptions options;
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;
400 }
401 } catch (const CYException &error) {
402 CYPool pool;
403 //std::cerr << error.PoolCString(pool) << std::endl;
404 goto skip;
405 } break;
406
407 default: {
408 return CXChildVisit_Recurse;
409 } break;
410 }
411
412 baton.keys[name] = value.str();
413
414 skip:
415 return CXChildVisit_Continue;
416 }
417
418 int main(int argc, const char *argv[]) {
419 CXIndex index(clang_createIndex(0, 0));
420
421 const char *file(argv[1]);
422
423 unsigned offset(3);
424 #if CY_OBJECTIVEC
425 argv[--offset] = "-ObjC++";
426 #endif
427
428 CXTranslationUnit unit(clang_parseTranslationUnit(index, file, argv + offset, argc - offset, NULL, 0, CXTranslationUnit_DetailedPreprocessingRecord));
429
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;
434 }
435
436 CYKeyMap keys;
437 CYChildBaton baton(unit, keys);
438 clang_visitChildren(clang_getTranslationUnitCursor(unit), &CYChildVisit, &baton);
439
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')
444 goto skip;
445 std::cout << key->first << "|\"" << value << "\"" << std::endl;
446 skip:; }
447
448 clang_disposeTranslationUnit(unit);
449 clang_disposeIndex(index);
450
451 return 0;
452 }