diff options
Diffstat (limited to 'node.c')
| -rw-r--r-- | node.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/node.c b/node.c new file mode 100644 index 0000000000..1f10258343 --- /dev/null +++ b/node.c @@ -0,0 +1,487 @@ +/********************************************************************** + + node.c - ruby node tree + + $Author: mame $ + created at: 09/12/06 21:23:44 JST + + Copyright (C) 2009 Yusuke Endoh + +**********************************************************************/ + +#ifdef UNIVERSAL_PARSER + +#include <stddef.h> +#include "node.h" +#include "rubyparser.h" +#include "internal/parse.h" +#define T_NODE 0x1b + +#else + +#include "internal.h" +#include "internal/hash.h" +#include "internal/variable.h" +#include "ruby/ruby.h" +#include "vm_core.h" + +#endif + +#define NODE_BUF_DEFAULT_SIZE (sizeof(struct RNode) * 16) + +static void +init_node_buffer_elem(node_buffer_elem_t *nbe, size_t allocated, void *xmalloc(size_t)) +{ + nbe->allocated = allocated; + nbe->used = 0; + nbe->len = 0; + nbe->nodes = xmalloc(allocated / sizeof(struct RNode) * sizeof(struct RNode *)); /* All node requires at least RNode */ +} + +static void +init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head, void *xmalloc(size_t)) +{ + init_node_buffer_elem(head, NODE_BUF_DEFAULT_SIZE, xmalloc); + nb->head = nb->last = head; + nb->head->next = NULL; +} + +#ifdef UNIVERSAL_PARSER +#define ruby_xmalloc config->malloc +#define Qnil config->qnil +#endif + +#ifdef UNIVERSAL_PARSER +static node_buffer_t * +rb_node_buffer_new(rb_parser_config_t *config) +#else +static node_buffer_t * +rb_node_buffer_new(void) +#endif +{ + const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE; + const size_t alloc_size = sizeof(node_buffer_t) + (bucket_size * 2); + STATIC_ASSERT( + integer_overflow, + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE + > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t)); + node_buffer_t *nb = ruby_xmalloc(alloc_size); + init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1], ruby_xmalloc); + init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size), ruby_xmalloc); + nb->local_tables = 0; + nb->mark_hash = Qnil; + nb->tokens = Qnil; +#ifdef UNIVERSAL_PARSER + nb->config = config; +#endif + return nb; +} + +#ifdef UNIVERSAL_PARSER +#undef ruby_xmalloc +#define ruby_xmalloc ast->node_buffer->config->malloc +#undef xfree +#define xfree ast->node_buffer->config->free +#define rb_ident_hash_new ast->node_buffer->config->ident_hash_new +#define rb_xmalloc_mul_add ast->node_buffer->config->xmalloc_mul_add +#define ruby_xrealloc(var,size) (ast->node_buffer->config->realloc_n((void *)var, 1, size)) +#define rb_gc_mark ast->node_buffer->config->gc_mark +#define rb_gc_location ast->node_buffer->config->gc_location +#define rb_gc_mark_movable ast->node_buffer->config->gc_mark_movable +#undef Qnil +#define Qnil ast->node_buffer->config->qnil +#define Qtrue ast->node_buffer->config->qtrue +#define NIL_P ast->node_buffer->config->nil_p +#define rb_hash_aset ast->node_buffer->config->hash_aset +#define rb_hash_delete ast->node_buffer->config->hash_delete +#define RB_OBJ_WRITE(old, slot, young) ast->node_buffer->config->obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young)) +#endif + +typedef void node_itr_t(rb_ast_t *ast, void *ctx, NODE *node); +static void iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx); + +/* Setup NODE structure. + * NODE is not an object managed by GC, but it imitates an object + * so that it can work with `RB_TYPE_P(obj, T_NODE)`. + * This dirty hack is needed because Ripper jumbles NODEs and other type + * objects. + */ +void +rb_node_init(NODE *n, enum node_type type) +{ + RNODE(n)->flags = T_NODE; + nd_init_type(RNODE(n), type); + RNODE(n)->nd_loc.beg_pos.lineno = 0; + RNODE(n)->nd_loc.beg_pos.column = 0; + RNODE(n)->nd_loc.end_pos.lineno = 0; + RNODE(n)->nd_loc.end_pos.column = 0; + RNODE(n)->node_id = -1; +} + +const char * +rb_node_name(int node) +{ + switch (node) { +#include "node_name.inc" + default: + return 0; + } +} + +#ifdef UNIVERSAL_PARSER +const char * +ruby_node_name(int node) +{ + return rb_node_name(node); +} +#else +const char * +ruby_node_name(int node) +{ + const char *name = rb_node_name(node); + + if (!name) rb_bug("unknown node: %d", node); + return name; +} +#endif + +static void +node_buffer_list_free(rb_ast_t *ast, node_buffer_list_t * nb) +{ + node_buffer_elem_t *nbe = nb->head; + while (nbe != nb->last) { + void *buf = nbe; + xfree(nbe->nodes); + nbe = nbe->next; + xfree(buf); + } + + /* The last node_buffer_elem_t is allocated in the node_buffer_t, so we + * only need to free the nodes. */ + xfree(nbe->nodes); +} + +struct rb_ast_local_table_link { + struct rb_ast_local_table_link *next; + // struct rb_ast_id_table { + int size; + ID ids[FLEX_ARY_LEN]; + // } +}; + +static void +free_ast_value(rb_ast_t *ast, void *ctx, NODE *node) +{ + switch (nd_type(node)) { + default: + break; + } +} + +static void +rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb) +{ + iterate_node_values(ast, &nb->unmarkable, free_ast_value, NULL); + node_buffer_list_free(ast, &nb->unmarkable); + node_buffer_list_free(ast, &nb->markable); + struct rb_ast_local_table_link *local_table = nb->local_tables; + while (local_table) { + struct rb_ast_local_table_link *next_table = local_table->next; + xfree(local_table); + local_table = next_table; + } + xfree(nb); +} + +#define buf_add_offset(nbe, offset) ((char *)(nbe->buf) + (offset)) + +static NODE * +ast_newnode_in_bucket(rb_ast_t *ast, node_buffer_list_t *nb, size_t size, size_t alignment) +{ + size_t padding; + NODE *ptr; + + padding = alignment - (size_t)buf_add_offset(nb->head, nb->head->used) % alignment; + padding = padding == alignment ? 0 : padding; + + if (nb->head->used + size + padding > nb->head->allocated) { + size_t n = nb->head->allocated * 2; + node_buffer_elem_t *nbe; + nbe = rb_xmalloc_mul_add(n, sizeof(char *), offsetof(node_buffer_elem_t, buf)); + init_node_buffer_elem(nbe, n, ruby_xmalloc); + nbe->next = nb->head; + nb->head = nbe; + padding = 0; /* malloc returns aligned address then no need to add padding */ + } + + ptr = (NODE *)buf_add_offset(nb->head, nb->head->used + padding); + nb->head->used += (size + padding); + nb->head->nodes[nb->head->len++] = ptr; + return ptr; +} + +RBIMPL_ATTR_PURE() +static bool +nodetype_markable_p(enum node_type type) +{ + switch (type) { + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + return true; + default: + return false; + } +} + +NODE * +rb_ast_newnode(rb_ast_t *ast, enum node_type type, size_t size, size_t alignment) +{ + node_buffer_t *nb = ast->node_buffer; + node_buffer_list_t *bucket = + (nodetype_markable_p(type) ? &nb->markable : &nb->unmarkable); + return ast_newnode_in_bucket(ast, bucket, size, alignment); +} + +#if RUBY_DEBUG +void +rb_ast_node_type_change(NODE *n, enum node_type type) +{ + enum node_type old_type = nd_type(n); + if (nodetype_markable_p(old_type) != nodetype_markable_p(type)) { + rb_bug("node type changed: %s -> %s", + ruby_node_name(old_type), ruby_node_name(type)); + } +} +#endif + +rb_ast_id_table_t * +rb_ast_new_local_table(rb_ast_t *ast, int size) +{ + size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); + struct rb_ast_local_table_link *link = ruby_xmalloc(alloc_size); + link->next = ast->node_buffer->local_tables; + ast->node_buffer->local_tables = link; + link->size = size; + + return (rb_ast_id_table_t *) &link->size; +} + +rb_ast_id_table_t * +rb_ast_resize_latest_local_table(rb_ast_t *ast, int size) +{ + struct rb_ast_local_table_link *link = ast->node_buffer->local_tables; + size_t alloc_size = sizeof(struct rb_ast_local_table_link) + size * sizeof(ID); + link = ruby_xrealloc(link, alloc_size); + ast->node_buffer->local_tables = link; + link->size = size; + + return (rb_ast_id_table_t *) &link->size; +} + +void +rb_ast_delete_node(rb_ast_t *ast, NODE *n) +{ + (void)ast; + (void)n; + /* should we implement freelist? */ +} + +#ifdef UNIVERSAL_PARSER +rb_ast_t * +rb_ast_new(rb_parser_config_t *config) +{ + node_buffer_t *nb = rb_node_buffer_new(config); + config->counter++; + return config->ast_new((VALUE)nb); +} +#else +rb_ast_t * +rb_ast_new(void) +{ + node_buffer_t *nb = rb_node_buffer_new(); + rb_ast_t *ast = (rb_ast_t *)rb_imemo_new(imemo_ast, 0, 0, 0, (VALUE)nb); + return ast; +} +#endif + +static void +iterate_buffer_elements(rb_ast_t *ast, node_buffer_elem_t *nbe, long len, node_itr_t *func, void *ctx) +{ + long cursor; + for (cursor = 0; cursor < len; cursor++) { + func(ast, ctx, nbe->nodes[cursor]); + } +} + +static void +iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, void *ctx) +{ + node_buffer_elem_t *nbe = nb->head; + + while (nbe) { + iterate_buffer_elements(ast, nbe, nbe->len, func, ctx); + nbe = nbe->next; + } +} + +static void +mark_ast_value(rb_ast_t *ast, void *ctx, NODE *node) +{ +#ifdef UNIVERSAL_PARSER + bug_report_func rb_bug = ast->node_buffer->config->bug; +#endif + + switch (nd_type(node)) { + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + rb_gc_mark_movable(RNODE_LIT(node)->nd_lit); + break; + default: + rb_bug("unreachable node %s", ruby_node_name(nd_type(node))); + } +} + +static void +update_ast_value(rb_ast_t *ast, void *ctx, NODE *node) +{ +#ifdef UNIVERSAL_PARSER + bug_report_func rb_bug = ast->node_buffer->config->bug; +#endif + + switch (nd_type(node)) { + case NODE_MATCH: + case NODE_LIT: + case NODE_STR: + case NODE_XSTR: + case NODE_DSTR: + case NODE_DXSTR: + case NODE_DREGX: + case NODE_DSYM: + RNODE_LIT(node)->nd_lit = rb_gc_location(RNODE_LIT(node)->nd_lit); + break; + default: + rb_bug("unreachable"); + } +} + +void +rb_ast_update_references(rb_ast_t *ast) +{ + if (ast->node_buffer) { + node_buffer_t *nb = ast->node_buffer; + + iterate_node_values(ast, &nb->markable, update_ast_value, NULL); + } +} + +void +rb_ast_mark(rb_ast_t *ast) +{ + if (ast->node_buffer) { + rb_gc_mark(ast->node_buffer->mark_hash); + rb_gc_mark(ast->node_buffer->tokens); + node_buffer_t *nb = ast->node_buffer; + iterate_node_values(ast, &nb->markable, mark_ast_value, NULL); + if (ast->body.script_lines) rb_gc_mark(ast->body.script_lines); + } +} + +void +rb_ast_free(rb_ast_t *ast) +{ + if (ast->node_buffer) { +#ifdef UNIVERSAL_PARSER + rb_parser_config_t *config = ast->node_buffer->config; +#endif + + rb_node_buffer_free(ast, ast->node_buffer); + ast->node_buffer = 0; +#ifdef UNIVERSAL_PARSER + config->counter--; + if (config->counter <= 0) { + rb_ruby_parser_config_free(config); + } +#endif + } +} + +static size_t +buffer_list_size(node_buffer_list_t *nb) +{ + size_t size = 0; + node_buffer_elem_t *nbe = nb->head; + while (nbe != nb->last) { + size += offsetof(node_buffer_elem_t, buf) + nbe->used; + nbe = nbe->next; + } + return size; +} + +size_t +rb_ast_memsize(const rb_ast_t *ast) +{ + size_t size = 0; + node_buffer_t *nb = ast->node_buffer; + + if (nb) { + size += sizeof(node_buffer_t); + size += buffer_list_size(&nb->unmarkable); + size += buffer_list_size(&nb->markable); + } + return size; +} + +void +rb_ast_dispose(rb_ast_t *ast) +{ + rb_ast_free(ast); +} + +void +rb_ast_add_mark_object(rb_ast_t *ast, VALUE obj) +{ + if (NIL_P(ast->node_buffer->mark_hash)) { + RB_OBJ_WRITE(ast, &ast->node_buffer->mark_hash, rb_ident_hash_new()); + } + rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue); +} + +void +rb_ast_delete_mark_object(rb_ast_t *ast, VALUE obj) +{ + if (NIL_P(ast->node_buffer->mark_hash)) return; + rb_hash_delete(ast->node_buffer->mark_hash, obj); +} + +VALUE +rb_ast_tokens(rb_ast_t *ast) +{ + return ast->node_buffer->tokens; +} + +void +rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens) +{ + RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens); +} + +VALUE +rb_node_set_type(NODE *n, enum node_type t) +{ +#if RUBY_DEBUG + rb_ast_node_type_change(n, t); +#endif + return nd_init_type(n, t); +} |
