fast_method_source 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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'