2012-01-14 07:56:46 -05:00
|
|
|
require 'test/unit'
|
2016-09-27 04:35:31 -04:00
|
|
|
require 'tempfile'
|
2012-01-14 07:56:46 -05:00
|
|
|
|
|
|
|
class TestISeq < Test::Unit::TestCase
|
|
|
|
ISeq = RubyVM::InstructionSequence
|
|
|
|
|
|
|
|
def test_no_linenum
|
|
|
|
bug5894 = '[ruby-dev:45130]'
|
|
|
|
assert_normal_exit('p RubyVM::InstructionSequence.compile("1", "mac", "", 0).to_a', bug5894)
|
|
|
|
end
|
2012-06-09 04:21:52 -04:00
|
|
|
|
2015-10-22 22:58:22 -04:00
|
|
|
def compile(src, line = nil, opt = nil)
|
2016-02-19 02:58:09 -05:00
|
|
|
EnvUtil.suppress_warning do
|
2016-03-18 22:49:21 -04:00
|
|
|
ISeq.new(src, __FILE__, __FILE__, line, opt)
|
2016-02-19 02:58:09 -05:00
|
|
|
end
|
2015-10-22 22:58:22 -04:00
|
|
|
end
|
|
|
|
|
2013-02-28 09:04:53 -05:00
|
|
|
def lines src
|
2015-10-22 22:58:22 -04:00
|
|
|
body = compile(src).to_a[13]
|
Use Integer instead of Fixnum and Bignum.
* object.c, numeric.c, enum.c, ext/-test-/bignum/mul.c,
lib/rexml/quickpath.rb, lib/rexml/text.rb, lib/rexml/xpath_parser.rb,
lib/rubygems/specification.rb, lib/uri/generic.rb,
bootstraptest/test_eval.rb, basictest/test.rb,
test/-ext-/bignum/test_big2str.rb, test/-ext-/bignum/test_div.rb,
test/-ext-/bignum/test_mul.rb, test/-ext-/bignum/test_str2big.rb,
test/csv/test_data_converters.rb, test/date/test_date.rb,
test/json/test_json_generate.rb, test/minitest/test_minitest_mock.rb,
test/openssl/test_cipher.rb, test/rexml/test_jaxen.rb,
test/ruby/test_array.rb, test/ruby/test_basicinstructions.rb,
test/ruby/test_bignum.rb, test/ruby/test_case.rb,
test/ruby/test_class.rb, test/ruby/test_complex.rb,
test/ruby/test_enum.rb, test/ruby/test_eval.rb,
test/ruby/test_iseq.rb, test/ruby/test_literal.rb,
test/ruby/test_math.rb, test/ruby/test_module.rb,
test/ruby/test_numeric.rb, test/ruby/test_range.rb,
test/ruby/test_rational.rb, test/ruby/test_refinement.rb,
test/ruby/test_rubyvm.rb, test/ruby/test_struct.rb,
test/ruby/test_variable.rb, test/rubygems/test_gem_specification.rb,
test/thread/test_queue.rb: Use Integer instead of Fixnum and Bignum.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55029 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2016-05-17 09:15:57 -04:00
|
|
|
body.find_all{|e| e.kind_of? Integer}
|
2013-02-28 09:04:53 -05:00
|
|
|
end
|
|
|
|
|
2016-08-21 20:02:59 -04:00
|
|
|
def test_allocate
|
|
|
|
assert_raise(TypeError) {ISeq.allocate}
|
|
|
|
end
|
|
|
|
|
2013-02-28 06:26:23 -05:00
|
|
|
def test_to_a_lines
|
|
|
|
src = <<-EOS
|
|
|
|
p __LINE__ # 1
|
|
|
|
p __LINE__ # 2
|
|
|
|
# 3
|
|
|
|
p __LINE__ # 4
|
|
|
|
EOS
|
2013-02-28 09:04:53 -05:00
|
|
|
assert_equal [1, 2, 4], lines(src)
|
|
|
|
|
|
|
|
src = <<-EOS
|
|
|
|
# 1
|
|
|
|
p __LINE__ # 2
|
|
|
|
# 3
|
|
|
|
p __LINE__ # 4
|
|
|
|
# 5
|
|
|
|
EOS
|
|
|
|
assert_equal [2, 4], lines(src)
|
|
|
|
|
|
|
|
src = <<-EOS
|
|
|
|
1 # should be optimized out
|
|
|
|
2 # should be optimized out
|
|
|
|
p __LINE__ # 3
|
|
|
|
p __LINE__ # 4
|
|
|
|
5 # should be optimized out
|
|
|
|
6 # should be optimized out
|
|
|
|
p __LINE__ # 7
|
|
|
|
8 # should be optimized out
|
|
|
|
9
|
|
|
|
EOS
|
|
|
|
assert_equal [3, 4, 7, 9], lines(src)
|
2013-02-28 06:26:23 -05:00
|
|
|
end
|
|
|
|
|
2012-06-09 04:21:52 -04:00
|
|
|
def test_unsupport_type
|
2016-03-18 22:49:21 -04:00
|
|
|
ary = compile("p").to_a
|
2012-06-09 04:21:52 -04:00
|
|
|
ary[9] = :foobar
|
2016-03-18 22:49:21 -04:00
|
|
|
assert_raise_with_message(TypeError, /:foobar/) {ISeq.load(ary)}
|
2012-06-09 04:21:52 -04:00
|
|
|
end if defined?(RubyVM::InstructionSequence.load)
|
2012-06-09 10:36:56 -04:00
|
|
|
|
2015-09-09 23:17:30 -04:00
|
|
|
def test_loaded_cdhash_mark
|
2015-10-22 22:58:22 -04:00
|
|
|
iseq = compile(<<-'end;', __LINE__+1)
|
2015-09-09 23:17:30 -04:00
|
|
|
def bug(kw)
|
|
|
|
case kw
|
|
|
|
when "false" then false
|
|
|
|
when "true" then true
|
|
|
|
when "nil" then nil
|
|
|
|
else raise("unhandled argument: #{kw.inspect}")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
assert_separately([], <<-"end;")
|
|
|
|
iseq = #{iseq.to_a.inspect}
|
|
|
|
RubyVM::InstructionSequence.load(iseq).eval
|
|
|
|
assert_equal(false, bug("false"))
|
|
|
|
GC.start
|
|
|
|
assert_equal(false, bug("false"))
|
|
|
|
end;
|
|
|
|
end if defined?(RubyVM::InstructionSequence.load)
|
|
|
|
|
2021-05-21 12:13:55 -04:00
|
|
|
def test_cdhash_after_roundtrip
|
|
|
|
# CDHASH was not built properly when loading from binary and
|
|
|
|
# was causing opt_case_dispatch to clobber its stack canary
|
|
|
|
# for its "leaf" instruction attribute.
|
|
|
|
iseq = compile(<<~EOF)
|
|
|
|
case Class.new(String).new("foo")
|
|
|
|
when "foo"
|
|
|
|
42
|
|
|
|
end
|
|
|
|
EOF
|
|
|
|
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
|
|
|
|
end
|
|
|
|
|
2021-10-06 16:55:02 -04:00
|
|
|
def test_lambda_with_ractor_roundtrip
|
|
|
|
iseq = compile(<<~EOF)
|
|
|
|
x = 42
|
|
|
|
y = lambda { x }
|
|
|
|
Ractor.make_shareable(y)
|
|
|
|
y.call
|
|
|
|
EOF
|
|
|
|
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
|
|
|
|
end
|
|
|
|
|
2021-10-29 04:34:09 -04:00
|
|
|
def test_ractor_unshareable_outer_variable
|
|
|
|
name = "\u{2603 26a1}"
|
|
|
|
y = eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
|
|
|
|
assert_raise_with_message(ArgumentError, /\(#{name}\)/) do
|
|
|
|
Ractor.make_shareable(y)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-06-09 10:36:56 -04:00
|
|
|
def test_disasm_encoding
|
2014-11-23 12:03:32 -05:00
|
|
|
src = "\u{3042} = 1; \u{3042}; \u{3043}"
|
2015-10-22 22:58:22 -04:00
|
|
|
asm = compile(src).disasm
|
2014-11-23 12:03:32 -05:00
|
|
|
assert_equal(src.encoding, asm.encoding)
|
|
|
|
assert_predicate(asm, :valid_encoding?)
|
2012-06-10 02:49:16 -04:00
|
|
|
src.encode!(Encoding::Shift_JIS)
|
2015-10-22 22:58:22 -04:00
|
|
|
asm = compile(src).disasm
|
2014-11-23 12:03:32 -05:00
|
|
|
assert_equal(src.encoding, asm.encoding)
|
|
|
|
assert_predicate(asm, :valid_encoding?)
|
2018-01-11 03:34:55 -05:00
|
|
|
|
|
|
|
obj = Object.new
|
|
|
|
name = "\u{2603 26a1}"
|
|
|
|
obj.instance_eval("def #{name}; tap {}; end")
|
|
|
|
assert_include(RubyVM::InstructionSequence.of(obj.method(name)).disasm, name)
|
2012-06-09 10:36:56 -04:00
|
|
|
end
|
2012-11-30 12:00:30 -05:00
|
|
|
|
2020-11-18 14:39:59 -05:00
|
|
|
def test_compile_file_encoding
|
|
|
|
Tempfile.create(%w"test_iseq .rb") do |f|
|
|
|
|
f.puts "{ '\u00de' => 'Th', '\u00df' => 'ss', '\u00e0' => 'a' }"
|
|
|
|
f.close
|
|
|
|
|
2020-11-19 01:41:38 -05:00
|
|
|
EnvUtil.with_default_external(Encoding::US_ASCII) do
|
|
|
|
assert_warn('') {
|
|
|
|
load f.path
|
|
|
|
}
|
|
|
|
assert_nothing_raised(SyntaxError) {
|
|
|
|
RubyVM::InstructionSequence.compile_file(f.path)
|
|
|
|
}
|
2020-11-18 14:39:59 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2012-12-01 15:07:58 -05:00
|
|
|
LINE_BEFORE_METHOD = __LINE__
|
|
|
|
def method_test_line_trace
|
|
|
|
|
2016-02-19 02:48:02 -05:00
|
|
|
_a = 1
|
2012-12-01 15:07:58 -05:00
|
|
|
|
2016-02-19 02:48:02 -05:00
|
|
|
_b = 2
|
2012-12-01 15:07:58 -05:00
|
|
|
|
|
|
|
end
|
|
|
|
|
2012-11-30 12:00:30 -05:00
|
|
|
def test_line_trace
|
2016-03-18 22:49:21 -04:00
|
|
|
iseq = compile \
|
2012-11-30 12:00:30 -05:00
|
|
|
%q{ a = 1
|
|
|
|
b = 2
|
|
|
|
c = 3
|
|
|
|
# d = 4
|
|
|
|
e = 5
|
|
|
|
# f = 6
|
|
|
|
g = 7
|
|
|
|
|
|
|
|
}
|
|
|
|
assert_equal([1, 2, 3, 5, 7], iseq.line_trace_all)
|
|
|
|
iseq.line_trace_specify(1, true) # line 2
|
|
|
|
iseq.line_trace_specify(3, true) # line 5
|
|
|
|
|
|
|
|
result = []
|
|
|
|
TracePoint.new(:specified_line){|tp|
|
|
|
|
result << tp.lineno
|
|
|
|
}.enable{
|
|
|
|
iseq.eval
|
|
|
|
}
|
|
|
|
assert_equal([2, 5], result)
|
2012-12-01 15:07:58 -05:00
|
|
|
|
|
|
|
iseq = ISeq.of(self.class.instance_method(:method_test_line_trace))
|
|
|
|
assert_equal([LINE_BEFORE_METHOD + 3, LINE_BEFORE_METHOD + 5], iseq.line_trace_all)
|
2012-12-21 08:36:17 -05:00
|
|
|
end if false # TODO: now, it is only for C APIs.
|
2012-11-30 13:02:43 -05:00
|
|
|
|
|
|
|
LINE_OF_HERE = __LINE__
|
|
|
|
def test_location
|
|
|
|
iseq = ISeq.of(method(:test_location))
|
|
|
|
|
|
|
|
assert_equal(__FILE__, iseq.path)
|
2013-12-13 04:18:05 -05:00
|
|
|
assert_match(/#{__FILE__}/, iseq.absolute_path)
|
2012-11-30 13:02:43 -05:00
|
|
|
assert_equal("test_location", iseq.label)
|
|
|
|
assert_equal("test_location", iseq.base_label)
|
|
|
|
assert_equal(LINE_OF_HERE+1, iseq.first_lineno)
|
|
|
|
|
|
|
|
line = __LINE__
|
|
|
|
iseq = ISeq.of(Proc.new{})
|
|
|
|
assert_equal(__FILE__, iseq.path)
|
2013-12-13 04:18:05 -05:00
|
|
|
assert_match(/#{__FILE__}/, iseq.absolute_path)
|
2012-11-30 13:02:43 -05:00
|
|
|
assert_equal("test_location", iseq.base_label)
|
|
|
|
assert_equal("block in test_location", iseq.label)
|
|
|
|
assert_equal(line+1, iseq.first_lineno)
|
|
|
|
end
|
2013-11-27 00:39:38 -05:00
|
|
|
|
|
|
|
def test_label_fstring
|
|
|
|
c = Class.new{ def foobar() end }
|
|
|
|
|
|
|
|
a, b = eval("# encoding: us-ascii\n'foobar'.freeze"),
|
2013-11-27 01:03:37 -05:00
|
|
|
ISeq.of(c.instance_method(:foobar)).label
|
|
|
|
assert_same a, b
|
2013-11-27 00:39:38 -05:00
|
|
|
end
|
2014-12-16 20:20:22 -05:00
|
|
|
|
2014-12-17 19:03:30 -05:00
|
|
|
def test_disable_opt
|
2014-12-16 20:20:22 -05:00
|
|
|
src = "a['foo'] = a['bar']; 'a'.freeze"
|
2015-10-22 22:58:22 -04:00
|
|
|
body= compile(src, __LINE__, false).to_a[13]
|
2014-12-16 20:20:22 -05:00
|
|
|
body.each{|insn|
|
2015-10-22 21:49:38 -04:00
|
|
|
next unless Array === insn
|
2014-12-16 20:20:22 -05:00
|
|
|
op = insn.first
|
|
|
|
assert(!op.to_s.match(/^opt_/), "#{op}")
|
|
|
|
}
|
|
|
|
end
|
2015-05-19 05:54:44 -04:00
|
|
|
|
|
|
|
def test_invalid_source
|
|
|
|
bug11159 = '[ruby-core:69219] [Bug #11159]'
|
2016-03-18 22:49:21 -04:00
|
|
|
assert_raise(TypeError, bug11159) {compile(nil)}
|
|
|
|
assert_raise(TypeError, bug11159) {compile(:foo)}
|
|
|
|
assert_raise(TypeError, bug11159) {compile(1)}
|
2015-05-19 05:54:44 -04:00
|
|
|
end
|
2015-08-21 16:47:53 -04:00
|
|
|
|
|
|
|
def test_frozen_string_literal_compile_option
|
|
|
|
$f = 'f'
|
|
|
|
line = __LINE__ + 2
|
|
|
|
code = <<-'EOS'
|
2015-10-08 23:58:34 -04:00
|
|
|
['foo', 'foo', "#{$f}foo", "#{'foo'}"]
|
2015-08-21 16:47:53 -04:00
|
|
|
EOS
|
2015-10-22 22:58:22 -04:00
|
|
|
s1, s2, s3, s4 = compile(code, line, {frozen_string_literal: true}).eval
|
2015-10-09 00:15:53 -04:00
|
|
|
assert_predicate(s1, :frozen?)
|
|
|
|
assert_predicate(s2, :frozen?)
|
2020-08-31 15:24:36 -04:00
|
|
|
assert_not_predicate(s3, :frozen?)
|
2020-09-02 10:12:22 -04:00
|
|
|
assert_not_predicate(s4, :frozen?)
|
2015-08-21 16:47:53 -04:00
|
|
|
end
|
2015-10-22 22:58:22 -04:00
|
|
|
|
2017-09-24 20:09:44 -04:00
|
|
|
# Safe call chain is not optimized when Coverage is running.
|
|
|
|
# So we can test it only when Coverage is not running.
|
2015-10-22 22:58:22 -04:00
|
|
|
def test_safe_call_chain
|
2015-11-05 22:39:23 -05:00
|
|
|
src = "a&.a&.a&.a&.a&.a"
|
2015-10-22 22:58:22 -04:00
|
|
|
body = compile(src, __LINE__, {peephole_optimization: true}).to_a[13]
|
|
|
|
labels = body.select {|op, arg| op == :branchnil}.map {|op, arg| arg}
|
|
|
|
assert_equal(1, labels.uniq.size)
|
2017-09-24 20:09:44 -04:00
|
|
|
end if (!defined?(Coverage) || !Coverage.running?)
|
2016-01-13 02:56:51 -05:00
|
|
|
|
|
|
|
def test_parent_iseq_mark
|
2016-01-18 21:52:37 -05:00
|
|
|
assert_separately([], <<-'end;', timeout: 20)
|
2016-01-13 02:56:51 -05:00
|
|
|
->{
|
|
|
|
->{
|
|
|
|
->{
|
|
|
|
eval <<-EOS
|
|
|
|
class Segfault
|
|
|
|
define_method :segfault do
|
|
|
|
x = nil
|
|
|
|
GC.disable
|
|
|
|
1000.times do |n|
|
|
|
|
n.times do
|
|
|
|
x = (foo rescue $!).local_variables
|
|
|
|
end
|
|
|
|
GC.start
|
|
|
|
end
|
|
|
|
x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
EOS
|
|
|
|
}.call
|
|
|
|
}.call
|
|
|
|
}.call
|
|
|
|
at_exit { assert_equal([:n, :x], Segfault.new.segfault.sort) }
|
|
|
|
end;
|
|
|
|
end
|
2016-03-19 01:46:20 -04:00
|
|
|
|
|
|
|
def test_syntax_error_message
|
|
|
|
feature11951 = '[Feature #11951]'
|
|
|
|
|
|
|
|
src, line = <<-'end;', __LINE__+1
|
|
|
|
def x@;end
|
|
|
|
def y@;end
|
|
|
|
end;
|
|
|
|
e1 = e2 = nil
|
|
|
|
m1 = EnvUtil.verbose_warning do
|
|
|
|
e1 = assert_raise(SyntaxError) do
|
|
|
|
eval(src, nil, __FILE__, line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
m2 = EnvUtil.verbose_warning do
|
|
|
|
e2 = assert_raise(SyntaxError) do
|
|
|
|
ISeq.new(src, __FILE__, __FILE__, line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert_equal([m1, e1.message], [m2, e2.message], feature11951)
|
2019-05-25 22:33:40 -04:00
|
|
|
message = e1.message.each_line
|
|
|
|
message.with_index(1) do |line, i|
|
|
|
|
next if /^ / =~ line
|
|
|
|
assert_send([line, :start_with?, __FILE__],
|
|
|
|
proc {message.map {|l, j| (i == j ? ">" : " ") + l}.join("")})
|
|
|
|
end
|
2016-03-19 01:46:20 -04:00
|
|
|
end
|
2016-07-13 01:26:00 -04:00
|
|
|
|
2016-09-27 04:35:31 -04:00
|
|
|
def test_compile_file_error
|
|
|
|
Tempfile.create(%w"test_iseq .rb") do |f|
|
|
|
|
f.puts "end"
|
|
|
|
f.close
|
|
|
|
path = f.path
|
2019-01-30 23:35:14 -05:00
|
|
|
assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected `end'/, [], success: true)
|
2016-09-27 04:35:31 -04:00
|
|
|
begin;
|
|
|
|
path = ARGV[0]
|
|
|
|
begin
|
|
|
|
RubyVM::InstructionSequence.compile_file(path)
|
|
|
|
rescue SyntaxError => e
|
|
|
|
puts e.message
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-07-13 01:26:00 -04:00
|
|
|
def test_translate_by_object
|
|
|
|
assert_separately([], <<-"end;")
|
|
|
|
class Object
|
|
|
|
def translate
|
|
|
|
end
|
|
|
|
end
|
|
|
|
assert_equal(0, eval("0"))
|
|
|
|
end;
|
|
|
|
end
|
2017-06-12 00:35:53 -04:00
|
|
|
|
|
|
|
def test_inspect
|
|
|
|
%W[foo \u{30d1 30b9}].each do |name|
|
2019-06-01 07:44:24 -04:00
|
|
|
assert_match(/@#{name}/, ISeq.compile("", name).inspect, name)
|
2017-06-12 00:35:53 -04:00
|
|
|
m = ISeq.compile("class TestISeq::Inspect; def #{name}; end; instance_method(:#{name}); end").eval
|
2019-06-01 07:44:24 -04:00
|
|
|
assert_match(/:#{name}@/, ISeq.of(m).inspect, name)
|
2017-06-12 00:35:53 -04:00
|
|
|
end
|
|
|
|
end
|
2017-12-23 07:48:24 -05:00
|
|
|
|
2018-11-06 01:36:51 -05:00
|
|
|
def strip_lineno(source)
|
|
|
|
source.gsub(/^.*?: /, "")
|
|
|
|
end
|
|
|
|
|
2017-12-23 09:46:59 -05:00
|
|
|
def sample_iseq
|
2018-11-06 01:36:51 -05:00
|
|
|
ISeq.compile(strip_lineno(<<-EOS))
|
2017-12-23 09:46:59 -05:00
|
|
|
1: class C
|
|
|
|
2: def foo
|
|
|
|
3: begin
|
|
|
|
4: rescue
|
|
|
|
5: p :rescue
|
|
|
|
6: ensure
|
|
|
|
7: p :ensure
|
|
|
|
8: end
|
|
|
|
9: end
|
|
|
|
10: def bar
|
|
|
|
11: 1.times{
|
|
|
|
12: 2.times{
|
|
|
|
13: }
|
|
|
|
14: }
|
|
|
|
15: end
|
|
|
|
16: end
|
|
|
|
17: class D < C
|
|
|
|
18: end
|
2017-12-23 07:48:24 -05:00
|
|
|
EOS
|
2017-12-23 09:46:59 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def test_each_child
|
|
|
|
iseq = sample_iseq
|
2017-12-23 07:48:24 -05:00
|
|
|
|
|
|
|
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
|
2017-12-23 09:46:59 -05:00
|
|
|
|
|
|
|
def test_trace_points
|
|
|
|
collect_iseq = lambda{|iseq|
|
|
|
|
iseqs = []
|
|
|
|
iseq.each_child{|child_iseq|
|
|
|
|
iseqs << collect_iseq.call(child_iseq)
|
|
|
|
}
|
|
|
|
[["#{iseq.label}@#{iseq.first_lineno}", iseq.trace_points], *iseqs.sort_by{|k, *| k}]
|
|
|
|
}
|
|
|
|
assert_equal [["<compiled>@1", [[1, :line],
|
|
|
|
[17, :line]]],
|
|
|
|
[["<class:C>@1", [[1, :class],
|
|
|
|
[2, :line],
|
|
|
|
[10, :line],
|
|
|
|
[16, :end]]],
|
|
|
|
[["bar@10", [[10, :call],
|
|
|
|
[11, :line],
|
|
|
|
[15, :return]]],
|
|
|
|
[["block in bar@11", [[11, :b_call],
|
|
|
|
[12, :line],
|
|
|
|
[14, :b_return]]],
|
|
|
|
[["block (2 levels) in bar@12", [[12, :b_call],
|
|
|
|
[13, :b_return]]]]]],
|
|
|
|
[["foo@2", [[2, :call],
|
|
|
|
[4, :line],
|
|
|
|
[7, :line],
|
|
|
|
[9, :return]]],
|
|
|
|
[["ensure in foo@2", [[7, :line]]]],
|
|
|
|
[["rescue in foo@4", [[5, :line]]]]]],
|
|
|
|
[["<class:D>@17", [[17, :class],
|
|
|
|
[18, :end]]]]], collect_iseq.call(sample_iseq)
|
|
|
|
end
|
2017-12-24 19:27:17 -05:00
|
|
|
|
|
|
|
def test_empty_iseq_lineno
|
|
|
|
iseq = ISeq.compile(<<-EOS)
|
|
|
|
# 1
|
|
|
|
# 2
|
|
|
|
def foo # line 3 empty method
|
|
|
|
end # line 4
|
|
|
|
1.time do # line 5 empty block
|
|
|
|
end # line 6
|
|
|
|
class C # line 7 empty class
|
|
|
|
end
|
|
|
|
EOS
|
|
|
|
|
|
|
|
iseq.each_child{|ci|
|
|
|
|
ary = ci.to_a
|
|
|
|
type = ary[9]
|
|
|
|
name = ary[5]
|
|
|
|
line = ary[13].first
|
2019-06-28 02:04:25 -04:00
|
|
|
case type
|
2017-12-24 19:27:17 -05:00
|
|
|
when :method
|
|
|
|
assert_equal "foo", name
|
|
|
|
assert_equal 3, line
|
|
|
|
when :class
|
|
|
|
assert_equal '<class:C>', name
|
|
|
|
assert_equal 7, line
|
|
|
|
when :block
|
|
|
|
assert_equal 'block in <compiled>', name
|
|
|
|
assert_equal 5, line
|
|
|
|
else
|
|
|
|
raise "unknown ary: " + ary.inspect
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
2018-02-28 18:31:42 -05:00
|
|
|
|
2018-04-06 23:48:45 -04:00
|
|
|
def hexdump(bin)
|
|
|
|
bin.unpack1("H*").gsub(/.{1,32}/) {|s|
|
|
|
|
"#{'%04x:' % $~.begin(0)}#{s.gsub(/../, " \\&").tap{|_|_[24]&&="-"}}\n"
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2018-04-05 09:13:00 -04:00
|
|
|
def assert_iseq_to_binary(code, mesg = nil)
|
2018-03-15 22:27:50 -04:00
|
|
|
iseq = RubyVM::InstructionSequence.compile(code)
|
2018-04-05 09:13:00 -04:00
|
|
|
bin = assert_nothing_raised(mesg) do
|
2018-03-16 08:06:55 -04:00
|
|
|
iseq.to_binary
|
|
|
|
rescue RuntimeError => e
|
|
|
|
skip e.message if /compile with coverage/ =~ e.message
|
|
|
|
raise
|
|
|
|
end
|
2018-04-05 09:13:00 -04:00
|
|
|
10.times do
|
2018-04-06 23:48:45 -04:00
|
|
|
bin2 = iseq.to_binary
|
|
|
|
assert_equal(bin, bin2, message(mesg) {diff hexdump(bin), hexdump(bin2)})
|
2018-04-05 09:13:00 -04:00
|
|
|
end
|
2018-03-21 06:06:23 -04:00
|
|
|
iseq2 = RubyVM::InstructionSequence.load_from_binary(bin)
|
2018-04-06 23:48:45 -04:00
|
|
|
a1 = iseq.to_a
|
|
|
|
a2 = iseq2.to_a
|
|
|
|
assert_equal(a1, a2, message(mesg) {diff iseq.disassemble, iseq2.disassemble})
|
2021-10-28 12:03:29 -04:00
|
|
|
if iseq2.script_lines
|
|
|
|
assert_kind_of(Array, iseq2.script_lines)
|
|
|
|
else
|
|
|
|
assert_nil(iseq2.script_lines)
|
|
|
|
end
|
2018-04-05 09:13:00 -04:00
|
|
|
iseq2
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_with_objects
|
|
|
|
assert_iseq_to_binary("[]"+100.times.map{|i|"<</#{i}/"}.join)
|
2018-04-16 08:46:08 -04:00
|
|
|
assert_iseq_to_binary("@x ||= (1..2)")
|
2018-04-05 09:13:00 -04:00
|
|
|
end
|
|
|
|
|
2019-08-07 22:15:45 -04:00
|
|
|
def test_to_binary_pattern_matching
|
2019-05-20 21:29:34 -04:00
|
|
|
code = "case foo; in []; end"
|
2019-08-07 22:15:45 -04:00
|
|
|
iseq = compile(code)
|
|
|
|
assert_include(iseq.disasm, "TypeError")
|
|
|
|
assert_include(iseq.disasm, "NoMatchingPatternError")
|
|
|
|
EnvUtil.suppress_warning do
|
|
|
|
assert_iseq_to_binary(code, "[Feature #14912]")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-10-02 03:05:40 -04:00
|
|
|
def test_to_binary_dumps_nokey
|
|
|
|
iseq = assert_iseq_to_binary(<<-RUBY)
|
|
|
|
o = Object.new
|
|
|
|
class << o
|
|
|
|
def foo(**nil); end
|
|
|
|
end
|
|
|
|
o
|
|
|
|
RUBY
|
|
|
|
assert_equal([[:nokey]], iseq.eval.singleton_method(:foo).parameters)
|
|
|
|
end
|
|
|
|
|
2018-04-05 09:13:00 -04:00
|
|
|
def test_to_binary_line_info
|
|
|
|
assert_iseq_to_binary("#{<<~"begin;"}\n#{<<~'end;'}", '[Bug #14660]').eval
|
|
|
|
begin;
|
|
|
|
class P
|
|
|
|
def p; end
|
|
|
|
def q; end
|
|
|
|
E = ""
|
|
|
|
N = "#{E}"
|
|
|
|
attr_reader :i
|
|
|
|
end
|
|
|
|
end;
|
2020-01-27 23:43:30 -05:00
|
|
|
|
|
|
|
# cleanup
|
|
|
|
::Object.class_eval do
|
|
|
|
remove_const :P
|
|
|
|
end
|
2018-02-28 18:31:42 -05:00
|
|
|
end
|
2018-08-23 00:12:14 -04:00
|
|
|
|
2018-11-06 01:36:51 -05:00
|
|
|
def collect_from_binary_tracepoint_lines(tracepoint_type, filename)
|
|
|
|
iseq = RubyVM::InstructionSequence.compile(strip_lineno(<<-RUBY), filename)
|
|
|
|
class A
|
|
|
|
class B
|
|
|
|
2.times {
|
|
|
|
def self.foo
|
2019-06-29 20:34:41 -04:00
|
|
|
_a = 'good day'
|
2018-11-06 01:36:51 -05:00
|
|
|
raise
|
|
|
|
rescue
|
|
|
|
'dear reader'
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
B.foo
|
|
|
|
end
|
|
|
|
RUBY
|
|
|
|
|
2018-08-23 00:12:14 -04:00
|
|
|
iseq_bin = iseq.to_binary
|
2019-06-29 20:55:50 -04:00
|
|
|
iseq = ISeq.load_from_binary(iseq_bin)
|
2018-11-06 01:36:51 -05:00
|
|
|
lines = []
|
|
|
|
TracePoint.new(tracepoint_type){|tp|
|
2018-08-23 00:12:14 -04:00
|
|
|
next unless tp.path == filename
|
2018-11-06 01:36:51 -05:00
|
|
|
lines << tp.lineno
|
2018-08-23 00:12:14 -04:00
|
|
|
}.enable{
|
2019-06-29 20:55:50 -04:00
|
|
|
EnvUtil.suppress_warning {iseq.eval}
|
2018-08-23 00:12:14 -04:00
|
|
|
}
|
2018-11-06 01:36:51 -05:00
|
|
|
|
|
|
|
lines
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_line_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:line, filename)
|
|
|
|
|
|
|
|
assert_equal [1, 2, 3, 4, 4, 12, 5, 6, 8], lines, '[Bug #14702]'
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_class_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:class, filename)
|
|
|
|
|
|
|
|
assert_equal [1, 2], lines, '[Bug #14702]'
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_end_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:end, filename)
|
|
|
|
|
|
|
|
assert_equal [11, 13], lines, '[Bug #14702]'
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_return_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:return, filename)
|
|
|
|
|
|
|
|
assert_equal [9], lines, '[Bug #14702]'
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_b_call_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:b_call, filename)
|
|
|
|
|
|
|
|
assert_equal [3, 3], lines, '[Bug #14702]'
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_to_binary_b_return_tracepoint
|
|
|
|
filename = "#{File.basename(__FILE__)}_#{__LINE__}"
|
|
|
|
lines = collect_from_binary_tracepoint_lines(:b_return, filename)
|
|
|
|
|
|
|
|
assert_equal [10, 10], lines, '[Bug #14702]'
|
2018-08-23 00:12:14 -04:00
|
|
|
end
|
2018-12-06 05:52:27 -05:00
|
|
|
|
|
|
|
def test_iseq_of
|
|
|
|
[proc{},
|
|
|
|
method(:test_iseq_of),
|
|
|
|
RubyVM::InstructionSequence.compile("p 1", __FILE__)].each{|src|
|
|
|
|
iseq = RubyVM::InstructionSequence.of(src)
|
|
|
|
assert_equal __FILE__, iseq.path
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
def test_iseq_of_twice_for_same_code
|
|
|
|
[proc{},
|
|
|
|
method(:test_iseq_of_twice_for_same_code),
|
|
|
|
RubyVM::InstructionSequence.compile("p 1")].each{|src|
|
|
|
|
iseq1 = RubyVM::InstructionSequence.of(src)
|
|
|
|
iseq2 = RubyVM::InstructionSequence.of(src)
|
|
|
|
|
|
|
|
# ISeq objects should be same for same src
|
|
|
|
assert_equal iseq1.object_id, iseq2.object_id
|
|
|
|
}
|
|
|
|
end
|
2019-11-10 00:40:38 -05:00
|
|
|
|
|
|
|
def test_iseq_builtin_to_a
|
2020-06-24 03:29:53 -04:00
|
|
|
invokebuiltin = eval(EnvUtil.invoke_ruby(['-e', <<~EOS], '', true).first)
|
|
|
|
insns = RubyVM::InstructionSequence.of([].method(:pack)).to_a.last
|
|
|
|
p insns.find { |insn| insn.is_a?(Array) && insn[0] == :opt_invokebuiltin_delegate_leave }
|
|
|
|
EOS
|
2019-11-10 00:40:38 -05:00
|
|
|
assert_not_nil(invokebuiltin)
|
|
|
|
assert_equal([:func_ptr, :argc, :index, :name], invokebuiltin[1].keys)
|
|
|
|
end
|
2020-04-15 01:27:38 -04:00
|
|
|
|
2020-11-30 01:19:49 -05:00
|
|
|
def test_iseq_builtin_load
|
|
|
|
Tempfile.create(["builtin", ".iseq"]) do |f|
|
|
|
|
f.binmode
|
|
|
|
f.write(RubyVM::InstructionSequence.of(1.method(:abs)).to_binary)
|
|
|
|
f.close
|
|
|
|
assert_separately(["-", f.path], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
|
|
begin;
|
|
|
|
bin = File.binread(ARGV[0])
|
|
|
|
assert_raise(ArgumentError) do
|
|
|
|
RubyVM::InstructionSequence.load_from_binary(bin)
|
|
|
|
end
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-15 01:27:38 -04:00
|
|
|
def test_iseq_option_debug_level
|
|
|
|
assert_raise(TypeError) {ISeq.compile("", debug_level: "")}
|
|
|
|
assert_ruby_status([], "#{<<~"begin;"}\n#{<<~'end;'}")
|
|
|
|
begin;
|
|
|
|
RubyVM::InstructionSequence.compile("", debug_level: 5)
|
|
|
|
end;
|
|
|
|
end
|
2012-01-14 07:56:46 -05:00
|
|
|
end
|