зеркало из https://github.com/github/ruby.git
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:
Родитель
6d497629b2
Коммит
ce798d08de
55
ast.c
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
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
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
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;
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче