]> git.saurik.com Git - cycript.git/blob - Analyze.cpp
Avoid clang_tokenize and just get the source file.
[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
95 template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *) = clang_getSpellingLocation>
96 struct CYCXPosition {
97 CXFile file_;
98 unsigned line_;
99 unsigned column_;
100 unsigned offset_;
101
102 CYCXPosition(CXSourceLocation location) {
103 clang_get_Location(location, &file_, &line_, &column_, &offset_);
104 }
105
106 CXSourceLocation Get(CXTranslationUnit unit) const {
107 return clang_getLocation(unit, file_, line_, column_);
108 }
109 };
110
111 template <void (&clang_get_Location)(CXSourceLocation, CXFile *, unsigned *, unsigned *, unsigned *)>
112 std::ostream &operator <<(std::ostream &out, const CYCXPosition<clang_get_Location> &position) {
113 if (position.file_ != NULL)
114 out << "[" << CYCXString(position.file_) << "]:";
115 out << position.line_ << ":" << position.column_;
116 }
117
118 typedef std::map<std::string, std::string> CYKeyMap;
119
120 struct CYChildBaton {
121 CXTranslationUnit unit;
122 CYKeyMap &keys;
123
124 CYChildBaton(CXTranslationUnit unit, CYKeyMap &keys) :
125 unit(unit),
126 keys(keys)
127 {
128 }
129 };
130
131 struct CYTokens {
132 CXTranslationUnit unit_;
133 CXToken *tokens_;
134 unsigned count_;
135
136 CYTokens(CXTranslationUnit unit, CXSourceRange range) :
137 unit_(unit)
138 {
139 clang_tokenize(unit_, range, &tokens_, &count_);
140 }
141
142 CYTokens(CXTranslationUnit unit, CXCursor cursor) :
143 CYTokens(unit, clang_getCursorExtent(cursor))
144 {
145 }
146
147 ~CYTokens() {
148 clang_disposeTokens(unit_, tokens_, count_);
149 }
150
151 operator CXToken *() const {
152 return tokens_;
153 }
154 };
155
156 static CYExpression *CYTranslateExpression(CXTranslationUnit unit, CXCursor cursor) {
157 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
158 case CXCursor_CallExpr: {
159 CYExpression *function(NULL);
160 CYList<CYArgument> arguments;
161 CYForChild(cursor, fun([&](CXCursor child) {
162 CYExpression *expression(CYTranslateExpression(unit, child));
163 if (function == NULL)
164 function = expression;
165 else
166 arguments->*$C_(expression);
167 }));
168 return $C(function, arguments);
169 } break;
170
171 case CXCursor_DeclRefExpr: {
172 return $V(CYCXString(cursor).Pool($pool));
173 } break;
174
175 case CXCursor_IntegerLiteral: {
176 // libclang doesn't provide any reasonable way to do this
177 // note: clang_tokenize doesn't work if this is a macro
178 // the token range starts inside the macro but ends after it
179 // the tokenizer freaks out and either fails with 0 tokens
180 // or returns some massive number of tokens ending here :/
181
182 CXSourceRange range(clang_getCursorExtent(cursor));
183 CYCXPosition<> start(clang_getRangeStart(range));
184 CYCXPosition<> end(clang_getRangeEnd(range));
185 CYCXString file(start.file_);
186 _assert(file == CYCXString(end.file_));
187
188 CYPool pool;
189 size_t size;
190 char *data(static_cast<char *>(CYPoolFile(pool, file, &size)));
191 _assert(start.offset_ <= size && end.offset_ <= size && start.offset_ <= end.offset_);
192
193 const char *token($pool.strndup(data + start.offset_, end.offset_ - start.offset_));
194 double value(CYCastDouble(token));
195 if (!std::isnan(value))
196 return $ CYNumber(value);
197
198 return $V(token);
199 } break;
200
201 case CXCursor_CStyleCastExpr:
202 // XXX: most of the time, this is a "NoOp" integer cast; but we should check it
203
204 case CXCursor_UnexposedExpr:
205 // there is a very high probability that this is actually an "ImplicitCastExpr"
206 // "Douglas Gregor" <dgregor@apple.com> err'd on the incorrect side of this one
207 // http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20110926/046998.html
208
209 case CXCursor_ParenExpr: {
210 CYExpression *pass(NULL);
211 CYOneChild(cursor, fun([&](CXCursor child) {
212 pass = CYTranslateExpression(unit, child);
213 }));
214 return pass;
215 } break;
216
217 default:
218 //std::cerr << "E:" << CYCXString(kind) << std::endl;
219 _assert(false);
220 }
221 }
222
223 static CYStatement *CYTranslateStatement(CXTranslationUnit unit, CXCursor cursor) {
224 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
225 case CXCursor_ReturnStmt: {
226 CYExpression *value(NULL);
227 CYOneChild(cursor, fun([&](CXCursor child) {
228 value = CYTranslateExpression(unit, child);
229 }));
230 return $ CYReturn(value);
231 } break;
232
233 default:
234 //std::cerr << "S:" << CYCXString(kind) << std::endl;
235 _assert(false);
236 }
237 }
238
239 static CYStatement *CYTranslateBlock(CXTranslationUnit unit, CXCursor cursor) {
240 CYList<CYStatement> statements;
241 CYForChild(cursor, fun([&](CXCursor child) {
242 statements->*CYTranslateStatement(unit, child);
243 }));
244 return $ CYBlock(statements);
245 }
246
247 static CXChildVisitResult CYChildVisit(CXCursor cursor, CXCursor parent, CXClientData arg) {
248 CYChildBaton &baton(*static_cast<CYChildBaton *>(arg));
249 CXTranslationUnit &unit(baton.unit);
250
251 CYCXString spelling(cursor);
252 std::string name(spelling);
253 std::ostringstream value;
254
255 /*CXSourceLocation location(clang_getCursorLocation(cursor));
256 CYCXPosition<> position(location);
257 std::cout << spelling << " " << position << std::endl;*/
258
259 switch (CXCursorKind kind = clang_getCursorKind(cursor)) {
260 case CXCursor_EnumConstantDecl: {
261 value << clang_getEnumConstantDeclValue(cursor);
262 } break;
263
264 case CXCursor_MacroDefinition: {
265 CYTokens tokens(unit, cursor);
266 if (tokens.count_ <= 2)
267 goto skip;
268
269 CXCursor cursors[tokens.count_];
270 clang_annotateTokens(unit, tokens, tokens.count_, cursors);
271
272 for (unsigned i(1); i != tokens.count_ - 1; ++i) {
273 CYCXString token(unit, tokens[i]);
274 if (i != 1)
275 value << " ";
276 else if (strcmp(token, "(") == 0)
277 goto skip;
278 value << token;
279 }
280 } break;
281
282 case CXCursor_StructDecl: {
283 if (!clang_isCursorDefinition(cursor))
284 goto skip;
285 if (spelling[0] == '\0')
286 goto skip;
287
288 std::ostringstream types;
289 std::ostringstream names;
290
291 CYForChild(cursor, fun([&](CXCursor child) {
292 if (clang_getCursorKind(child) == CXCursor_FieldDecl) {
293 CXType type(clang_getCursorType(child));
294 types << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << "),";
295 names << "'" << CYCXString(child) << "',";
296 }
297 }));
298
299 name += "$cy";
300 value << "new Type([" << types.str() << "],[" << names.str() << "])";
301 } break;
302
303 case CXCursor_TypedefDecl: {
304 CXType type(clang_getTypedefDeclUnderlyingType(cursor));
305 value << "(typedef " << CYCXString(clang_getTypeSpelling(type)) << ")";
306 } break;
307
308 case CXCursor_FunctionDecl:
309 case CXCursor_VarDecl: try {
310 std::string label;
311
312 CYList<CYFunctionParameter> parameters;
313 CYStatement *code(NULL);
314
315 CYLocalPool local;
316
317 CYForChild(cursor, fun([&](CXCursor child) {
318 switch (CXCursorKind kind = clang_getCursorKind(child)) {
319 case CXCursor_AsmLabelAttr:
320 label = CYCXString(child);
321 break;
322
323 case CXCursor_CompoundStmt:
324 code = CYTranslateBlock(unit, child);
325 break;
326
327 case CXCursor_ParmDecl:
328 parameters->*$P($B($I(CYCXString(child).Pool($pool))));
329 break;
330
331 case CXCursor_IntegerLiteral:
332 case CXCursor_ObjCClassRef:
333 case CXCursor_TypeRef:
334 case CXCursor_UnexposedAttr:
335 break;
336
337 default:
338 //std::cerr << "A:" << CYCXString(child) << std::endl;
339 break;
340 }
341 }));
342
343 if (label.empty()) {
344 label = spelling;
345 label = '_' + label;
346 } else if (label[0] != '_')
347 goto skip;
348
349 if (code == NULL) {
350 CXType type(clang_getCursorType(cursor));
351 value << "*(typedef " << CYCXString(clang_getTypeSpelling(type)) << ").pointerTo()(dlsym(RTLD_DEFAULT,'" << label.substr(1) << "'))";
352 } else {
353 CYOptions options;
354 CYOutput out(*value.rdbuf(), options);
355 CYFunctionExpression *function($ CYFunctionExpression(NULL, parameters, code));
356 function->Output(out, CYNoBFC);
357 //std::cerr << value.str() << std::endl;
358 }
359 } catch (const CYException &error) {
360 CYPool pool;
361 //std::cerr << error.PoolCString(pool) << std::endl;
362 goto skip;
363 } break;
364
365 default: {
366 return CXChildVisit_Recurse;
367 } break;
368 }
369
370 baton.keys[name] = value.str();
371
372 skip:
373 return CXChildVisit_Continue;
374 }
375
376 int main(int argc, const char *argv[]) {
377 CXIndex index(clang_createIndex(0, 0));
378
379 const char *file(argv[1]);
380
381 unsigned offset(3);
382 #if CY_OBJECTIVEC
383 argv[--offset] = "-ObjC++";
384 #endif
385
386 CXTranslationUnit unit(clang_parseTranslationUnit(index, file, argv + offset, argc - offset, NULL, 0, CXTranslationUnit_DetailedPreprocessingRecord));
387
388 for (unsigned i(0), e(clang_getNumDiagnostics(unit)); i != e; ++i) {
389 CXDiagnostic diagnostic(clang_getDiagnostic(unit, i));
390 CYCXString spelling(clang_getDiagnosticSpelling(diagnostic));
391 std::cerr << spelling << std::endl;
392 }
393
394 CYKeyMap keys;
395 CYChildBaton baton(unit, keys);
396 clang_visitChildren(clang_getTranslationUnitCursor(unit), &CYChildVisit, &baton);
397
398 for (CYKeyMap::const_iterator key(keys.begin()); key != keys.end(); ++key) {
399 std::string value(key->second);
400 for (size_t i(0), e(value.size()); i != e; ++i)
401 if (value[i] <= 0 || value[i] >= 0x7f || value[i] == '\n')
402 goto skip;
403 std::cout << key->first << "|\"" << value << "\"" << std::endl;
404 skip:; }
405
406 clang_disposeTranslationUnit(unit);
407 clang_disposeIndex(index);
408
409 return 0;
410 }