mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
RubyVM.keep_script_lines
`RubyVM.keep_script_lines` enables to keep script lines for each ISeq and AST. This feature is for debugger/REPL support. ```ruby RubyVM.keep_script_lines = true RubyVM::keep_script_lines = true eval("def foo = nil\ndef bar = nil") pp RubyVM::InstructionSequence.of(method(:foo)).script_lines ```
This commit is contained in:
parent
3b16d07e45
commit
c7550537f1
Notes:
git
2021-10-21 16:18:11 +09:00
8 changed files with 137 additions and 6 deletions
|
@ -7194,6 +7194,7 @@ iseq.$(OBJEXT): {$(VPATH)}node.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}node_name.inc
|
iseq.$(OBJEXT): {$(VPATH)}node_name.inc
|
||||||
iseq.$(OBJEXT): {$(VPATH)}onigmo.h
|
iseq.$(OBJEXT): {$(VPATH)}onigmo.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
|
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
|
||||||
|
iseq.$(OBJEXT): {$(VPATH)}ractor.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
|
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
|
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
|
||||||
iseq.$(OBJEXT): {$(VPATH)}st.h
|
iseq.$(OBJEXT): {$(VPATH)}st.h
|
||||||
|
|
|
@ -1328,7 +1328,7 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
|
||||||
|
|
||||||
ast.root = node;
|
ast.root = node;
|
||||||
ast.compile_option = 0;
|
ast.compile_option = 0;
|
||||||
ast.script_lines = INT2FIX(-1);
|
ast.script_lines = iseq->body->variable.script_lines;
|
||||||
|
|
||||||
debugs("[new_child_iseq]> ---------------------------------------\n");
|
debugs("[new_child_iseq]> ---------------------------------------\n");
|
||||||
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
|
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
|
||||||
|
|
|
@ -25,6 +25,7 @@ st_index_t rb_iseq_cdhash_hash(VALUE a);
|
||||||
/* iseq.c */
|
/* iseq.c */
|
||||||
int rb_vm_insn_addr2insn(const void *);
|
int rb_vm_insn_addr2insn(const void *);
|
||||||
int rb_vm_insn_decode(const VALUE encoded);
|
int rb_vm_insn_decode(const VALUE encoded);
|
||||||
|
extern bool ruby_vm_keep_script_lines;
|
||||||
|
|
||||||
MJIT_SYMBOL_EXPORT_BEGIN
|
MJIT_SYMBOL_EXPORT_BEGIN
|
||||||
/* iseq.c (export) */
|
/* iseq.c (export) */
|
||||||
|
|
44
iseq.c
44
iseq.c
|
@ -595,7 +595,8 @@ new_arena(void)
|
||||||
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, const int node_id,
|
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, int isolated_depth, enum iseq_type type, const rb_compile_option_t *option)
|
const rb_iseq_t *parent, int isolated_depth, enum iseq_type type,
|
||||||
|
VALUE script_lines, const rb_compile_option_t *option)
|
||||||
{
|
{
|
||||||
VALUE coverage = Qfalse;
|
VALUE coverage = Qfalse;
|
||||||
VALUE err_info = Qnil;
|
VALUE err_info = Qnil;
|
||||||
|
@ -616,6 +617,8 @@ prepare_iseq_build(rb_iseq_t *iseq,
|
||||||
ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
|
ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
|
||||||
body->variable.flip_count = 0;
|
body->variable.flip_count = 0;
|
||||||
|
|
||||||
|
RB_OBJ_WRITE(iseq, &body->variable.script_lines, script_lines);
|
||||||
|
|
||||||
ISEQ_COMPILE_DATA_ALLOC(iseq);
|
ISEQ_COMPILE_DATA_ALLOC(iseq);
|
||||||
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
|
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
|
||||||
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, Qnil);
|
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, Qnil);
|
||||||
|
@ -894,7 +897,17 @@ rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE rea
|
||||||
}
|
}
|
||||||
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, node ? nd_node_id(node) : -1, parent, isolated_depth, type, &new_opt);
|
VALUE script_lines = Qnil;
|
||||||
|
|
||||||
|
if (ast && !FIXNUM_P(ast->script_lines) && ast->script_lines) {
|
||||||
|
script_lines = ast->script_lines;
|
||||||
|
}
|
||||||
|
else if (parent) {
|
||||||
|
script_lines = parent->body->variable.script_lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1,
|
||||||
|
parent, isolated_depth, type, script_lines, &new_opt);
|
||||||
|
|
||||||
rb_iseq_compile_node(iseq, node);
|
rb_iseq_compile_node(iseq, node);
|
||||||
finish_iseq_build(iseq);
|
finish_iseq_build(iseq);
|
||||||
|
@ -913,7 +926,7 @@ rb_iseq_new_with_callback(
|
||||||
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, -1, parent, 0, type, option);
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, 0, type, Qnil, option);
|
||||||
|
|
||||||
rb_iseq_compile_callback(iseq, ifunc);
|
rb_iseq_compile_callback(iseq, ifunc);
|
||||||
finish_iseq_build(iseq);
|
finish_iseq_build(iseq);
|
||||||
|
@ -1026,7 +1039,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, NUM2INT(node_id),
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
|
||||||
parent, 0, (enum iseq_type)iseq_type, &option);
|
parent, 0, (enum iseq_type)iseq_type, Qnil, &option);
|
||||||
|
|
||||||
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
|
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
|
||||||
|
|
||||||
|
@ -3680,6 +3693,26 @@ succ_index_lookup(const struct succ_index_table *sd, int x)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* iseq.script_lines -> array or nil
|
||||||
|
*
|
||||||
|
* It returns recorded script lines if it is availalble.
|
||||||
|
* The script lines are not limited to the iseq range, but
|
||||||
|
* are entire lines of the source file.
|
||||||
|
*
|
||||||
|
* Note that this is an API for ruby internal use, debugging,
|
||||||
|
* and research. Do not use this for any other purpose.
|
||||||
|
* The compatibility is not guaranteed.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
iseqw_script_lines(VALUE self)
|
||||||
|
{
|
||||||
|
const rb_iseq_t *iseq = iseqw_check(self);
|
||||||
|
return iseq->body->variable.script_lines;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Document-class: RubyVM::InstructionSequence
|
* Document-class: RubyVM::InstructionSequence
|
||||||
*
|
*
|
||||||
|
@ -3747,6 +3780,9 @@ Init_ISeq(void)
|
||||||
rb_define_singleton_method(rb_cISeq, "disassemble", iseqw_s_disasm, 1);
|
rb_define_singleton_method(rb_cISeq, "disassemble", iseqw_s_disasm, 1);
|
||||||
rb_define_singleton_method(rb_cISeq, "of", iseqw_s_of, 1);
|
rb_define_singleton_method(rb_cISeq, "of", iseqw_s_of, 1);
|
||||||
|
|
||||||
|
// script lines
|
||||||
|
rb_define_method(rb_cISeq, "script_lines", iseqw_script_lines, 0);
|
||||||
|
|
||||||
rb_undef_method(CLASS_OF(rb_cISeq), "translate");
|
rb_undef_method(CLASS_OF(rb_cISeq), "translate");
|
||||||
rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq");
|
rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq");
|
||||||
}
|
}
|
||||||
|
|
3
parse.y
3
parse.y
|
@ -6306,7 +6306,8 @@ yycompile0(VALUE arg)
|
||||||
cov = Qtrue;
|
cov = Qtrue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (p->keep_script_lines) {
|
|
||||||
|
if (p->keep_script_lines || ruby_vm_keep_script_lines) {
|
||||||
if (!p->debug_lines) {
|
if (!p->debug_lines) {
|
||||||
p->debug_lines = rb_ary_new();
|
p->debug_lines = rb_ary_new();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,4 +15,56 @@ class TestRubyVM < Test::Unit::TestCase
|
||||||
assert_raise(ArgumentError){ RubyVM.stat(:unknown) }
|
assert_raise(ArgumentError){ RubyVM.stat(:unknown) }
|
||||||
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {RubyVM.stat(:"\u{30eb 30d3 30fc}")}
|
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {RubyVM.stat(:"\u{30eb 30d3 30fc}")}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def parse_and_compile
|
||||||
|
script = <<~RUBY
|
||||||
|
a = 1
|
||||||
|
def foo
|
||||||
|
b = 2
|
||||||
|
end
|
||||||
|
1.times{
|
||||||
|
c = 3
|
||||||
|
}
|
||||||
|
RUBY
|
||||||
|
|
||||||
|
ast = RubyVM::AbstractSyntaxTree.parse(script)
|
||||||
|
iseq = RubyVM::InstructionSequence.compile(script)
|
||||||
|
|
||||||
|
[ast, iseq]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_keep_script_lines
|
||||||
|
prev_conf = RubyVM.keep_script_lines
|
||||||
|
|
||||||
|
# keep
|
||||||
|
RubyVM.keep_script_lines = true
|
||||||
|
|
||||||
|
ast, iseq = *parse_and_compile
|
||||||
|
|
||||||
|
lines = ast.script_lines
|
||||||
|
assert_equal Array, lines.class
|
||||||
|
|
||||||
|
lines = iseq.script_lines
|
||||||
|
assert_equal Array, lines.class
|
||||||
|
iseq.each_child{|child|
|
||||||
|
assert_equal lines, child.script_lines
|
||||||
|
}
|
||||||
|
|
||||||
|
# don't keep
|
||||||
|
RubyVM.keep_script_lines = false
|
||||||
|
|
||||||
|
ast, iseq = *parse_and_compile
|
||||||
|
|
||||||
|
lines = ast.script_lines
|
||||||
|
assert_equal nil, lines
|
||||||
|
|
||||||
|
lines = iseq.script_lines
|
||||||
|
assert_equal nil, lines
|
||||||
|
iseq.each_child{|child|
|
||||||
|
assert_equal lines, child.script_lines
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure
|
||||||
|
RubyVM.keep_script_lines = prev_conf
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
39
vm.c
39
vm.c
|
@ -380,6 +380,7 @@ VALUE rb_block_param_proxy;
|
||||||
VALUE ruby_vm_const_missing_count = 0;
|
VALUE ruby_vm_const_missing_count = 0;
|
||||||
rb_vm_t *ruby_current_vm_ptr = NULL;
|
rb_vm_t *ruby_current_vm_ptr = NULL;
|
||||||
rb_ractor_t *ruby_single_main_ractor;
|
rb_ractor_t *ruby_single_main_ractor;
|
||||||
|
bool ruby_vm_keep_script_lines;
|
||||||
|
|
||||||
#ifdef RB_THREAD_LOCAL_SPECIFIER
|
#ifdef RB_THREAD_LOCAL_SPECIFIER
|
||||||
RB_THREAD_LOCAL_SPECIFIER rb_execution_context_t *ruby_current_ec;
|
RB_THREAD_LOCAL_SPECIFIER rb_execution_context_t *ruby_current_ec;
|
||||||
|
@ -3338,6 +3339,41 @@ vm_mtbl2(VALUE self, VALUE obj, VALUE sym)
|
||||||
return Qnil;
|
return Qnil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* RubyVM.keep_script_lines -> true or false
|
||||||
|
*
|
||||||
|
* Return current +keep_script_lines+ status. Now it only returns
|
||||||
|
* +true+ of +false+, but it can return other objects in future.
|
||||||
|
*
|
||||||
|
* Note that this is an API for ruby internal use, debugging,
|
||||||
|
* and research. Do not use this for any other purpose.
|
||||||
|
* The compatibility is not guaranteed.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
vm_keep_script_lines(VALUE self)
|
||||||
|
{
|
||||||
|
return RBOOL(ruby_vm_keep_script_lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* call-seq:
|
||||||
|
* RubyVM.keep_script_lines = true / false
|
||||||
|
*
|
||||||
|
* It set +keep_script_lines+ flag. If the flag is set, all
|
||||||
|
* loaded scripts are recorded in a interpreter process.
|
||||||
|
*
|
||||||
|
* Note that this is an API for ruby internal use, debugging,
|
||||||
|
* and research. Do not use this for any other purpose.
|
||||||
|
* The compatibility is not guaranteed.
|
||||||
|
*/
|
||||||
|
static VALUE
|
||||||
|
vm_keep_script_lines_set(VALUE self, VALUE flags)
|
||||||
|
{
|
||||||
|
ruby_vm_keep_script_lines = RTEST(flags);
|
||||||
|
return flags;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Init_VM(void)
|
Init_VM(void)
|
||||||
{
|
{
|
||||||
|
@ -3361,6 +3397,9 @@ Init_VM(void)
|
||||||
rb_undef_alloc_func(rb_cRubyVM);
|
rb_undef_alloc_func(rb_cRubyVM);
|
||||||
rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
|
rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
|
||||||
rb_define_singleton_method(rb_cRubyVM, "stat", vm_stat, -1);
|
rb_define_singleton_method(rb_cRubyVM, "stat", vm_stat, -1);
|
||||||
|
rb_define_singleton_method(rb_cRubyVM, "keep_script_lines", vm_keep_script_lines, 0);
|
||||||
|
rb_define_singleton_method(rb_cRubyVM, "keep_script_lines=", vm_keep_script_lines_set, 1);
|
||||||
|
|
||||||
#if USE_DEBUG_COUNTER
|
#if USE_DEBUG_COUNTER
|
||||||
rb_define_singleton_method(rb_cRubyVM, "reset_debug_counters", rb_debug_counter_reset, 0);
|
rb_define_singleton_method(rb_cRubyVM, "reset_debug_counters", rb_debug_counter_reset, 0);
|
||||||
rb_define_singleton_method(rb_cRubyVM, "show_debug_counters", rb_debug_counter_show, 0);
|
rb_define_singleton_method(rb_cRubyVM, "show_debug_counters", rb_debug_counter_show, 0);
|
||||||
|
|
|
@ -433,6 +433,7 @@ struct rb_iseq_constant_body {
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
rb_snum_t flip_count;
|
rb_snum_t flip_count;
|
||||||
|
VALUE script_lines;
|
||||||
VALUE coverage;
|
VALUE coverage;
|
||||||
VALUE pc2branchindex;
|
VALUE pc2branchindex;
|
||||||
VALUE *original_iseq;
|
VALUE *original_iseq;
|
||||||
|
|
Loading…
Reference in a new issue