summaryrefslogtreecommitdiff
path: root/node.c
diff options
context:
space:
mode:
Diffstat (limited to 'node.c')
-rw-r--r--node.c514
1 files changed, 240 insertions, 274 deletions
diff --git a/node.c b/node.c
index bd03454bef..5d00823eb6 100644
--- a/node.c
+++ b/node.c
@@ -10,59 +10,79 @@
**********************************************************************/
#ifdef UNIVERSAL_PARSER
-
#include <stddef.h>
#include "node.h"
#include "rubyparser.h"
-#include "internal/parse.h"
-#define T_NODE 0x1b
+#endif
-#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
-#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_OBJ_WRITE(old, slot, young) ast->node_buffer->config->obj_write((VALUE)(old), (VALUE *)(slot), (VALUE)(young))
+#include "internal/variable.h"
-#else
+#define NODE_BUF_DEFAULT_SIZE (sizeof(struct RNode) * 16)
-#include "internal.h"
-#include "internal/hash.h"
-#include "internal/variable.h"
-#include "ruby/ruby.h"
-#include "vm_core.h"
+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
+#endif
+
+#ifdef UNIVERSAL_PARSER
+static node_buffer_t *
+rb_node_buffer_new(const 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);
+ STATIC_ASSERT(
+ integer_overflow,
+ offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_SIZE
+ > sizeof(node_buffer_t) + sizeof(node_buffer_elem_t));
+ node_buffer_t *nb = ruby_xmalloc(alloc_size);
+ init_node_buffer_list(&nb->buffer_list, (node_buffer_elem_t*)&nb[1], ruby_xmalloc);
+ nb->local_tables = 0;
+ nb->tokens = 0;
+ return nb;
+}
+#ifdef UNIVERSAL_PARSER
+#undef ruby_xmalloc
+#define ruby_xmalloc ast->config->malloc
+#undef xfree
+#define xfree ast->config->free
+#define rb_xmalloc_mul_add ast->config->xmalloc_mul_add
+#define ruby_xrealloc(var,size) (ast->config->realloc_n((void *)var, 1, size))
#endif
-#define NODE_BUF_DEFAULT_LEN 16
+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, VALUE a0, VALUE a1, VALUE a2)
+rb_node_init(NODE *n, enum node_type type)
{
- n->flags = T_NODE;
- nd_init_type(n, type);
- n->u1.value = a0;
- n->u2.value = a1;
- n->u3.value = a2;
- n->nd_loc.beg_pos.lineno = 0;
- n->nd_loc.beg_pos.column = 0;
- n->nd_loc.end_pos.lineno = 0;
- n->nd_loc.end_pos.column = 0;
- n->node_id = -1;
+ RNODE(n)->flags = 0;
+ 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 *
@@ -93,64 +113,19 @@ ruby_node_name(int node)
#endif
static void
-init_node_buffer_list(node_buffer_list_t * nb, node_buffer_elem_t *head)
-{
- nb->idx = 0;
- nb->len = NODE_BUF_DEFAULT_LEN;
- nb->head = nb->last = head;
- nb->head->len = nb->len;
- nb->head->next = NULL;
-}
-
-#ifdef UNIVERSAL_PARSER
-static node_buffer_t *
-rb_node_buffer_new(rb_parser_config_t *config)
-{
- const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE);
- 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_LEN * sizeof(NODE)
- > sizeof(node_buffer_t) + 2 * sizeof(node_buffer_elem_t));
- node_buffer_t *nb = config->malloc(alloc_size);
- init_node_buffer_list(&nb->unmarkable, (node_buffer_elem_t*)&nb[1]);
- init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size));
- nb->local_tables = 0;
- nb->mark_hash = config->qnil;
- nb->tokens = config->qnil;
- nb->config = config;
- return nb;
-}
-#else
-static node_buffer_t *
-rb_node_buffer_new(void)
-{
- const size_t bucket_size = offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE);
- 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_LEN * sizeof(NODE)
- > 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]);
- init_node_buffer_list(&nb->markable, (node_buffer_elem_t*)((size_t)nb->unmarkable.head + bucket_size));
- nb->local_tables = 0;
- nb->mark_hash = Qnil;
- nb->tokens = Qnil;
- return nb;
-}
-#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 {
@@ -162,10 +137,99 @@ struct rb_ast_local_table_link {
};
static void
+parser_string_free(rb_ast_t *ast, rb_parser_string_t *str)
+{
+ if (!str) return;
+ xfree(str->ptr);
+ xfree(str);
+}
+
+static void
+parser_ast_token_free(rb_ast_t *ast, rb_parser_ast_token_t *token)
+{
+ if (!token) return;
+ parser_string_free(ast, token->str);
+ xfree(token);
+}
+
+static void
+parser_tokens_free(rb_ast_t *ast, rb_parser_ary_t *tokens)
+{
+ for (long i = 0; i < tokens->len; i++) {
+ parser_ast_token_free(ast, tokens->data[i]);
+ }
+ xfree(tokens->data);
+ xfree(tokens);
+}
+
+static void
+parser_nodes_free(rb_ast_t *ast, rb_parser_ary_t *nodes)
+{
+ /* Do nothing for nodes because nodes are freed when rb_ast_t is freed */
+ xfree(nodes->data);
+ xfree(nodes);
+}
+
+static void
+free_ast_value(rb_ast_t *ast, void *ctx, NODE *node)
+{
+ switch (nd_type(node)) {
+ case NODE_STR:
+ parser_string_free(ast, RNODE_STR(node)->string);
+ break;
+ case NODE_DSTR:
+ parser_string_free(ast, RNODE_DSTR(node)->string);
+ break;
+ case NODE_XSTR:
+ parser_string_free(ast, RNODE_XSTR(node)->string);
+ break;
+ case NODE_DXSTR:
+ parser_string_free(ast, RNODE_DXSTR(node)->string);
+ break;
+ case NODE_SYM:
+ parser_string_free(ast, RNODE_SYM(node)->string);
+ break;
+ case NODE_REGX:
+ case NODE_MATCH:
+ parser_string_free(ast, RNODE_REGX(node)->string);
+ break;
+ case NODE_DSYM:
+ parser_string_free(ast, RNODE_DSYM(node)->string);
+ break;
+ case NODE_DREGX:
+ parser_string_free(ast, RNODE_DREGX(node)->string);
+ break;
+ case NODE_FILE:
+ parser_string_free(ast, RNODE_FILE(node)->path);
+ break;
+ case NODE_INTEGER:
+ xfree(RNODE_INTEGER(node)->val);
+ break;
+ case NODE_FLOAT:
+ xfree(RNODE_FLOAT(node)->val);
+ break;
+ case NODE_RATIONAL:
+ xfree(RNODE_RATIONAL(node)->val);
+ break;
+ case NODE_IMAGINARY:
+ xfree(RNODE_IMAGINARY(node)->val);
+ break;
+ case NODE_UNDEF:
+ parser_nodes_free(ast, RNODE_UNDEF(node)->nd_undefs);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb)
{
- node_buffer_list_free(ast, &nb->unmarkable);
- node_buffer_list_free(ast, &nb->markable);
+ if (nb->tokens) {
+ parser_tokens_free(ast, nb->tokens);
+ }
+ iterate_node_values(ast, &nb->buffer_list, free_ast_value, NULL);
+ node_buffer_list_free(ast, &nb->buffer_list);
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;
@@ -175,65 +239,41 @@ rb_node_buffer_free(rb_ast_t *ast, node_buffer_t *nb)
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)
+ast_newnode_in_bucket(rb_ast_t *ast, node_buffer_list_t *nb, size_t size, size_t alignment)
{
- if (nb->idx >= nb->len) {
- long n = nb->len * 2;
+ 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(NODE), offsetof(node_buffer_elem_t, buf));
- nbe->len = n;
- nb->idx = 0;
- nb->len = n;
+ 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 */
}
- return &nb->head->buf[nb->idx++];
-}
-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:
- case NODE_ARGS:
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- return true;
- default:
- return false;
- }
+ 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;
}
NODE *
-rb_ast_newnode(rb_ast_t *ast, enum node_type type)
+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);
+ node_buffer_list_t *bucket = &nb->buffer_list;
+ 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)
{
@@ -268,30 +308,31 @@ rb_ast_delete_node(rb_ast_t *ast, NODE *n)
#ifdef UNIVERSAL_PARSER
rb_ast_t *
-rb_ast_new(rb_parser_config_t *config)
+rb_ast_new(const rb_parser_config_t *config)
{
node_buffer_t *nb = rb_node_buffer_new(config);
- config->counter++;
- return config->ast_new((VALUE)nb);
+ rb_ast_t *ast = (rb_ast_t *)config->calloc(1, sizeof(rb_ast_t));
+ ast->config = config;
+ ast->node_buffer = nb;
+ return ast;
}
#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);
+ rb_ast_t *ast = ruby_xcalloc(1, sizeof(rb_ast_t));
+ ast->node_buffer = nb;
return ast;
}
#endif
-typedef void node_itr_t(rb_ast_t *ast, void *ctx, NODE * node);
-
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->buf[cursor]);
+ func(ast, ctx, nbe->nodes[cursor]);
}
}
@@ -300,10 +341,6 @@ iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, vo
{
node_buffer_elem_t *nbe = nb->head;
- /* iterate over the head first because it's not full */
- iterate_buffer_elements(ast, nbe, nb->idx, func, ctx);
-
- nbe = nbe->next;
while (nbe) {
iterate_buffer_elements(ast, nbe, nbe->len, func, ctx);
nbe = nbe->next;
@@ -311,110 +348,21 @@ iterate_node_values(rb_ast_t *ast, node_buffer_list_t *nb, node_itr_t * func, vo
}
static void
-mark_ast_value(rb_ast_t *ast, void *ctx, NODE * node)
+script_lines_free(rb_ast_t *ast, rb_parser_ary_t *script_lines)
{
-#ifdef UNIVERSAL_PARSER
- bug_report_func rb_bug = ast->node_buffer->config->bug;
-#endif
-
- switch (nd_type(node)) {
- case NODE_ARGS:
- {
- struct rb_args_info *args = node->nd_ainfo;
- rb_gc_mark_movable(args->imemo);
- break;
- }
- 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(node->nd_lit);
- break;
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- rb_gc_mark_movable(node->nd_rval);
- 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_ARGS:
- {
- struct rb_args_info *args = node->nd_ainfo;
- args->imemo = rb_gc_location(args->imemo);
- break;
- }
- case NODE_MATCH:
- case NODE_LIT:
- case NODE_STR:
- case NODE_XSTR:
- case NODE_DSTR:
- case NODE_DXSTR:
- case NODE_DREGX:
- case NODE_DSYM:
- node->nd_lit = rb_gc_location(node->nd_lit);
- break;
- case NODE_ARYPTN:
- case NODE_FNDPTN:
- node->nd_rval = rb_gc_location(node->nd_rval);
- 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);
+ if (!script_lines) return;
+ for (long i = 0; i < script_lines->len; i++) {
+ parser_string_free(ast, (rb_parser_string_t *)script_lines->data[i]);
}
+ xfree(script_lines->data);
+ xfree(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
- }
+ rb_ast_dispose(ast);
+ xfree(ast);
}
static size_t
@@ -423,8 +371,8 @@ 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;
- size += offsetof(node_buffer_elem_t, buf) + nb->len * sizeof(NODE);
}
return size;
}
@@ -432,49 +380,67 @@ buffer_list_size(node_buffer_list_t *nb)
size_t
rb_ast_memsize(const rb_ast_t *ast)
{
- size_t size = 0;
+ size_t size = sizeof(rb_ast_t);
node_buffer_t *nb = ast->node_buffer;
+ rb_parser_ary_t *tokens = NULL;
+ struct rb_ast_local_table_link *link = NULL;
+ rb_parser_ary_t *script_lines = ast->body.script_lines;
+
+ long i;
if (nb) {
- size += sizeof(node_buffer_t) + offsetof(node_buffer_elem_t, buf) + NODE_BUF_DEFAULT_LEN * sizeof(NODE);
- size += buffer_list_size(&nb->unmarkable);
- size += buffer_list_size(&nb->markable);
+ size += sizeof(node_buffer_t);
+ size += buffer_list_size(&nb->buffer_list);
+ link = nb->local_tables;
+ tokens = nb->tokens;
}
- return size;
-}
-void
-rb_ast_dispose(rb_ast_t *ast)
-{
- rb_ast_free(ast);
-}
+ while (link) {
+ size += sizeof(struct rb_ast_local_table_link);
+ size += link->size * sizeof(ID);
+ link = link->next;
+ }
-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());
+ if (tokens) {
+ size += sizeof(rb_parser_ary_t);
+ for (i = 0; i < tokens->len; i++) {
+ size += sizeof(rb_parser_ast_token_t);
+ rb_parser_ast_token_t *token = tokens->data[i];
+ size += sizeof(rb_parser_string_t);
+ size += token->str->len + 1;
+ }
}
- rb_hash_aset(ast->node_buffer->mark_hash, obj, Qtrue);
-}
-VALUE
-rb_ast_tokens(rb_ast_t *ast)
-{
- return ast->node_buffer->tokens;
+ if (script_lines) {
+ size += sizeof(rb_parser_ary_t);
+ for (i = 0; i < script_lines->len; i++) {
+ size += sizeof(rb_parser_string_t);
+ size += ((rb_parser_string_t *)script_lines->data[i])->len + 1;
+ }
+ }
+
+ return size;
}
void
-rb_ast_set_tokens(rb_ast_t *ast, VALUE tokens)
+rb_ast_dispose(rb_ast_t *ast)
{
- RB_OBJ_WRITE(ast, &ast->node_buffer->tokens, tokens);
+ if (ast && ast->node_buffer) {
+ script_lines_free(ast, ast->body.script_lines);
+ ast->body.script_lines = NULL;
+ rb_node_buffer_free(ast, ast->node_buffer);
+ ast->node_buffer = 0;
+ }
}
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);
}
+
+enum node_type
+rb_node_get_type(const NODE *n)
+{
+ return (enum node_type)nd_type(n);
+}