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

ruby-changes:62118

From: Koichi <ko1@a...>
Date: Sat, 4 Jul 2020 17:29:22 +0900 (JST)
Subject: [ruby-changes:62118] 74e1bca79d (master): support all locals for cexpr!, cstmt!

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

From 74e1bca79d703f2658ac9cfa6d990e7e77c59757 Mon Sep 17 00:00:00 2001
From: Koichi Sasada <ko1@a...>
Date: Sat, 4 Jul 2020 17:23:34 +0900
Subject: support all locals for cexpr!, cstmt!

Primitve.cexpr! and .cstmt! can access Ruby's parameter and
*local variables* (note that local parameters are also local
variables). However recent changes only allow to access
parameters. This patch fix it.

For example, the following code can work:

def foo a, b, k: :kw, **kwrest
  c = a + b
  d = k
  e = kwrest
  p Primitive.cstmt!(%q(rb_p(rb_ary_new_from_args(5, a, b, c, d, e));
                        return Qnil;))
end

diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb
index 1aa52c6..cec0556 100644
--- a/tool/mk_builtin_loader.rb
+++ b/tool/mk_builtin_loader.rb
@@ -49,37 +49,28 @@ def make_cfunc_name inlines, name, lineno https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L49
   end
 end
 
-def collect_params tree
-  while tree
-    case tree.first
-    when :params
-      params = []
-      _, mand, opt, rest, post, kwds, kwrest, block = tree
-      mand.each {|_, v| params << v.to_sym} if mand
-      opt.each {|(_, v), | params << v.to_sym} if opt
-      params << rest[1][1].to_sym if rest
-      post.each {|_, v| params << v.to_sym} if post
-      params << kwrest[1][1].to_sym if kwrest
-      params << block[1][1].to_sym if block
-      return params
-    when :paren
-      tree = tree[1]
-    else
-      raise "unknown sexp: #{tree.first}"
+def collect_locals tree
+  type, name, (line, cols) = tree
+  if locals = LOCALS_DB[[name, line]]
+    locals
+  else
+    if false # for debugging
+      pp LOCALS_DB
+      raise "not found: [#{name}, #{line}]"
     end
   end
 end
 
-def collect_builtin base, tree, name, bs, inlines, params = nil
+def collect_builtin base, tree, name, bs, inlines, locals = nil
   while tree
     call = recv = sep = mid = args = nil
     case tree.first
     when :def
-      params = collect_params(tree[2])
+      locals = collect_locals(tree[1])
       tree = tree[3]
       next
     when :defs
-      params = collect_params(tree[4])
+      locals = collect_locals(tree[3])
       tree = tree[5]
       next
     when :class
@@ -146,7 +137,7 @@ def collect_builtin base, tree, name, bs, inlines, params = nil https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L137
 
             func_name = "_bi#{inlines.size}"
             cfunc_name = make_cfunc_name(inlines, name, lineno)
-            inlines[cfunc_name] = [lineno, text, params, func_name]
+            inlines[cfunc_name] = [lineno, text, locals, func_name]
             argc -= 1
           when 'cexpr', 'cconst'
             text = inline_text argc, args.first
@@ -155,8 +146,8 @@ def collect_builtin base, tree, name, bs, inlines, params = nil https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L146
             func_name = "_bi#{inlines.size}"
             cfunc_name = make_cfunc_name(inlines, name, lineno)
 
-            params = [] if $1 == 'cconst'
-            inlines[cfunc_name] = [lineno, code, params, func_name]
+            locals = [] if $1 == 'cconst'
+            inlines[cfunc_name] = [lineno, code, locals, func_name]
             argc -= 1
           when 'cinit'
             text = inline_text argc, args.first
@@ -177,21 +168,53 @@ def collect_builtin base, tree, name, bs, inlines, params = nil https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L168
     end
 
     tree.each do |t|
-      collect_builtin base, t, name, bs, inlines, params if Array === t
+      collect_builtin base, t, name, bs, inlines, locals if Array === t
     end
     break
   end
 end
+
 # ruby mk_builtin_loader.rb TARGET_FILE.rb
 # #=> generate TARGET_FILE.rbinc
 #
 
+LOCALS_DB = {} # [method_name, first_line] = locals
+
+def collect_iseq iseq_ary
+  # iseq_ary.each_with_index{|e, i| p [i, e]}
+  label = iseq_ary[5]
+  first_line = iseq_ary[8]
+  type = iseq_ary[9]
+  locals = iseq_ary[10]
+  insns = iseq_ary[13]
+
+  if type == :method
+    LOCALS_DB[[label, first_line].freeze] = locals
+  end
+
+  insns.each{|insn|
+    case insn
+    when Integer
+      # ignore
+    when Array
+      # p insn.shift # insn name
+      insn.each{|op|
+        if Array === op && op[0] == "YARVInstructionSequence/SimpleDataFormat"
+          collect_iseq op
+        end
+      }
+    end
+  }
+end
+
 def mk_builtin_header file
   base = File.basename(file, '.rb')
   ofile = "#{file}inc"
 
   # bs = { func_name => argc }
-  collect_builtin(base, Ripper.sexp(File.read(file)), 'top', bs = {}, inlines = {})
+  code = File.read(file)
+  collect_iseq RubyVM::InstructionSequence.compile(code).to_a
+  collect_builtin(base, Ripper.sexp(code), 'top', bs = {}, inlines = {})
 
   begin
     f = open(ofile, 'w')
@@ -219,12 +242,12 @@ def mk_builtin_header file https://github.com/ruby/ruby/blob/trunk/tool/mk_builtin_loader.rb#L242
     lineno = __LINE__ - lineno - 1
     line_file = file
 
-    inlines.each{|cfunc_name, (body_lineno, text, params, func_name)|
+    inlines.each{|cfunc_name, (body_lineno, text, locals, func_name)|
       if String === cfunc_name
         f.puts "static VALUE #{cfunc_name}(struct rb_execution_context_struct *ec, const VALUE self) {"
         lineno += 1
 
-        params.reverse_each.with_index{|param, i|
+        locals.reverse_each.with_index{|param, i|
           next unless Symbol === param
           f.puts "MAYBE_UNUSED(const VALUE) #{param} = rb_vm_lvar(ec, #{-3 - i});"
           lineno += 1
-- 
cgit v0.10.2


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

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