Implement `RubyVM::AST.of` [Feature #14836]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65542 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
yui-knk 2018-11-05 02:13:45 +00:00
Родитель 6d497629b2
Коммит ce798d08de
7 изменённых файлов: 94 добавлений и 7 удалений

55
ast.c
Просмотреть файл

@ -5,6 +5,7 @@
#include "internal.h" #include "internal.h"
#include "node.h" #include "node.h"
#include "vm_core.h" #include "vm_core.h"
#include "iseq.h"
static VALUE rb_mAST; static VALUE rb_mAST;
static VALUE rb_cNode; static VALUE rb_cNode;
@ -125,6 +126,59 @@ rb_ast_s_parse_file(VALUE module, VALUE path)
return obj; 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 static VALUE
rb_ast_node_alloc(VALUE klass) rb_ast_node_alloc(VALUE klass)
{ {
@ -619,6 +673,7 @@ Init_ast(void)
rb_undef_alloc_func(rb_cNode); 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", 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, "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, "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_lineno", rb_ast_node_first_lineno, 0);
rb_define_method(rb_cNode, "first_column", rb_ast_node_first_column, 0); rb_define_method(rb_cNode, "first_column", rb_ast_node_first_column, 0);

Просмотреть файл

@ -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.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)); 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.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->location.code_location = body->location.code_location;
load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size); load_body->is_entries = ZALLOC_N(union iseq_inline_storage_entry, body->is_size);

18
iseq.c
Просмотреть файл

@ -371,7 +371,7 @@ rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
} }
static rb_iseq_location_t * 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; 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); RB_OBJ_WRITE(iseq, &loc->base_label, name);
loc->first_lineno = first_lineno; loc->first_lineno = first_lineno;
if (code_location) { if (code_location) {
loc->node_id = node_id;
loc->code_location = *code_location; loc->code_location = *code_location;
} }
else { else {
@ -420,7 +421,7 @@ set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
static VALUE static VALUE
prepare_iseq_build(rb_iseq_t *iseq, 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_iseq_t *parent, enum iseq_type type,
const rb_compile_option_t *option) const rb_compile_option_t *option)
{ {
@ -435,7 +436,7 @@ prepare_iseq_build(rb_iseq_t *iseq,
set_relation(iseq, parent); set_relation(iseq, parent);
name = rb_fstring(name); 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) { if (iseq != body->local_iseq) {
RB_OBJ_WRITE(iseq, &body->location.base_label, body->local_iseq->body->location.label); 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; new_opt = option ? *option : COMPILE_OPTION_DEFAULT;
if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option); 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); rb_iseq_compile_node(iseq, node);
finish_iseq_build(iseq); 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(); rb_iseq_t *iseq = iseq_alloc();
if (!option) option = &COMPILE_OPTION_DEFAULT; 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); rb_iseq_compile_ifunc(iseq, ifunc);
finish_iseq_build(iseq); 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(); rb_iseq_t *iseq = iseq_alloc();
VALUE magic, version1, version2, format_type, misc; 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; VALUE type, body, locals, params, exception;
st_data_t iseq_type; 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)); 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"))); code_location = rb_hash_aref(misc, ID2SYM(rb_intern("code_location")));
if (RB_TYPE_P(code_location, T_ARRAY) && RARRAY_LEN(code_location) == 4) { 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)); 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); make_compile_option(&option, opt);
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */ 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); parent, (enum iseq_type)iseq_type, &option);
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body); 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("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("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("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_hash_aset(misc, ID2SYM(rb_intern("code_location")),
rb_ary_new_from_args(4, rb_ary_new_from_args(4,
INT2FIX(iseq_body->location.code_location.beg_pos.lineno), INT2FIX(iseq_body->location.code_location.beg_pos.lineno),

3
node.h
Просмотреть файл

@ -164,6 +164,7 @@ typedef struct RNode {
VALUE value; VALUE value;
} u3; } u3;
rb_code_location_t nd_loc; rb_code_location_t nd_loc;
int node_id;
} NODE; } NODE;
#define RNODE(obj) (R_CAST(RNode)(obj)) #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_set_last_lineno(n, v) ((n)->nd_loc.end_pos.lineno = (v))
#define nd_last_loc(n) ((n)->nd_loc.end_pos) #define nd_last_loc(n) ((n)->nd_loc.end_pos)
#define nd_set_last_loc(n, v) (nd_last_loc(n) = (v)) #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_head u1.node
#define nd_alen u2.argc #define nd_alen u2.argc

11
parse.y
Просмотреть файл

@ -234,6 +234,7 @@ struct parser_params {
ID cur_arg; ID cur_arg;
rb_ast_t *ast; rb_ast_t *ast;
int node_id;
unsigned int command_start:1; unsigned int command_start:1;
unsigned int eofp: 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 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 #ifndef RIPPER
static inline void static inline void
set_line_body(NODE *body, int line) 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); rb_node_init(n, type, a0, a1, a2);
nd_set_loc(n, loc); nd_set_loc(n, loc);
nd_set_node_id(n, parser_get_node_id(p));
return n; return n;
} }
@ -10774,6 +10784,7 @@ parser_initialize(struct parser_params *p)
p->command_start = TRUE; p->command_start = TRUE;
p->ruby_sourcefile_string = Qnil; p->ruby_sourcefile_string = Qnil;
p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */ p->lex.lpar_beg = -1; /* make lambda_beginning_p() == FALSE at first */
p->node_id = 0;
#ifdef RIPPER #ifdef RIPPER
p->delayed = Qnil; p->delayed = Qnil;
p->result = Qnil; p->result = Qnil;

Просмотреть файл

@ -170,6 +170,18 @@ class TestAst < Test::Unit::TestCase
end end
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 def test_scope_local_variables
node = RubyVM::AST.parse("x = 0") node = RubyVM::AST.parse("x = 0")
lv, _, body = *node.children lv, _, body = *node.children

Просмотреть файл

@ -298,6 +298,7 @@ typedef struct rb_iseq_location_struct {
VALUE base_label; /* String */ VALUE base_label; /* String */
VALUE label; /* String */ VALUE label; /* String */
VALUE first_lineno; /* TODO: may be unsigned short */ VALUE first_lineno; /* TODO: may be unsigned short */
int node_id;
rb_code_location_t code_location; rb_code_location_t code_location;
} rb_iseq_location_t; } rb_iseq_location_t;