diff options
Diffstat (limited to 'test')
| -rw-r--r-- | test/ruby/test_zjit.rb | 4494 |
1 files changed, 0 insertions, 4494 deletions
diff --git a/test/ruby/test_zjit.rb b/test/ruby/test_zjit.rb index 095b690c7b..333e34e927 100644 --- a/test/ruby/test_zjit.rb +++ b/test/ruby/test_zjit.rb @@ -112,167 +112,6 @@ class TestZJIT < Test::Unit::TestCase RUBY end - def test_call_itself - assert_compiles '42', <<~RUBY, call_threshold: 2 - def test = 42.itself - test - test - RUBY - end - - def test_nil - assert_compiles 'nil', %q{ - def test = nil - test - } - end - - def test_putobject - assert_compiles '1', %q{ - def test = 1 - test - } - end - - def test_putstring - assert_compiles '""', %q{ - def test = "#{""}" - test - }, insns: [:putstring] - end - - def test_putchilldedstring - assert_compiles '""', %q{ - def test = "" - test - }, insns: [:putchilledstring] - end - - def test_leave_param - assert_compiles '5', %q{ - def test(n) = n - test(5) - } - end - - def test_getglobal_with_warning - assert_compiles('"rescued"', %q{ - Warning[:deprecated] = true - - module Warning - def warn(message) - raise - end - end - - def test - $= - rescue - "rescued" - end - - $VERBOSE = true - test - }, insns: [:getglobal]) - end - - def test_setglobal - assert_compiles '1', %q{ - def test - $a = 1 - $a - end - - test - }, insns: [:setglobal] - end - - def test_string_intern - assert_compiles ':foo123', %q{ - def test - :"foo#{123}" - end - - test - }, insns: [:intern] - end - - def test_duphash - assert_compiles '{a: 1}', %q{ - def test - {a: 1} - end - - test - }, insns: [:duphash] - end - - def test_pushtoarray - assert_compiles '[1, 2, 3]', %q{ - def test - [*[], 1, 2, 3] - end - test - }, insns: [:pushtoarray] - end - - def test_splatarray_new_array - assert_compiles '[1, 2, 3]', %q{ - def test a - [*a, 3] - end - test [1, 2] - }, insns: [:splatarray] - end - - def test_splatarray_existing_array - assert_compiles '[1, 2, 3]', %q{ - def foo v - [1, 2, v] - end - def test a - foo(*a) - end - test [3] - }, insns: [:splatarray] - end - - def test_concattoarray - assert_compiles '[1, 2, 3]', %q{ - def test(*a) - [1, 2, *a] - end - test 3 - }, insns: [:concattoarray] - end - - def test_definedivar - assert_compiles '[nil, "instance-variable", nil]', %q{ - def test - v0 = defined?(@a) - @a = nil - v1 = defined?(@a) - remove_instance_variable :@a - v2 = defined?(@a) - [v0, v1, v2] - end - test - }, insns: [:definedivar] - end - - def test_setglobal_with_trace_var_exception - assert_compiles '"rescued"', %q{ - def test - $a = 1 - rescue - "rescued" - end - - trace_var(:$a) { raise } - test - }, insns: [:setglobal] - end - def test_toplevel_binding # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`. out, err, status = eval_with_jit(%q{ @@ -286,391 +125,6 @@ class TestZJIT < Test::Unit::TestCase assert_equal "[1, 3, 4]", out end - def test_toplevel_local_after_eval - # Not using assert_compiles, which doesn't use the toplevel frame for `test_script`. - out, err, status = eval_with_jit(%q{ - a = 1 - b = 2 - eval('b = 3') - c = 4 - print [a, b, c] - }) - assert_success(out, err, status) - assert_equal "[1, 3, 4]", out - end - - def test_getlocal_after_eval - assert_compiles '2', %q{ - def test - a = 1 - eval('a = 2') - a - end - test - } - end - - def test_getlocal_after_instance_eval - assert_compiles '2', %q{ - def test - a = 1 - instance_eval('a = 2') - a - end - test - } - end - - def test_getlocal_after_module_eval - assert_compiles '2', %q{ - def test - a = 1 - Kernel.module_eval('a = 2') - a - end - test - } - end - - def test_getlocal_after_class_eval - assert_compiles '2', %q{ - def test - a = 1 - Kernel.class_eval('a = 2') - a - end - test - } - end - - def test_setlocal - assert_compiles '3', %q{ - def test(n) - m = n - m - end - test(3) - } - end - - def test_return_nonparam_local - # Use dead code (if false) to create a local without initialization instructions. - assert_compiles 'nil', %q{ - def foo(a) - if false - x = nil - end - x - end - def test = foo(1) - test - test - }, call_threshold: 2 - end - - def test_nonparam_local_nil_in_jit_call - # Non-parameter locals must be initialized to nil in JIT-to-JIT calls. - # Use dead code (if false) to create locals without initialization instructions. - # Then eval a string that accesses the uninitialized locals. - assert_compiles '["x", "x", "x", "x"]', %q{ - def f(a) - a ||= 1 - if false; b = 1; end - eval("-> { p 'x#{b}' }") - end - - 4.times.map { f(1).call } - }, call_threshold: 2 - end - - def test_kwargs_with_exit_and_local_invalidation - assert_compiles ':ok', %q{ - def a(b:, c:) - if c == :b - return -> {} - end - Class # invalidate locals - - raise "c is :b!" if c == :b - end - - def test - # note opposite order of kwargs - a(c: :c, b: :b) - end - - 4.times { test } - :ok - }, call_threshold: 2 - end - - def test_kwargs_with_max_direct_send_arg_count - # Ensure that we only reorder the args when we _can_ use direct send (< 6 args). - assert_compiles '[[1, 2, 3, 4, 5, 6, 7, 8]]', %q{ - def kwargs(five, six, a:, b:, c:, d:, e:, f:) - [a, b, c, d, five, six, e, f] - end - - 5.times.flat_map do - [ - kwargs(5, 6, d: 4, c: 3, a: 1, b: 2, e: 7, f: 8), - kwargs(5, 6, d: 4, c: 3, b: 2, a: 1, e: 7, f: 8) - ] - end.uniq - }, call_threshold: 2 - end - - def test_setlocal_on_eval - assert_compiles '1', %q{ - @b = binding - eval('a = 1', @b) - eval('a', @b) - } - end - - def test_optional_arguments - assert_compiles '[[1, 2, 3], [10, 20, 3], [100, 200, 300]]', %q{ - def test(a, b = 2, c = 3) - [a, b, c] - end - [test(1), test(10, 20), test(100, 200, 300)] - } - end - - def test_optional_arguments_setlocal - assert_compiles '[[2, 2], [1, nil]]', %q{ - def test(a = (b = 2)) - [a, b] - end - [test, test(1)] - } - end - - def test_optional_arguments_cyclic - assert_compiles '[nil, 1]', %q{ - test = proc { |a=a| a } - [test.call, test.call(1)] - } - end - - def test_optional_arguments_side_exit - # This leads to FailedOptionalArguments, so not using assert_compiles - assert_runs '[:foo, nil, 1]', %q{ - def test(a = (def foo = nil)) = a - [test, (undef :foo), test(1)] - } - end - - def test_getblockparamproxy - assert_compiles '1', %q{ - def test(&block) - 0.then(&block) - end - test { 1 } - }, insns: [:getblockparamproxy] - end - - def test_getblockparam - assert_compiles '2', %q{ - def test(&blk) - blk - end - test { 2 }.call - test { 2 }.call - }, insns: [:getblockparam] - end - - def test_getblockparam_proxy_side_exit_restores_block_local - assert_compiles '2', %q{ - def test(&block) - b = block - # sideexits here - raise "test" unless block - b ? 2 : 3 - end - test {} - test {} - }, insns: [:getblockparam, :getblockparamproxy] - end - - def test_getblockparam_used_twice_in_args - assert_compiles '1', %q{ - def f(*args) = args - def test(&blk) - b = blk - f(*[1], blk) - blk - end - test {1}.call - test {1}.call - }, insns: [:getblockparam] - end - - def test_optimized_method_call_proc_call - assert_compiles '2', %q{ - p = proc { |x| x * 2 } - def test(p) - p.call(1) - end - test(p) - test(p) - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_optimized_method_call_proc_aref - assert_compiles '4', %q{ - p = proc { |x| x * 2 } - def test(p) - p[2] - end - test(p) - test(p) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_optimized_method_call_proc_yield - assert_compiles '6', %q{ - p = proc { |x| x * 2 } - def test(p) - p.yield(3) - end - test(p) - test(p) - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_optimized_method_call_proc_kw_splat - assert_compiles '3', %q{ - p = proc { |**kw| kw[:a] + kw[:b] } - def test(p, h) - p.call(**h) - end - h = { a: 1, b: 2 } - test(p, h) - test(p, h) - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_optimized_method_call_proc_call_splat - assert_compiles '43', %q{ - p = proc { |x| x + 1 } - def test(p) - ary = [42] - p.call(*ary) - end - test(p) - test(p) - }, call_threshold: 2 - end - - def test_optimized_method_call_proc_call_kwarg - assert_compiles '1', %q{ - p = proc { |a:| a } - def test(p) - p.call(a: 1) - end - test(p) - test(p) - }, call_threshold: 2 - end - - def test_call_a_forwardable_method - assert_runs '[]', %q{ - def test_root = forwardable - def forwardable(...) = Array.[](...) - test_root - test_root - }, call_threshold: 2 - end - - def test_setlocal_on_eval_with_spill - assert_compiles '1', %q{ - @b = binding - eval('a = 1; itself', @b) - eval('a', @b) - } - end - - def test_nested_local_access - assert_compiles '[1, 2, 3]', %q{ - 1.times do |l2| - 1.times do |l1| - define_method(:test) do - l1 = 1 - l2 = 2 - l3 = 3 - [l1, l2, l3] - end - end - end - - test - test - test - }, call_threshold: 3, insns: [:getlocal, :setlocal, :getlocal_WC_0, :setlocal_WC_1] - end - - def test_send_with_local_written_by_blockiseq - assert_compiles '[1, 2]', %q{ - def test - l1 = nil - l2 = nil - tap do |_| - l1 = 1 - tap do |_| - l2 = 2 - end - end - - [l1, l2] - end - - test - test - }, call_threshold: 2 - end - - def test_send_without_block - assert_compiles '[1, 2, 3]', %q{ - def foo = 1 - def bar(a) = a - 1 - def baz(a, b) = a - b - - def test1 = foo - def test2 = bar(3) - def test3 = baz(4, 1) - - [test1, test2, test3] - } - end - - def test_send_with_six_args - assert_compiles '[1, 2, 3, 4, 5, 6]', %q{ - def foo(a1, a2, a3, a4, a5, a6) - [a1, a2, a3, a4, a5, a6] - end - - def test - foo(1, 2, 3, 4, 5, 6) - end - - test # profile send - test - }, call_threshold: 2 - end - - def test_send_on_heap_object_in_spilled_arg - # This leads to a register spill, so not using `assert_compiles` - assert_runs 'Hash', %q{ - def entry(a1, a2, a3, a4, a5, a6, a7, a8, a9) - a9.itself.class - end - - entry(1, 2, 3, 4, 5, 6, 7, 8, {}) # profile - entry(1, 2, 3, 4, 5, 6, 7, 8, {}) - }, call_threshold: 2 - end - def test_send_exit_with_uninitialized_locals assert_runs 'nil', %q{ def entry(init) @@ -687,1467 +141,6 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 2, allowed_iseqs: 'entry@-e:2' end - def test_send_optional_arguments - assert_compiles '[[1, 2], [3, 4]]', %q{ - def test(a, b = 2) = [a, b] - def entry = [test(1), test(3, 4)] - entry - entry - }, call_threshold: 2 - end - - def test_send_nil_block_arg - assert_compiles 'false', %q{ - def test = block_given? - def entry = test(&nil) - test - } - end - - def test_send_symbol_block_arg - assert_compiles '["1", "2"]', %q{ - def test = [1, 2].map(&:to_s) - test - } - end - - def test_send_variadic_with_block - assert_compiles '[[1, "a"], [2, "b"], [3, "c"]]', %q{ - A = [1, 2, 3] - B = ["a", "b", "c"] - - def test - result = [] - A.zip(B) { |x, y| result << [x, y] } - result - end - - test; test - }, call_threshold: 2 - end - - def test_send_splat - assert_runs '[1, 2]', %q{ - def test(a, b) = [a, b] - def entry(arr) = test(*arr) - entry([1, 2]) - } - end - - def test_send_kwarg - assert_runs '[1, 2]', %q{ - def test(a:, b:) = [a, b] - def entry = test(b: 2, a: 1) # change order - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_optional - assert_compiles '[1, 2]', %q{ - def test(a: 1, b: 2) = [a, b] - def entry = test - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_optional_too_many - assert_compiles '[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]', %q{ - def test(a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10) = [a, b, c, d, e, f, g, h, i, j] - def entry = test - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_required_and_optional - assert_compiles '[3, 2]', %q{ - def test(a:, b: 2) = [a, b] - def entry = test(a: 3) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_to_hash - assert_compiles '{a: 3}', %q{ - def test(hash) = hash - def entry = test(a: 3) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_to_ccall - assert_compiles '["a", "b", "c"]', %q{ - def test(s) = s.each_line(chomp: true).to_a - def entry = test(%(a\nb\nc)) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_and_block_to_ccall - assert_compiles '["a", "b", "c"]', %q{ - def test(s) - a = [] - s.each_line(chomp: true) { |l| a << l } - a - end - def entry = test(%(a\nb\nc)) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_with_too_many_args_to_c_call - assert_compiles '"a b c d {kwargs: :e}"', %q{ - def test(a:, b:, c:, d:, e:) = sprintf("%s %s %s %s %s", a, b, c, d, kwargs: e) - def entry = test(e: :e, d: :d, c: :c, a: :a, b: :b) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwsplat - assert_compiles '3', %q{ - def test(a:) = a - def entry = test(**{a: 3}) - entry - entry - }, call_threshold: 2 - end - - def test_send_kwrest - assert_compiles '{a: 3}', %q{ - def test(**kwargs) = kwargs - def entry = test(a: 3) - entry - entry - }, call_threshold: 2 - end - - def test_send_req_kwreq - assert_compiles '[1, 3]', %q{ - def test(a, c:) = [a, c] - def entry = test(1, c: 3) - entry - entry - }, call_threshold: 2 - end - - def test_send_req_opt_kwreq - assert_compiles '[[1, 2, 3], [-1, -2, -3]]', %q{ - def test(a, b = 2, c:) = [a, b, c] - def entry = [test(1, c: 3), test(-1, -2, c: -3)] # specify all, change kw order - entry - entry - }, call_threshold: 2 - end - - def test_send_req_opt_kwreq_kwopt - assert_compiles '[[1, 2, 3, 4], [-1, -2, -3, -4]]', %q{ - def test(a, b = 2, c:, d: 4) = [a, b, c, d] - def entry = [test(1, c: 3), test(-1, -2, d: -4, c: -3)] # specify all, change kw order - entry - entry - }, call_threshold: 2 - end - - def test_send_unexpected_keyword - assert_compiles ':error', %q{ - def test(a: 1) = a*2 - def entry - test(z: 2) - rescue ArgumentError - :error - end - - entry - entry - }, call_threshold: 2 - end - - def test_pos_optional_with_maybe_too_many_args - assert_compiles '[[1, 2, 3, 4, 5, 6], [10, 20, 30, 4, 5, 6], [10, 20, 30, 40, 50, 60]]', %q{ - def target(a = 1, b = 2, c = 3, d = 4, e = 5, f:) = [a, b, c, d, e, f] - def test = [target(f: 6), target(10, 20, 30, f: 6), target(10, 20, 30, 40, 50, f: 60)] - test - test - }, call_threshold: 2 - end - - def test_send_kwarg_partial_optional - assert_compiles '[[1, 2, 3], [1, 20, 3], [10, 2, 30]]', %q{ - def test(a: 1, b: 2, c: 3) = [a, b, c] - def entry = [test, test(b: 20), test(c: 30, a: 10)] - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_optional_a_lot - assert_compiles '[[1, 2, 3, 4, 5, 6], [1, 2, 3, 7, 8, 9], [2, 4, 6, 8, 10, 12]]', %q{ - def test(a: 1, b: 2, c: 3, d: 4, e: 5, f: 6) = [a, b, c, d, e, f] - def entry = [test, test(d: 7, f: 9, e: 8), test(f: 12, e: 10, d: 8, c: 6, b: 4, a: 2)] - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_non_constant_default - assert_compiles '[[1, 2], [10, 2]]', %q{ - def make_default = 2 - def test(a: 1, b: make_default) = [a, b] - def entry = [test, test(a: 10)] - entry - entry - }, call_threshold: 2 - end - - def test_send_kwarg_optional_static_with_side_exit - # verify frame reconstruction with synthesized keyword defaults is correct - assert_compiles '[10, 2, 10]', %q{ - def callee(a: 1, b: 2) - # use binding to force side-exit - x = binding.local_variable_get(:a) - [a, b, x] - end - - def entry - callee(a: 10) # b should get default value - end - - entry - entry - }, call_threshold: 2 - end - - def test_send_all_arg_types - assert_compiles '[:req, :opt, :post, :kwr, :kwo, true]', %q{ - def test(a, b = :opt, c, d:, e: :kwo) = [a, b, c, d, e, block_given?] - def entry = test(:req, :post, d: :kwr) {} - entry - entry - }, call_threshold: 2 - end - - def test_send_ccall_variadic_with_different_receiver_classes - assert_compiles '[true, true]', %q{ - def test(obj) = obj.start_with?("a") - [test("abc"), test(:abc)] - }, call_threshold: 2 - end - - def test_forwardable_iseq - assert_compiles '1', %q{ - def test(...) = 1 - test - } - end - - def test_sendforward - assert_compiles '[1, 2]', %q{ - def callee(a, b) = [a, b] - def test(...) = callee(...) - test(1, 2) - }, insns: [:sendforward] - end - - def test_iseq_with_optional_arguments - assert_compiles '[[1, 2], [3, 4]]', %q{ - def test(a, b = 2) = [a, b] - [test(1), test(3, 4)] - } - end - - def test_invokesuper - assert_compiles '[6, 60]', %q{ - class Foo - def foo(a) = a + 1 - def bar(a) = a + 10 - end - - class Bar < Foo - def foo(a) = super(a) + 2 - def bar(a) = super + 20 - end - - bar = Bar.new - [bar.foo(3), bar.bar(30)] - } - end - - def test_invokesuper_with_local_written_by_blockiseq - # Using `assert_runs` because we don't compile invokeblock yet - assert_runs '3', %q{ - class Foo - def test - yield - end - end - - class Bar < Foo - def test - a = 1 - super do - a += 2 - end - a - end - end - - Bar.new.test - } - end - - def test_invokesuper_to_iseq - assert_compiles '["B", "A"]', %q{ - class A - def foo - "A" - end - end - - class B < A - def foo - ["B", super] - end - end - - def test - B.new.foo - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_args - assert_compiles '["B", 11]', %q{ - class A - def foo(x) - x * 2 - end - end - - class B < A - def foo(x) - ["B", super(x) + 1] - end - end - - def test - B.new.foo(5) - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test super with explicit args when callee has rest parameter. - # This should fall back to dynamic dispatch since we can't handle rest params yet. - def test_invokesuper_with_args_to_rest_param - assert_compiles '["B", "a", ["b", "c"]]', %q{ - class A - def foo(x, *rest) - [x, rest] - end - end - - class B < A - def foo(x, y, z) - ["B", *super(x, y, z)] - end - end - - def test - B.new.foo("a", "b", "c") - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_block - assert_compiles '["B", "from_block"]', %q{ - class A - def foo - block_given? ? yield : "no_block" - end - end - - class B < A - def foo - ["B", super { "from_block" }] - end - end - - def test - B.new.foo - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_to_cfunc_no_args - assert_compiles '["MyString", 3]', %q{ - class MyString < String - def length - ["MyString", super] - end - end - - def test - MyString.new("abc").length - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_to_cfunc_simple_args - assert_compiles '["MyString", true]', %q{ - class MyString < String - def include?(other) - ["MyString", super(other)] - end - end - - def test - MyString.new("abc").include?("bc") - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - - def test_invokesuper_to_cfunc_with_optional_arg - assert_compiles '["MyString", 6]', %q{ - class MyString < String - def byteindex(needle, offset = 0) - ["MyString", super(needle, offset)] - end - end - - def test - MyString.new("hello world").byteindex("world") - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_to_cfunc_varargs - assert_compiles '["MyString", true]', %q{ - class MyString < String - def end_with?(str) - ["MyString", super(str)] - end - end - - def test - MyString.new("abc").end_with?("bc") - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_multilevel - assert_compiles '["C", ["B", "A"]]', %q{ - class A - def foo - "A" - end - end - - class B < A - def foo - ["B", super] - end - end - - class C < B - def foo - ["C", super] - end - end - - def test - C.new.foo - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test implicit block forwarding - super without explicit block should forward caller's block - # Note: We call test twice to ensure ZJIT compiles it before the final call that we check - def test_invokesuper_forwards_block_implicitly - assert_compiles '["B", "forwarded_block"]', %q{ - class A - def foo - block_given? ? yield : "no_block" - end - end - - class B < A - def foo - ["B", super] # should forward the block from caller - end - end - - def test - B.new.foo { "forwarded_block" } - end - - test # profile invokesuper - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test implicit block forwarding with explicit arguments - def test_invokesuper_forwards_block_implicitly_with_args - assert_compiles '["B", ["arg_value", "forwarded"]]', %q{ - class A - def foo(x) - [x, (block_given? ? yield : "no_block")] - end - end - - class B < A - def foo(x) - ["B", super(x)] # explicit args, but block should still be forwarded - end - end - - def test - B.new.foo("arg_value") { "forwarded" } - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test implicit block forwarding when no block is given (should not fail) - def test_invokesuper_forwards_block_implicitly_no_block_given - assert_compiles '["B", "no_block"]', %q{ - class A - def foo - block_given? ? yield : "no_block" - end - end - - class B < A - def foo - ["B", super] # no block given by caller - end - end - - def test - B.new.foo # called without a block - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test implicit block forwarding through multiple inheritance levels - def test_invokesuper_forwards_block_implicitly_multilevel - assert_compiles '["C", ["B", "deep_block"]]', %q{ - class A - def foo - block_given? ? yield : "no_block" - end - end - - class B < A - def foo - ["B", super] # forwards block to A - end - end - - class C < B - def foo - ["C", super] # forwards block to B, which forwards to A - end - end - - def test - C.new.foo { "deep_block" } - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test implicit block forwarding with block parameter syntax - def test_invokesuper_forwards_block_param - assert_compiles '["B", "block_param_forwarded"]', %q{ - class A - def foo - block_given? ? yield : "no_block" - end - end - - class B < A - def foo(&block) - ["B", super] # should forward &block implicitly - end - end - - def test - B.new.foo { "block_param_forwarded" } - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_blockarg - assert_compiles '["B", "different block"]', %q{ - class A - def foo - block_given? ? yield : "no block" - end - end - - class B < A - def foo(&blk) - other_block = proc { "different block" } - ["B", super(&other_block)] - end - end - - def test - B.new.foo { "passed block" } - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_symbol_to_proc - assert_compiles '["B", [3, 5, 7]]', %q{ - class A - def foo(items, &blk) - items.map(&blk) - end - end - - class B < A - def foo(items) - ["B", super(items, &:succ)] - end - end - - def test - B.new.foo([2, 4, 6]) - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_splat - assert_compiles '["B", 6]', %q{ - class A - def foo(a, b, c) - a + b + c - end - end - - class B < A - def foo(*args) - ["B", super(*args)] - end - end - - def test - B.new.foo(1, 2, 3) - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_kwargs - assert_compiles '["B", "x=1, y=2"]', %q{ - class A - def foo(x:, y:) - "x=#{x}, y=#{y}" - end - end - - class B < A - def foo(x:, y:) - ["B", super(x: x, y: y)] - end - end - - def test - B.new.foo(x: 1, y: 2) - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - def test_invokesuper_with_kw_splat - assert_compiles '["B", "x=1, y=2"]', %q{ - class A - def foo(x:, y:) - "x=#{x}, y=#{y}" - end - end - - class B < A - def foo(**kwargs) - ["B", super(**kwargs)] - end - end - - def test - B.new.foo(x: 1, y: 2) - end - - test # profile - test # compile + run compiled code - }, call_threshold: 2 - end - - # Test that including a module after compilation correctly changes the super target. - # The included module's method should be called, not the original super target. - def test_invokesuper_with_include - assert_compiles '["B", "M"]', %q{ - class A - def foo - "A" - end - end - - class B < A - def foo - ["B", super] - end - end - - def test - B.new.foo - end - - test # profile invokesuper (super -> A#foo) - test # compile with super -> A#foo - - # Now include a module in B that defines foo - super should go to M#foo instead - module M - def foo - "M" - end - end - B.include(M) - - test # should call M#foo, not A#foo - }, call_threshold: 2 - end - - # Test that prepending a module after compilation correctly changes the super target. - # The prepended module's method should be called, not the original super target. - def test_invokesuper_with_prepend - assert_compiles '["B", "M"]', %q{ - class A - def foo - "A" - end - end - - class B < A - def foo - ["B", super] - end - end - - def test - B.new.foo - end - - test # profile invokesuper (super -> A#foo) - test # compile with super -> A#foo - - # Now prepend a module that defines foo - super should go to M#foo instead - module M - def foo - "M" - end - end - A.prepend(M) - - test # should call M#foo, not A#foo - }, call_threshold: 2 - end - - # Test super with positional and keyword arguments (pattern from chunky_png) - def test_invokesuper_with_keyword_args - assert_compiles '{content: "image data"}', %q{ - class A - def foo(attributes = {}) - @attributes = attributes - end - end - - class B < A - def foo(content = '') - super(content: content) - end - end - - def test - B.new.foo("image data") - end - - test - test - }, call_threshold: 2 - end - - def test_invokesuper_with_optional_keyword_args - assert_compiles '[1, 2, 3]', %q{ - class Parent - def foo(a, b: 2, c: 3) = [a, b, c] - end - - class Child < Parent - def foo(a) = super(a) - end - - def test = Child.new.foo(1) - - test - test - }, call_threshold: 2 - end - - def test_invokesuperforward - assert_compiles '[1, 2, 3]', %q{ - class A - def foo(a,b,c) = [a,b,c] - end - - class B < A - def foo(...) = super - end - - def test - B.new.foo(1, 2, 3) - end - - test - test - }, call_threshold: 2 - end - - def test_invokesuperforward_with_args_kwargs_and_block - assert_compiles '[[1, 2], {x: 3}, 4]', %q{ - class A - def foo(*args, **kwargs, &block) - [args, kwargs, block&.call] - end - end - - class B < A - def foo(...) = super - end - - def test - B.new.foo(1, 2, x: 3) { 4 } - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default - assert_compiles '[[2, 4, 16], [10, 4, 16], [2, 20, 16], [2, 4, 30], [10, 20, 30]]', %q{ - def dbl(x = 1) = x * 2 - - def foo(a: dbl, b: dbl(2), c: dbl(2 ** 3)) - [a, b, c] - end - - def test - [ - foo, - foo(a: 10), - foo(b: 20), - foo(c: 30), - foo(a: 10, b: 20, c: 30) - ] - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default_not_evaluated_when_provided - assert_compiles '[1, 2, 3]', %q{ - def foo(a: raise, b: raise, c: raise) - [a, b, c] - end - - def test - foo(a: 1, b: 2, c: 3) - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default_evaluated_when_not_provided - assert_compiles '["a", "b", "c"]', %q{ - def raise_a = raise "a" - def raise_b = raise "b" - def raise_c = raise "c" - - def foo(a: raise_a, b: raise_b, c: raise_c) - [a, b, c] - end - - def test_a - foo(b: 2, c: 3) - rescue RuntimeError => e - e.message - end - - def test_b - foo(a: 1, c: 3) - rescue RuntimeError => e - e.message - end - - def test_c - foo(a: 1, b: 2) - rescue RuntimeError => e - e.message - end - - def test - [test_a, test_b, test_c] - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default_jit_to_jit - # Test that kw_bits passing works correctly in JIT-to-JIT calls - assert_compiles '[2, 4, 6]', %q{ - def make_default(x) = x * 2 - - def callee(a: make_default(1), b: make_default(2), c: make_default(3)) - [a, b, c] - end - - def caller_method - callee - end - - # Warm up callee first so it gets JITted - callee - callee - - # Now warm up caller - this creates JIT-to-JIT call - caller_method - caller_method - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default_side_exit - # Verify frame reconstruction includes correct values for non-constant defaults - assert_compiles '[10, 2, 30]', %q{ - def make_b = 2 - - def callee(a: 1, b: make_b, c: 3) - x = binding.local_variable_get(:a) - y = binding.local_variable_get(:b) - z = binding.local_variable_get(:c) - [x, y, z] - end - - def test - callee(a: 10, c: 30) - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_non_constant_keyword_default_evaluation_order - # Verify defaults are evaluated left-to-right and only when not provided - assert_compiles '[["a", "b", "c"], ["b", "c"], ["a", "c"], ["a", "b"]]', %q{ - def log(x) - $order << x - x - end - - def foo(a: log("a"), b: log("b"), c: log("c")) - [a, b, c] - end - - def test - results = [] - - $order = [] - foo - results << $order.dup - - $order = [] - foo(a: "A") - results << $order.dup - - $order = [] - foo(b: "B") - results << $order.dup - - $order = [] - foo(c: "C") - results << $order.dup - - results - end - - test - test - }, call_threshold: 2 - end - - def test_send_with_too_many_non_constant_keyword_defaults - assert_compiles '35', %q{ - def many_kwargs( k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6, k7: 7, k8: 8, k9: 9, k10: 10, k11: 11, k12: 12, k13: 13, k14: 14, k15: 15, k16: 16, k17: 17, k18: 18, k19: 19, k20: 20, k21: 21, k22: 22, k23: 23, k24: 24, k25: 25, k26: 26, k27: 27, k28: 28, k29: 29, k30: 30, k31: 31, k32: 32, k33: 33, k34: k33 + 1) = k1 + k34 - def t = many_kwargs - t - t - }, call_threshold: 2 - end - - def test_invokebuiltin - # Not using assert_compiles due to register spill - assert_runs '["."]', %q{ - def test = Dir.glob(".") - test - } - end - - def test_invokebuiltin_delegate - assert_compiles '[[], true]', %q{ - def test = [].clone(freeze: true) - r = test - [r, r.frozen?] - } - end - - def test_opt_plus_const - assert_compiles '3', %q{ - def test = 1 + 2 - test # profile opt_plus - test - }, call_threshold: 2 - end - - def test_opt_plus_fixnum - assert_compiles '3', %q{ - def test(a, b) = a + b - test(0, 1) # profile opt_plus - test(1, 2) - }, call_threshold: 2 - end - - def test_opt_plus_chain - assert_compiles '6', %q{ - def test(a, b, c) = a + b + c - test(0, 1, 2) # profile opt_plus - test(1, 2, 3) - }, call_threshold: 2 - end - - def test_opt_plus_left_imm - assert_compiles '3', %q{ - def test(a) = 1 + a - test(1) # profile opt_plus - test(2) - }, call_threshold: 2 - end - - def test_opt_plus_type_guard_exit - assert_compiles '[3, 3.0]', %q{ - def test(a) = 1 + a - test(1) # profile opt_plus - [test(2), test(2.0)] - }, call_threshold: 2 - end - - def test_opt_plus_type_guard_exit_with_locals - assert_compiles '[6, 6.0]', %q{ - def test(a) - local = 3 - 1 + a + local - end - test(1) # profile opt_plus - [test(2), test(2.0)] - }, call_threshold: 2 - end - - def test_opt_plus_type_guard_nested_exit - assert_compiles '[4, 4.0]', %q{ - def side_exit(n) = 1 + n - def jit_frame(n) = 1 + side_exit(n) - def entry(n) = jit_frame(n) - entry(2) # profile send - [entry(2), entry(2.0)] - }, call_threshold: 2 - end - - def test_opt_plus_type_guard_nested_exit_with_locals - assert_compiles '[9, 9.0]', %q{ - def side_exit(n) - local = 2 - 1 + n + local - end - def jit_frame(n) - local = 3 - 1 + side_exit(n) + local - end - def entry(n) = jit_frame(n) - entry(2) # profile send - [entry(2), entry(2.0)] - }, call_threshold: 2 - end - - # Test argument ordering - def test_opt_minus - assert_compiles '2', %q{ - def test(a, b) = a - b - test(2, 1) # profile opt_minus - test(6, 4) - }, call_threshold: 2 - end - - def test_opt_mult - assert_compiles '6', %q{ - def test(a, b) = a * b - test(1, 2) # profile opt_mult - test(2, 3) - }, call_threshold: 2 - end - - def test_opt_mult_overflow - assert_compiles '[6, -6, 9671406556917033397649408, -9671406556917033397649408, 21267647932558653966460912964485513216]', %q{ - def test(a, b) - a * b - end - test(1, 1) # profile opt_mult - - r1 = test(2, 3) - r2 = test(2, -3) - r3 = test(2 << 40, 2 << 41) - r4 = test(2 << 40, -2 << 41) - r5 = test(1 << 62, 1 << 62) - - [r1, r2, r3, r4, r5] - }, call_threshold: 2 - end - - def test_opt_eq - assert_compiles '[true, false]', %q{ - def test(a, b) = a == b - test(0, 2) # profile opt_eq - [test(1, 1), test(0, 1)] - }, insns: [:opt_eq], call_threshold: 2 - end - - def test_opt_eq_with_minus_one - assert_compiles '[false, true]', %q{ - def test(a) = a == -1 - test(1) # profile opt_eq - [test(0), test(-1)] - }, insns: [:opt_eq], call_threshold: 2 - end - - def test_opt_neq_dynamic - # TODO(max): Don't split this test; instead, run all tests with and without - # profiling. - assert_compiles '[false, true]', %q{ - def test(a, b) = a != b - test(0, 2) # profile opt_neq - [test(1, 1), test(0, 1)] - }, insns: [:opt_neq], call_threshold: 1 - end - - def test_opt_neq_fixnum - assert_compiles '[false, true]', %q{ - def test(a, b) = a != b - test(0, 2) # profile opt_neq - [test(1, 1), test(0, 1)] - }, call_threshold: 2 - end - - def test_opt_lt - assert_compiles '[true, false, false]', %q{ - def test(a, b) = a < b - test(2, 3) # profile opt_lt - [test(0, 1), test(0, 0), test(1, 0)] - }, insns: [:opt_lt], call_threshold: 2 - end - - def test_opt_lt_with_literal_lhs - assert_compiles '[false, false, true]', %q{ - def test(n) = 2 < n - test(2) # profile opt_lt - [test(1), test(2), test(3)] - }, insns: [:opt_lt], call_threshold: 2 - end - - def test_opt_le - assert_compiles '[true, true, false]', %q{ - def test(a, b) = a <= b - test(2, 3) # profile opt_le - [test(0, 1), test(0, 0), test(1, 0)] - }, insns: [:opt_le], call_threshold: 2 - end - - def test_opt_gt - assert_compiles '[false, false, true]', %q{ - def test(a, b) = a > b - test(2, 3) # profile opt_gt - [test(0, 1), test(0, 0), test(1, 0)] - }, insns: [:opt_gt], call_threshold: 2 - end - - def test_opt_empty_p - assert_compiles('[false, false, true]', <<~RUBY, insns: [:opt_empty_p]) - def test(x) = x.empty? - return test([1]), test("1"), test({}) - RUBY - end - - def test_opt_succ - assert_compiles('[0, "B"]', <<~RUBY, insns: [:opt_succ]) - def test(obj) = obj.succ - return test(-1), test("A") - RUBY - end - - def test_opt_and - assert_compiles('[1, [3, 2, 1]]', <<~RUBY, insns: [:opt_and]) - def test(x, y) = x & y - return test(0b1101, 3), test([3, 2, 1, 4], [8, 1, 2, 3]) - RUBY - end - - def test_opt_or - assert_compiles('[11, [3, 2, 1]]', <<~RUBY, insns: [:opt_or]) - def test(x, y) = x | y - return test(0b1000, 3), test([3, 2, 1], [1, 2, 3]) - RUBY - end - - def test_fixnum_and - assert_compiles '[1, 2, 4]', %q{ - def test(a, b) = a & b - [ - test(5, 3), - test(0b011, 0b110), - test(-0b011, 0b110) - ] - }, call_threshold: 2, insns: [:opt_and] - end - - def test_fixnum_and_side_exit - assert_compiles '[2, 2, false]', %q{ - def test(a, b) = a & b - [ - test(2, 2), - test(0b011, 0b110), - test(true, false) - ] - }, call_threshold: 2, insns: [:opt_and] - end - - def test_fixnum_or - assert_compiles '[7, 3, -3]', %q{ - def test(a, b) = a | b - [ - test(5, 3), - test(1, 2), - test(1, -4) - ] - }, call_threshold: 2, insns: [:opt_or] - end - - def test_fixnum_or_side_exit - assert_compiles '[3, 2, true]', %q{ - def test(a, b) = a | b - [ - test(1, 2), - test(2, 2), - test(true, false) - ] - }, call_threshold: 2, insns: [:opt_or] - end - - def test_fixnum_xor - assert_compiles '[6, -8, 3]', %q{ - def test(a, b) = a ^ b - [ - test(5, 3), - test(-5, 3), - test(1, 2) - ] - }, call_threshold: 2 - end - - def test_fixnum_xor_side_exit - assert_compiles '[6, 6, true]', %q{ - def test(a, b) = a ^ b - [ - test(5, 3), - test(5, 3), - test(true, false) - ] - }, call_threshold: 2 - end - - def test_fixnum_mul - assert_compiles '12', %q{ - C = 3 - def test(n) = C * n - test(4) - test(4) - test(4) - }, call_threshold: 2, insns: [:opt_mult] - end - - def test_fixnum_div - assert_compiles '12', %q{ - C = 48 - def test(n) = C / n - test(4) - test(4) - }, call_threshold: 2, insns: [:opt_div] - end - - def test_fixnum_floor - assert_compiles '0', %q{ - C = 3 - def test(n) = C / n - test(4) - test(4) - }, call_threshold: 2, insns: [:opt_div] - end - - def test_fixnum_div_zero - assert_runs '"divided by 0"', %q{ - def test(n) - n / 0 - rescue ZeroDivisionError => e - e.message - end - - test(0) - test(0) - }, call_threshold: 2, insns: [:opt_div] - end - - def test_opt_not - assert_compiles('[true, true, false]', <<~RUBY, insns: [:opt_not]) - def test(obj) = !obj - return test(nil), test(false), test(0) - RUBY - end - - def test_opt_regexpmatch2 - assert_compiles('[1, nil]', <<~RUBY, insns: [:opt_regexpmatch2]) - def test(haystack) = /needle/ =~ haystack - return test("kneedle"), test("") - RUBY - end - - def test_opt_ge - assert_compiles '[false, true, true]', %q{ - def test(a, b) = a >= b - test(2, 3) # profile opt_ge - [test(0, 1), test(0, 0), test(1, 0)] - }, insns: [:opt_ge], call_threshold: 2 - end - - def test_opt_new_does_not_push_frame - assert_compiles 'nil', %q{ - class Foo - attr_reader :backtrace - - def initialize - @backtrace = caller - end - end - def test = Foo.new - - foo = test - foo.backtrace.find do |frame| - frame.include?('Class#new') - end - }, insns: [:opt_new] - end - - def test_opt_new_with_redefined - assert_compiles '"foo"', %q{ - class Foo - def self.new = "foo" - - def initialize = raise("unreachable") - end - def test = Foo.new - - test - }, insns: [:opt_new] - end - - def test_opt_new_invalidate_new - assert_compiles '["Foo", "foo"]', %q{ - class Foo; end - def test = Foo.new - test; test - result = [test.class.name] - def Foo.new = "foo" - result << test - result - }, insns: [:opt_new], call_threshold: 2 - end - def test_opt_new_with_custom_allocator assert_compiles '"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"', %q{ require "digest" @@ -2171,1208 +164,6 @@ class TestZJIT < Test::Unit::TestCase }, insns: [:opt_new], call_threshold: 2 end - def test_opt_newarray_send_include_p - assert_compiles '[true, false]', %q{ - def test(x) - [:y, 1, Object.new].include?(x) - end - [test(1), test("n")] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_include_p_redefined - assert_compiles '[:true, :false]', %q{ - class Array - alias_method :old_include?, :include? - def include?(x) - old_include?(x) ? :true : :false - end - end - - def test(x) - [:y, 1, Object.new].include?(x) - end - [test(1), test("n")] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_duparray_send_include_p - assert_compiles '[true, false]', %q{ - def test(x) - [:y, 1].include?(x) - end - [test(1), test("n")] - }, insns: [:opt_duparray_send], call_threshold: 1 - end - - def test_opt_duparray_send_include_p_redefined - assert_compiles '[:true, :false]', %q{ - class Array - alias_method :old_include?, :include? - def include?(x) - old_include?(x) ? :true : :false - end - end - - def test(x) - [:y, 1].include?(x) - end - [test(1), test("n")] - }, insns: [:opt_duparray_send], call_threshold: 1 - end - - def test_opt_newarray_send_pack_buffer - assert_compiles '["ABC", "ABC", "ABC", "ABC"]', %q{ - def test(num, buffer) - [num].pack('C', buffer:) - end - buf = "" - [test(65, buf), test(66, buf), test(67, buf), buf] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_pack_buffer_redefined - assert_compiles '["b", "A"]', %q{ - class Array - alias_method :old_pack, :pack - def pack(fmt, buffer: nil) - old_pack(fmt, buffer: buffer) - "b" - end - end - - def test(num, buffer) - [num].pack('C', buffer:) - end - buf = "" - [test(65, buf), buf] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_hash - assert_compiles 'Integer', %q{ - def test(x) - [1, 2, x].hash - end - test(20).class - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_hash_redefined - assert_compiles '42', %q{ - Array.class_eval { def hash = 42 } - - def test(x) - [1, 2, x].hash - end - test(20) - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_max - assert_compiles '[20, 40]', %q{ - def test(a,b) = [a,b].max - [test(10, 20), test(40, 30)] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_opt_newarray_send_max_redefined - assert_compiles '[60, 90]', %q{ - class Array - alias_method :old_max, :max - def max - old_max * 2 - end - end - - def test(a,b) = [a,b].max - [test(15, 30), test(45, 35)] - }, insns: [:opt_newarray_send], call_threshold: 1 - end - - def test_new_hash_empty - assert_compiles '{}', %q{ - def test = {} - test - }, insns: [:newhash] - end - - def test_new_hash_nonempty - assert_compiles '{"key" => "value", 42 => 100}', %q{ - def test - key = "key" - value = "value" - num = 42 - result = 100 - {key => value, num => result} - end - test - }, insns: [:newhash] - end - - def test_new_hash_single_key_value - assert_compiles '{"key" => "value"}', %q{ - def test = {"key" => "value"} - test - }, insns: [:newhash] - end - - def test_new_hash_with_computation - assert_compiles '{"sum" => 5, "product" => 6}', %q{ - def test(a, b) - {"sum" => a + b, "product" => a * b} - end - test(2, 3) - }, insns: [:newhash] - end - - def test_new_hash_with_user_defined_hash_method - assert_runs 'true', %q{ - class CustomKey - attr_reader :val - - def initialize(val) - @val = val - end - - def hash - @val.hash - end - - def eql?(other) - other.is_a?(CustomKey) && @val == other.val - end - end - - def test - key = CustomKey.new("key") - hash = {key => "value"} - hash[key] == "value" - end - test - } - end - - def test_new_hash_with_user_hash_method_exception - assert_runs 'RuntimeError', %q{ - class BadKey - def hash - raise "Hash method failed!" - end - end - - def test - key = BadKey.new - {key => "value"} - end - - begin - test - rescue => e - e.class - end - } - end - - def test_new_hash_with_user_eql_method_exception - assert_runs 'RuntimeError', %q{ - class BadKey - def hash - 42 - end - - def eql?(other) - raise "Eql method failed!" - end - end - - def test - key1 = BadKey.new - key2 = BadKey.new - {key1 => "value1", key2 => "value2"} - end - - begin - test - rescue => e - e.class - end - } - end - - def test_opt_hash_freeze - assert_compiles "[{}, 5]", %q{ - def test = {}.freeze - result = [test] - class Hash - def freeze = 5 - end - result << test - }, insns: [:opt_hash_freeze], call_threshold: 1 - end - - def test_opt_hash_freeze_rewritten - assert_compiles "5", %q{ - class Hash - def freeze = 5 - end - def test = {}.freeze - test - }, insns: [:opt_hash_freeze], call_threshold: 1 - end - - def test_opt_aset_hash - assert_compiles '42', %q{ - def test(h, k, v) - h[k] = v - end - h = {} - test(h, :key, 42) - test(h, :key, 42) - h[:key] - }, call_threshold: 2, insns: [:opt_aset] - end - - def test_opt_aset_hash_returns_value - assert_compiles '100', %q{ - def test(h, k, v) - h[k] = v - end - test({}, :key, 100) - test({}, :key, 100) - }, call_threshold: 2 - end - - def test_opt_aset_hash_string_key - assert_compiles '"bar"', %q{ - def test(h, k, v) - h[k] = v - end - h = {} - test(h, "foo", "bar") - test(h, "foo", "bar") - h["foo"] - }, call_threshold: 2 - end - - def test_opt_aset_hash_subclass - assert_compiles '42', %q{ - class MyHash < Hash; end - def test(h, k, v) - h[k] = v - end - h = MyHash.new - test(h, :key, 42) - test(h, :key, 42) - h[:key] - }, call_threshold: 2 - end - - def test_opt_aset_hash_too_few_args - assert_compiles '"ArgumentError"', %q{ - def test(h) - h.[]= 123 - rescue ArgumentError - "ArgumentError" - end - test({}) - test({}) - }, call_threshold: 2 - end - - def test_opt_aset_hash_too_many_args - assert_compiles '"ArgumentError"', %q{ - def test(h) - h[:a, :b] = :c - rescue ArgumentError - "ArgumentError" - end - test({}) - test({}) - }, call_threshold: 2 - end - - def test_opt_ary_freeze - assert_compiles "[[], 5]", %q{ - def test = [].freeze - result = [test] - class Array - def freeze = 5 - end - result << test - }, insns: [:opt_ary_freeze], call_threshold: 1 - end - - def test_opt_ary_freeze_rewritten - assert_compiles "5", %q{ - class Array - def freeze = 5 - end - def test = [].freeze - test - }, insns: [:opt_ary_freeze], call_threshold: 1 - end - - def test_opt_str_freeze - assert_compiles "[\"\", 5]", %q{ - def test = ''.freeze - result = [test] - class String - def freeze = 5 - end - result << test - }, insns: [:opt_str_freeze], call_threshold: 1 - end - - def test_opt_str_freeze_rewritten - assert_compiles "5", %q{ - class String - def freeze = 5 - end - def test = ''.freeze - test - }, insns: [:opt_str_freeze], call_threshold: 1 - end - - def test_opt_str_uminus - assert_compiles "[\"\", 5]", %q{ - def test = -'' - result = [test] - class String - def -@ = 5 - end - result << test - }, insns: [:opt_str_uminus], call_threshold: 1 - end - - def test_opt_str_uminus_rewritten - assert_compiles "5", %q{ - class String - def -@ = 5 - end - def test = -'' - test - }, insns: [:opt_str_uminus], call_threshold: 1 - end - - def test_new_array_empty - assert_compiles '[]', %q{ - def test = [] - test - }, insns: [:newarray] - end - - def test_new_array_nonempty - assert_compiles '[5]', %q{ - def a = 5 - def test = [a] - test - } - end - - def test_new_array_order - assert_compiles '[3, 2, 1]', %q{ - def a = 3 - def b = 2 - def c = 1 - def test = [a, b, c] - test - } - end - - def test_array_dup - assert_compiles '[1, 2, 3]', %q{ - def test = [1,2,3] - test - } - end - - def test_array_fixnum_aref - assert_compiles '3', %q{ - def test(x) = [1,2,3][x] - test(2) - test(2) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_array_fixnum_aref_negative_index - assert_compiles '3', %q{ - def test(x) = [1,2,3][x] - test(-1) - test(-1) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_array_fixnum_aref_out_of_bounds_positive - assert_compiles 'nil', %q{ - def test(x) = [1,2,3][x] - test(10) - test(10) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_array_fixnum_aref_out_of_bounds_negative - assert_compiles 'nil', %q{ - def test(x) = [1,2,3][x] - test(-10) - test(-10) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_array_fixnum_aref_array_subclass - assert_compiles '3', %q{ - class MyArray < Array; end - def test(arr, idx) = arr[idx] - arr = MyArray[1,2,3] - test(arr, 2) - arr = MyArray[1,2,3] - test(arr, 2) - }, call_threshold: 2, insns: [:opt_aref] - end - - def test_array_aref_non_fixnum_index - assert_compiles 'TypeError', %q{ - def test(arr, idx) = arr[idx] - test([1,2,3], 1) - test([1,2,3], 1) - begin - test([1,2,3], "1") - rescue => e - e.class - end - }, call_threshold: 2 - end - - def test_array_fixnum_aset - assert_compiles '[1, 2, 7]', %q{ - def test(arr, idx) - arr[idx] = 7 - end - arr = [1,2,3] - test(arr, 2) - arr = [1,2,3] - test(arr, 2) - arr - }, call_threshold: 2, insns: [:opt_aset] - end - - def test_array_fixnum_aset_returns_value - assert_compiles '7', %q{ - def test(arr, idx) - arr[idx] = 7 - end - test([1,2,3], 2) - test([1,2,3], 2) - }, call_threshold: 2, insns: [:opt_aset] - end - - def test_array_fixnum_aset_out_of_bounds - assert_compiles '[1, 2, 3, nil, nil, 7]', %q{ - def test(arr) - arr[5] = 7 - end - arr = [1,2,3] - test(arr) - arr = [1,2,3] - test(arr) - arr - }, call_threshold: 2 - end - - def test_array_fixnum_aset_negative_index - assert_compiles '[1, 2, 7]', %q{ - def test(arr) - arr[-1] = 7 - end - arr = [1,2,3] - test(arr) - arr = [1,2,3] - test(arr) - arr - }, call_threshold: 2 - end - - def test_array_fixnum_aset_shared - assert_compiles '[10, 999, -1, -2]', %q{ - def test(arr, idx, val) - arr[idx] = val - end - arr = (0..50).to_a - test(arr, 0, -1) - test(arr, 1, -2) - shared = arr[10, 20] - test(shared, 0, 999) - [arr[10], shared[0], arr[0], arr[1]] - }, call_threshold: 2 - end - - def test_array_fixnum_aset_frozen - assert_compiles 'FrozenError', %q{ - def test(arr, idx, val) - arr[idx] = val - end - arr = [1,2,3] - test(arr, 1, 9) - test(arr, 1, 9) - arr.freeze - begin - test(arr, 1, 9) - rescue => e - e.class - end - }, call_threshold: 2 - end - - def test_array_fixnum_aset_array_subclass - assert_compiles '7', %q{ - class MyArray < Array; end - def test(arr, idx) - arr[idx] = 7 - end - arr = MyArray.new - test(arr, 0) - arr = MyArray.new - test(arr, 0) - arr[0] - }, call_threshold: 2, insns: [:opt_aset] - end - - def test_array_aset_non_fixnum_index - assert_compiles 'TypeError', %q{ - def test(arr, idx) - arr[idx] = 7 - end - test([1,2,3], 0) - test([1,2,3], 0) - begin - test([1,2,3], "0") - rescue => e - e.class - end - }, call_threshold: 2 - end - - def test_empty_array_pop - assert_compiles 'nil', %q{ - def test(arr) = arr.pop - test([]) - test([]) - }, call_threshold: 2 - end - - def test_array_pop_no_arg - assert_compiles '42', %q{ - def test(arr) = arr.pop - test([32, 33, 42]) - test([32, 33, 42]) - }, call_threshold: 2 - end - - def test_array_pop_arg - assert_compiles '[33, 42]', %q{ - def test(arr) = arr.pop(2) - test([32, 33, 42]) - test([32, 33, 42]) - }, call_threshold: 2 - end - - def test_new_range_inclusive - assert_compiles '1..5', %q{ - def test(a, b) = a..b - test(1, 5) - } - end - - def test_new_range_exclusive - assert_compiles '1...5', %q{ - def test(a, b) = a...b - test(1, 5) - } - end - - def test_new_range_with_literal - assert_compiles '3..10', %q{ - def test(n) = n..10 - test(3) - } - end - - def test_new_range_fixnum_both_literals_inclusive - assert_compiles '1..2', %q{ - def test() - a = 2 - (1..a) - end - test; test - }, call_threshold: 2, insns: [:newrange] - end - - def test_new_range_fixnum_both_literals_exclusive - assert_compiles '1...2', %q{ - def test() - a = 2 - (1...a) - end - test; test - }, call_threshold: 2, insns: [:newrange] - end - - def test_new_range_fixnum_low_literal_inclusive - assert_compiles '1..3', %q{ - def test(a) - (1..a) - end - test(2); test(3) - }, call_threshold: 2, insns: [:newrange] - end - - def test_new_range_fixnum_low_literal_exclusive - assert_compiles '1...3', %q{ - def test(a) - (1...a) - end - test(2); test(3) - }, call_threshold: 2, insns: [:newrange] - end - - def test_new_range_fixnum_high_literal_inclusive - assert_compiles '3..10', %q{ - def test(a) - (a..10) - end - test(2); test(3) - }, call_threshold: 2, insns: [:newrange] - end - - def test_new_range_fixnum_high_literal_exclusive - assert_compiles '3...10', %q{ - def test(a) - (a...10) - end - test(2); test(3) - }, call_threshold: 2, insns: [:newrange] - end - - def test_if - assert_compiles '[0, nil]', %q{ - def test(n) - if n < 5 - 0 - end - end - [test(3), test(7)] - } - end - - def test_if_else - assert_compiles '[0, 1]', %q{ - def test(n) - if n < 5 - 0 - else - 1 - end - end - [test(3), test(7)] - } - end - - def test_if_else_params - assert_compiles '[1, 20]', %q{ - def test(n, a, b) - if n < 5 - a - else - b - end - end - [test(3, 1, 2), test(7, 10, 20)] - } - end - - def test_if_else_nested - assert_compiles '[3, 8, 9, 14]', %q{ - def test(a, b, c, d, e) - if 2 < a - if a < 4 - b - else - c - end - else - if a < 0 - d - else - e - end - end - end - [ - test(-1, 1, 2, 3, 4), - test( 0, 5, 6, 7, 8), - test( 3, 9, 10, 11, 12), - test( 5, 13, 14, 15, 16), - ] - } - end - - def test_if_else_chained - assert_compiles '[12, 11, 21]', %q{ - def test(a) - (if 2 < a then 1 else 2 end) + (if a < 4 then 10 else 20 end) - end - [test(0), test(3), test(5)] - } - end - - def test_if_elsif_else - assert_compiles '[0, 2, 1]', %q{ - def test(n) - if n < 5 - 0 - elsif 8 < n - 1 - else - 2 - end - end - [test(3), test(7), test(9)] - } - end - - def test_ternary_operator - assert_compiles '[1, 20]', %q{ - def test(n, a, b) - n < 5 ? a : b - end - [test(3, 1, 2), test(7, 10, 20)] - } - end - - def test_ternary_operator_nested - assert_compiles '[2, 21]', %q{ - def test(n, a, b) - (n < 5 ? a : b) + 1 - end - [test(3, 1, 2), test(7, 10, 20)] - } - end - - def test_while_loop - assert_compiles '10', %q{ - def test(n) - i = 0 - while i < n - i = i + 1 - end - i - end - test(10) - } - end - - def test_while_loop_chain - assert_compiles '[135, 270]', %q{ - def test(n) - i = 0 - while i < n - i = i + 1 - end - while i < n * 10 - i = i * 3 - end - i - end - [test(5), test(10)] - } - end - - def test_while_loop_nested - assert_compiles '[0, 4, 12]', %q{ - def test(n, m) - i = 0 - while i < n - j = 0 - while j < m - j += 2 - end - i += j - end - i - end - [test(0, 0), test(1, 3), test(10, 5)] - } - end - - def test_while_loop_if_else - assert_compiles '[9, -1]', %q{ - def test(n) - i = 0 - while i < n - if n >= 10 - return -1 - else - i = i + 1 - end - end - i - end - [test(9), test(10)] - } - end - - def test_if_while_loop - assert_compiles '[9, 12]', %q{ - def test(n) - i = 0 - if n < 10 - while i < n - i += 1 - end - else - while i < n - i += 3 - end - end - i - end - [test(9), test(10)] - } - end - - def test_live_reg_past_ccall - assert_compiles '2', %q{ - def callee = 1 - def test = callee + callee - test - } - end - - def test_method_call - assert_compiles '12', %q{ - def callee(a, b) - a - b - end - - def test - callee(4, 2) + 10 - end - - test # profile test - test - }, call_threshold: 2 - end - - def test_recursive_fact - assert_compiles '[1, 6, 720]', %q{ - def fact(n) - if n == 0 - return 1 - end - return n * fact(n-1) - end - [fact(0), fact(3), fact(6)] - } - end - - def test_profiled_fact - assert_compiles '[1, 6, 720]', %q{ - def fact(n) - if n == 0 - return 1 - end - return n * fact(n-1) - end - fact(1) # profile fact - [fact(0), fact(3), fact(6)] - }, call_threshold: 3, num_profiles: 2 - end - - def test_recursive_fib - assert_compiles '[0, 2, 3]', %q{ - def fib(n) - if n < 2 - return n - end - return fib(n-1) + fib(n-2) - end - [fib(0), fib(3), fib(4)] - } - end - - def test_profiled_fib - assert_compiles '[0, 2, 3]', %q{ - def fib(n) - if n < 2 - return n - end - return fib(n-1) + fib(n-2) - end - fib(3) # profile fib - [fib(0), fib(3), fib(4)] - }, call_threshold: 5, num_profiles: 3 - end - - def test_spilled_basic_block_args - assert_compiles '55', %q{ - def test(n1, n2) - n3 = 3 - n4 = 4 - n5 = 5 - n6 = 6 - n7 = 7 - n8 = 8 - n9 = 9 - n10 = 10 - if n1 < n2 - n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8 + n9 + n10 - end - end - test(1, 2) - } - end - - def test_spilled_method_args - assert_runs '55', %q{ - def foo(n1, n2, n3, n4, n5, n6, n7, n8, n9, n10) - n1 + n2 + n3 + n4 + n5 + n6 + n7 + n8 + n9 + n10 - end - - def test - foo(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) - end - - test - } - - # TODO(Shopify/ruby#716): Support spills and change to assert_compiles - assert_runs '1', %q{ - def a(n1,n2,n3,n4,n5,n6,n7,n8,n9) = n1+n9 - a(2,0,0,0,0,0,0,0,-1) - } - - # TODO(Shopify/ruby#716): Support spills and change to assert_compiles - assert_runs '0', %q{ - def a(n1,n2,n3,n4,n5,n6,n7,n8) = n8 - a(1,1,1,1,1,1,1,0) - } - - # TODO(Shopify/ruby#716): Support spills and change to assert_compiles - # self param with spilled param - assert_runs '"main"', %q{ - def a(n1,n2,n3,n4,n5,n6,n7,n8) = self - a(1,0,0,0,0,0,0,0).to_s - } - end - - def test_spilled_param_new_arary - # TODO(Shopify/ruby#716): Support spills and change to assert_compiles - assert_runs '[:ok]', %q{ - def a(n1,n2,n3,n4,n5,n6,n7,n8) = [n8] - a(0,0,0,0,0,0,0, :ok) - } - end - - def test_forty_param_method - # This used to a trigger a miscomp on A64 due - # to a memory displacement larger than 9 bits. - # Using assert_runs again due to register spill. - # TODO: It should be fixed by register spill support. - assert_runs '1', %Q{ - def foo(#{'_,' * 39} n40) = n40 - - foo(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1) - } - end - - def test_putself - assert_compiles '3', %q{ - class Integer - def minus(a) - self - a - end - end - 5.minus(2) - } - end - - def test_getinstancevariable - assert_compiles 'nil', %q{ - def test() = @foo - - test() - } - assert_compiles '3', %q{ - @foo = 3 - def test() = @foo - - test() - } - end - - def test_getinstancevariable_miss - assert_compiles '[1, 1, 4]', %q{ - class C - def foo - @foo - end - - def foo_then_bar - @foo = 1 - @bar = 2 - end - - def bar_then_foo - @bar = 3 - @foo = 4 - end - end - - o1 = C.new - o1.foo_then_bar - result = [] - result << o1.foo - result << o1.foo - o2 = C.new - o2.bar_then_foo - result << o2.foo - result - } - end - - def test_setinstancevariable - assert_compiles '1', %q{ - def test() = @foo = 1 - - test() - @foo - } - end - - def test_getclassvariable - assert_compiles '42', %q{ - class Foo - def self.test = @@x - end - - Foo.class_variable_set(:@@x, 42) - Foo.test() - } - end - - def test_getclassvariable_raises - assert_compiles '"uninitialized class variable @@x in Foo"', %q{ - class Foo - def self.test = @@x - end - - begin - Foo.test - rescue NameError => e - e.message - end - } - end - - def test_setclassvariable - assert_compiles '42', %q{ - class Foo - def self.test = @@x = 42 - end - - Foo.test() - Foo.class_variable_get(:@@x) - } - end - - def test_setclassvariable_raises - assert_compiles '"can\'t modify frozen Class: Foo"', %q{ - class Foo - def self.test = @@x = 42 - freeze - end - - begin - Foo.test - rescue FrozenError => e - e.message - end - } - end - - def test_attr_reader - assert_compiles '[4, 4]', %q{ - class C - attr_reader :foo - - def initialize - @foo = 4 - end - end - - def test(c) = c.foo - c = C.new - [test(c), test(c)] - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_attr_accessor_getivar - assert_compiles '[4, 4]', %q{ - class C - attr_accessor :foo - - def initialize - @foo = 4 - end - end - - def test(c) = c.foo - c = C.new - [test(c), test(c)] - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_attr_accessor_setivar - assert_compiles '[5, 5]', %q{ - class C - attr_accessor :foo - - def initialize - @foo = 4 - end - end - - def test(c) - c.foo = 5 - c.foo - end - - c = C.new - [test(c), test(c)] - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_attr_writer - assert_compiles '[5, 5]', %q{ - class C - attr_writer :foo - - def initialize - @foo = 4 - end - - def get_foo = @foo - end - - def test(c) - c.foo = 5 - c.get_foo - end - c = C.new - [test(c), test(c)] - }, call_threshold: 2, insns: [:opt_send_without_block] - end - def test_uncached_getconstant_path assert_compiles RUBY_COPYRIGHT.dump, %q{ def test = RUBY_COPYRIGHT @@ -3380,51 +171,6 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 1, insns: [:opt_getconstant_path] end - def test_getconstant - assert_compiles '1', %q{ - class Foo - CONST = 1 - end - - def test(klass) - klass::CONST - end - - test(Foo) - test(Foo) - }, call_threshold: 2, insns: [:getconstant] - end - - def test_expandarray_no_splat - assert_compiles '[3, 4]', %q{ - def test(o) - a, b = o - [a, b] - end - test [3, 4] - }, call_threshold: 1, insns: [:expandarray] - end - - def test_expandarray_splat - assert_compiles '[3, [4]]', %q{ - def test(o) - a, *b = o - [a, b] - end - test [3, 4] - }, call_threshold: 1, insns: [:expandarray] - end - - def test_expandarray_splat_post - assert_compiles '[3, [4], 5]', %q{ - def test(o) - a, *b, c = o - [a, b, c] - end - test [3, 4, 5] - }, call_threshold: 1, insns: [:expandarray] - end - def test_getconstant_path_autoload # A constant-referencing expression can run arbitrary code through Kernel#autoload. Dir.mktmpdir('autoload') do |tmpdir| @@ -3439,78 +185,6 @@ class TestZJIT < Test::Unit::TestCase end end - def test_constant_invalidation - assert_compiles '123', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path] - class C; end - def test = C - test - test - - C = 123 - test - RUBY - end - - def test_constant_path_invalidation - assert_compiles '["Foo::C", "Foo::C", "Bar::C"]', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path] - module A - module B; end - end - - module Foo - C = "Foo::C" - end - - module Bar - C = "Bar::C" - end - - A::B = Foo - - def test = A::B::C - - result = [] - - result << test - result << test - - A::B = Bar - - result << test - result - RUBY - end - - def test_single_ractor_mode_invalidation - # Without invalidating the single-ractor mode, the test would crash - assert_compiles '"errored but not crashed"', <<~RUBY, call_threshold: 2, insns: [:opt_getconstant_path] - C = Object.new - - def test - C - rescue Ractor::IsolationError - "errored but not crashed" - end - - test - test - - Ractor.new { - test - }.value - RUBY - end - - def test_dupn - assert_compiles '[[1], [1, 1], :rhs, [nil, :rhs]]', <<~RUBY, insns: [:dupn] - def test(array) = (array[1, 2] ||= :rhs) - - one = [1, 1] - start_empty = [] - [test(one), one, test(start_empty), start_empty] - RUBY - end - def test_send_backtrace backtrace = [ "-e:2:in 'Object#jit_frame1'", @@ -3527,276 +201,6 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 2 end - def test_bop_invalidation - assert_compiles '100', %q{ - def test - eval(<<~RUBY) - class Integer - def +(_) = 100 - end - RUBY - 1 + 2 - end - test - } - end - - def test_defined_with_defined_values - assert_compiles '["constant", "method", "global-variable"]', %q{ - class Foo; end - def bar; end - $ruby = 1 - - def test = return defined?(Foo), defined?(bar), defined?($ruby) - - test - }, insns: [:defined] - end - - def test_defined_with_undefined_values - assert_compiles '[nil, nil, nil]', %q{ - def test = return defined?(Foo), defined?(bar), defined?($ruby) - - test - }, insns: [:defined] - end - - def test_defined_with_method_call - assert_compiles '["method", nil]', %q{ - def test = return defined?("x".reverse(1)), defined?("x".reverse(1).reverse) - - test - }, insns: [:defined] - end - - def test_defined_method_raise - assert_compiles '[nil, nil, nil]', %q{ - class C - def assert_equal expected, actual - if expected != actual - raise "NO" - end - end - - def test_defined_method - assert_equal(nil, defined?("x".reverse(1).reverse)) - end - end - - c = C.new - result = [] - result << c.test_defined_method - result << c.test_defined_method - result << c.test_defined_method - result - } - end - - def test_defined_yield - assert_compiles "nil", "defined?(yield)" - assert_compiles '[nil, nil, "yield"]', %q{ - def test = defined?(yield) - [test, test, test{}] - }, call_threshold: 2, insns: [:defined] - end - - def test_defined_yield_from_block - # This will do some EP hopping to find the local EP, - # so it's slightly different than doing it outside of a block. - - assert_compiles '[nil, nil, "yield"]', %q{ - def test - yield_self { yield_self { defined?(yield) } } - end - - [test, test, test{}] - }, call_threshold: 2 - end - - def test_block_given_p - assert_compiles "false", "block_given?" - assert_compiles '[false, false, true]', %q{ - def test = block_given? - [test, test, test{}] - }, call_threshold: 2, insns: [:opt_send_without_block] - end - - def test_block_given_p_from_block - # This will do some EP hopping to find the local EP, - # so it's slightly different than doing it outside of a block. - - assert_compiles '[false, false, true]', %q{ - def test - yield_self { yield_self { block_given? } } - end - - [test, test, test{}] - }, call_threshold: 2 - end - - def test_invokeblock_without_block_after_jit_call - assert_compiles '"no block given (yield)"', %q{ - def test(*arr, &b) - arr.class - yield - end - begin - test - rescue => e - e.message - end - } - end - - def test_putspecialobject_vm_core_and_cbase - assert_compiles '10', %q{ - def test - alias bar test - 10 - end - - test - bar - }, insns: [:putspecialobject] - end - - def test_putspecialobject_const_base - assert_compiles '1', %q{ - Foo = 1 - - def test = Foo - - # First call: populates the constant cache - test - # Second call: triggers ZJIT compilation with warm cache - # RubyVM::ZJIT.assert_compiles will panic if this fails to compile - test - }, call_threshold: 2 - end - - def test_branchnil - assert_compiles '[2, nil]', %q{ - def test(x) - x&.succ - end - [test(1), test(nil)] - }, call_threshold: 1, insns: [:branchnil] - end - - def test_nil_nil - assert_compiles 'true', %q{ - def test = nil.nil? - test - }, insns: [:opt_nil_p] - end - - def test_non_nil_nil - assert_compiles 'false', %q{ - def test = 1.nil? - test - }, insns: [:opt_nil_p] - end - - def test_getspecial_last_match - assert_compiles '"hello"', %q{ - def test(str) - str =~ /hello/ - $& - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_match_pre - assert_compiles '"hello "', %q{ - def test(str) - str =~ /world/ - $` - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_match_post - assert_compiles '" world"', %q{ - def test(str) - str =~ /hello/ - $' - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_match_last_group - assert_compiles '"world"', %q{ - def test(str) - str =~ /(hello) (world)/ - $+ - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_numbered_match_1 - assert_compiles '"hello"', %q{ - def test(str) - str =~ /(hello) (world)/ - $1 - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_numbered_match_2 - assert_compiles '"world"', %q{ - def test(str) - str =~ /(hello) (world)/ - $2 - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_numbered_match_nonexistent - assert_compiles 'nil', %q{ - def test(str) - str =~ /(hello)/ - $2 - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_no_match - assert_compiles 'nil', %q{ - def test(str) - str =~ /xyz/ - $& - end - test("hello world") - }, insns: [:getspecial] - end - - def test_getspecial_complex_pattern - assert_compiles '"123"', %q{ - def test(str) - str =~ /(\d+)/ - $1 - end - test("abc123def") - }, insns: [:getspecial] - end - - def test_getspecial_multiple_groups - assert_compiles '"456"', %q{ - def test(str) - str =~ /(\d+)-(\d+)/ - $2 - end - test("123-456") - }, insns: [:getspecial] - end - # tool/ruby_vm/views/*.erb relies on the zjit instructions a) being contiguous and # b) being reliably ordered after all the other instructions. def test_instruction_order @@ -3880,653 +284,6 @@ class TestZJIT < Test::Unit::TestCase } end - def test_profile_under_nested_jit_call - assert_compiles '[nil, nil, 3]', %q{ - def profile - 1 + 2 - end - - def jit_call(flag) - if flag - profile - end - end - - def entry(flag) - jit_call(flag) - end - - [entry(false), entry(false), entry(true)] - }, call_threshold: 2 - end - - def test_bop_redefined - assert_runs '[3, :+, 100]', %q{ - def test - 1 + 2 - end - - test # profile opt_plus - [test, Integer.class_eval { def +(_) = 100 }, test] - }, call_threshold: 2 - end - - def test_bop_redefined_with_adjacent_patch_points - assert_runs '[15, :+, 100]', %q{ - def test - 1 + 2 + 3 + 4 + 5 - end - - test # profile opt_plus - [test, Integer.class_eval { def +(_) = 100 }, test] - }, call_threshold: 2 - end - - # ZJIT currently only generates a MethodRedefined patch point when the method - # is called on the top-level self. - def test_method_redefined_with_top_self - assert_runs '["original", "redefined"]', %q{ - def foo - "original" - end - - def test = foo - - test; test - - result1 = test - - # Redefine the method - def foo - "redefined" - end - - result2 = test - - [result1, result2] - }, call_threshold: 2 - end - - def test_method_redefined_with_module - assert_runs '["original", "redefined"]', %q{ - module Foo - def self.foo = "original" - end - - def test = Foo.foo - test - result1 = test - - def Foo.foo = "redefined" - result2 = test - - [result1, result2] - }, call_threshold: 2 - end - - def test_module_name_with_guard_passes - assert_compiles '"Integer"', %q{ - def test(mod) - mod.name - end - - test(String) - test(Integer) - }, call_threshold: 2 - end - - def test_module_name_with_guard_side_exit - # This test demonstrates that the guard side exit works correctly - # In this case, when we call with a non-Class object, it should fall back to interpreter - assert_compiles '["String", "Integer", "Bar"]', %q{ - class MyClass - def name = "Bar" - end - - def test(mod) - mod.name - end - - results = [] - results << test(String) - results << test(Integer) - results << test(MyClass.new) - - results - }, call_threshold: 2 - end - - def test_objtostring_calls_to_s_on_non_strings - assert_compiles '["foo", "foo"]', %q{ - results = [] - - class Foo - def to_s - "foo" - end - end - - def test(str) - "#{str}" - end - - results << test(Foo.new) - results << test(Foo.new) - - results - } - end - - def test_objtostring_rewrite_does_not_call_to_s_on_strings - assert_compiles '["foo", "foo"]', %q{ - results = [] - - class String - def to_s - "bad" - end - end - - def test(foo) - "#{foo}" - end - - results << test("foo") - results << test("foo") - - results - } - end - - def test_objtostring_rewrite_does_not_call_to_s_on_string_subclasses - assert_compiles '["foo", "foo"]', %q{ - results = [] - - class StringSubclass < String - def to_s - "bad" - end - end - - foo = StringSubclass.new("foo") - - def test(str) - "#{str}" - end - - results << test(foo) - results << test(foo) - - results - } - end - - def test_objtostring_profiled_string_fastpath - assert_compiles '"foo"', %q{ - def test(str) - "#{str}" - end - test('foo'); test('foo') # profile as string - }, call_threshold: 2 - end - - def test_objtostring_profiled_string_subclass_fastpath - assert_compiles '"foo"', %q{ - class MyString < String; end - - def test(str) - "#{str}" - end - - foo = MyString.new("foo") - test(foo); test(foo) # still profiles as string - }, call_threshold: 2 - end - - def test_objtostring_profiled_string_fastpath_exits_on_nonstring - assert_compiles '"1"', %q{ - def test(str) - "#{str}" - end - - test('foo') # profile as string - test(1) - }, call_threshold: 2 - end - - def test_objtostring_profiled_nonstring_calls_to_s - assert_compiles '"[1, 2, 3]"', %q{ - def test(str) - "#{str}" - end - - test([1,2,3]); # profile as nonstring - test([1,2,3]); - }, call_threshold: 2 - end - - def test_objtostring_profiled_nonstring_guard_exits_when_string - assert_compiles '"foo"', %q{ - def test(str) - "#{str}" - end - - test([1,2,3]); # profiles as nonstring - test('foo'); - }, call_threshold: 2 - end - - def test_string_bytesize_with_guard - assert_compiles '5', %q{ - def test(str) - str.bytesize - end - - test('hello') - test('world') - }, call_threshold: 2 - end - - def test_string_bytesize_multibyte - assert_compiles '4', %q{ - def test(s) - s.bytesize - end - - test("💎") - }, call_threshold: 2 - end - - def test_nil_value_nil_opt_with_guard - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(nil) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_nil_value_nil_opt_with_guard_side_exit - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(nil) - test(nil) - test(1) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_true_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(true) - test(true) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_true_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(true) - test(true) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_false_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(false) - test(false) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_false_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(false) - test(false) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_integer_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(1) - test(2) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_integer_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(1) - test(2) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_float_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(1.0) - test(2.0) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_float_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(1.0) - test(2.0) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_symbol_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(:foo) - test(:bar) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_symbol_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(:foo) - test(:bar) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_class_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(String) - test(Integer) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_class_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(String) - test(Integer) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_module_nil_opt_with_guard - assert_compiles 'false', %q{ - def test(val) = val.nil? - - test(Enumerable) - test(Kernel) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_module_nil_opt_with_guard_side_exit - assert_compiles 'true', %q{ - def test(val) = val.nil? - - test(Enumerable) - test(Kernel) - test(nil) - }, call_threshold: 2, insns: [:opt_nil_p] - end - - def test_basic_object_guard_works_with_immediate - assert_compiles 'NilClass', %q{ - class Foo; end - - def test(val) = val.class - - test(Foo.new) - test(Foo.new) - test(nil) - }, call_threshold: 2 - end - - def test_basic_object_guard_works_with_false - assert_compiles 'FalseClass', %q{ - class Foo; end - - def test(val) = val.class - - test(Foo.new) - test(Foo.new) - test(false) - }, call_threshold: 2 - end - - def test_string_concat - assert_compiles '"123"', %q{ - def test = "#{1}#{2}#{3}" - - test - }, insns: [:concatstrings] - end - - def test_string_concat_empty - assert_compiles '""', %q{ - def test = "#{}" - - test - }, insns: [:concatstrings] - end - - def test_regexp_interpolation - assert_compiles '/123/', %q{ - def test = /#{1}#{2}#{3}/ - - test - }, insns: [:toregexp] - end - - def test_new_range_non_leaf - assert_compiles '(0/1)..1', %q{ - def jit_entry(v) = make_range_then_exit(v) - - def make_range_then_exit(v) - range = (v..1) - super rescue range # TODO(alan): replace super with side-exit intrinsic - end - - jit_entry(0) # profile - jit_entry(0) # compile - jit_entry(0/1r) # run without stub - }, call_threshold: 2 - end - - def test_raise_in_second_argument - assert_compiles '{ok: true}', %q{ - def write(hash, key) - hash[key] = raise rescue true - hash - end - - write({}, :ok) - } - end - - def test_ivar_attr_reader_optimization_with_multi_ractor_mode - assert_compiles '42', %q{ - class Foo - class << self - attr_accessor :bar - - def get_bar - bar - rescue Ractor::IsolationError - 42 - end - end - end - - Foo.bar = [] # needs to be a ractor unshareable object - - def test - Foo.get_bar - end - - test - test - - Ractor.new { test }.value - }, call_threshold: 2 - end - - def test_ivar_get_with_multi_ractor_mode - assert_compiles '42', %q{ - class Foo - def self.set_bar - @bar = [] # needs to be a ractor unshareable object - end - - def self.bar - @bar - rescue Ractor::IsolationError - 42 - end - end - - Foo.set_bar - - def test - Foo.bar - end - - test - test - - Ractor.new { test }.value - }, call_threshold: 2 - end - - def test_ivar_get_with_already_multi_ractor_mode - assert_compiles '42', %q{ - class Foo - def self.set_bar - @bar = [] # needs to be a ractor unshareable object - end - - def self.bar - @bar - rescue Ractor::IsolationError - 42 - end - end - - Foo.set_bar - r = Ractor.new { - Ractor.receive - Foo.bar - } - - Foo.bar - Foo.bar - - r << :go - r.value - }, call_threshold: 2 - end - - def test_ivar_set_with_multi_ractor_mode - assert_compiles '42', %q{ - class Foo - def self.bar - _foo = 1 - _bar = 2 - begin - @bar = _foo + _bar - rescue Ractor::IsolationError - 42 - end - end - end - - def test - Foo.bar - end - - test - test - - Ractor.new { test }.value - } - end - - def test_struct_set - assert_compiles '[42, 42, :frozen_error]', %q{ - C = Struct.new(:foo).new(1) - - def test - C.foo = Object.new - 42 - end - - r = [test, test] - C.freeze - r << begin - test - rescue FrozenError - :frozen_error - end - }, call_threshold: 2 - end - - def test_global_tracepoint - assert_compiles 'true', %q{ - def foo = 1 - - foo - foo - - called = false - - tp = TracePoint.new(:return) { |event| - if event.method_id == :foo - called = true - end - } - tp.enable do - foo - end - called - } - end - - def test_local_tracepoint - assert_compiles 'true', %q{ - def foo = 1 - - foo - foo - - called = false - - tp = TracePoint.new(:return) { |_| called = true } - tp.enable(target: method(:foo)) do - foo - end - called - } - end - def test_line_tracepoint_on_c_method assert_compiles '"[[:line, true]]"', %q{ events = [] @@ -4573,203 +330,6 @@ class TestZJIT < Test::Unit::TestCase } end - def test_opt_case_dispatch - assert_compiles '[true, false]', %q{ - def test(x) - case x - when :foo - true - else - false - end - end - - results = [] - results << test(:foo) - results << test(1) - results - }, insns: [:opt_case_dispatch] - end - - def test_stack_overflow - assert_compiles 'nil', %q{ - def recurse(n) - return if n == 0 - recurse(n-1) - nil # no tail call - end - - recurse(2) - recurse(2) - begin - recurse(20_000) - rescue SystemStackError - # Not asserting an exception is raised here since main - # thread stack size is environment-sensitive. Only - # that we don't crash or infinite loop. - end - }, call_threshold: 2 - end - - def test_invokeblock - assert_compiles '42', %q{ - def test - yield - end - test { 42 } - }, insns: [:invokeblock] - end - - def test_invokeblock_with_args - assert_compiles '3', %q{ - def test(x, y) - yield x, y - end - test(1, 2) { |a, b| a + b } - }, insns: [:invokeblock] - end - - def test_invokeblock_no_block_given - assert_compiles ':error', %q{ - def test - yield rescue :error - end - test - }, insns: [:invokeblock] - end - - def test_invokeblock_multiple_yields - assert_compiles "[1, 2, 3]", %q{ - results = [] - def test - yield 1 - yield 2 - yield 3 - end - test { |x| results << x } - results - }, insns: [:invokeblock] - end - - def test_ccall_variadic_with_multiple_args - assert_compiles "[1, 2, 3]", %q{ - def test - a = [] - a.push(1, 2, 3) - a - end - - test - test - }, insns: [:opt_send_without_block] - end - - def test_ccall_variadic_with_no_args - assert_compiles "[1]", %q{ - def test - a = [1] - a.push - end - - test - test - }, insns: [:opt_send_without_block] - end - - def test_ccall_variadic_with_no_args_causing_argument_error - assert_compiles ":error", %q{ - def test - format - rescue ArgumentError - :error - end - - test - test - }, insns: [:opt_send_without_block] - end - - def test_allocating_in_hir_c_method_is - assert_compiles ":k", %q{ - # Put opt_new in a frame JIT code sets up that doesn't set cfp->pc - def a(f) = test(f) - def test(f) = (f.new if f) - # A parallel couple methods that will set PC at the same stack height - def second = third - def third = nil - - a(nil) - a(nil) - - class Foo - def self.new = :k - end - - second - - a(Foo) - }, call_threshold: 2, insns: [:opt_new] - end - - def test_singleton_class_invalidation_annotated_ccall - assert_compiles '[false, true]', %q{ - def define_singleton(obj, define) - if define - # Wrap in C method frame to avoid exiting JIT on defineclass - [nil].reverse_each do - class << obj - def ==(_) - true - end - end - end - end - false - end - - def test(define) - obj = BasicObject.new - # This == call gets compiled to a CCall - obj == define_singleton(obj, define) - end - - result = [] - result << test(false) # Compiles BasicObject#== - result << test(true) # Should use singleton#== now - result - }, call_threshold: 2 - end - - def test_singleton_class_invalidation_optimized_variadic_ccall - assert_compiles '[1, 1000]', %q{ - def define_singleton(arr, define) - if define - # Wrap in C method frame to avoid exiting JIT on defineclass - [nil].reverse_each do - class << arr - def push(x) - super(x * 1000) - end - end - end - end - 1 - end - - def test(define) - arr = [] - val = define_singleton(arr, define) - arr.push(val) # This CCall should be invalidated if singleton was defined - arr[0] - end - - result = [] - result << test(false) # Compiles Array#push as CCall - result << test(true) # Singleton defined, CCall should be invalidated - result - }, call_threshold: 2 - end - def test_regression_cfp_sp_set_correctly_before_leaf_gc_call assert_compiles ':ok', %q{ def check(l, r) @@ -4799,60 +359,6 @@ class TestZJIT < Test::Unit::TestCase }, call_threshold: 14, num_profiles: 5 end - def test_is_a_string_special_case - assert_compiles '[true, false, false, false, false, false]', %q{ - def test(x) - x.is_a?(String) - end - test("foo") - [test("bar"), test(1), test(false), test(:foo), test([]), test({})] - } - end - - def test_is_a_array_special_case - assert_compiles '[true, true, false, false, false, false, false]', %q{ - def test(x) - x.is_a?(Array) - end - test([]) - [test([1,2,3]), test([]), test(1), test(false), test(:foo), test("foo"), test({})] - } - end - - def test_is_a_hash_special_case - assert_compiles '[true, true, false, false, false, false, false]', %q{ - def test(x) - x.is_a?(Hash) - end - test({}) - [test({:a => "b"}), test({}), test(1), test(false), test(:foo), test([]), test("foo")] - } - end - - def test_is_a_hash_subclass - assert_compiles 'true', %q{ - class MyHash < Hash - end - def test(x) - x.is_a?(Hash) - end - test({}) - test(MyHash.new) - } - end - - def test_is_a_normal_case - assert_compiles '[true, false]', %q{ - class MyClass - end - def test(x) - x.is_a?(MyClass) - end - test("a") - [test(MyClass.new), test("a")] - } - end - def test_exit_tracing # This is a very basic smoke test. The StackProf format # this option generates is external to us. |
