summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.c55
-rw-r--r--compile.c1
-rw-r--r--iseq.c18
-rw-r--r--node.h3
-rw-r--r--parse.y11
-rw-r--r--test/ruby/test_ast.rb12
-rw-r--r--vm_core.h1
7 files changed, 94 insertions, 7 deletions
diff --git a/ast.c b/ast.c
index b5f40fad57..babfc1fbf6 100644
--- a/ast.c
+++ b/ast.c
@@ -5,6 +5,7 @@
#include "internal.h"
#include "node.h"
#include "vm_core.h"
+#include "iseq.h"
static VALUE rb_mAST;
static VALUE rb_cNode;
@@ -125,6 +126,59 @@ rb_ast_s_parse_file(VALUE module, VALUE path)
return obj;
}
+static VALUE node_children(rb_ast_t*, NODE*);
+
+static VALUE
+node_find(VALUE self, const int node_id)
+{
+ VALUE ary;
+ long i;
+ struct ASTNodeData *data;
+ TypedData_Get_Struct(self, struct ASTNodeData, &rb_node_type, data);
+
+ if (nd_node_id(data->node) == node_id) return self;
+
+ ary = node_children(data->ast, data->node);
+
+ for (i = 0; i < RARRAY_LEN(ary); i++) {
+ VALUE child = RARRAY_AREF(ary, i);
+
+ if (CLASS_OF(child) == rb_cNode) {
+ VALUE result = node_find(child, node_id);
+ if (RTEST(result)) return result;
+ }
+ }
+
+ return Qnil;
+}
+
+static VALUE
+rb_ast_s_of(VALUE module, VALUE body)
+{
+ VALUE path, node;
+ int node_id;
+ const rb_iseq_t *iseq = NULL;
+
+ if (rb_obj_is_proc(body)) {
+ iseq = vm_proc_iseq(body);
+
+ if (!rb_obj_is_iseq((VALUE)iseq)) {
+ iseq = NULL;
+ }
+ }
+ else {
+ iseq = rb_method_iseq(body);
+ }
+
+ if (!iseq) return Qnil;
+
+ path = rb_iseq_path(iseq);
+ node_id = iseq->body->location.node_id;
+ node = rb_ast_s_parse_file(module, path);
+
+ return node_find(node, node_id);
+}
+
static VALUE
rb_ast_node_alloc(VALUE klass)
{
@@ -619,6 +673,7 @@ Init_ast(void)
rb_undef_alloc_func(rb_cNode);
rb_define_singleton_method(rb_mAST, "parse", rb_ast_s_parse, 1);
rb_define_singleton_method(rb_mAST, "parse_file", rb_ast_s_parse_file, 1);
+ rb_define_singleton_method(rb_mAST, "of", rb_ast_s_of, 1);
rb_define_method(rb_cNode, "type", rb_ast_node_type, 0);
rb_define_method(rb_cNode, "first_lineno", rb_ast_node_first_lineno, 0);
rb_define_method(rb_cNode, "first_column", rb_ast_node_first_column, 0);
diff --git a/compile.c b/compile.c
index d5ce9f6edd..1109e4faa7 100644
--- a/compile.c
+++ b/compile.c
@@ -9077,6 +9077,7 @@ ibf_load_iseq_each(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t of
RB_OBJ_WRITE(iseq, &load_body->location.base_label, ibf_load_location_str(load, body->location.base_label));
RB_OBJ_WRITE(iseq, &load_body->location.label, ibf_load_location_str(load, body->location.label));
load_body->location.first_lineno = body->location.first_lineno;
+ load_body->location.node_id = body->location.node_id;
load_body->location.code_location = body->location.code_location;
load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);
diff --git a/iseq.c b/iseq.c
index 164a1f477c..2e33e4f589 100644
--- a/iseq.c
+++ b/iseq.c
@@ -371,7 +371,7 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
}
static rb_iseq_location_t *
-iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location)
+iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id)
{
rb_iseq_location_t *loc = &iseq->body->location;
@@ -380,6 +380,7 @@ iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VAL
RB_OBJ_WRITE(iseq, &loc->base_label, name);
loc->first_lineno = first_lineno;
if (code_location) {
+ loc->node_id = node_id;
loc->code_location = *code_location;
}
else {
@@ -420,7 +421,7 @@ set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
- VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location,
+ VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id,
const rb_iseq_t *parent, enum iseq_type type,
const rb_compile_option_t *option)
{
@@ -435,7 +436,7 @@ prepare_iseq_build(rb_iseq_t *iseq,
set_relation(iseq, parent);
name = rb_fstring(name);
- iseq_location_setup(iseq, name, path, realpath, first_lineno, code_location);
+ iseq_location_setup(iseq, name, path, realpath, first_lineno, code_location, node_id);
if (iseq != body->local_iseq) {
RB_OBJ_WRITE(iseq, &body->location.base_label, body->local_iseq->body->location.label);
}
@@ -704,7 +705,7 @@ rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE rea
new_opt = option ? *option : COMPILE_OPTION_DEFAULT;
if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option);
- prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, parent, type, &new_opt);
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, type, &new_opt);
rb_iseq_compile_node(iseq, node);
finish_iseq_build(iseq);
@@ -721,7 +722,7 @@ rb_iseq_new_ifunc(const struct vm_ifunc *ifunc, VALUE name, VALUE path, VALUE re
rb_iseq_t *iseq = iseq_alloc();
if (!option) option = &COMPILE_OPTION_DEFAULT;
- prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, parent, type, option);
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, type, option);
rb_iseq_compile_ifunc(iseq, ifunc);
finish_iseq_build(iseq);
@@ -780,7 +781,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
rb_iseq_t *iseq = iseq_alloc();
VALUE magic, version1, version2, format_type, misc;
- VALUE name, path, realpath, first_lineno, code_location;
+ VALUE name, path, realpath, first_lineno, code_location, node_id;
VALUE type, body, locals, params, exception;
st_data_t iseq_type;
@@ -821,6 +822,8 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
rb_raise(rb_eTypeError, "unsupport type: :%"PRIsVALUE, rb_sym2str(type));
}
+ node_id = rb_hash_aref(misc, ID2SYM(rb_intern("node_id")));
+
code_location = rb_hash_aref(misc, ID2SYM(rb_intern("code_location")));
if (RB_TYPE_P(code_location, T_ARRAY) && RARRAY_LEN(code_location) == 4) {
tmp_loc.beg_pos.lineno = NUM2INT(rb_ary_entry(code_location, 0));
@@ -831,7 +834,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
make_compile_option(&option, opt);
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
- prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc,
+ prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
parent, (enum iseq_type)iseq_type, &option);
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
@@ -2730,6 +2733,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq_body->param.size));
rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq_body->local_table_size));
rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq_body->stack_max));
+ rb_hash_aset(misc, ID2SYM(rb_intern("node_id")), INT2FIX(iseq_body->location.node_id));
rb_hash_aset(misc, ID2SYM(rb_intern("code_location")),
rb_ary_new_from_args(4,
INT2FIX(iseq_body->location.code_location.beg_pos.lineno),
diff --git a/node.h b/node.h
index f725a38df4..ad2798b48c 100644
--- a/node.h
+++ b/node.h
@@ -164,6 +164,7 @@ typedef struct RNode {
VALUE value;
} u3;
rb_code_location_t nd_loc;
+ int node_id;
} NODE;
#define RNODE(obj) (R_CAST(RNode)(obj))
@@ -201,6 +202,8 @@ typedef struct RNode {
#define nd_set_last_lineno(n, v) ((n)->nd_loc.end_pos.lineno = (v))
#define nd_last_loc(n) ((n)->nd_loc.end_pos)
#define nd_set_last_loc(n, v) (nd_last_loc(n) = (v))
+#define nd_node_id(n) ((n)->node_id)
+#define nd_set_node_id(n,id) ((n)->node_id = (id))
#define nd_head u1.node
#define nd_alen u2.argc
diff --git a/parse.y b/parse.y
index 8dcc25bfea..aed392822e 100644
--- a/parse.y
+++ b/parse.y
@@ -234,6 +234,7 @@ struct parser_params {
ID cur_arg;
rb_ast_t *ast;
+ int node_id;
unsigned int command_start:1;
unsigned int eofp: 1;
@@ -339,6 +340,14 @@ static NODE* node_newnode(struct parser_params *, enum node_type, VALUE, VALUE,
static NODE *nd_set_loc(NODE *nd, const YYLTYPE *loc);
+static int
+parser_get_node_id(struct parser_params *p)
+{
+ int node_id = p->node_id;
+ p->node_id++;
+ return node_id;
+}
+
#ifndef RIPPER
static inline void
set_line_body(NODE *body, int line)
@@ -8294,6 +8303,7 @@ node_newnode(struct parser_params *p, enum node_type type, VALUE a0, VALUE a1, V
rb_node_init(n, type, a0, a1, a2);
nd_set_loc(n, loc);
+ nd_set_node_id(n, parser_get_node_id(p));
return n;
}
@@ -10774,6 +10784,7 @@ parser_initialize(struct parser_params *p)
p->command_start = TRUE;
p->ruby_sourcefile_string = Qnil;
p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
+ p->node_id = 0;
#ifdef RIPPER
p->delayed = Qnil;
p->result = Qnil;
diff --git a/test/ruby/test_ast.rb b/test/ruby/test_ast.rb
index d053ce5a49..70f0b4af1f 100644
--- a/test/ruby/test_ast.rb
+++ b/test/ruby/test_ast.rb
@@ -170,6 +170,18 @@ class TestAst < Test::Unit::TestCase
end
end
+ def test_of
+ proc = Proc.new { 1 + 2 }
+ method = self.method(__method__)
+
+ node_proc = RubyVM::AST.of(proc)
+ node_method = RubyVM::AST.of(method)
+
+ assert_instance_of(RubyVM::AST::Node, node_proc)
+ assert_instance_of(RubyVM::AST::Node, node_method)
+ assert_raise(TypeError) { RubyVM::AST.of("1 + 2") }
+ end
+
def test_scope_local_variables
node = RubyVM::AST.parse("x = 0")
lv, _, body = *node.children
diff --git a/vm_core.h b/vm_core.h
index fbcb013e46..e5fdba44b9 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -298,6 +298,7 @@ typedef struct rb_iseq_location_struct {
VALUE base_label; /* String */
VALUE label; /* String */
VALUE first_lineno; /* TODO: may be unsigned short */
+ int node_id;
rb_code_location_t code_location;
} rb_iseq_location_t;