fast_method_source 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,428 @@
1
+ #include "fast_method_source.h"
2
+
3
+ static VALUE rb_eSourceNotFoundError;
4
+
5
+ static unsigned
6
+ read_lines_after(const unsigned method_location,
7
+ const char *filename, char **filebuf[])
8
+ {
9
+ read_order order = {1, 0};
10
+ return read_lines(order, method_location, filename, filebuf);
11
+ }
12
+
13
+ static unsigned
14
+ read_lines_before(const unsigned method_location,
15
+ const char *filename, char **filebuf[])
16
+ {
17
+ read_order order = {0, 1};
18
+ return read_lines(order, method_location, filename, filebuf);
19
+ }
20
+
21
+ static unsigned
22
+ read_lines(read_order order, const unsigned method_location,
23
+ const char *filename, char **filebuf[])
24
+ {
25
+ FILE *fp;
26
+
27
+ if ((fp = fopen(filename, "r")) == NULL) {
28
+ rb_raise(rb_eIOError, "No such file or directory - %s", filename);
29
+ }
30
+
31
+ ssize_t cl_len;
32
+
33
+ char *current_line = NULL;
34
+ char **current_linebuf = NULL;
35
+ size_t current_linebuf_size = 0;
36
+ unsigned rl_n = 0;
37
+ unsigned line_count = 0;
38
+
39
+ while ((cl_len = getline(&current_line, &current_linebuf_size, fp)) != -1) {
40
+ line_count++;
41
+ if (order.forward) {
42
+ if (line_count < method_location) {
43
+ continue;
44
+ }
45
+ } else if (order.backward) {
46
+ if (line_count > method_location) {
47
+ break;
48
+ }
49
+ }
50
+
51
+ if ((rl_n != 0) && (rl_n % (MAXLINES-1) == 0)) {
52
+ reallocate_filebuf(filebuf, rl_n);
53
+ }
54
+
55
+ current_linebuf = &(*filebuf)[rl_n];
56
+
57
+ if (cl_len >= MAXLINELEN) {
58
+ reallocate_linebuf(current_linebuf, cl_len);
59
+ }
60
+
61
+ strncpy(*current_linebuf, current_line, cl_len);
62
+ (*current_linebuf)[cl_len] = '\0';
63
+ rl_n++;
64
+ }
65
+
66
+ free(current_line);
67
+ fclose(fp);
68
+
69
+ return rl_n;
70
+ }
71
+
72
+ static void
73
+ reallocate_linebuf(char **linebuf, const unsigned cl_len)
74
+ {
75
+ char *tmp_line;
76
+
77
+ if ((tmp_line = realloc(*linebuf, cl_len + 1)) == NULL) {
78
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
79
+ }
80
+
81
+ *linebuf = tmp_line;
82
+ }
83
+
84
+ static void
85
+ reallocate_filebuf(char **lines[], unsigned rl_len)
86
+ {
87
+ unsigned new_size = rl_len + MAXLINES + 1;
88
+ char **temp_lines = realloc(*lines, sizeof(*temp_lines) * new_size);
89
+
90
+ if (temp_lines == NULL) {
91
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
92
+ } else {
93
+ *lines = temp_lines;
94
+
95
+ for (int i = 0; i < MAXLINES; i++) {
96
+ if (((*lines)[rl_len + i] = malloc(sizeof(char) * MAXLINELEN)) == NULL) {
97
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
98
+ }
99
+ }
100
+ }
101
+ }
102
+
103
+ static NODE *
104
+ parse_with_silenced_stderr(VALUE rb_str)
105
+ {
106
+ int old_stderr;
107
+ FILE *null_fd;
108
+
109
+ old_stderr = DUP(STDERR_FILENO);
110
+ fflush(stderr);
111
+ null_fd = fopen(null_filename, "w");
112
+ DUP2(fileno(null_fd), STDERR_FILENO);
113
+
114
+ volatile VALUE vparser = rb_parser_new();
115
+ NODE *node = rb_parser_compile_string(vparser, "-", rb_str, 1);
116
+ rb_str_free(rb_str);
117
+
118
+ fflush(stderr);
119
+ fclose(null_fd);
120
+
121
+ DUP2(old_stderr, STDERR_FILENO);
122
+ close(old_stderr);
123
+
124
+ return node;
125
+ }
126
+
127
+ static NODE *
128
+ parse_expr(VALUE rb_str) {
129
+ return parse_with_silenced_stderr(rb_str);
130
+ }
131
+
132
+ static void
133
+ filter_interp(char *line)
134
+ {
135
+ int brackets = 0;
136
+
137
+ for (int i = 0; line[i] != '\0'; i++) {
138
+ if (line[i] == '#') {
139
+ if (line[i + 1] == '{') {
140
+ brackets++;
141
+
142
+ line[i] = SAFE_CHAR;
143
+
144
+ if (line[i - 1] == '\\')
145
+ line[i - 1] = SAFE_CHAR;
146
+ }
147
+ }
148
+
149
+ if (line[i] == '}') {
150
+ brackets--;
151
+
152
+ if (brackets == 0) {
153
+ line[i] = SAFE_CHAR;
154
+ }
155
+ } else if (brackets > 0) {
156
+ line[i] = SAFE_CHAR;
157
+ }
158
+ }
159
+ }
160
+
161
+ static int
162
+ contains_end_kw(const char *line)
163
+ {
164
+ char *match;
165
+ char prev_ch;
166
+
167
+ if ((match = strstr(line, "end")) != NULL) {
168
+ prev_ch = (match - 1)[0];
169
+ return prev_ch == ' ' || prev_ch == '\0' || prev_ch == ';';
170
+ } else {
171
+ return 0;
172
+ }
173
+ }
174
+
175
+ static int
176
+ is_accessor(const char *line)
177
+ {
178
+ return strstr(line, "attr_reader") != NULL ||
179
+ strstr(line, "attr_writer") != NULL ||
180
+ strstr(line, "attr_accessor") != NULL;
181
+ }
182
+
183
+ static int
184
+ is_comment(const char *line, const size_t line_len)
185
+ {
186
+ for (size_t i = 0; i < line_len; i++) {
187
+ if (line[i] == ' ')
188
+ continue;
189
+
190
+ if (line[i] == '#' && line[i + 1] != '{') {
191
+ for (size_t j = i - 1; j != 0; j--) {
192
+ if (line[j] != ' ')
193
+ return 0;
194
+ }
195
+
196
+ return 1;
197
+ } else {
198
+ return 0;
199
+ }
200
+ }
201
+
202
+ return 0;
203
+ }
204
+
205
+ static int
206
+ is_static_definition(const char *line)
207
+ {
208
+ return strstr(line, " def ") != NULL || strncmp(line, "def ", 4);
209
+ }
210
+
211
+ static int
212
+ is_dangling_literal_end(const char *line)
213
+ {
214
+ return strstr(line, "}") != NULL && strstr(line, "{") == NULL;
215
+ }
216
+
217
+ static int
218
+ is_dangling_literal_begin(const char *line)
219
+ {
220
+ return strstr(line, "%{") != NULL && strstr(line, "}") == NULL;
221
+ }
222
+
223
+ static VALUE
224
+ find_source(char **filebuf[], const unsigned relevant_lines_n)
225
+ {
226
+ VALUE rb_expr;
227
+
228
+ const unsigned expr_size = relevant_lines_n * MAXLINELEN;
229
+ char *expr = malloc(expr_size);
230
+ expr[0] = '\0';
231
+ char *parseable_expr = malloc(expr_size);
232
+ parseable_expr[0] = '\0';
233
+
234
+ int l = 0;
235
+ while ((*filebuf)[l][0] == '\n') {
236
+ l++;
237
+ continue;
238
+ }
239
+ char *first_line = (*filebuf)[l];
240
+
241
+ char *current_line = NULL;
242
+ int should_parse = 0;
243
+ int dangling_literal = 0;
244
+ size_t current_line_len;
245
+
246
+ if (is_static_definition(first_line) || is_accessor(first_line)) {
247
+ should_parse = 1;
248
+ }
249
+
250
+ for (unsigned i = l; i < relevant_lines_n; i++) {
251
+ current_line = (*filebuf)[i];
252
+ current_line_len = strlen(current_line);
253
+
254
+ strncat(expr, current_line, current_line_len);
255
+
256
+ if (is_comment(current_line, current_line_len))
257
+ continue;
258
+
259
+ if (is_dangling_literal_end(current_line)) {
260
+ dangling_literal = 0;
261
+ } else if (is_dangling_literal_begin(current_line)) {
262
+ dangling_literal = 1;
263
+ } else if (dangling_literal) {
264
+ continue;
265
+ }
266
+
267
+ filter_interp(current_line);
268
+ strncat(parseable_expr, current_line, current_line_len);
269
+
270
+ if (should_parse || contains_end_kw(current_line)) {
271
+ if (parse_expr(rb_str_new2(parseable_expr)) != NULL) {
272
+ rb_expr = rb_str_new2(expr);
273
+ free(expr);
274
+ free(parseable_expr);
275
+ return rb_expr;
276
+ }
277
+ }
278
+ }
279
+
280
+ free(expr);
281
+ free(parseable_expr);
282
+ free_memory_for_file(filebuf, relevant_lines_n);
283
+
284
+ return Qnil;
285
+ }
286
+
287
+ static void
288
+ strnprep(char *s, const char *t, size_t len)
289
+ {
290
+ size_t i;
291
+
292
+ memmove(s + len, s, strlen(s) + 1);
293
+
294
+ for (i = 0; i < len; ++i) {
295
+ s[i] = t[i];
296
+ }
297
+ }
298
+
299
+ static VALUE
300
+ find_comment(char **filebuf[], const unsigned method_location,
301
+ const unsigned relevant_lines_n)
302
+ {
303
+ char comment[COMMENT_SIZE];
304
+ comment[0] = '\0';
305
+
306
+ int i = method_location - 2;
307
+
308
+ while ((*filebuf)[i][0] == '\n') {
309
+ i--;
310
+ continue;
311
+ }
312
+
313
+ if (!is_comment((*filebuf)[i], strlen((*filebuf)[i]))) {
314
+ return rb_str_new("", 0);
315
+ } else {
316
+ while (is_comment((*filebuf)[i], strlen((*filebuf)[i]))) {
317
+ strnprep(comment, (*filebuf)[i], strlen((*filebuf)[i]));
318
+ i--;
319
+ }
320
+
321
+ return rb_str_new2(comment);
322
+ }
323
+
324
+ free_memory_for_file(filebuf, relevant_lines_n);
325
+ return Qnil;
326
+ }
327
+
328
+ static char **
329
+ allocate_memory_for_file(void)
330
+ {
331
+ char **file;
332
+
333
+ if ((file = malloc(sizeof(*file) * MAXLINES)) == NULL) {
334
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
335
+ }
336
+
337
+ for (int i = 0; i < MAXLINES; i++) {
338
+ if ((file[i] = malloc(sizeof(char) * MAXLINELEN)) == NULL) {
339
+ rb_raise(rb_eNoMemError, "failed to allocate memory");
340
+ };
341
+ }
342
+
343
+ return file;
344
+ }
345
+
346
+ static void
347
+ free_memory_for_file(char **file[], const unsigned relevant_lines_n)
348
+ {
349
+ int lines_to_free = relevant_lines_n >= MAXLINES ? relevant_lines_n : MAXLINES;
350
+
351
+ for (int i = 0; i < lines_to_free; i++) {
352
+ free((*file)[i]);
353
+ }
354
+
355
+ free(*file);
356
+ }
357
+
358
+ static VALUE
359
+ mMethodExtensions_source(VALUE self)
360
+ {
361
+ VALUE source_location = rb_funcall(self, rb_intern("source_location"), 0);
362
+ VALUE name = rb_funcall(self, rb_intern("name"), 0);
363
+ VALUE rb_filename = RARRAY_AREF(source_location, 0);
364
+ VALUE rb_method_location = RARRAY_AREF(source_location, 1);
365
+
366
+ if (NIL_P(source_location) || NIL_P(rb_method_location)) {
367
+ rb_raise(rb_eSourceNotFoundError, "could not locate source for %s!",
368
+ RSTRING_PTR(rb_sym2str(name)));
369
+ }
370
+
371
+ const char *filename = RSTRING_PTR(rb_filename);
372
+ const unsigned method_location = FIX2INT(rb_method_location);
373
+
374
+ char **filebuf = allocate_memory_for_file();
375
+ const unsigned relevant_lines_n = read_lines_after(method_location,
376
+ filename, &filebuf);
377
+ VALUE source = find_source(&filebuf, relevant_lines_n);
378
+
379
+ if (NIL_P(source)) {
380
+ rb_raise(rb_eSourceNotFoundError, "could not locate source for %s!",
381
+ RSTRING_PTR(rb_sym2str(name)));
382
+ }
383
+ free_memory_for_file(&filebuf, relevant_lines_n);
384
+
385
+ return source;
386
+ }
387
+
388
+ static VALUE
389
+ mMethodExtensions_comment(VALUE self)
390
+ {
391
+ VALUE source_location = rb_funcall(self, rb_intern("source_location"), 0);
392
+ VALUE name = rb_funcall(self, rb_intern("name"), 0);
393
+ VALUE rb_filename = RARRAY_AREF(source_location, 0);
394
+ VALUE rb_method_location = RARRAY_AREF(source_location, 1);
395
+
396
+ if (NIL_P(source_location) || NIL_P(rb_method_location)) {
397
+ rb_raise(rb_eSourceNotFoundError, "could not locate source for %s!",
398
+ RSTRING_PTR(rb_sym2str(name)));
399
+ }
400
+
401
+ const char *filename = RSTRING_PTR(rb_filename);
402
+ const unsigned method_location = FIX2INT(rb_method_location);
403
+
404
+ char **filebuf = allocate_memory_for_file();
405
+ const unsigned relevant_lines_n = read_lines_before(method_location,
406
+ filename, &filebuf);
407
+ VALUE comment = find_comment(&filebuf, method_location, relevant_lines_n);
408
+
409
+ if (NIL_P(comment)) {
410
+ rb_raise(rb_eSourceNotFoundError, "could not locate comment for %s!",
411
+ RSTRING_PTR(rb_sym2str(name)));
412
+ }
413
+ free_memory_for_file(&filebuf, relevant_lines_n);
414
+
415
+ return comment;
416
+ }
417
+
418
+
419
+ void Init_fast_method_source(void)
420
+ {
421
+ VALUE rb_mFastMethodSource = rb_define_module_under(rb_cObject, "FastMethodSource");
422
+
423
+ rb_eSourceNotFoundError = rb_define_class_under(rb_mFastMethodSource,"SourceNotFoundError", rb_eStandardError);
424
+ VALUE rb_mMethodExtensions = rb_define_module_under(rb_mFastMethodSource, "MethodExtensions");
425
+
426
+ rb_define_method(rb_mMethodExtensions, "source", mMethodExtensions_source, 0);
427
+ rb_define_method(rb_mMethodExtensions, "comment", mMethodExtensions_comment, 0);
428
+ }
@@ -0,0 +1,53 @@
1
+ #define _XOPEN_SOURCE 700
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <ruby.h>
5
+ #include "node.h"
6
+
7
+ #ifdef _WIN32
8
+ #include <io.h>
9
+ static const char *null_filename = "NUL";
10
+ #define DUP(fd) _dup(fd)
11
+ #define DUP2(fd, newfd) _dup2(fd, newfd)
12
+ #else
13
+ #include <unistd.h>
14
+ static const char *null_filename = "/dev/null";
15
+ #define DUP(fd) dup(fd)
16
+ #define DUP2(fd, newfd) dup2(fd, newfd)
17
+ #endif
18
+
19
+ #define MAXLINES 1000
20
+ #define MAXLINELEN 300
21
+ #define COMMENT_SIZE 15000
22
+
23
+ typedef struct {
24
+ int forward : 1;
25
+ int backward : 1;
26
+ } read_order;
27
+
28
+ static unsigned read_lines(read_order order, const unsigned method_location,
29
+ const char *filename, char **filebuf[]);
30
+ static unsigned read_lines_before(const unsigned method_location,
31
+ const char *filename, char **filebuf[]);
32
+ static unsigned read_lines_after(const unsigned method_location,
33
+ const char *filename, char **filebuf[]);
34
+ static void reallocate_filebuf(char **lines[], unsigned rl_len);
35
+ static void reallocate_linebuf(char **linebuf, const unsigned cl_len);
36
+ static VALUE find_source(char **filebuf[], const unsigned relevant_lines_n);
37
+ static VALUE find_comment(char **filebuf[], const unsigned relevant_lines_n,
38
+ const unsigned method_location);
39
+ static VALUE mMethodExtensions_source(VALUE self);
40
+ static NODE *parse_expr(VALUE rb_str);
41
+ static NODE *parse_with_silenced_stderr(VALUE rb_str);
42
+ static void filter_interp(char *line);
43
+ static char **allocate_memory_for_file(void);
44
+ static void free_memory_for_file(char **file[], const unsigned relevant_lines_n);
45
+ static int contains_end_kw(const char *line);
46
+ static int is_comment(const char *line, const size_t line_len);
47
+ static int is_static_definition(const char *line);
48
+ static int is_accessor(const char *line);
49
+ static int is_dangling_literal_end(const char *line);
50
+ static int is_dangling_literal_begin(const char *line);
51
+ static void strnprep(char *s, const char *t, size_t len);
52
+
53
+ #define SAFE_CHAR 'z'