summaryrefslogtreecommitdiff
path: root/test/ruby/test_iseq.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_iseq.rb')
-rw-r--r--test/ruby/test_iseq.rb235
1 files changed, 216 insertions, 19 deletions
diff --git a/test/ruby/test_iseq.rb b/test/ruby/test_iseq.rb
index edd131823e..dc84d8bd7c 100644
--- a/test/ruby/test_iseq.rb
+++ b/test/ruby/test_iseq.rb
@@ -10,13 +10,16 @@ class TestISeq < Test::Unit::TestCase
end
def compile(src, line = nil, opt = nil)
+ unless line
+ line = caller_locations(1).first.lineno
+ end
EnvUtil.suppress_warning do
ISeq.new(src, __FILE__, __FILE__, line, opt)
end
end
- def lines src
- body = compile(src).to_a[13]
+ def lines src, lines = nil
+ body = compile(src, lines).to_a[13]
body.find_all{|e| e.kind_of? Integer}
end
@@ -25,24 +28,22 @@ class TestISeq < Test::Unit::TestCase
end
def test_to_a_lines
- src = <<-EOS
+ assert_equal [__LINE__+1, __LINE__+2, __LINE__+4], lines(<<-EOS, __LINE__+1)
p __LINE__ # 1
p __LINE__ # 2
# 3
p __LINE__ # 4
EOS
- assert_equal [1, 2, 4], lines(src)
- src = <<-EOS
+ assert_equal [__LINE__+2, __LINE__+4], lines(<<-EOS, __LINE__+1)
# 1
p __LINE__ # 2
# 3
p __LINE__ # 4
# 5
EOS
- assert_equal [2, 4], lines(src)
- src = <<-EOS
+ assert_equal [__LINE__+3, __LINE__+4, __LINE__+7, __LINE__+9], lines(<<~EOS, __LINE__+1)
1 # should be optimized out
2 # should be optimized out
p __LINE__ # 3
@@ -53,10 +54,9 @@ class TestISeq < Test::Unit::TestCase
8 # should be optimized out
9
EOS
- assert_equal [3, 4, 7, 9], lines(src)
end
- def test_unsupport_type
+ def test_unsupported_type
ary = compile("p").to_a
ary[9] = :foobar
assert_raise_with_message(TypeError, /:foobar/) {ISeq.load(ary)}
@@ -86,7 +86,7 @@ class TestISeq < Test::Unit::TestCase
# 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)
+ iseq = compile(<<~EOF, __LINE__+1)
case Class.new(String).new("foo")
when "foo"
42
@@ -95,18 +95,80 @@ class TestISeq < Test::Unit::TestCase
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
end
+ def test_super_with_block
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(*) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_block_hash_0
+ iseq = compile(<<~EOF, __LINE__+1)
+ # [Bug #18250] `req` specifically cause `Assertion failed: (key != 0), function hash_table_raw_insert`
+ def (Object.new).touch(req, *)
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_super_with_block_and_kwrest
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(**) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
def test_lambda_with_ractor_roundtrip
- iseq = compile(<<~EOF)
+ iseq = compile(<<~EOF, __LINE__+1)
x = 42
- y = lambda { x }
+ y = nil.instance_eval{ lambda { x } }
Ractor.make_shareable(y)
y.call
EOF
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
end
+ def test_super_with_anonymous_block
+ iseq = compile(<<~EOF, __LINE__+1)
+ def (Object.new).touch(&) # :nodoc:
+ foo { super }
+ end
+ 42
+ EOF
+ assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
+ end
+
+ def test_ractor_unshareable_outer_variable
+ name = "\u{2603 26a1}"
+ y = nil.instance_eval do
+ eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
+ end
+ assert_raise_with_message(ArgumentError, /\(#{name}\)/) do
+ Ractor.make_shareable(y)
+ end
+ y = nil.instance_eval do
+ eval("proc {#{name} = []; proc {|x| #{name}}}").call
+ end
+ assert_raise_with_message(Ractor::IsolationError, /'#{name}'/) do
+ Ractor.make_shareable(y)
+ end
+ obj = Object.new
+ def obj.foo(*) nil.instance_eval{ ->{super} } end
+ assert_raise_with_message(Ractor::IsolationError, /refer unshareable object \[\] from variable '\*'/) do
+ Ractor.make_shareable(obj.foo)
+ end
+ end
+
def test_disasm_encoding
- src = "\u{3042} = 1; \u{3042}; \u{3043}"
+ src = +"\u{3042} = 1; \u{3042}; \u{3043}"
asm = compile(src).disasm
assert_equal(src.encoding, asm.encoding)
assert_predicate(asm, :valid_encoding?)
@@ -147,16 +209,16 @@ class TestISeq < Test::Unit::TestCase
end
def test_line_trace
- iseq = compile \
- %q{ a = 1
+ iseq = compile(<<~EOF, __LINE__+1)
+ a = 1
b = 2
c = 3
# d = 4
e = 5
# f = 6
g = 7
+ EOF
- }
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
@@ -293,12 +355,19 @@ class TestISeq < Test::Unit::TestCase
end
end
+ # [Bug #19173]
+ def test_compile_error
+ assert_raise SyntaxError do
+ RubyVM::InstructionSequence.compile 'using Module.new; yield'
+ end
+ end
+
def test_compile_file_error
Tempfile.create(%w"test_iseq .rb") do |f|
f.puts "end"
f.close
path = f.path
- assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected `end'/, [], success: true)
+ assert_in_out_err(%W[- #{path}], "#{<<-"begin;"}\n#{<<-"end;"}", /unexpected 'end'/, [], success: true)
begin;
path = ARGV[0]
begin
@@ -328,6 +397,30 @@ class TestISeq < Test::Unit::TestCase
end
end
+ def anon_star(*); end
+
+ def test_anon_rest_param_in_disasm
+ iseq = RubyVM::InstructionSequence.of(method(:anon_star))
+ param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
+ assert_equal [:*], param_names
+ end
+
+ def anon_keyrest(**); end
+
+ def test_anon_keyrest_param_in_disasm
+ iseq = RubyVM::InstructionSequence.of(method(:anon_keyrest))
+ param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
+ assert_equal [:**], param_names
+ end
+
+ def anon_block(&); end
+
+ def test_anon_block_param_in_disasm
+ iseq = RubyVM::InstructionSequence.of(method(:anon_block))
+ param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
+ assert_equal [:&], param_names
+ end
+
def strip_lineno(source)
source.gsub(/^.*?: /, "")
end
@@ -404,7 +497,8 @@ class TestISeq < Test::Unit::TestCase
[7, :line],
[9, :return]]],
[["ensure in foo@2", [[7, :line]]]],
- [["rescue in foo@4", [[5, :line]]]]]],
+ [["rescue in foo@4", [[5, :line],
+ [5, :rescue]]]]]],
[["<class:D>@17", [[17, :class],
[18, :end]]]]], collect_iseq.call(sample_iseq)
end
@@ -453,7 +547,7 @@ class TestISeq < Test::Unit::TestCase
bin = assert_nothing_raised(mesg) do
iseq.to_binary
rescue RuntimeError => e
- skip e.message if /compile with coverage/ =~ e.message
+ omit e.message if /compile with coverage/ =~ e.message
raise
end
10.times do
@@ -464,9 +558,31 @@ class TestISeq < Test::Unit::TestCase
a1 = iseq.to_a
a2 = iseq2.to_a
assert_equal(a1, a2, message(mesg) {diff iseq.disassemble, iseq2.disassemble})
+ if iseq2.script_lines
+ assert_kind_of(Array, iseq2.script_lines)
+ else
+ assert_nil(iseq2.script_lines)
+ end
iseq2
end
+ def test_to_binary_with_hidden_local_variables
+ assert_iseq_to_binary("for foo in bar; end")
+
+ bin = RubyVM::InstructionSequence.compile(<<-RUBY).to_binary
+ Object.new.instance_eval do
+ a = []
+ def self.bar; [1] end
+ for foo in bar
+ a << (foo * 2)
+ end
+ a
+ end
+ RUBY
+ v = RubyVM::InstructionSequence.load_from_binary(bin).eval
+ assert_equal([2], v)
+ end
+
def test_to_binary_with_objects
assert_iseq_to_binary("[]"+100.times.map{|i|"<</#{i}/"}.join)
assert_iseq_to_binary("@x ||= (1..2)")
@@ -539,6 +655,8 @@ class TestISeq < Test::Unit::TestCase
}
lines
+ ensure
+ Object.send(:remove_const, :A) rescue nil
end
def test_to_binary_line_tracepoint
@@ -635,4 +753,83 @@ class TestISeq < Test::Unit::TestCase
RubyVM::InstructionSequence.compile("", debug_level: 5)
end;
end
+
+ def test_mandatory_only
+ assert_separately [], <<~RUBY
+ at0 = Time.at(0)
+ assert_equal at0, Time.public_send(:at, 0, 0)
+ RUBY
+ end
+
+ def test_mandatory_only_redef
+ assert_separately ['-W0'], <<~RUBY
+ r = Ractor.new{
+ Float(10)
+ module Kernel
+ undef Float
+ def Float(n)
+ :new
+ end
+ end
+ GC.start
+ Float(30)
+ }
+ assert_equal :new, r.take
+ RUBY
+ end
+
+ def test_ever_condition_loop
+ assert_ruby_status([], "BEGIN {exit}; while true && true; end")
+ end
+
+ def test_unreachable_syntax_error
+ mesg = /Invalid break/
+ assert_syntax_error("false and break", mesg)
+ assert_syntax_error("if false and break; end", mesg)
+ end
+
+ def test_unreachable_pattern_matching
+ assert_in_out_err([], "#{<<~"begin;"}\n#{<<~'end;'}", %w[1])
+ begin;
+ if true or {a: 0} in {a:}
+ p 1
+ else
+ p a
+ end
+ end;
+ end
+
+ def test_loading_kwargs_memory_leak
+ assert_no_memory_leak([], "#{<<~"begin;"}", "#{<<~'end;'}", rss: true)
+ a = RubyVM::InstructionSequence.compile("foo(bar: :baz)").to_binary
+ begin;
+ 1_000_000.times do
+ RubyVM::InstructionSequence.load_from_binary(a)
+ end
+ end;
+ end
+
+ def test_ibf_bignum
+ iseq = RubyVM::InstructionSequence.compile("0x0"+"_0123_4567_89ab_cdef"*5)
+ expected = iseq.eval
+ result = RubyVM::InstructionSequence.load_from_binary(iseq.to_binary).eval
+ assert_equal expected, result, proc {sprintf("expected: %x, result: %x", expected, result)}
+ end
+
+ def test_compile_prism_with_file
+ Tempfile.create(%w"test_iseq .rb") do |f|
+ f.puts "name = 'Prism'; puts 'hello'"
+ f.close
+
+ assert_nothing_raised(TypeError) do
+ RubyVM::InstructionSequence.compile_prism(f)
+ end
+ end
+ end
+
+ def test_compile_prism_with_invalid_object_type
+ assert_raise(TypeError) do
+ RubyVM::InstructionSequence.compile_prism(Object.new)
+ end
+ end
end