[前][次][番号順一覧][スレッド一覧]

ruby-changes:69304

From: Koichi <ko1@a...>
Date: Thu, 21 Oct 2021 16:18:04 +0900 (JST)
Subject: [ruby-changes:69304] c7550537f1 (master): `RubyVM.keep_script_lines`

https://git.ruby-lang.org/ruby.git/commit/?id=c7550537f1

From c7550537f11dcf6450a9d3df3af3fa1f4fe05b15 Mon Sep 17 00:00:00 2001
From: Koichi Sasada <ko1@a...>
Date: Thu, 30 Sep 2021 16:58:46 +0900
Subject: `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
```
---
 common.mk                |  1 +
 compile.c                |  2 +-
 internal/compile.h       |  1 +
 iseq.c                   | 44 ++++++++++++++++++++++++++++++++++++----
 parse.y                  |  3 ++-
 test/ruby/test_rubyvm.rb | 52 ++++++++++++++++++++++++++++++++++++++++++++++++
 vm.c                     | 39 ++++++++++++++++++++++++++++++++++++
 vm_core.h                |  1 +
 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 https://github.com/ruby/ruby/blob/trunk/common.mk#L7194
 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, https://github.com/ruby/ruby/blob/trunk/compile.c#L1328
 
     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); https://github.com/ruby/ruby/blob/trunk/internal/compile.h#L25
 /* 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) https://github.com/ruby/ruby/blob/trunk/iseq.c#L595
 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, https://github.com/ruby/ruby/blob/trunk/iseq.c#L617
     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 https://github.com/ruby/ruby/blob/trunk/iseq.c#L897
     }
     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( https://github.com/ruby/ruby/blob/trunk/iseq.c#L926
     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) https://github.com/ruby/ruby/blob/trunk/iseq.c#L1039
     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) https://github.com/ruby/ruby/blob/trunk/iseq.c#L3693
 }
 #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) https://github.com/ruby/ruby/blob/trunk/iseq.c#L3780
     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) https://github.com/ruby/ruby/blob/trunk/parse.y#L6306
 	    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 https://github.com/ruby/ruby/blob/trunk/test/ruby/test_rubyvm.rb#L15
     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; https://github.com/ruby/ruby/blob/trunk/vm.c#L380
 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) https://github.com/ruby/ruby/blob/trunk/vm.c#L3339
     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  (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]