PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/test/ruby/test_optimization.rb

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