From 74e1bca79d703f2658ac9cfa6d990e7e77c59757 Mon Sep 17 00:00:00 2001 From: Koichi Sasada 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 --- tool/mk_builtin_loader.rb | 77 ++++++++++++++++++++++++++++++----------------- 1 file changed, 50 insertions(+), 27 deletions(-) (limited to 'tool') diff --git a/tool/mk_builtin_loader.rb b/tool/mk_builtin_loader.rb index 1aa52c671c..cec0556f7a 100644 --- a/tool/mk_builtin_loader.rb +++ b/tool/mk_builtin_loader.rb @@ -49,37 +49,28 @@ def make_cfunc_name inlines, name, lineno 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 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 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 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 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 v1.2.3