summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common.mk1
-rw-r--r--compile.c2
-rw-r--r--internal/compile.h1
-rw-r--r--iseq.c44
-rw-r--r--parse.y3
-rw-r--r--test/ruby/test_rubyvm.rb52
-rw-r--r--vm.c39
-rw-r--r--vm_core.h1
8 files changed, 137 insertions, 6 deletions
diff --git a/common.mk b/common.mk
index bc0fd462ea..c703a6da48 100644
--- a/common.mk
+++ b/common.mk
@@ -7194,6 +7194,7 @@ iseq.$(OBJEXT): {$(VPATH)}node.h
iseq.$(OBJEXT): {$(VPATH)}node_name.inc
iseq.$(OBJEXT): {$(VPATH)}onigmo.h
iseq.$(OBJEXT): {$(VPATH)}oniguruma.h
+iseq.$(OBJEXT): {$(VPATH)}ractor.h
iseq.$(OBJEXT): {$(VPATH)}ruby_assert.h
iseq.$(OBJEXT): {$(VPATH)}ruby_atomic.h
iseq.$(OBJEXT): {$(VPATH)}st.h
diff --git a/compile.c b/compile.c
index 71e078d20c..055d4e85f5 100644
--- a/compile.c
+++ b/compile.c
@@ -1328,7 +1328,7 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node,
ast.root = node;
ast.compile_option = 0;
- ast.script_lines = INT2FIX(-1);
+ ast.script_lines = iseq->body->variable.script_lines;
debugs("[new_child_iseq]> ---------------------------------------\n");
int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth;
diff --git a/internal/compile.h b/internal/compile.h
index 932dce2744..d32c2233c9 100644
--- a/internal/compile.h
+++ b/internal/compile.h
@@ -25,6 +25,7 @@ st_index_t rb_iseq_cdhash_hash(VALUE a);
/* iseq.c */
int rb_vm_insn_addr2insn(const void *);
int rb_vm_insn_decode(const VALUE encoded);
+extern bool ruby_vm_keep_script_lines;
MJIT_SYMBOL_EXPORT_BEGIN
/* iseq.c (export) */
diff --git a/iseq.c b/iseq.c
index 68611798c7..0551e0c99a 100644
--- a/iseq.c
+++ b/iseq.c
@@ -595,7 +595,8 @@ new_arena(void)
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, 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 err_info = Qnil;
@@ -616,6 +617,8 @@ prepare_iseq_build(rb_iseq_t *iseq,
ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
body->variable.flip_count = 0;
+ RB_OBJ_WRITE(iseq, &body->variable.script_lines, script_lines);
+
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)->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);
- 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);
finish_iseq_build(iseq);
@@ -913,7 +926,7 @@ rb_iseq_new_with_callback(
rb_iseq_t *iseq = iseq_alloc();
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);
finish_iseq_build(iseq);
@@ -1026,7 +1039,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, 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);
@@ -3680,6 +3693,26 @@ succ_index_lookup(const struct succ_index_table *sd, int x)
}
#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
*
@@ -3747,6 +3780,9 @@ Init_ISeq(void)
rb_define_singleton_method(rb_cISeq, "disassemble", iseqw_s_disasm, 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), "load_iseq");
}
diff --git a/parse.y b/parse.y
index 9f44ff6235..2ec9d7770c 100644
--- a/parse.y
+++ b/parse.y
@@ -6306,7 +6306,8 @@ yycompile0(VALUE arg)
cov = Qtrue;
}
}
- if (p->keep_script_lines) {
+
+ if (p->keep_script_lines || ruby_vm_keep_script_lines) {
if (!p->debug_lines) {
p->debug_lines = rb_ary_new();
}
diff --git a/test/ruby/test_rubyvm.rb b/test/ruby/test_rubyvm.rb
index 67d46e27ad..fecaacfa78 100644
--- a/test/ruby/test_rubyvm.rb
+++ b/test/ruby/test_rubyvm.rb
@@ -15,4 +15,56 @@ class TestRubyVM < Test::Unit::TestCase
assert_raise(ArgumentError){ RubyVM.stat(:unknown) }
assert_raise_with_message(ArgumentError, /\u{30eb 30d3 30fc}/) {RubyVM.stat(:"\u{30eb 30d3 30fc}")}
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
diff --git a/vm.c b/vm.c
index c1099d699b..1f60906d1a 100644
--- a/vm.c
+++ b/vm.c
@@ -380,6 +380,7 @@ VALUE rb_block_param_proxy;
VALUE ruby_vm_const_missing_count = 0;
rb_vm_t *ruby_current_vm_ptr = NULL;
rb_ractor_t *ruby_single_main_ractor;
+bool ruby_vm_keep_script_lines;
#ifdef RB_THREAD_LOCAL_SPECIFIER
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;
}
+/*
+ * 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
Init_VM(void)
{
@@ -3361,6 +3397,9 @@ Init_VM(void)
rb_undef_alloc_func(rb_cRubyVM);
rb_undef_method(CLASS_OF(rb_cRubyVM), "new");
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
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);
diff --git a/vm_core.h b/vm_core.h
index 11af8a365d..8519da8f9e 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -433,6 +433,7 @@ struct rb_iseq_constant_body {
struct {
rb_snum_t flip_count;
+ VALUE script_lines;
VALUE coverage;
VALUE pc2branchindex;
VALUE *original_iseq;