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