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

ruby-changes:49308

From: ko1 <ko1@a...>
Date: Sat, 23 Dec 2017 21:48:29 +0900 (JST)
Subject: [ruby-changes:49308] ko1:r61425 (trunk): RubyVM::InstructionSequence#each_child.

ko1	2017-12-23 21:48:24 +0900 (Sat, 23 Dec 2017)

  New Revision: 61425

  https://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=61425

  Log:
    RubyVM::InstructionSequence#each_child.
    
    * iseq.c (iseqw_each_child): add RubyVM::InstructionSequence#each_child
      method for tools which want to manipulate ISeq.
    
    * test/ruby/test_iseq.rb: add a test for this method.

  Modified files:
    trunk/NEWS
    trunk/iseq.c
    trunk/test/ruby/test_iseq.rb
Index: iseq.c
===================================================================
--- iseq.c	(revision 61424)
+++ iseq.c	(revision 61425)
@@ -1786,6 +1786,46 @@ rb_iseq_disasm(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/iseq.c#L1786
     return str;
 }
 
+static VALUE
+rb_iseq_all_children(const rb_iseq_t *iseq)
+{
+    unsigned int i;
+    VALUE *code = rb_iseq_original_iseq(iseq);
+    VALUE all_children = rb_obj_hide(rb_ident_hash_new());
+    VALUE child;
+
+    if (iseq->body->catch_table) {
+	for (i = 0; i < iseq->body->catch_table->size; i++) {
+	    const struct iseq_catch_table_entry *entry = &iseq->body->catch_table->entries[i];
+	    child = (VALUE)entry->iseq;
+	    if (child) {
+		rb_hash_aset(all_children, child, Qtrue);
+	    }
+	}
+    }
+    for (i=0; i<iseq->body->iseq_size;) {
+	VALUE insn = code[i];
+	int len = insn_len(insn);
+	const char *types = insn_op_types(insn);
+	int j;
+
+	for (j=0; types[j]; j++) {
+	    switch (types[j]) {
+	      case TS_ISEQ:
+		child = code[i+j+1];
+		if (child) {
+		    rb_hash_aset(all_children, child, Qtrue);
+		}
+		break;
+	      default:
+		break;
+	    }
+	}
+	i += len;
+    }
+    return all_children;
+}
+
 /*
  *  call-seq:
  *     iseq.disasm -> str
@@ -1810,6 +1850,30 @@ iseqw_disasm(VALUE self) https://github.com/ruby/ruby/blob/trunk/iseq.c#L1850
     return rb_iseq_disasm(iseqw_check(self));
 }
 
+static int
+iseqw_each_child_i(VALUE key, VALUE value, VALUE dummy)
+{
+    rb_yield(iseqw_new((const rb_iseq_t *)key));
+    return ST_CONTINUE;
+}
+
+/*
+ *  call-seq:
+ *     iseq.each_child{|child_iseq| ...} -> iseq
+ *
+ *  Iterate all direct child instruction sequences.
+ *  Iteration order is implementation/version defined
+ *  so that people should not rely on the order.
+ */
+static VALUE
+iseqw_each_child(VALUE self)
+{
+    const rb_iseq_t *iseq = iseqw_check(self);
+    VALUE all_children = rb_iseq_all_children(iseq);
+    rb_hash_foreach(all_children, iseqw_each_child_i, Qnil);
+    return self;
+}
+
 /*
  *  Returns the instruction sequence containing the given proc or method.
  *
@@ -2615,6 +2679,7 @@ Init_ISeq(void) https://github.com/ruby/ruby/blob/trunk/iseq.c#L2679
     rb_define_method(rb_cISeq, "label", iseqw_label, 0);
     rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
     rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
+    rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0);
 
 #if 0 /* TBD */
     rb_define_private_method(rb_cISeq, "marshal_dump", iseqw_marshal_dump, 0);
Index: NEWS
===================================================================
--- NEWS	(revision 61424)
+++ NEWS	(revision 61425)
@@ -163,6 +163,12 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L163
 
   * Support new 5 emoji-related Unicode character properties
 
+* RubyVM::InstructionSequence
+
+  * New method:
+
+    * RubyVM::InstructionSequence#each_child
+
 * String
 
   * String#-@ deduplicates unfrozen strings.  Already-frozen
Index: test/ruby/test_iseq.rb
===================================================================
--- test/ruby/test_iseq.rb	(revision 61424)
+++ test/ruby/test_iseq.rb	(revision 61425)
@@ -280,4 +280,45 @@ class TestISeq < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_iseq.rb#L280
       assert_match /:#{name}@/, ISeq.of(m).inspect, name
     end
   end
+
+  def test_each_child
+    iseq = ISeq.compile <<-EOS
+    class C
+      def foo
+        begin
+        rescue
+          p :rescue
+        ensure
+          p :ensure
+        end
+      end
+      def bar
+        1.times{
+          2.times{
+          }
+        }
+      end
+    end
+    class D < C
+    end
+    EOS
+
+    collect_iseq = lambda{|iseq|
+      iseqs = []
+      iseq.each_child{|child_iseq|
+        iseqs << collect_iseq.call(child_iseq)
+      }
+      ["#{iseq.label}@#{iseq.first_lineno}", *iseqs.sort_by{|k, *| k}]
+    }
+
+    expected = ["<compiled>@1",
+                  ["<class:C>@1",
+                    ["bar@10", ["block in bar@11",
+                            ["block (2 levels) in bar@12"]]],
+                    ["foo@2", ["ensure in foo@2"],
+                              ["rescue in foo@4"]]],
+                  ["<class:D>@17"]]
+
+    assert_equal expected, collect_iseq.call(iseq)
+  end
 end

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

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