* 2022-01-18 [ci skip]
[ruby-80x24.org.git] / bootstraptest / test_yjit.rb
blobd124d180d172d6fbad1c4c335f234e6b6c804eb7
1 assert_equal '2022', %q{
2  def contrivance(hash, key)
3     # Expect this to compile to an `opt_aref`.
4     hash[key]
6     # The [] call above tracks that the `hash` local has a VALUE that
7     # is a heap pointer and the guard for the Kernel#itself call below
8     # doesn't check that it's a heap pointer VALUE.
9     #
10     # As you can see from the crash, the call to rb_hash_aref() can set the
11     # `hash` local, making eliding the heap object guard unsound.
12     hash.itself
13   end
15   # This is similar to ->(recv, mid) { send(recv, mid).local_variable_set(...) }.
16   # By composing we avoid creating new Ruby frames and so sending :binding
17   # captures the environment of the frame that does the missing key lookup.
18   # We use it to capture the environment inside of `contrivance`.
19   cap_then_set =
20     Kernel.instance_method(:send).method(:bind_call).to_proc >>
21       ->(binding) { binding.local_variable_set(:hash, 2022) }
22   special_missing = Hash.new(&cap_then_set)
24   # Make YJIT speculate that it's a hash and generate code
25   # that calls rb_hash_aref().
26   contrivance({}, :warmup)
27   contrivance({}, :warmup)
29   contrivance(special_missing, :binding)
32 assert_equal '18374962167983112447', %q{
33   # regression test for incorrectly discarding 32 bits of a pointer when it
34   # comes to default values.
35   def large_literal_default(n: 0xff00_fabcafe0_00ff)
36     n
37   end
39   def call_graph_root
40     large_literal_default
41   end
43   call_graph_root
44   call_graph_root
47 assert_normal_exit %q{
48   # regression test for a leak caught by an asert on --yjit-call-threshold=2
49   Foo = 1
51   eval("def foo = [#{(['Foo,']*256).join}]")
53   foo
54   foo
56   Object.send(:remove_const, :Foo)
59 assert_equal '[nil, nil, nil, nil, nil, nil]', %q{
60   [NilClass, TrueClass, FalseClass, Integer, Float, Symbol].each do |klass|
61     klass.class_eval("def foo = @foo")
62   end
64   [nil, true, false, 0xFABCAFE, 0.42, :cake].map do |instance|
65     instance.foo
66     instance.foo
67   end
70 assert_equal '0', %q{
71   # This is a regression test for incomplete invalidation from
72   # opt_setinlinecache. This test might be brittle, so
73   # feel free to remove it in the future if it's too annoying.
74   # This test assumes --yjit-call-threshold=2.
75   module M
76     Foo = 1
77     def foo
78       Foo
79     end
81     def pin_self_type_then_foo
82       _ = @foo
83       foo
84     end
86     def only_ints
87       1 + self
88       foo
89     end
90   end
92   class Integer
93     include M
94   end
96   class Sub
97     include M
98   end
100   foo_method = M.instance_method(:foo)
102   dbg = ->(message) do
103     return # comment this out to get printouts
105     $stderr.puts RubyVM::YJIT.disasm(foo_method)
106     $stderr.puts message
107   end
109   2.times { 42.only_ints }
111   dbg["There should be two versions of getinlineache"]
113   module M
114     remove_const(:Foo)
115   end
117   dbg["There should be no getinlinecaches"]
119   2.times do
120     42.only_ints
121   rescue NameError => err
122     _ = "caught name error #{err}"
123   end
125   dbg["There should be one version of getinlineache"]
127   2.times do
128     Sub.new.pin_self_type_then_foo
129   rescue NameError
130     _ = 'second specialization'
131   end
133   dbg["There should be two versions of getinlineache"]
135   module M
136     Foo = 1
137   end
139   dbg["There should still be two versions of getinlineache"]
141   42.only_ints
143   dbg["There should be no getinlinecaches"]
145   # Find name of the first VM instruction in M#foo.
146   insns = RubyVM::InstructionSequence.of(foo_method).to_a
147   if defined?(RubyVM::YJIT.blocks_for) && (insns.last.find { Array === _1 }&.first == :opt_getinlinecache)
148     RubyVM::YJIT.blocks_for(RubyVM::InstructionSequence.of(foo_method))
149       .filter { _1.iseq_start_index == 0 }.count
150   else
151     0 # skip the test
152   end
155 # Check that frozen objects are respected
156 assert_equal 'great', %q{
157   class Foo
158     attr_accessor :bar
159     def initialize
160       @bar = 1
161       freeze
162     end
163   end
165   foo = Foo.new
167   5.times do
168     begin
169       foo.bar = 2
170     rescue FrozenError
171     end
172   end
174   foo.bar == 1 ? "great" : "NG"
177 # Check that global variable set works
178 assert_equal 'string', %q{
179   def foo
180     $foo = "string"
181   end
183   foo
186 # Check that exceptions work when setting global variables
187 assert_equal 'rescued', %q{
188   def set_var
189     $var = 100
190   rescue
191     :rescued
192   end
194   set_var
195   trace_var(:$var) { raise }
196   set_var
199 # Check that global variables work
200 assert_equal 'string', %q{
201   $foo = "string"
203   def foo
204     $foo
205   end
207   foo
210 # Check that exceptions work when getting global variable
211 assert_equal 'rescued', %q{
212   module Warning
213     def warn(message)
214       raise
215     end
216   end
218   def get_var
219     $=
220   rescue
221     :rescued
222   end
224   $VERBOSE = true
225   get_var
226   get_var
229 # Check that global tracepoints work
230 assert_equal 'true', %q{
231   def foo
232     1
233   end
235   foo
236   foo
237   foo
239   called = false
241   tp = TracePoint.new(:return) { |event|
242     if event.method_id == :foo
243       called = true
244     end
245   }
246   tp.enable
247   foo
248   tp.disable
249   called
252 # Check that local tracepoints work
253 assert_equal 'true', %q{
254   def foo
255     1
256   end
258   foo
259   foo
260   foo
262   called = false
264   tp = TracePoint.new(:return) { |_| called = true }
265   tp.enable(target: method(:foo))
266   foo
267   tp.disable
268   called
271 # Make sure that optional param methods return the correct value
272 assert_equal '1', %q{
273   def m(ary = [])
274     yield(ary)
275   end
277   # Warm the JIT with a 0 param call
278   2.times { m { } }
279   m(1) { |v| v }
282 # Test for topn
283 assert_equal 'array', %q{
284   def threequals(a)
285     case a
286     when Array
287       "array"
288     when Hash
289       "hash"
290     else
291       "unknown"
292     end
293   end
295   threequals([])
296   threequals([])
297   threequals([])
300 # Test for opt_mod
301 assert_equal '2', %q{
302   def mod(a, b)
303     a % b
304   end
306   mod(7, 5)
307   mod(7, 5)
310 # Test for opt_mult
311 assert_equal '12', %q{
312   def mult(a, b)
313     a * b
314   end
316   mult(6, 2)
317   mult(6, 2)
320 # Test for opt_div
321 assert_equal '3', %q{
322   def div(a, b)
323     a / b
324   end
326   div(6, 2)
327   div(6, 2)
330 # BOP redefined methods work when JIT compiled
331 assert_equal 'false', %q{
332   def less_than x
333     x < 10
334   end
336   class Integer
337     def < x
338       false
339     end
340   end
342   less_than 2
343   less_than 2
344   less_than 2
347 # BOP redefinition works on Integer#<
348 assert_equal 'false', %q{
349   def less_than x
350     x < 10
351   end
353   less_than 2
354   less_than 2
356   class Integer
357     def < x
358       false
359     end
360   end
362   less_than 2
365 # Putobject, less-than operator, fixnums
366 assert_equal '2', %q{
367     def check_index(index)
368         if 0x40000000 < index
369             raise "wat? #{index}"
370         end
371         index
372     end
373     check_index 2
374     check_index 2
377 # foo leaves a temp on the stack before the call
378 assert_equal '6', %q{
379     def bar
380         return 5
381     end
383     def foo
384         return 1 + bar
385     end
387     foo()
388     retval = foo()
391 # Method with one arguments
392 # foo leaves a temp on the stack before the call
393 assert_equal '7', %q{
394     def bar(a)
395         return a + 1
396     end
398     def foo
399         return 1 + bar(5)
400     end
402     foo()
403     retval = foo()
406 # Method with two arguments
407 # foo leaves a temp on the stack before the call
408 assert_equal '0', %q{
409     def bar(a, b)
410         return a - b
411     end
413     def foo
414         return 1 + bar(1, 2)
415     end
417     foo()
418     retval = foo()
421 # Passing argument types to callees
422 assert_equal '8.5', %q{
423     def foo(x, y)
424         x + y
425     end
427     def bar
428         foo(7, 1.5)
429     end
431     bar
432     bar
435 # Recursive Ruby-to-Ruby calls
436 assert_equal '21', %q{
437     def fib(n)
438         if n < 2
439             return n
440         end
442         return fib(n-1) + fib(n-2)
443     end
445     r = fib(8)
448 # Ruby-to-Ruby call and C call
449 assert_normal_exit %q{
450   def bar
451     puts('hi!')
452   end
454   def foo
455     bar
456   end
458   foo()
459   foo()
462 # Method aliasing
463 assert_equal '42', %q{
464   class Foo
465     def method_a
466       42
467     end
469     alias method_b method_a
471     def method_a
472         :somethingelse
473     end
474   end
476   @obj = Foo.new
478   def test
479     @obj.method_b
480   end
482   test
483   test
486 # Method aliasing with method from parent class
487 assert_equal '777', %q{
488   class A
489     def method_a
490       777
491     end
492   end
494   class B < A
495     alias method_b method_a
496   end
498   @obj = B.new
500   def test
501     @obj.method_b
502   end
504   test
505   test
508 # The hash method is a C function and uses the self argument
509 assert_equal 'true', %q{
510     def lehashself
511         hash
512     end
514     a = lehashself
515     b = lehashself
516     a == b
519 # Method redefinition (code invalidation) test
520 assert_equal '1', %q{
521     def ret1
522         return 1
523     end
525     klass = Class.new do
526         def alias_then_hash(klass, method_to_redefine)
527             # Redefine the method to be ret1
528             klass.alias_method(method_to_redefine, :ret1)
529             hash
530         end
531     end
533     instance = klass.new
535     i = 0
536     while i < 12
537         if i < 11
538             # Redefine the bar method
539             instance.alias_then_hash(klass, :bar)
540         else
541             # Redefine the hash method to be ret1
542             retval = instance.alias_then_hash(klass, :hash)
543         end
544         i += 1
545     end
547     retval
550 # Code invalidation and opt_getinlinecache
551 assert_normal_exit %q{
552   class Foo; end
554   # Uses the class constant Foo
555   def use_constant(arg)
556     [Foo.new, arg]
557   end
559   def propagate_type
560     i = Array.new
561     i.itself # make it remember that i is on-heap
562     use_constant(i)
563   end
565   propagate_type
566   propagate_type
567   use_constant(Foo.new)
568   class Jo; end # bump global constant state
569   use_constant(3)
572 # Method redefinition (code invalidation) and GC
573 assert_equal '7', %q{
574     def bar()
575         return 5
576     end
578     def foo()
579         bar()
580     end
582     foo()
583     foo()
585     def bar()
586         return 7
587     end
589     4.times { GC.start }
591     foo()
592     foo()
595 # Method redefinition with two block versions
596 assert_equal '7', %q{
597     def bar()
598         return 5
599     end
601     def foo(n)
602         return ((n < 5)? 5:false), bar()
603     end
605     foo(4)
606     foo(4)
607     foo(10)
608     foo(10)
610     def bar()
611         return 7
612     end
614     4.times { GC.start }
616     foo(4)
617     foo(4)[1]
620 # Method redefinition while the method is on the stack
621 assert_equal '[777, 1]', %q{
622     def foo
623         redef()
624         777
625     end
627     def redef
628         # Redefine the global foo
629         eval("def foo; 1; end", TOPLEVEL_BINDING)
631         # Collect dead code
632         GC.stress = true
633         GC.start
635         # But we will return to the original foo,
636         # which remains alive because it's on the stack
637     end
639     # Must produce [777, 1]
640     [foo, foo]
643 # Test for GC safety. Don't invalidate dead iseqs.
644 assert_normal_exit %q{
645   Class.new do
646     def foo
647       itself
648     end
650     new.foo
651     new.foo
652     new.foo
653     new.foo
654   end
656   4.times { GC.start }
657   def itself
658     self
659   end
662 # test setinstancevariable on extended objects
663 assert_equal '1', %q{
664   class Extended
665     attr_reader :one
667     def write_many
668       @a = 1
669       @b = 2
670       @c = 3
671       @d = 4
672       @one = 1
673     end
674   end
676   foo = Extended.new
677   foo.write_many
678   foo.write_many
679   foo.write_many
682 # test setinstancevariable on embedded objects
683 assert_equal '1', %q{
684   class Embedded
685     attr_reader :one
687     def write_one
688       @one = 1
689     end
690   end
692   foo = Embedded.new
693   foo.write_one
694   foo.write_one
695   foo.write_one
698 # test setinstancevariable after extension
699 assert_equal '[10, 11, 12, 13, 1]', %q{
700   class WillExtend
701     attr_reader :one
703     def make_extended
704       @foo1 = 10
705       @foo2 = 11
706       @foo3 = 12
707       @foo4 = 13
708     end
710     def write_one
711       @one = 1
712     end
714     def read_all
715       [@foo1, @foo2, @foo3, @foo4, @one]
716     end
717   end
719   foo = WillExtend.new
720   foo.write_one
721   foo.write_one
722   foo.make_extended
723   foo.write_one
724   foo.read_all
727 # test setinstancevariable on frozen object
728 assert_equal 'object was not modified', %q{
729   class WillFreeze
730     def write
731       @ivar = 1
732     end
733   end
735   wf = WillFreeze.new
736   wf.write
737   wf.write
738   wf.freeze
740   begin
741     wf.write
742   rescue FrozenError
743     "object was not modified"
744   end
747 # Test getinstancevariable and inline caches
748 assert_equal '6', %q{
749   class Foo
750     def initialize
751       @x1 = 1
752       @x2 = 1
753       @x2 = 1
754       @x3 = 1
755       @x4 = 3
756     end
758     def bar
759       x = 1
760       @x4 + @x4
761     end
762   end
764   f = Foo.new
765   f.bar
766   f.bar
769 # Test that getinstancevariable codegen checks for extended table size
770 assert_equal "nil\n", %q{
771   class A
772     def read
773       @ins1000
774     end
775   end
777   ins = A.new
778   other = A.new
779   10.times { other.instance_variable_set(:"@otr#{_1}", 'value') }
780   1001.times { ins.instance_variable_set(:"@ins#{_1}", 'value') }
782   ins.read
783   ins.read
784   ins.read
786   p other.read
789 # Test that opt_aref checks the class of the receiver
790 assert_equal 'special', %q{
791   def foo(array)
792     array[30]
793   end
795   foo([])
796   foo([])
798   special = []
799   def special.[](idx)
800     'special'
801   end
803   foo(special)
806 # Test that object references in generated code get marked and moved
807 assert_equal "good", %q{
808   def bar
809     "good"
810   end
812   def foo
813     bar
814   end
816   foo
817   foo
819   begin
820     GC.verify_compaction_references(double_heap: true, toward: :empty)
821   rescue NotImplementedError
822     # in case compaction isn't supported
823   end
825   foo
828 # Test polymorphic getinstancevariable. T_OBJECT -> T_STRING
829 assert_equal 'ok', %q{
830   @hello = @h1 = @h2 = @h3 = @h4 = 'ok'
831   str = ""
832   str.instance_variable_set(:@hello, 'ok')
834   public def get
835     @hello
836   end
838   get
839   get
840   str.get
841   str.get
844 # Test polymorphic getinstancevariable, two different classes
845 assert_equal 'ok', %q{
846   class Embedded
847     def initialize
848       @ivar = 0
849     end
851     def get
852       @ivar
853     end
854   end
856   class Extended < Embedded
857     def initialize
858       @v1 = @v2 = @v3 = @v4 = @ivar = 'ok'
859     end
860   end
862   embed = Embedded.new
863   extend = Extended.new
865   embed.get
866   embed.get
867   extend.get
868   extend.get
871 # Test megamorphic getinstancevariable
872 assert_equal 'ok', %q{
873   parent = Class.new do
874     def initialize
875       @hello = @h1 = @h2 = @h3 = @h4 = 'ok'
876     end
878     def get
879       @hello
880     end
881   end
883   subclasses = 300.times.map { Class.new(parent) }
884   subclasses.each { _1.new.get }
885   parent.new.get
888 # Test polymorphic opt_aref. array -> hash
889 assert_equal '[42, :key]', %q{
890   def index(obj, idx)
891     obj[idx]
892   end
894   index([], 0) # get over compilation threshold
896   [
897     index([42], 0),
898     index({0=>:key}, 0),
899   ]
902 # Test polymorphic opt_aref. hash -> array -> custom class
903 assert_equal '[nil, nil, :custom]', %q{
904   def index(obj, idx)
905     obj[idx]
906   end
908   custom = Object.new
909   def custom.[](_idx)
910     :custom
911   end
913   index({}, 0) # get over compilation threshold
915   [
916     index({}, 0),
917     index([], 0),
918     index(custom, 0)
919   ]
922 # Test polymorphic opt_aref. array -> custom class
923 assert_equal '[42, :custom]', %q{
924   def index(obj, idx)
925     obj[idx]
926   end
928   custom = Object.new
929   def custom.[](_idx)
930     :custom
931   end
933   index([], 0) # get over compilation threshold
935   [
936     index([42], 0),
937     index(custom, 0)
938   ]
941 # Test custom hash method with opt_aref
942 assert_equal '[nil, :ok]', %q{
943   def index(obj, idx)
944     obj[idx]
945   end
947   custom = Object.new
948   def custom.hash
949     42
950   end
952   h = {custom => :ok}
954   [
955     index(h, 0),
956     index(h, custom)
957   ]
960 # Test default value block for Hash with opt_aref
961 assert_equal '[42, :default]', %q{
962   def index(obj, idx)
963     obj[idx]
964   end
966   h = Hash.new { :default }
967   h[0] = 42
969   [
970     index(h, 0),
971     index(h, 1)
972   ]
975 # A regression test for making sure cfp->sp is proper when
976 # hitting stubs. See :stub-sp-flush:
977 assert_equal 'ok', %q{
978   class D
979     def foo
980       Object.new
981     end
982   end
984   GC.stress = true
985   10.times do
986     D.new.foo
987     #    ^
988     #  This hits a stub with sp_offset > 0
989   end
991   :ok
994 # Test polymorphic callsite, cfunc -> iseq
995 assert_equal '[Cfunc, Iseq]', %q{
996   public def call_itself
997     itself # the polymorphic callsite
998   end
1000   class Cfunc; end
1002   class Iseq
1003     def itself
1004       self
1005     end
1006   end
1008   call_itself # cross threshold
1010   [Cfunc.call_itself, Iseq.call_itself]
1013 # Test polymorphic callsite, iseq -> cfunc
1014 assert_equal '[Iseq, Cfunc]', %q{
1015   public def call_itself
1016     itself # the polymorphic callsite
1017   end
1019   class Cfunc; end
1021   class Iseq
1022     def itself
1023       self
1024     end
1025   end
1027   call_itself # cross threshold
1029   [Iseq.call_itself, Cfunc.call_itself]
1032 # attr_reader method
1033 assert_equal '[100, 299]', %q{
1034   class A
1035     attr_reader :foo
1037     def initialize
1038       @foo = 100
1039     end
1041     # Make it extended
1042     def fill!
1043       @bar = @jojo = @as = @sdfsdf = @foo = 299
1044     end
1045   end
1047   def bar(ins)
1048     ins.foo
1049   end
1051   ins = A.new
1052   oth = A.new
1053   oth.fill!
1055   bar(ins)
1056   bar(oth)
1058   [bar(ins), bar(oth)]
1061 # get ivar on object, then on hash
1062 assert_equal '[42, 100]', %q{
1063   class Hash
1064     attr_accessor :foo
1065   end
1067   class A
1068     attr_reader :foo
1070     def initialize
1071       @foo = 42
1072     end
1073   end
1075   def use(val)
1076     val.foo
1077   end
1080   h = {}
1081   h.foo = 100
1082   obj = A.new
1084   use(obj)
1085   [use(obj), use(h)]
1088 # get ivar on String
1089 assert_equal '[nil, nil, 42, 42]', %q{
1090   # @foo to exercise the getinstancevariable instruction
1091   public def get_foo
1092     @foo
1093   end
1095   get_foo
1096   get_foo # compile it for the top level object
1098   class String
1099     attr_reader :foo
1100   end
1102   def run
1103     str = String.new
1105     getter = str.foo
1106     insn = str.get_foo
1108     str.instance_variable_set(:@foo, 42)
1110     [getter, insn, str.foo, str.get_foo]
1111   end
1113   run
1114   run
1117 # splatting an empty array on a getter
1118 assert_equal '42', %q{
1119   @foo = 42
1120   module Kernel
1121     attr_reader :foo
1122   end
1124   def run
1125     foo(*[])
1126   end
1128   run
1129   run
1132 # getinstancevariable on Symbol
1133 assert_equal '[nil, nil]', %q{
1134   # @foo to exercise the getinstancevariable instruction
1135   public def get_foo
1136     @foo
1137   end
1139   dyn_sym = ("a" + "b").to_sym
1140   sym = :static
1142   # compile get_foo
1143   dyn_sym.get_foo
1144   dyn_sym.get_foo
1146   [dyn_sym.get_foo, sym.get_foo]
1149 # attr_reader on Symbol
1150 assert_equal '[nil, nil]', %q{
1151   class Symbol
1152     attr_reader :foo
1153   end
1155   public def get_foo
1156     foo
1157   end
1159   dyn_sym = ("a" + "b").to_sym
1160   sym = :static
1162   # compile get_foo
1163   dyn_sym.get_foo
1164   dyn_sym.get_foo
1166   [dyn_sym.get_foo, sym.get_foo]
1169 # passing too few arguments to method with optional parameters
1170 assert_equal 'raised', %q{
1171   def opt(a, b = 0)
1172   end
1174   def use
1175     opt
1176   end
1178   use rescue nil
1179   begin
1180     use
1181     :ng
1182   rescue ArgumentError
1183     :raised
1184   end
1187 # passing too many arguments to method with optional parameters
1188 assert_equal 'raised', %q{
1189   def opt(a, b = 0)
1190   end
1192   def use
1193     opt(1, 2, 3, 4)
1194   end
1196   use rescue nil
1197   begin
1198     use
1199     :ng
1200   rescue ArgumentError
1201     :raised
1202   end
1205 # test calling Ruby method with a block
1206 assert_equal '[1, 2, 42]', %q{
1207   def thing(a, b)
1208     [a, b, yield]
1209   end
1211   def use
1212     thing(1,2) { 42 }
1213   end
1215   use
1216   use
1219 # test calling C method with a block
1220 assert_equal '[42, 42]', %q{
1221   def use(array, initial)
1222     array.reduce(initial) { |a, b| a + b }
1223   end
1225   use([], 0)
1226   [use([2, 2], 38), use([14, 14, 14], 0)]
1229 # test calling block param
1230 assert_equal '[1, 2, 42]', %q{
1231   def foo(&block)
1232     block.call
1233   end
1235   [foo {1}, foo {2}, foo {42}]
1238 # test calling block param failing
1239 assert_equal '42', %q{
1240   def foo(&block)
1241     block.call
1242   end
1244   foo {} # warmup
1246   begin
1247     foo
1248   rescue NoMethodError => e
1249     42 if nil == e.receiver
1250   end
1253 # test calling method taking block param
1254 assert_equal '[Proc, 1, 2, 3, Proc]', %q{
1255   def three(a, b, c, &block)
1256     [a, b, c, block.class]
1257   end
1259   def zero(&block)
1260     block.class
1261   end
1263   def use_three
1264     three(1, 2, 3) {}
1265   end
1267   def use_zero
1268     zero {}
1269   end
1271   use_three
1272   use_zero
1274   [use_zero] + use_three
1277 # test building empty array
1278 assert_equal '[]', %q{
1279   def build_arr
1280     []
1281   end
1283   build_arr
1284   build_arr
1287 # test building array of one element
1288 assert_equal '[5]', %q{
1289   def build_arr(val)
1290     [val]
1291   end
1293   build_arr(5)
1294   build_arr(5)
1297 # test building array of several element
1298 assert_equal '[5, 5, 5, 5, 5]', %q{
1299   def build_arr(val)
1300     [val, val, val, val, val]
1301   end
1303   build_arr(5)
1304   build_arr(5)
1307 # test building empty hash
1308 assert_equal '{}', %q{
1309   def build_hash
1310     {}
1311   end
1313   build_hash
1314   build_hash
1317 # test building hash with values
1318 assert_equal '{:foo=>:bar}', %q{
1319   def build_hash(val)
1320     { foo: val }
1321   end
1323   build_hash(:bar)
1324   build_hash(:bar)
1327 # test string interpolation with known types
1328 assert_equal 'foobar', %q{
1329   def make_str
1330     foo = -"foo"
1331     bar = -"bar"
1332     "#{foo}#{bar}"
1333   end
1335   make_str
1336   make_str
1339 # test string interpolation with unknown types
1340 assert_equal 'foobar', %q{
1341   def make_str(foo, bar)
1342     "#{foo}#{bar}"
1343   end
1345   make_str("foo", "bar")
1346   make_str("foo", "bar")
1349 # test string interpolation with known non-strings
1350 assert_equal 'foo123', %q{
1351   def make_str
1352     foo = -"foo"
1353     bar = 123
1354     "#{foo}#{bar}"
1355   end
1357   make_str
1358   make_str
1361 # test string interpolation with unknown non-strings
1362 assert_equal 'foo123', %q{
1363   def make_str(foo, bar)
1364     "#{foo}#{bar}"
1365   end
1367   make_str("foo", 123)
1368   make_str("foo", 123)
1371 # test string interpolation with overridden to_s
1372 assert_equal 'foo', %q{
1373   class String
1374     def to_s
1375       "bad"
1376     end
1377   end
1379   def make_str(foo)
1380     "#{foo}"
1381   end
1383   make_str("foo")
1384   make_str("foo")
1388 # test invokebuiltin as used in struct assignment
1389 assert_equal '123', %q{
1390   def foo(obj)
1391     obj.foo = 123
1392   end
1394   struct = Struct.new(:foo)
1395   obj = struct.new
1396   foo(obj)
1397   foo(obj)
1400 # test invokebuiltin_delegate as used inside Dir.open
1401 assert_equal '.', %q{
1402   def foo(path)
1403     Dir.open(path).path
1404   end
1406   foo(".")
1407   foo(".")
1410 # test invokebuiltin_delegate_leave in method called from jit
1411 assert_normal_exit %q{
1412   def foo(obj)
1413     obj.clone
1414   end
1416   foo(Object.new)
1417   foo(Object.new)
1420 # test invokebuiltin_delegate_leave in method called from cfunc
1421 assert_normal_exit %q{
1422   def foo(obj)
1423     [obj].map(&:clone)
1424   end
1426   foo(Object.new)
1427   foo(Object.new)
1430 # defining TrueClass#!
1431 assert_equal '[false, false, :ok]', %q{
1432   def foo(obj)
1433     !obj
1434   end
1436   x = foo(true)
1437   y = foo(true)
1439   class TrueClass
1440     def !
1441       :ok
1442     end
1443   end
1445   z = foo(true)
1447   [x, y, z]
1450 # defining FalseClass#!
1451 assert_equal '[true, true, :ok]', %q{
1452   def foo(obj)
1453     !obj
1454   end
1456   x = foo(false)
1457   y = foo(false)
1459   class FalseClass
1460     def !
1461       :ok
1462     end
1463   end
1465   z = foo(false)
1467   [x, y, z]
1470 # defining NilClass#!
1471 assert_equal '[true, true, :ok]', %q{
1472   def foo(obj)
1473     !obj
1474   end
1476   x = foo(nil)
1477   y = foo(nil)
1479   class NilClass
1480     def !
1481       :ok
1482     end
1483   end
1485   z = foo(nil)
1487   [x, y, z]
1490 # polymorphic opt_not
1491 assert_equal '[true, true, false, false, false, false, false]', %q{
1492   def foo(obj)
1493     !obj
1494   end
1496   foo(0)
1497   [foo(nil), foo(false), foo(true), foo([]), foo(0), foo(4.2), foo(:sym)]
1500 # getlocal with 2 levels
1501 assert_equal '7', %q{
1502   def foo(foo, bar)
1503     while foo > 0
1504       while bar > 0
1505         return foo + bar
1506       end
1507     end
1508   end
1510   foo(5,2)
1511   foo(5,2)
1514 # test pattern matching
1515 assert_equal '[:ok, :ok]', %q{
1516   class C
1517     def destructure_keys
1518       {}
1519     end
1520   end
1522   pattern_match = ->(i) do
1523     case i
1524     in a: 0
1525       :ng
1526     else
1527       :ok
1528     end
1529   end
1531   [{}, C.new].map(&pattern_match)
1534 # Call to object with singleton
1535 assert_equal '123', %q{
1536   obj = Object.new
1537   def obj.foo
1538     123
1539   end
1541   def foo(obj)
1542     obj.foo()
1543   end
1545   foo(obj)
1546   foo(obj)
1549 # Call method on an object that has a non-material
1550 # singleton class.
1551 # TODO: assert that it takes no side exits? This
1552 # test case revealed that we were taking exits unnecessarily.
1553 assert_normal_exit %q{
1554   def foo(obj)
1555     obj.itself
1556   end
1558   o = Object.new.singleton_class
1559   foo(o)
1560   foo(o)
1563 # Call to singleton class
1564 assert_equal '123', %q{
1565   class Foo
1566     def self.foo
1567       123
1568     end
1569   end
1571   def foo(obj)
1572     obj.foo()
1573   end
1575   foo(Foo)
1576   foo(Foo)
1579 # invokesuper edge case
1580 assert_equal '[:A, [:A, :B]]', %q{
1581   class B
1582     def foo = :B
1583   end
1585   class A < B
1586     def foo = [:A, super()]
1587   end
1589   A.new.foo
1590   A.new.foo # compile A#foo
1592   class C < A
1593     define_method(:bar, A.instance_method(:foo))
1594   end
1596   C.new.bar
1599 # Same invokesuper bytecode, multiple destinations
1600 assert_equal '[:Forward, :SecondTerminus]', %q{
1601   module Terminus
1602     def foo = :Terminus
1603   end
1605   module SecondTerminus
1606     def foo = :SecondTerminus
1607   end
1610   module Forward
1611     def foo = [:Forward, super]
1612   end
1614   class B
1615     include SecondTerminus
1616   end
1618   class A < B
1619     include Terminus
1620     include Forward
1621   end
1623   A.new.foo
1624   A.new.foo # compile
1626   class B
1627     include Forward
1628     alias bar foo
1629   end
1631   # A.ancestors.take(5) == [A, Forward, Terminus, B, Forward, SecondTerminus]
1633   A.new.bar
1636 # invokesuper calling into itself
1637 assert_equal '[:B, [:B, :m]]', %q{
1638   module M
1639     def foo = :m
1640   end
1642   class B
1643     include M
1644     def foo = [:B, super]
1645   end
1647   ins = B.new
1648   ins.singleton_class # materialize the singleton class
1649   ins.foo
1650   ins.foo # compile
1652   ins.singleton_class.define_method(:bar, B.instance_method(:foo))
1653   ins.bar
1656 # invokesuper changed ancestor
1657 assert_equal '[:A, [:M, :B]]', %q{
1658   class B
1659     def foo
1660       :B
1661     end
1662   end
1664   class A < B
1665     def foo
1666       [:A, super]
1667     end
1668   end
1670   module M
1671     def foo
1672       [:M, super]
1673     end
1674   end
1676   ins = A.new
1677   ins.foo
1678   ins.foo
1679   A.include(M)
1680   ins.foo
1683 # invokesuper changed ancestor via prepend
1684 assert_equal '[:A, [:M, :B]]', %q{
1685   class B
1686     def foo
1687       :B
1688     end
1689   end
1691   class A < B
1692     def foo
1693       [:A, super]
1694     end
1695   end
1697   module M
1698     def foo
1699       [:M, super]
1700     end
1701   end
1703   ins = A.new
1704   ins.foo
1705   ins.foo
1706   B.prepend(M)
1707   ins.foo
1710 # invokesuper replaced method
1711 assert_equal '[:A, :Btwo]', %q{
1712   class B
1713     def foo
1714       :B
1715     end
1716   end
1718   class A < B
1719     def foo
1720       [:A, super]
1721     end
1722   end
1724   ins = A.new
1725   ins.foo
1726   ins.foo
1727   class B
1728     def foo
1729       :Btwo
1730     end
1731   end
1732   ins.foo
1735 # Call to fixnum
1736 assert_equal '[true, false]', %q{
1737   def is_odd(obj)
1738     obj.odd?
1739   end
1741   is_odd(1)
1742   is_odd(1)
1744   [is_odd(123), is_odd(456)]
1747 # Call to bignum
1748 assert_equal '[true, false]', %q{
1749   def is_odd(obj)
1750     obj.odd?
1751   end
1753   bignum = 99999999999999999999
1754   is_odd(bignum)
1755   is_odd(bignum)
1757   [is_odd(bignum), is_odd(bignum+1)]
1760 # Call to fixnum and bignum
1761 assert_equal '[true, false, true, false]', %q{
1762   def is_odd(obj)
1763     obj.odd?
1764   end
1766   bignum = 99999999999999999999
1767   is_odd(bignum)
1768   is_odd(bignum)
1769   is_odd(123)
1770   is_odd(123)
1772   [is_odd(123), is_odd(456), is_odd(bignum), is_odd(bignum+1)]
1775 # Call to static and dynamic symbol
1776 assert_equal 'bar', %q{
1777   def to_string(obj)
1778     obj.to_s
1779   end
1781   to_string(:foo)
1782   to_string(:foo)
1783   to_string((-"bar").to_sym)
1784   to_string((-"bar").to_sym)
1787 # Call to flonum and heap float
1788 assert_equal '[nil, nil, nil, 1]', %q{
1789   def is_inf(obj)
1790     obj.infinite?
1791   end
1793   is_inf(0.0)
1794   is_inf(0.0)
1795   is_inf(1e256)
1796   is_inf(1e256)
1798   [
1799     is_inf(0.0),
1800     is_inf(1.0),
1801     is_inf(1e256),
1802     is_inf(1.0/0.0)
1803   ]
1806 assert_equal '[1, 2, 3, 4, 5]', %q{
1807   def splatarray
1808     [*(1..5)]
1809   end
1811   splatarray
1812   splatarray
1815 assert_equal '[1, 1, 2, 1, 2, 3]', %q{
1816   def expandarray
1817     arr = [1, 2, 3]
1819     a, = arr
1820     b, c, = arr
1821     d, e, f = arr
1823     [a, b, c, d, e, f]
1824   end
1826   expandarray
1827   expandarray
1830 assert_equal '[1, 1]', %q{
1831   def expandarray_useless_splat
1832     arr = (1..10).to_a
1834     a, * = arr
1835     b, (*) = arr
1837     [a, b]
1838   end
1840   expandarray_useless_splat
1841   expandarray_useless_splat
1844 assert_equal '[:not_heap, nil, nil]', %q{
1845   def expandarray_not_heap
1846     a, b, c = :not_heap
1847     [a, b, c]
1848   end
1850   expandarray_not_heap
1851   expandarray_not_heap
1854 assert_equal '[:not_array, nil, nil]', %q{
1855   def expandarray_not_array(obj)
1856     a, b, c = obj
1857     [a, b, c]
1858   end
1860   obj = Object.new
1861   def obj.to_ary
1862     [:not_array]
1863   end
1865   expandarray_not_array(obj)
1866   expandarray_not_array(obj)
1869 assert_equal '[1, 2, nil]', %q{
1870   def expandarray_rhs_too_small
1871     a, b, c = [1, 2]
1872     [a, b, c]
1873   end
1875   expandarray_rhs_too_small
1876   expandarray_rhs_too_small
1879 assert_equal '[1, [2]]', %q{
1880   def expandarray_splat
1881     a, *b = [1, 2]
1882     [a, b]
1883   end
1885   expandarray_splat
1886   expandarray_splat
1889 assert_equal '2', %q{
1890   def expandarray_postarg
1891     *, a = [1, 2]
1892     a
1893   end
1895   expandarray_postarg
1896   expandarray_postarg
1899 assert_equal '10', %q{
1900   obj = Object.new
1901   val = nil
1902   obj.define_singleton_method(:to_ary) { val = 10; [] }
1904   def expandarray_always_call_to_ary(object)
1905     * = object
1906   end
1908   expandarray_always_call_to_ary(obj)
1909   expandarray_always_call_to_ary(obj)
1911   val
1914 # regression test of local type change
1915 assert_equal '1.1', %q{
1916 def bar(baz, quux)
1917   if baz.integer?
1918     baz, quux = quux, nil
1919   end
1920   baz.to_s
1923 bar(123, 1.1)
1924 bar(123, 1.1)
1927 # test enabling a line TracePoint in a C method call
1928 assert_equal '[[:line, true]]', %q{
1929   events = []
1930   events.instance_variable_set(
1931     :@tp,
1932     TracePoint.new(:line) { |tp| events << [tp.event, tp.lineno] if tp.path == __FILE__ }
1933   )
1934   def events.to_str
1935     @tp.enable; ''
1936   end
1938   # Stay in generated code while enabling tracing
1939   def events.compiled(obj)
1940     String(obj)
1941     @tp.disable; __LINE__
1942   end
1944   line = events.compiled(events)
1945   events[0][-1] = (events[0][-1] == line)
1947   events
1950 # test enabling a c_return TracePoint in a C method call
1951 assert_equal '[[:c_return, :String, :string_alias, "events_to_str"]]', %q{
1952   events = []
1953   events.instance_variable_set(:@tp, TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.callee_id, tp.return_value] })
1954   def events.to_str
1955     @tp.enable; 'events_to_str'
1956   end
1958   # Stay in generated code while enabling tracing
1959   alias string_alias String
1960   def events.compiled(obj)
1961     string_alias(obj)
1962     @tp.disable
1963   end
1965   events.compiled(events)
1967   events
1970 # test enabling a TracePoint that targets a particular line in a C method call
1971 assert_equal '[true]', %q{
1972   events = []
1973   events.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
1974   def events.to_str
1975     @tp.enable(target: method(:compiled))
1976     ''
1977   end
1979   # Stay in generated code while enabling tracing
1980   def events.compiled(obj)
1981     String(obj)
1982     __LINE__
1983   end
1985   line = events.compiled(events)
1986   events[0] = (events[0] == line)
1988   events
1991 # test enabling tracing in the middle of splatarray
1992 assert_equal '[true]', %q{
1993   events = []
1994   obj = Object.new
1995   obj.instance_variable_set(:@tp, TracePoint.new(:line) { |tp| events << tp.lineno })
1996   def obj.to_a
1997     @tp.enable(target: method(:compiled))
1998     []
1999   end
2001   # Enable tracing in the middle of the splatarray instruction
2002   def obj.compiled(obj)
2003     * = *obj
2004     __LINE__
2005   end
2007   obj.compiled([])
2008   line = obj.compiled(obj)
2009   events[0] = (events[0] == line)
2011   events
2014 # test enabling tracing in the middle of opt_aref. Different since the codegen
2015 # for it ends in a jump.
2016 assert_equal '[true]', %q{
2017   def lookup(hash, tp)
2018     hash[42]
2019     tp.disable; __LINE__
2020   end
2022   lines = []
2023   tp = TracePoint.new(:line) { lines << _1.lineno if _1.path == __FILE__ }
2025   lookup(:foo, tp)
2026   lookup({}, tp)
2028   enable_tracing_on_missing = Hash.new { tp.enable }
2030   expected_line = lookup(enable_tracing_on_missing, tp)
2032   lines[0] = true if lines[0] == expected_line
2034   lines
2037 # test enabling c_call tracing before compiling
2038 assert_equal '[[:c_call, :itself]]', %q{
2039   def shouldnt_compile
2040     itself
2041   end
2043   events = []
2044   tp = TracePoint.new(:c_call) { |tp| events << [tp.event, tp.method_id] }
2046   # assume first call compiles
2047   tp.enable { shouldnt_compile }
2049   events
2052 # test enabling c_return tracing before compiling
2053 assert_equal '[[:c_return, :itself, main]]', %q{
2054   def shouldnt_compile
2055     itself
2056   end
2058   events = []
2059   tp = TracePoint.new(:c_return) { |tp| events << [tp.event, tp.method_id, tp.return_value] }
2061   # assume first call compiles
2062   tp.enable { shouldnt_compile }
2064   events
2067 # test enabling tracing for a suspended fiber
2068 assert_equal '[[:return, 42]]', %q{
2069   def traced_method
2070     Fiber.yield
2071     42
2072   end
2074   events = []
2075   tp = TracePoint.new(:return) { events << [_1.event, _1.return_value] }
2076   # assume first call compiles
2077   fiber = Fiber.new { traced_method }
2078   fiber.resume
2079   tp.enable(target: method(:traced_method))
2080   fiber.resume
2082   events
2085 # test compiling on non-tracing ractor then running on a tracing one
2086 assert_equal '[:itself]', %q{
2087   def traced_method
2088     itself
2089   end
2092   tracing_ractor = Ractor.new do
2093     # 1: start tracing
2094     events = []
2095     tp = TracePoint.new(:c_call) { events << _1.method_id }
2096     tp.enable
2097     Ractor.yield(nil)
2099     # 3: run compiled method on tracing ractor
2100     Ractor.yield(nil)
2101     traced_method
2103     events
2104   ensure
2105     tp&.disable
2106   end
2108   tracing_ractor.take
2110   # 2: compile on non tracing ractor
2111   traced_method
2113   tracing_ractor.take
2114   tracing_ractor.take
2117 # Try to hit a lazy branch stub while another ractor enables tracing
2118 assert_equal '42', %q{
2119   def compiled(arg)
2120     if arg
2121       arg + 1
2122     else
2123       itself
2124       itself
2125     end
2126   end
2128   ractor = Ractor.new do
2129     compiled(false)
2130     Ractor.yield(nil)
2131     compiled(41)
2132   end
2134   tp = TracePoint.new(:line) { itself }
2135   ractor.take
2136   tp.enable
2138   ractor.take
2141 # Test equality with changing types
2142 assert_equal '[true, false, false, false]', %q{
2143   def eq(a, b)
2144     a == b
2145   end
2147   [
2148     eq("foo", "foo"),
2149     eq("foo", "bar"),
2150     eq(:foo, "bar"),
2151     eq("foo", :bar)
2152   ]
2155 # Redefined String eq
2156 assert_equal 'true', %q{
2157   class String
2158     def ==(other)
2159       true
2160     end
2161   end
2163   def eq(a, b)
2164     a == b
2165   end
2167   eq("foo", "bar")
2168   eq("foo", "bar")
2171 # Redefined Integer eq
2172 assert_equal 'true', %q{
2173   class Integer
2174     def ==(other)
2175       true
2176     end
2177   end
2179   def eq(a, b)
2180     a == b
2181   end
2183   eq(1, 2)
2184   eq(1, 2)
2187 # aset on array with invalid key
2188 assert_normal_exit %q{
2189   def foo(arr)
2190     arr[:foo] = 123
2191   end
2193   foo([1]) rescue nil
2194   foo([1]) rescue nil
2197 # test ractor exception on when setting ivar
2198 assert_equal '42',  %q{
2199   class A
2200     def self.foo
2201       _foo = 1
2202       _bar = 2
2203       begin
2204         @bar = _foo + _bar
2205       rescue Ractor::IsolationError
2206         42
2207       end
2208     end
2209   end
2211   A.foo
2212   A.foo
2214   Ractor.new { A.foo }.take
2217 assert_equal '["plain", "special", "sub", "plain"]', %q{
2218   def foo(arg)
2219     arg.to_s
2220   end
2222   class Sub < String
2223   end
2225   special = String.new("special")
2226   special.singleton_class
2228   [
2229     foo("plain"),
2230     foo(special),
2231     foo(Sub.new("sub")),
2232     foo("plain")
2233   ]
2236 assert_equal '["sub", "sub"]', %q{
2237   def foo(arg)
2238     arg.to_s
2239   end
2241   class Sub < String
2242     def to_s
2243       super
2244     end
2245   end
2247   sub = Sub.new("sub")
2249   [foo(sub), foo(sub)]
2252 assert_equal '[1]', %q{
2253   def kwargs(value:)
2254     value
2255   end
2257   5.times.map { kwargs(value: 1) }.uniq
2260 assert_equal '[:ok]', %q{
2261   def kwargs(value:)
2262     value
2263   end
2265   5.times.map { kwargs() rescue :ok }.uniq
2268 assert_equal '[:ok]', %q{
2269   def kwargs(a:, b: nil)
2270     value
2271   end
2273   5.times.map { kwargs(b: 123) rescue :ok }.uniq
2276 assert_equal '[[1, 2]]', %q{
2277   def kwargs(left:, right:)
2278     [left, right]
2279   end
2281   5.times.flat_map do
2282     [
2283       kwargs(left: 1, right: 2),
2284       kwargs(right: 2, left: 1)
2285     ]
2286   end.uniq
2289 assert_equal '[[1, 2]]', %q{
2290   def kwargs(lead, kwarg:)
2291     [lead, kwarg]
2292   end
2294   5.times.map { kwargs(1, kwarg: 2) }.uniq
2297 # optional and keyword args
2298 assert_equal '[[1, 2, 3]]', %q{
2299   def opt_and_kwargs(a, b=2, c: nil)
2300     [a,b,c]
2301   end
2303   5.times.map { opt_and_kwargs(1, c: 3) }.uniq
2306 assert_equal '[[1, 2, 3]]', %q{
2307   def opt_and_kwargs(a, b=nil, c: nil)
2308     [a,b,c]
2309   end
2311   5.times.map { opt_and_kwargs(1, 2, c: 3) }.uniq
2314 # Bug #18453
2315 assert_equal '[[1, nil, 2]]', %q{
2316   def opt_and_kwargs(a = {}, b: nil, c: nil)
2317     [a, b, c]
2318   end
2320   5.times.map { opt_and_kwargs(1, c: 2) }.uniq
2323 assert_equal '[[{}, nil, 1]]', %q{
2324   def opt_and_kwargs(a = {}, b: nil, c: nil)
2325     [a, b, c]
2326   end
2328   5.times.map { opt_and_kwargs(c: 1) }.uniq
2331 # leading and keyword arguments are swapped into the right order
2332 assert_equal '[[1, 2, 3, 4, 5, 6]]', %q{
2333   def kwargs(five, six, a:, b:, c:, d:)
2334     [a, b, c, d, five, six]
2335   end
2337   5.times.flat_map do
2338     [
2339       kwargs(5, 6, a: 1, b: 2, c: 3, d: 4),
2340       kwargs(5, 6, a: 1, b: 2, d: 4, c: 3),
2341       kwargs(5, 6, a: 1, c: 3, b: 2, d: 4),
2342       kwargs(5, 6, a: 1, c: 3, d: 4, b: 2),
2343       kwargs(5, 6, a: 1, d: 4, b: 2, c: 3),
2344       kwargs(5, 6, a: 1, d: 4, c: 3, b: 2),
2345       kwargs(5, 6, b: 2, a: 1, c: 3, d: 4),
2346       kwargs(5, 6, b: 2, a: 1, d: 4, c: 3),
2347       kwargs(5, 6, b: 2, c: 3, a: 1, d: 4),
2348       kwargs(5, 6, b: 2, c: 3, d: 4, a: 1),
2349       kwargs(5, 6, b: 2, d: 4, a: 1, c: 3),
2350       kwargs(5, 6, b: 2, d: 4, c: 3, a: 1),
2351       kwargs(5, 6, c: 3, a: 1, b: 2, d: 4),
2352       kwargs(5, 6, c: 3, a: 1, d: 4, b: 2),
2353       kwargs(5, 6, c: 3, b: 2, a: 1, d: 4),
2354       kwargs(5, 6, c: 3, b: 2, d: 4, a: 1),
2355       kwargs(5, 6, c: 3, d: 4, a: 1, b: 2),
2356       kwargs(5, 6, c: 3, d: 4, b: 2, a: 1),
2357       kwargs(5, 6, d: 4, a: 1, b: 2, c: 3),
2358       kwargs(5, 6, d: 4, a: 1, c: 3, b: 2),
2359       kwargs(5, 6, d: 4, b: 2, a: 1, c: 3),
2360       kwargs(5, 6, d: 4, b: 2, c: 3, a: 1),
2361       kwargs(5, 6, d: 4, c: 3, a: 1, b: 2),
2362       kwargs(5, 6, d: 4, c: 3, b: 2, a: 1)
2363     ]
2364   end.uniq
2367 # implicit hashes get skipped and don't break compilation
2368 assert_equal '[[:key]]', %q{
2369   def implicit(hash)
2370     hash.keys
2371   end
2373   5.times.map { implicit(key: :value) }.uniq
2376 # default values on keywords don't mess up argument order
2377 assert_equal '[2]', %q{
2378   def default_value
2379     1
2380   end
2382   def default_expression(value: default_value)
2383     value
2384   end
2386   5.times.map { default_expression(value: 2) }.uniq
2389 # constant default values on keywords
2390 assert_equal '[3]', %q{
2391   def default_expression(value: 3)
2392     value
2393   end
2395   5.times.map { default_expression }.uniq
2398 # non-constant default values on keywords
2399 assert_equal '[3]', %q{
2400   def default_value
2401     3
2402   end
2404   def default_expression(value: default_value)
2405     value
2406   end
2408   5.times.map { default_expression }.uniq
2411 # reordered optional kwargs
2412 assert_equal '[[100, 1]]', %q{
2413   def foo(capacity: 100, max: nil)
2414     [capacity, max]
2415   end
2417   5.times.map { foo(max: 1) }.uniq
2420 # invalid lead param
2421 assert_equal 'ok', %q{
2422   def bar(baz: 2)
2423     baz
2424   end
2426   def foo
2427     bar(1, baz: 123)
2428   end
2430   begin
2431     foo
2432     foo
2433   rescue ArgumentError => e
2434     print "ok"
2435   end
2438 # reordered required kwargs
2439 assert_equal '[[1, 2, 3, 4]]', %q{
2440   def foo(default1: 1, required1:, default2: 3, required2:)
2441     [default1, required1, default2, required2]
2442   end
2444   5.times.map { foo(required1: 2, required2: 4) }.uniq
2447 # reordered default expression kwargs
2448 assert_equal '[[:one, :two, 3]]', %q{
2449   def foo(arg1: (1+0), arg2: (2+0), arg3: (3+0))
2450     [arg1, arg2, arg3]
2451   end
2453   5.times.map { foo(arg2: :two, arg1: :one) }.uniq
2456 # complex kwargs
2457 assert_equal '[[1, 2, 3, 4]]', %q{
2458   def foo(required:, specified: 999, simple_default: 3, complex_default: "4".to_i)
2459     [required, specified, simple_default, complex_default]
2460   end
2462   5.times.map { foo(specified: 2, required: 1) }.uniq
2465 # cfunc kwargs
2466 assert_equal '{:foo=>123}', %q{
2467   def foo(bar)
2468     bar.store(:value, foo: 123)
2469     bar[:value]
2470   end
2472   foo({})
2473   foo({})
2476 # cfunc kwargs
2477 assert_equal '{:foo=>123}', %q{
2478   def foo(bar)
2479     bar.replace(foo: 123)
2480   end
2482   foo({})
2483   foo({})
2486 # cfunc kwargs
2487 assert_equal '{:foo=>123, :bar=>456}', %q{
2488   def foo(bar)
2489     bar.replace(foo: 123, bar: 456)
2490   end
2492   foo({})
2493   foo({})
2496 # variadic cfunc kwargs
2497 assert_equal '{:foo=>123}', %q{
2498   def foo(bar)
2499     bar.merge(foo: 123)
2500   end
2502   foo({})
2503   foo({})
2506 # optimized cfunc kwargs
2507 assert_equal 'false', %q{
2508   def foo
2509     :foo.eql?(foo: :foo)
2510   end
2512   foo
2513   foo
2516 # attr_reader on frozen object
2517 assert_equal 'false', %q{
2518   class Foo
2519     attr_reader :exception
2521     def failed?
2522       !exception.nil?
2523     end
2524   end
2526   foo = Foo.new.freeze
2527   foo.failed?
2528   foo.failed?
2531 # regression test for doing kwarg shuffle before checking for interrupts
2532 assert_equal 'ok', %q{
2533   def new_media_drop(attributes:, product_drop:, context:, sources:)
2534     nil.nomethod rescue nil # force YJIT to bail to side exit
2536     [attributes, product_drop, context, sources]
2537   end
2539   def load_medias(product_drop: nil, raw_medias:, context:)
2540     raw_medias.map do |raw_media|
2541       case new_media_drop(context: context, attributes: raw_media, product_drop: product_drop, sources: [])
2542       in [Hash, ProductDrop, Context, Array]
2543       else
2544         raise "bad shuffle"
2545       end
2546     end
2547   end
2549   class Context; end
2551   class ProductDrop
2552     attr_reader :title
2553     def initialize(title)
2554       @title = title
2555     end
2556   end
2558   # Make a thread so we have thread switching interrupts
2559   th = Thread.new do
2560     while true; end
2561   end
2562   1_000.times do |i|
2563     load_medias(product_drop: ProductDrop.new("foo"), raw_medias: [{}, {}], context: Context.new)
2564   end
2565   th.kill.join
2567   :ok
2570 # regression test for tracing attr_accessor methods.
2571 assert_equal "true", %q{
2572     c = Class.new do
2573       attr_accessor :x
2574       alias y x
2575       alias y= x=
2576     end
2577     obj = c.new
2579     ar_meth = obj.method(:x)
2580     aw_meth = obj.method(:x=)
2581     aar_meth = obj.method(:y)
2582     aaw_meth = obj.method(:y=)
2583     events = []
2584     trace = TracePoint.new(:c_call, :c_return){|tp|
2585       next if tp.path != __FILE__
2586       next if tp.method_id == :call
2587       case tp.event
2588       when :c_call
2589         events << [tp.event, tp.method_id, tp.callee_id]
2590       when :c_return
2591         events << [tp.event, tp.method_id, tp.callee_id, tp.return_value]
2592       end
2593     }
2594     test_proc = proc do
2595       obj.x = 1
2596       obj.x
2597       obj.y = 2
2598       obj.y
2599       aw_meth.call(1)
2600       ar_meth.call
2601       aaw_meth.call(2)
2602       aar_meth.call
2603     end
2604     test_proc.call # populate call caches
2605     trace.enable(&test_proc)
2606     expected = [
2607       [:c_call, :x=, :x=],
2608       [:c_return, :x=, :x=, 1],
2609       [:c_call, :x, :x],
2610       [:c_return, :x, :x, 1],
2611       [:c_call, :x=, :y=],
2612       [:c_return, :x=, :y=, 2],
2613       [:c_call, :x, :y],
2614       [:c_return, :x, :y, 2],
2615     ] * 2
2617     expected == events
2620 # duphash
2621 assert_equal '{:foo=>123}', %q{
2622   def foo
2623     {foo: 123}
2624   end
2626   foo
2627   foo
2630 # newhash
2631 assert_equal '{:foo=>2}', %q{
2632   def foo
2633     {foo: 1+1}
2634   end
2636   foo
2637   foo
2640 # block invalidation edge case
2641 assert_equal 'undef', %q{
2642   class A
2643     def foo(arg)
2644       arg.times { A.remove_method(:bar) }
2645       self
2646     end
2648     def bar
2649       4
2650     end
2652     def use(arg)
2653       # two consecutive sends. When bar is removed, the return address
2654       # for calling it is already on foo's control frame
2655       foo(arg).bar
2656     rescue NoMethodError
2657       :undef
2658     end
2659   end
2661   A.new.use 0
2662   A.new.use 0
2663   A.new.use 1
2666 # block invalidation edge case
2667 assert_equal 'ok', %q{
2668   class A
2669     Good = :ng
2670     def foo(arg)
2671       arg.times { A.const_set(:Good, :ok) }
2672       self
2673     end
2675     def id(arg)
2676       arg
2677     end
2679     def use(arg)
2680       # send followed by an opt_getinlinecache.
2681       # The return address remains on the control frame
2682       # when opt_getinlinecache is invalidated.
2683       foo(arg).id(Good)
2684     end
2685   end
2687   A.new.use 0
2688   A.new.use 0
2689   A.new.use 1
2692 assert_equal 'ok', %q{
2693   # test hitting a branch stub when out of memory
2694   def nimai(jita)
2695     if jita
2696       :ng
2697     else
2698       :ok
2699     end
2700   end
2702   nimai(true)
2703   nimai(true)
2705   RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
2707   nimai(false)
2710 assert_equal 'new', %q{
2711   # test block invalidation while out of memory
2712   def foo
2713     :old
2714   end
2716   def test
2717     foo
2718   end
2720   test
2721   test
2723   RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
2725   def foo
2726     :new
2727   end
2729   test
2732 assert_equal 'ok', %q{
2733   # Try to compile new method while OOM
2734   def foo
2735     :ok
2736   end
2738   RubyVM::YJIT.simulate_oom! if defined?(RubyVM::YJIT)
2740   foo
2741   foo
2744 # struct aref embedded
2745 assert_equal '2', %q{
2746   def foo(s)
2747     s.foo
2748   end
2750   S = Struct.new(:foo)
2751   foo(S.new(1))
2752   foo(S.new(2))
2755 # struct aref non-embedded
2756 assert_equal '4', %q{
2757   def foo(s)
2758     s.d
2759   end
2761   S = Struct.new(:a, :b, :c, :d, :e)
2762   foo(S.new(1,2,3,4,5))
2763   foo(S.new(1,2,3,4,5))
2766 # struct aset embedded
2767 assert_equal '123', %q{
2768   def foo(s)
2769     s.foo = 123
2770   end
2772   s = Struct.new(:foo).new
2773   foo(s)
2774   s = Struct.new(:foo).new
2775   foo(s)
2776   s.foo
2779 # struct aset non-embedded
2780 assert_equal '[1, 2, 3, 4, 5]', %q{
2781   def foo(s)
2782     s.a = 1
2783     s.b = 2
2784     s.c = 3
2785     s.d = 4
2786     s.e = 5
2787   end
2789   S = Struct.new(:a, :b, :c, :d, :e)
2790   s = S.new
2791   foo(s)
2792   s = S.new
2793   foo(s)
2794   [s.a, s.b, s.c, s.d, s.e]
2797 # struct aref too many args
2798 assert_equal 'ok', %q{
2799   def foo(s)
2800     s.foo(:bad)
2801   end
2803   s = Struct.new(:foo).new
2804   foo(s) rescue :ok
2805   foo(s) rescue :ok
2808 # struct aset too many args
2809 assert_equal 'ok', %q{
2810   def foo(s)
2811     s.set_foo(123, :bad)
2812   end
2814   s = Struct.new(:foo) do
2815     alias :set_foo :foo=
2816   end
2817   foo(s) rescue :ok
2818   foo(s) rescue :ok
2821 # File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
2822 assert_equal 'foo/bar', %q{
2823   def foo
2824     File.join("foo", "bar")
2825   end
2827   foo
2828   foo
2831 # File.join is a cfunc accepting variable arguments as a Ruby array (argc = -2)
2832 assert_equal '', %q{
2833   def foo
2834     File.join()
2835   end
2837   foo
2838   foo