2012-01-14 07:56:46 -05:00
|
|
|
require 'test/unit'
|
|
|
|
|
|
|
|
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)
|
|
|
|
RubyVM::InstructionSequence.new(src, __FILE__, __FILE__, line, opt)
|
|
|
|
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]
|
2014-03-04 08:55:24 -05:00
|
|
|
body.find_all{|e| e.kind_of? Fixnum}
|
2013-02-28 09:04:53 -05:00
|
|
|
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
|
|
|
|
ary = RubyVM::InstructionSequence.compile("p").to_a
|
|
|
|
ary[9] = :foobar
|
2013-10-09 04:43:12 -04:00
|
|
|
assert_raise_with_message(TypeError, /:foobar/) {RubyVM::InstructionSequence.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)
|
|
|
|
|
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?)
|
2012-06-09 10:36:56 -04:00
|
|
|
end
|
2012-11-30 12:00:30 -05:00
|
|
|
|
2012-12-01 15:07:58 -05:00
|
|
|
LINE_BEFORE_METHOD = __LINE__
|
|
|
|
def method_test_line_trace
|
|
|
|
|
|
|
|
a = 1
|
|
|
|
|
|
|
|
b = 2
|
|
|
|
|
|
|
|
end
|
|
|
|
|
2012-11-30 12:00:30 -05:00
|
|
|
def test_line_trace
|
|
|
|
iseq = ISeq.compile \
|
|
|
|
%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]'
|
|
|
|
assert_raise(TypeError, bug11159) {ISeq.compile(nil)}
|
|
|
|
assert_raise(TypeError, bug11159) {ISeq.compile(:foo)}
|
|
|
|
assert_raise(TypeError, bug11159) {ISeq.compile(1)}
|
|
|
|
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?)
|
2015-11-09 03:32:37 -05:00
|
|
|
assert_predicate(s3, :frozen?)
|
2015-10-09 00:15:53 -04:00
|
|
|
assert_predicate(s4, :frozen?)
|
2015-08-21 16:47:53 -04:00
|
|
|
end
|
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)
|
|
|
|
end
|
2016-01-13 02:56:51 -05:00
|
|
|
|
|
|
|
def test_parent_iseq_mark
|
|
|
|
assert_separately([], <<-'end;')
|
|
|
|
->{
|
|
|
|
->{
|
|
|
|
->{
|
|
|
|
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
|
2012-01-14 07:56:46 -05:00
|
|
|
end
|