1# frozen_string_literal: false
2require 'test/unit'
3require 'objspace'
4
5class TestRubyOptimization < Test::Unit::TestCase
6  def assert_redefine_method(klass, method, code, msg = nil)
7    assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
8    begin;
9      class #{klass}
10        undef #{method}
11        def #{method}(*args)
12          args[0]
13        end
14      end
15      #{code}
16    end;
17  end
18
19  def disasm(name)
20    RubyVM::InstructionSequence.of(method(name)).disasm
21  end
22
23  def test_fixnum_plus
24    assert_equal 21, 10 + 11
25    assert_redefine_method('Integer', '+', 'assert_equal 11, 10 + 11')
26  end
27
28  def test_fixnum_minus
29    assert_equal 5, 8 - 3
30    assert_redefine_method('Integer', '-', 'assert_equal 3, 8 - 3')
31  end
32
33  def test_fixnum_mul
34    assert_equal 15, 3 * 5
35    assert_redefine_method('Integer', '*', 'assert_equal 5, 3 * 5')
36  end
37
38  def test_fixnum_div
39    assert_equal 3, 15 / 5
40    assert_redefine_method('Integer', '/', 'assert_equal 5, 15 / 5')
41  end
42
43  def test_fixnum_mod
44    assert_equal 1, 8 % 7
45    assert_redefine_method('Integer', '%', 'assert_equal 7, 8 % 7')
46  end
47
48  def test_fixnum_lt
49    assert_equal true, 1 < 2
50    assert_redefine_method('Integer', '<', 'assert_equal 2, 1 < 2')
51  end
52
53  def test_fixnum_le
54    assert_equal true, 1 <= 2
55    assert_redefine_method('Integer', '<=', 'assert_equal 2, 1 <= 2')
56  end
57
58  def test_fixnum_gt
59    assert_equal false, 1 > 2
60    assert_redefine_method('Integer', '>', 'assert_equal 2, 1 > 2')
61  end
62
63  def test_fixnum_ge
64    assert_equal false, 1 >= 2
65    assert_redefine_method('Integer', '>=', 'assert_equal 2, 1 >= 2')
66  end
67
68  def test_float_plus
69    assert_equal 4.0, 2.0 + 2.0
70    assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
71  end
72
73  def test_float_minus
74    assert_equal 4.0, 2.0 + 2.0
75    assert_redefine_method('Float', '+', 'assert_equal 2.0, 2.0 + 2.0')
76  end
77
78  def test_float_mul
79    assert_equal 29.25, 4.5 * 6.5
80    assert_redefine_method('Float', '*', 'assert_equal 6.5, 4.5 * 6.5')
81  end
82
83  def test_float_div
84    assert_in_delta 0.63063063063063063, 4.2 / 6.66
85    assert_redefine_method('Float', '/', 'assert_equal 6.66, 4.2 / 6.66', "[Bug #9238]")
86  end
87
88  def test_float_lt
89    assert_equal true, 1.1 < 2.2
90    assert_redefine_method('Float', '<', 'assert_equal 2.2, 1.1 < 2.2')
91  end
92
93  def test_float_le
94    assert_equal true, 1.1 <= 2.2
95    assert_redefine_method('Float', '<=', 'assert_equal 2.2, 1.1 <= 2.2')
96  end
97
98  def test_float_gt
99    assert_equal false, 1.1 > 2.2
100    assert_redefine_method('Float', '>', 'assert_equal 2.2, 1.1 > 2.2')
101  end
102
103  def test_float_ge
104    assert_equal false, 1.1 >= 2.2
105    assert_redefine_method('Float', '>=', 'assert_equal 2.2, 1.1 >= 2.2')
106  end
107
108  def test_string_length
109    assert_equal 6, "string".length
110    assert_redefine_method('String', 'length', 'assert_nil "string".length')
111  end
112
113  def test_string_size
114    assert_equal 6, "string".size
115    assert_redefine_method('String', 'size', 'assert_nil "string".size')
116  end
117
118  def test_string_empty?
119    assert_equal true, "".empty?
120    assert_equal false, "string".empty?
121    assert_redefine_method('String', 'empty?', 'assert_nil "string".empty?')
122  end
123
124  def test_string_plus
125    assert_equal "", "" + ""
126    assert_equal "x", "x" + ""
127    assert_equal "x", "" + "x"
128    assert_equal "ab", "a" + "b"
129    assert_redefine_method('String', '+', 'assert_equal "b", "a" + "b"')
130  end
131
132  def test_string_succ
133    assert_equal 'b', 'a'.succ
134    assert_equal 'B', 'A'.succ
135  end
136
137  def test_string_format
138    assert_equal '2', '%d' % 2
139    assert_redefine_method('String', '%', 'assert_equal 2, "%d" % 2')
140  end
141
142  def test_string_freeze
143    assert_equal "foo", "foo".freeze
144    assert_equal "foo".freeze.object_id, "foo".freeze.object_id
145    assert_redefine_method('String', 'freeze', 'assert_nil "foo".freeze')
146  end
147
148  def test_string_uminus
149    assert_same "foo".freeze, -"foo"
150    assert_redefine_method('String', '-@', 'assert_nil(-"foo")')
151  end
152
153  def test_string_freeze_saves_memory
154    n = 16384
155    data = '.'.freeze
156    r, w = IO.pipe
157    w.write data
158
159    s = r.readpartial(n, '')
160    assert_operator ObjectSpace.memsize_of(s), :>=, n,
161      'IO buffer NOT resized prematurely because will likely be reused'
162
163    s.freeze
164    assert_equal ObjectSpace.memsize_of(data), ObjectSpace.memsize_of(s),
165      'buffer resized on freeze since it cannot be written to again'
166  ensure
167    r.close if r
168    w.close if w
169  end
170
171  def test_string_eq_neq
172    %w(== !=).each do |m|
173      assert_redefine_method('String', m, <<-end)
174        assert_equal :b, ("a" #{m} "b").to_sym
175        b = 'b'
176        assert_equal :b, ("a" #{m} b).to_sym
177        assert_equal :b, (b #{m} "b").to_sym
178      end
179    end
180  end
181
182  def test_string_ltlt
183    assert_equal "", "" << ""
184    assert_equal "x", "x" << ""
185    assert_equal "x", "" << "x"
186    assert_equal "ab", "a" << "b"
187    assert_redefine_method('String', '<<', 'assert_equal "b", "a" << "b"')
188  end
189
190  def test_fixnum_and
191    assert_equal 1, 1&3
192    assert_redefine_method('Integer', '&', 'assert_equal 3, 1&3')
193  end
194
195  def test_fixnum_or
196    assert_equal 3, 1|3
197    assert_redefine_method('Integer', '|', 'assert_equal 1, 3|1')
198  end
199
200  def test_array_plus
201    assert_equal [1,2], [1]+[2]
202    assert_redefine_method('Array', '+', 'assert_equal [2], [1]+[2]')
203  end
204
205  def test_array_minus
206    assert_equal [2], [1,2] - [1]
207    assert_redefine_method('Array', '-', 'assert_equal [1], [1,2]-[1]')
208  end
209
210  def test_array_length
211    assert_equal 0, [].length
212    assert_equal 3, [1,2,3].length
213    assert_redefine_method('Array', 'length', 'assert_nil([].length); assert_nil([1,2,3].length)')
214  end
215
216  def test_array_empty?
217    assert_equal true, [].empty?
218    assert_equal false, [1,2,3].empty?
219    assert_redefine_method('Array', 'empty?', 'assert_nil([].empty?); assert_nil([1,2,3].empty?)')
220  end
221
222  def test_hash_length
223    assert_equal 0, {}.length
224    assert_equal 1, {1=>1}.length
225    assert_redefine_method('Hash', 'length', 'assert_nil({}.length); assert_nil({1=>1}.length)')
226  end
227
228  def test_hash_empty?
229    assert_equal true, {}.empty?
230    assert_equal false, {1=>1}.empty?
231    assert_redefine_method('Hash', 'empty?', 'assert_nil({}.empty?); assert_nil({1=>1}.empty?)')
232  end
233
234  def test_hash_aref_with
235    h = { "foo" => 1 }
236    assert_equal 1, h["foo"]
237    assert_redefine_method('Hash', '[]', "#{<<-"begin;"}\n#{<<~"end;"}")
238    begin;
239      h = { "foo" => 1 }
240      assert_equal "foo", h["foo"]
241    end;
242  end
243
244  def test_hash_aset_with
245    h = {}
246    assert_equal 1, h["foo"] = 1
247    assert_redefine_method('Hash', '[]=', "#{<<-"begin;"}\n#{<<~"end;"}")
248    begin;
249      h = {}
250      assert_equal 1, h["foo"] = 1, "assignment always returns value set"
251      assert_nil h["foo"]
252    end;
253  end
254
255  class MyObj
256    def ==(other)
257      true
258    end
259  end
260
261  def test_eq
262    assert_equal true, nil == nil
263    assert_equal true, 1 == 1
264    assert_equal true, 'string' == 'string'
265    assert_equal true, 1 == MyObj.new
266    assert_equal false, nil == MyObj.new
267    assert_equal true, MyObj.new == 1
268    assert_equal true, MyObj.new == nil
269  end
270
271  def self.tailcall(klass, src, file = nil, path = nil, line = nil, tailcall: true)
272    unless file
273      loc, = caller_locations(1, 1)
274      file = loc.path
275      line ||= loc.lineno
276    end
277    RubyVM::InstructionSequence.new("proc {|_|_.class_eval {#{src}}}",
278                                    file, (path || file), line,
279                                    tailcall_optimization: tailcall,
280                                    trace_instruction: false)
281      .eval[klass]
282  end
283
284  def tailcall(*args)
285    self.class.tailcall(singleton_class, *args)
286  end
287
288  def test_tailcall
289    bug4082 = '[ruby-core:33289]'
290
291    tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
292    begin;
293      def fact_helper(n, res)
294        if n == 1
295          res
296        else
297          fact_helper(n - 1, n * res)
298        end
299      end
300      def fact(n)
301        fact_helper(n, 1)
302      end
303    end;
304    assert_equal(9131, fact(3000).to_s.size, message(bug4082) {disasm(:fact_helper)})
305  end
306
307  def test_tailcall_with_block
308    bug6901 = '[ruby-dev:46065]'
309
310    tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
311    begin;
312      def identity(val)
313        val
314      end
315
316      def delay
317        -> {
318          identity(yield)
319        }
320      end
321    end;
322    assert_equal(123, delay { 123 }.call, message(bug6901) {disasm(:delay)})
323  end
324
325  def just_yield
326    yield
327  end
328
329  def test_tailcall_inhibited_by_block
330    tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
331    begin;
332      def yield_result
333        just_yield {:ok}
334      end
335    end;
336    assert_equal(:ok, yield_result, message {disasm(:yield_result)})
337  end
338
339  def do_raise
340    raise "should be rescued"
341  end
342
343  def errinfo
344    $!
345  end
346
347  def test_tailcall_inhibited_by_rescue
348    bug12082 = '[ruby-core:73871] [Bug #12082]'
349
350    tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
351    begin;
352      def to_be_rescued
353        return do_raise
354        1 + 2
355      rescue
356        errinfo
357      end
358    end;
359    result = assert_nothing_raised(RuntimeError, message(bug12082) {disasm(:to_be_rescued)}) {
360      to_be_rescued
361    }
362    assert_instance_of(RuntimeError, result, bug12082)
363    assert_equal("should be rescued", result.message, bug12082)
364  end
365
366  def test_tailcall_symbol_block_arg
367    bug12565 = '[ruby-core:46065]'
368    tailcall("#{<<-"begin;"}\n#{<<~"end;"}")
369    begin;
370      def apply_one_and_two(&block)
371        yield(1, 2)
372      end
373
374      def add_one_and_two
375        apply_one_and_two(&:+)
376      end
377    end;
378    assert_equal(3, add_one_and_two,
379                 message(bug12565) {disasm(:add_one_and_two)})
380  end
381
382  def test_tailcall_interrupted_by_sigint
383    bug12576 = 'ruby-core:76327'
384    script = "#{<<-"begin;"}\n#{<<~'end;'}"
385    begin;
386      RubyVM::InstructionSequence.compile_option = {
387        :tailcall_optimization => true,
388        :trace_instruction => false
389      }
390
391      eval "#{<<~"begin;"}\n#{<<~'end;1'}"
392      begin;
393        def foo
394          foo
395        end
396        puts("start")
397        STDOUT.flush
398        foo
399      end;1
400    end;
401    status, _err = EnvUtil.invoke_ruby([], "", true, true, {}) {
402      |in_p, out_p, err_p, pid|
403      in_p.write(script)
404      in_p.close
405      out_p.gets
406      sig = :INT
407      begin
408        Process.kill(sig, pid)
409        Timeout.timeout(1) do
410          *, stat = Process.wait2(pid)
411          [stat, err_p.read]
412        end
413      rescue Timeout::Error
414        if sig == :INT
415          sig = :KILL
416          retry
417        else
418          raise
419        end
420      end
421    }
422    assert_not_equal("SEGV", Signal.signame(status.termsig || 0), bug12576)
423  end unless /mswin|mingw/ =~ RUBY_PLATFORM
424
425  def test_tailcall_condition_block
426    bug = '[ruby-core:78015] [Bug #12905]'
427
428    src = "#{<<-"begin;"}\n#{<<~"end;"}"
429    begin;
430      def run(current, final)
431        if current < final
432          run(current+1, final)
433        else
434          nil
435        end
436      end
437    end;
438
439    obj = Object.new
440    self.class.tailcall(obj.singleton_class, src, tailcall: false)
441    e = assert_raise(SystemStackError) {
442      obj.run(1, Float::INFINITY)
443    }
444    level = e.backtrace_locations.size
445    obj = Object.new
446    self.class.tailcall(obj.singleton_class, src, tailcall: true)
447    level *= 2
448    mesg = message {"#{bug}: #{$!.backtrace_locations.size} / #{level} stack levels"}
449    assert_nothing_raised(SystemStackError, mesg) {
450      obj.run(1, level)
451    }
452  end
453
454  class Bug10557
455    def [](_)
456      block_given?
457    end
458
459    def []=(_, _)
460      block_given?
461    end
462  end
463
464  def test_block_given_aset_aref
465    bug10557 = '[ruby-core:66595]'
466    assert_equal(true, Bug10557.new.[](nil){}, bug10557)
467    assert_equal(true, Bug10557.new.[](0){}, bug10557)
468    assert_equal(true, Bug10557.new.[](false){}, bug10557)
469    assert_equal(true, Bug10557.new.[](''){}, bug10557)
470    assert_equal(true, Bug10557.new.[]=(nil, 1){}, bug10557)
471    assert_equal(true, Bug10557.new.[]=(0, 1){}, bug10557)
472    assert_equal(true, Bug10557.new.[]=(false, 1){}, bug10557)
473    assert_equal(true, Bug10557.new.[]=('', 1){}, bug10557)
474  end
475
476  def test_string_freeze_block
477    assert_separately([], "#{<<-"begin;"}\n#{<<~"end;"}")
478    begin;
479      class String
480        undef freeze
481        def freeze
482          block_given?
483        end
484      end
485      assert_equal(true, "block".freeze {})
486      assert_equal(false, "block".freeze)
487    end;
488  end
489
490  def test_opt_case_dispatch
491    code = "#{<<-"begin;"}\n#{<<~"end;"}"
492    begin;
493      case foo
494      when "foo" then :foo
495      when true then true
496      when false then false
497      when :sym then :sym
498      when 6 then :fix
499      when nil then nil
500      when 0.1 then :float
501      when 0xffffffffffffffff then :big
502      else
503        :nomatch
504      end
505    end;
506    check = {
507      'foo' => :foo,
508      true => true,
509      false => false,
510      :sym => :sym,
511      6 => :fix,
512      nil => nil,
513      0.1 => :float,
514      0xffffffffffffffff => :big,
515    }
516    iseq = RubyVM::InstructionSequence.compile(code)
517    assert_match %r{\bopt_case_dispatch\b}, iseq.disasm
518    check.each do |foo, expect|
519      assert_equal expect, eval("foo = #{foo.inspect}\n#{code}")
520    end
521    assert_equal :nomatch, eval("foo = :blah\n#{code}")
522    check.each do |foo, _|
523      klass = foo.class.to_s
524      assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}")
525      begin;
526        class #{klass}
527          undef ===
528          def ===(*args)
529            false
530          end
531        end
532        foo = #{foo.inspect}
533        ret = #{code}
534        assert_equal :nomatch, ret, foo.inspect
535      end;
536    end
537  end
538
539  def test_eqq
540    [ nil, true, false, 0.1, :sym, 'str', 0xffffffffffffffff ].each do |v|
541      k = v.class.to_s
542      assert_redefine_method(k, '===', "assert_equal(#{v.inspect} === 0, 0)")
543    end
544  end
545
546  def test_opt_case_dispatch_inf
547    inf = 1.0/0.0
548    result = case inf
549             when 1 then 1
550             when 0 then 0
551             else
552               inf.to_i rescue nil
553             end
554    assert_nil result, '[ruby-dev:49423] [Bug #11804]'
555  end
556
557  def test_nil_safe_conditional_assign
558    bug11816 = '[ruby-core:74993] [Bug #11816]'
559    assert_ruby_status([], 'nil&.foo &&= false', bug11816)
560  end
561
562  def test_peephole_string_literal_range
563    code = "#{<<~"begin;"}\n#{<<~"end;"}"
564    begin;
565      case ver
566      when "2.0.0".."2.3.2" then :foo
567      when "1.8.0"..."1.8.8" then :bar
568      end
569    end;
570    [ true, false ].each do |opt|
571      iseq = RubyVM::InstructionSequence.compile(code,
572                                                 frozen_string_literal: opt)
573      insn = iseq.disasm
574      assert_match %r{putobject\s+#{Regexp.quote('"1.8.0"..."1.8.8"')}}, insn
575      assert_match %r{putobject\s+#{Regexp.quote('"2.0.0".."2.3.2"')}}, insn
576      assert_no_match(/putstring/, insn)
577      assert_no_match(/newrange/, insn)
578    end
579  end
580
581  def test_peephole_dstr
582    code = "#{<<~'begin;'}\n#{<<~'end;'}"
583    begin;
584      exp = -'a'
585      z = 'a'
586      [exp, -"#{z}"]
587    end;
588    [ false, true ].each do |fsl|
589      iseq = RubyVM::InstructionSequence.compile(code,
590                                                 frozen_string_literal: fsl)
591      assert_same(*iseq.eval,
592                  "[ruby-core:85542] [Bug #14475] fsl: #{fsl}")
593    end
594  end
595
596  def test_branch_condition_backquote
597    bug = '[ruby-core:80740] [Bug #13444] redefined backquote should be called'
598    class << self
599      def `(s)
600        @q = s
601        @r
602      end
603    end
604
605    @q = nil
606    @r = nil
607    assert_equal("bar", ("bar" unless `foo`), bug)
608    assert_equal("foo", @q, bug)
609
610    @q = nil
611    @r = true
612    assert_equal("bar", ("bar" if `foo`), bug)
613    assert_equal("foo", @q, bug)
614
615    @q = nil
616    @r = "z"
617    assert_equal("bar", ("bar" if `foo#{@r}`))
618    assert_equal("fooz", @q, bug)
619  end
620
621  def test_branch_condition_def
622    bug = '[ruby-core:80740] [Bug #13444] method should be defined'
623    c = Class.new do
624      raise "bug" unless def t;:ok;end
625    end
626    assert_nothing_raised(NoMethodError, bug) do
627      assert_equal(:ok, c.new.t)
628    end
629  end
630
631  def test_branch_condition_defs
632    bug = '[ruby-core:80740] [Bug #13444] singleton method should be defined'
633    raise "bug" unless def self.t;:ok;end
634    assert_nothing_raised(NameError, bug) do
635      assert_equal(:ok, t)
636    end
637  end
638
639  def test_retry_label_in_unreachable_chunk
640    bug = '[ruby-core:81272] [Bug #13578]'
641    assert_valid_syntax("#{<<-"begin;"}\n#{<<-"end;"}", bug)
642    begin;
643      def t; if false; case 42; when s {}; end; end; end
644    end;
645  end
646
647  def bptest_yield &b
648    yield
649  end
650
651  def bptest_yield_pass &b
652    bptest_yield(&b)
653  end
654
655  def bptest_bp_value &b
656    b
657  end
658
659  def bptest_bp_pass_bp_value &b
660    bptest_bp_value(&b)
661  end
662
663  def bptest_binding &b
664    binding
665  end
666
667  def bptest_set &b
668    b = Proc.new{2}
669  end
670
671  def test_block_parameter
672    assert_equal(1, bptest_yield{1})
673    assert_equal(1, bptest_yield_pass{1})
674    assert_equal(1, send(:bptest_yield){1})
675
676    assert_equal(Proc, bptest_bp_value{}.class)
677    assert_equal nil, bptest_bp_value
678    assert_equal(Proc, bptest_bp_pass_bp_value{}.class)
679    assert_equal nil, bptest_bp_pass_bp_value
680
681    assert_equal Proc, bptest_binding{}.local_variable_get(:b).class
682
683    assert_equal 2, bptest_set{1}.call
684  end
685
686  def test_block_parameter_should_not_create_objects
687    assert_separately [], <<-END
688      #
689      def foo &b
690      end
691      h1 = {}; h2 = {}
692      ObjectSpace.count_objects(h1) # reharsal
693      ObjectSpace.count_objects(h1)
694      foo{}
695      ObjectSpace.count_objects(h2)
696
697      assert_equal 0, h2[:TOTAL] - h1[:TOTAL]
698    END
699  end
700
701  def test_block_parameter_should_restore_safe_level
702    assert_separately [], <<-END
703      #
704      def foo &b
705        $SAFE = 1
706        b.call
707      end
708      assert_equal 1, foo{$SAFE}
709    END
710  end
711
712  def test_peephole_optimization_without_trace
713    assert_separately [], <<-END
714      RubyVM::InstructionSequence.compile_option = {trace_instruction: false}
715      eval "def foo; 1.times{|(a), &b| nil && a}; end"
716    END
717  end
718
719  def test_clear_unreachable_keyword_args
720    assert_separately [], <<-END, timeout: 30
721      script =  <<-EOS
722        if true
723        else
724          foo(k1:1)
725        end
726      EOS
727      GC.stress = true
728      30.times{
729        RubyVM::InstructionSequence.compile(script)
730      }
731    END
732  end
733
734  def test_callinfo_unreachable_path
735    assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
736    begin;
737      iseq = RubyVM::InstructionSequence.compile("if false; foo(bar: :baz); else :ok end")
738      bin = iseq.to_binary
739      iseq = RubyVM::InstructionSequence.load_from_binary(bin)
740      assert_instance_of(RubyVM::InstructionSequence, iseq)
741      assert_equal(:ok, iseq.eval)
742    end;
743  end
744
745  def test_side_effect_in_popped_splat
746    bug = '[ruby-core:84340] [Bug #14201]'
747    eval("{**(bug = nil; {})};42")
748    assert_nil(bug)
749
750    bug = '[ruby-core:85486] [Bug #14459]'
751    h = {}
752    assert_equal(bug, eval('{ok: 42, **h}; bug'))
753    assert_equal(:ok, eval('{ok: bug = :ok, **h}; bug'))
754  end
755
756  def test_overwritten_blockparam
757    obj = Object.new
758    def obj.a(&block)
759      block = 1
760      return :ok if block
761      :ng
762    end
763    assert_equal(:ok, obj.a())
764  end
765
766  def test_blockparam_in_rescue
767    obj = Object.new
768    def obj.foo(&b)
769      raise
770    rescue
771      b.call
772    end
773    result = nil
774    assert_equal(42, obj.foo {result = 42})
775    assert_equal(42, result)
776  end
777
778  def test_unconditional_branch_to_leave_block
779    assert_valid_syntax("#{<<~"begin;"}\n#{<<~'end;'}")
780    begin;
781      tap {true || tap {}}
782    end;
783  end
784
785  def test_jump_elimination_with_optimized_out_block
786    x = Object.new
787    def x.bug(obj)
788      if obj || obj
789        obj = obj
790      else
791        raise "[ruby-core:87830] [Bug #14897]"
792      end
793      obj
794    end
795    assert_equal(:ok, x.bug(:ok))
796  end
797
798  def test_jump_elimination_with_optimized_out_block_2
799    x = Object.new
800    def x.bug
801      a = "aaa"
802      ok = :NG
803      if a == "bbb" || a == "ccc" then
804        a = a
805      else
806        ok = :ok
807      end
808      ok
809    end
810    assert_equal(:ok, x.bug)
811  end
812
813  def test_peephole_jump_after_newarray
814    i = 0
815    %w(1) || 2 while (i += 1) < 100
816    assert_equal(100, i)
817  end
818
819  def test_optimized_empty_ensure
820    assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}", timeout: 1)
821    begin;
822      assert_raise(RuntimeError) {
823        begin raise ensure nil if nil end
824      }
825    end;
826  end
827end
828