Coverage Report

Created: 2024-02-13 07:03

/src/ruby/node.c
Line
Count
Source (jump to first uncovered line)
1
/**********************************************************************
2
3
  node.c - ruby node tree
4
5
  $Author: mame $
6
  created at: 09/12/06 21:23:44 JST
7
8
  Copyright (C) 2009 Yusuke Endoh
9
10
**********************************************************************/
11
12
#ifdef UNIVERSAL_PARSER
13
14
#include <stddef.h>
15
#include "node.h"
16
#include "rubyparser.h"
17
#include "internal/parse.h"
18
#define T_NODE 0x1b
19
20
#else
21
22
#include "internal.h"
23
#include "internal/hash.h"
24
#include "internal/variable.h"
25
#include "ruby/ruby.h"
26
#include "vm_core.h"
27
28
#endif
29
30
0
#define NODE_BUF_DEFAULT_SIZE (sizeof(struct RNode) * 16)
31
32
static void
33
init_node_buffer_elem(node_buffer_elem_t *nbe, size_t allocated, void *xmalloc(size_t))
34
0
{
35
0
    nbe->allocated = allocated;
36
0
    nbe->used = 0;
37
0
    nbe->len = 0;
38
0
    nbe->nodes = xmalloc(allocated / sizeof(struct RNode) * sizeof(struct RNode *)); /* All node requires at least RNode */
39
0
}
40
41
static void
42
init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head, void *xmalloc(size_t))
43
0
{
44
0
    init_node_buffer_elem(head, NODE_BUF_DEFAULT_SIZE, xmalloc);
45
0
    nb->head = nb->last = head;
46
0
    nb->head->next = NULL;
47
0
}
48
49
#ifdef UNIVERSAL_PARSER
50
#define ruby_xmalloc config->malloc
51
#define Qnil config->qnil
52
#endif
53
54
#ifdef UNIVERSAL_PARSER
55
static node_buffer_t *
56
rb_node_buffer_new(const rb_parser_config_t *config)
57
#else
58
static node_buffer_t *
59
rb_node_buffer_new(void)
60
#endif
61
0
{
62
0
    const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE;
63
0
    const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2);
64
0
    STATIC_ASSERT(
65
0
        integer_overflow,
66
0
        offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE
67
0
        > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t));
68
0
    node_buffer_t *nb = ruby_xmalloc(alloc_size);
69
0
    init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1], ruby_xmalloc);
70
0
    init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size), ruby_xmalloc);
71
0
    nb->local_tables = 0;
72
0
    nb->mark_hash = Qnil;
73
0
    nb->tokens = Qnil;
74
#ifdef UNIVERSAL_PARSER
75
    nb->config = config;
76
#endif
77
0
    return nb;
78
0
}
79
80
#ifdef UNIVERSAL_PARSER
81
#undef ruby_xmalloc
82
#define ruby_xmalloc ast->node_buffer->config->malloc
83
#undef xfree
84
#define xfree ast->node_buffer->config->free
85
#define rb_ident_hash_new ast->node_buffer->config->ident_hash_new
86
#define rb_xmalloc_mul_add ast->node_buffer->config->xmalloc_mul_add
87
#define ruby_xrealloc(var,size) (ast->node_buffer->config->realloc_n((void *)var, 1, size))
88
#define rb_gc_mark ast->node_buffer->config->gc_mark
89
#define rb_gc_location ast->node_buffer->config->gc_location
90
#define rb_gc_mark_movable ast->node_buffer->config->gc_mark_movable
91
#undef Qnil
92
#define Qnil ast->node_buffer->config->qnil
93
#define Qtrue ast->node_buffer->config->qtrue
94
#define NIL_P ast->node_buffer->config->nil_p
95
#define rb_hash_aset ast->node_buffer->config->hash_aset
96
#define rb_hash_delete ast->node_buffer->config->hash_delete
97
#define RB_OBJ_WRITE(old, slot, young) ast->node_buffer->config->obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young))
98
#endif
99
100
typedef void node_itr_t(rb_ast_t *ast, void *ctx, NODE *node);
101
static void iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx);
102
103
/* Setup NODE structure.
104
 * NODE is not an object managed by GC, but it imitates an object
105
 * so that it can work with `RB_TYPE_P(obj, T_NODE)`.
106
 * This dirty hack is needed because Ripper jumbles NODEs and other type
107
 * objects.
108
 */
109
void
110
rb_node_init(NODE *n, enum node_type type)
111
0
{
112
0
    RNODE(n)->flags = T_NODE;
113
0
    nd_init_type(RNODE(n), type);
114
0
    RNODE(n)->nd_loc.beg_pos.lineno = 0;
115
0
    RNODE(n)->nd_loc.beg_pos.column = 0;
116
0
    RNODE(n)->nd_loc.end_pos.lineno = 0;
117
0
    RNODE(n)->nd_loc.end_pos.column = 0;
118
0
    RNODE(n)->node_id = -1;
119
0
}
120
121
const char *
122
rb_node_name(int node)
123
0
{
124
0
    switch (node) {
125
0
#include "node_name.inc"
126
0
      default:
127
0
        return 0;
128
0
    }
129
0
}
130
131
#ifdef UNIVERSAL_PARSER
132
const char *
133
ruby_node_name(int node)
134
{
135
    return rb_node_name(node);
136
}
137
#else
138
const char *
139
ruby_node_name(int node)
140
0
{
141
0
    const char *name = rb_node_name(node);
142
143
0
    if (!name) rb_bug("unknown node: %d", node);
144
0
    return name;
145
0
}
146
#endif
147
148
static void
149
node_buffer_list_free(rb_ast_t *ast, node_buffer_list_t * nb)
150
0
{
151
0
    node_buffer_elem_t *nbe = nb->head;
152
0
    while (nbe != nb->last) {
153
0
        void *buf = nbe;
154
0
        xfree(nbe->nodes);
155
0
        nbe = nbe->next;
156
0
        xfree(buf);
157
0
    }
158
159
    /* The last node_buffer_elem_t is allocated in the node_buffer_t, so we
160
     * only need to free the nodes. */
161
0
    xfree(nbe->nodes);
162
0
}
163
164
struct rb_ast_local_table_link {
165
    struct rb_ast_local_table_link *next;
166
    // struct rb_ast_id_table {
167
    int size;
168
    ID ids[FLEX_ARY_LEN];
169
    // }
170
};
171
172
static void
173
parser_string_free(rb_ast_t *ast, rb_parser_string_t *str)
174
0
{
175
0
    if (!str) return;
176
0
    xfree(str->ptr);
177
0
    xfree(str);
178
0
}
179
180
static void
181
free_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
182
0
{
183
0
    switch (nd_type(node)) {
184
0
      case NODE_STR:
185
0
        parser_string_free(ast, RNODE_STR(node)->string);
186
0
        break;
187
0
      case NODE_DSTR:
188
0
        parser_string_free(ast, RNODE_DSTR(node)->string);
189
0
        break;
190
0
      case NODE_XSTR:
191
0
        parser_string_free(ast, RNODE_XSTR(node)->string);
192
0
        break;
193
0
      case NODE_DXSTR:
194
0
        parser_string_free(ast, RNODE_DXSTR(node)->string);
195
0
        break;
196
0
      case NODE_SYM:
197
0
        parser_string_free(ast, RNODE_SYM(node)->string);
198
0
        break;
199
0
      case NODE_DSYM:
200
0
        parser_string_free(ast, RNODE_DSYM(node)->string);
201
0
        break;
202
0
      case NODE_DREGX:
203
0
        parser_string_free(ast, RNODE_DREGX(node)->string);
204
0
        break;
205
0
      case NODE_FILE:
206
0
        parser_string_free(ast, RNODE_FILE(node)->path);
207
0
        break;
208
0
      case NODE_INTEGER:
209
0
        xfree(RNODE_INTEGER(node)->val);
210
0
        break;
211
0
      case NODE_FLOAT:
212
0
        xfree(RNODE_FLOAT(node)->val);
213
0
        break;
214
0
      case NODE_RATIONAL:
215
0
        xfree(RNODE_RATIONAL(node)->val);
216
0
        break;
217
0
      case NODE_IMAGINARY:
218
0
        xfree(RNODE_IMAGINARY(node)->val);
219
0
        break;
220
0
      default:
221
0
        break;
222
0
    }
223
0
}
224
225
static void
226
rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb)
227
0
{
228
0
    iterate_node_values(ast, &nb->unmarkable, free_ast_value, NULL);
229
0
    node_buffer_list_free(ast, &nb->unmarkable);
230
0
    node_buffer_list_free(ast, &nb->markable);
231
0
    struct rb_ast_local_table_link *local_table = nb->local_tables;
232
0
    while (local_table) {
233
0
        struct rb_ast_local_table_link *next_table = local_table->next;
234
0
        xfree(local_table);
235
0
        local_table = next_table;
236
0
    }
237
0
    xfree(nb);
238
0
}
239
240
0
#define buf_add_offset(nbe, offset) ((char *)(nbe->buf) + (offset))
241
242
static NODE *
243
ast_newnode_in_bucket(rb_ast_t *ast, node_buffer_list_t *nb, size_t size, size_t alignment)
244
0
{
245
0
    size_t padding;
246
0
    NODE *ptr;
247
248
0
    padding = alignment - (size_t)buf_add_offset(nb->head, nb->head->used) % alignment;
249
0
    padding = padding == alignment ? 0 : padding;
250
251
0
    if (nb->head->used + size + padding > nb->head->allocated) {
252
0
        size_t n = nb->head->allocated * 2;
253
0
        node_buffer_elem_t *nbe;
254
0
        nbe = rb_xmalloc_mul_add(n, sizeof(char *), offsetof(node_buffer_elem_t, buf));
255
0
        init_node_buffer_elem(nbe, n, ruby_xmalloc);
256
0
        nbe->next = nb->head;
257
0
        nb->head = nbe;
258
0
        padding = 0; /* malloc returns aligned address then no need to add padding */
259
0
    }
260
261
0
    ptr = (NODE *)buf_add_offset(nb->head, nb->head->used + padding);
262
0
    nb->head->used += (size + padding);
263
0
    nb->head->nodes[nb->head->len++] = ptr;
264
0
    return ptr;
265
0
}
266
267
RBIMPL_ATTR_PURE()
268
static bool
269
nodetype_markable_p(enum node_type type)
270
0
{
271
0
    switch (type) {
272
0
      case NODE_MATCH:
273
0
      case NODE_LIT:
274
0
        return true;
275
0
      default:
276
0
        return false;
277
0
    }
278
0
}
279
280
NODE *
281
rb_ast_newnode(rb_ast_t *ast, enum node_type type, size_t size, size_t alignment)
282
0
{
283
0
    node_buffer_t *nb = ast->node_buffer;
284
0
    node_buffer_list_t *bucket =
285
0
        (nodetype_markable_p(type) ? &nb->markable : &nb->unmarkable);
286
0
    return ast_newnode_in_bucket(ast, bucket, size, alignment);
287
0
}
288
289
#if RUBY_DEBUG
290
void
291
rb_ast_node_type_change(NODE *n, enum node_type type)
292
{
293
    enum node_type old_type = nd_type(n);
294
    if (nodetype_markable_p(old_type) != nodetype_markable_p(type)) {
295
        rb_bug("node type changed: %s -> %s",
296
               ruby_node_name(old_type), ruby_node_name(type));
297
    }
298
}
299
#endif
300
301
rb_ast_id_table_t *
302
rb_ast_new_local_table(rb_ast_t *ast, int size)
303
0
{
304
0
    size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID);
305
0
    struct rb_ast_local_table_link *link = ruby_xmalloc(alloc_size);
306
0
    link->next = ast->node_buffer->local_tables;
307
0
    ast->node_buffer->local_tables = link;
308
0
    link->size = size;
309
310
0
    return (rb_ast_id_table_t *) &link->size;
311
0
}
312
313
rb_ast_id_table_t *
314
rb_ast_resize_latest_local_table(rb_ast_t *ast, int size)
315
0
{
316
0
    struct rb_ast_local_table_link *link = ast->node_buffer->local_tables;
317
0
    size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID);
318
0
    link = ruby_xrealloc(link, alloc_size);
319
0
    ast->node_buffer->local_tables = link;
320
0
    link->size = size;
321
322
0
    return (rb_ast_id_table_t *) &link->size;
323
0
}
324
325
void
326
rb_ast_delete_node(rb_ast_t *ast, NODE *n)
327
0
{
328
0
    (void)ast;
329
0
    (void)n;
330
    /* should we implement freelist? */
331
0
}
332
333
#ifdef UNIVERSAL_PARSER
334
rb_ast_t *
335
rb_ast_new(const rb_parser_config_t *config)
336
{
337
    node_buffer_t *nb = rb_node_buffer_new(config);
338
    return config->ast_new((VALUE)nb);
339
}
340
#else
341
rb_ast_t *
342
rb_ast_new(void)
343
0
{
344
0
    node_buffer_t *nb = rb_node_buffer_new();
345
0
    rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb);
346
0
    return ast;
347
0
}
348
#endif
349
350
static void
351
iterate_buffer_elements(rb_ast_t *ast, node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx)
352
0
{
353
0
    long cursor;
354
0
    for (cursor = 0; cursor < len; cursor++) {
355
0
        func(ast, ctx, nbe->nodes[cursor]);
356
0
    }
357
0
}
358
359
static void
360
iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx)
361
0
{
362
0
    node_buffer_elem_t *nbe = nb->head;
363
364
0
    while (nbe) {
365
0
        iterate_buffer_elements(ast, nbe, nbe->len, func, ctx);
366
0
        nbe = nbe->next;
367
0
    }
368
0
}
369
370
static void
371
mark_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
372
0
{
373
#ifdef UNIVERSAL_PARSER
374
    bug_report_func rb_bug = ast->node_buffer->config->bug;
375
#endif
376
377
0
    switch (nd_type(node)) {
378
0
      case NODE_MATCH:
379
0
      case NODE_LIT:
380
0
        rb_gc_mark_movable(RNODE_LIT(node)->nd_lit);
381
0
        break;
382
0
      default:
383
0
        rb_bug("unreachable node %s", ruby_node_name(nd_type(node)));
384
0
    }
385
0
}
386
387
static void
388
update_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
389
0
{
390
#ifdef UNIVERSAL_PARSER
391
    bug_report_func rb_bug = ast->node_buffer->config->bug;
392
#endif
393
394
0
    switch (nd_type(node)) {
395
0
      case NODE_MATCH:
396
0
      case NODE_LIT:
397
0
        RNODE_LIT(node)->nd_lit = rb_gc_location(RNODE_LIT(node)->nd_lit);
398
0
        break;
399
0
      default:
400
0
        rb_bug("unreachable");
401
0
    }
402
0
}
403
404
void
405
rb_ast_update_references(rb_ast_t *ast)
406
0
{
407
0
    if (ast->node_buffer) {
408
0
        node_buffer_t *nb = ast->node_buffer;
409
410
0
        iterate_node_values(ast, &nb->markable, update_ast_value, NULL);
411
0
    }
412
0
}
413
414
void
415
rb_ast_mark(rb_ast_t *ast)
416
0
{
417
0
    if (ast->node_buffer) {
418
0
        rb_gc_mark(ast->node_buffer->mark_hash);
419
0
        rb_gc_mark(ast->node_buffer->tokens);
420
0
        node_buffer_t *nb = ast->node_buffer;
421
0
        iterate_node_values(ast, &nb->markable, mark_ast_value, NULL);
422
0
        if (ast->body.script_lines) rb_gc_mark(ast->body.script_lines);
423
0
    }
424
0
}
425
426
void
427
rb_ast_free(rb_ast_t *ast)
428
0
{
429
0
    if (ast->node_buffer) {
430
0
        rb_node_buffer_free(ast, ast->node_buffer);
431
0
        ast->node_buffer = 0;
432
0
    }
433
0
}
434
435
static size_t
436
buffer_list_size(node_buffer_list_t *nb)
437
0
{
438
0
    size_t size = 0;
439
0
    node_buffer_elem_t *nbe = nb->head;
440
0
    while (nbe != nb->last) {
441
0
        size += offsetof(node_buffer_elem_t, buf) + nbe->used;
442
0
        nbe = nbe->next;
443
0
    }
444
0
    return size;
445
0
}
446
447
size_t
448
rb_ast_memsize(const rb_ast_t *ast)
449
0
{
450
0
    size_t size = 0;
451
0
    node_buffer_t *nb = ast->node_buffer;
452
453
0
    if (nb) {
454
0
        size += sizeof(node_buffer_t);
455
0
        size += buffer_list_size(&nb->unmarkable);
456
0
        size += buffer_list_size(&nb->markable);
457
0
    }
458
0
    return size;
459
0
}
460
461
void
462
rb_ast_dispose(rb_ast_t *ast)
463
0
{
464
0
    rb_ast_free(ast);
465
0
}
466
467
void
468
rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj)
469
0
{
470
0
    if (NIL_P(ast->node_buffer->mark_hash)) {
471
0
        RB_OBJ_WRITE(ast, &ast->node_buffer->mark_hash, rb_ident_hash_new());
472
0
    }
473
0
    rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue);
474
0
}
475
476
void
477
rb_ast_delete_mark_object(rb_ast_t *ast, VALUE obj)
478
0
{
479
0
    if (NIL_P(ast->node_buffer->mark_hash)) return;
480
0
    rb_hash_delete(ast->node_buffer->mark_hash, obj);
481
0
}
482
483
VALUE
484
rb_ast_tokens(rb_ast_t *ast)
485
0
{
486
0
    return ast->node_buffer->tokens;
487
0
}
488
489
void
490
rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens)
491
0
{
492
0
    RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens);
493
0
}
494
495
VALUE
496
rb_node_set_type(NODE *n, enum node_type t)
497
0
{
498
#if RUBY_DEBUG
499
    rb_ast_node_type_change(n, t);
500
#endif
501
0
    return nd_init_type(n, t);
502
0
}