2017-01-23 05:47:16 +03:00
|
|
|
# C0 coverage of each instructions
|
|
|
|
|
|
|
|
# :NOTE: This is for development purpose; never consider this file as
|
|
|
|
# ISeq compilation specification.
|
|
|
|
|
|
|
|
begin
|
|
|
|
# This library brings some additional coverage.
|
|
|
|
# Not mandatory.
|
|
|
|
require 'rbconfig/sizeof'
|
|
|
|
rescue LoadError
|
|
|
|
# OK, just skip
|
|
|
|
else
|
2019-01-18 05:04:41 +03:00
|
|
|
if defined? RbConfig::LIMITS
|
|
|
|
$FIXNUM_MAX = RbConfig::LIMITS["FIXNUM_MAX"]
|
|
|
|
$FIXNUM_MIN = RbConfig::LIMITS["FIXNUM_MIN"]
|
|
|
|
end
|
2017-01-23 05:47:16 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
fsl = { frozen_string_literal: true } # used later
|
|
|
|
tests = [
|
|
|
|
# insn , expression to generate such insn
|
|
|
|
[ 'nop', %q{ raise rescue true }, ],
|
|
|
|
|
|
|
|
[ 'setlocal *, 0', %q{ x = true }, ],
|
|
|
|
[ 'setlocal *, 1', %q{ x = nil; -> { x = true }.call }, ],
|
|
|
|
[ 'setlocal', %q{ x = nil; -> { -> { x = true }.() }.() }, ],
|
|
|
|
[ 'getlocal *, 0', %q{ x = true; x }, ],
|
|
|
|
[ 'getlocal *, 1', %q{ x = true; -> { x }.call }, ],
|
|
|
|
[ 'getlocal', %q{ x = true; -> { -> { x }.() }.() }, ],
|
|
|
|
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'setblockparam', <<-'},', ], # {
|
2018-06-29 07:56:07 +03:00
|
|
|
def m&b
|
|
|
|
b = # here
|
|
|
|
proc { true }
|
|
|
|
end
|
|
|
|
m { false }.call
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'getblockparam', <<-'},', ], # {
|
2018-06-29 07:56:07 +03:00
|
|
|
def m&b
|
|
|
|
b # here
|
|
|
|
end
|
|
|
|
m { true }.call
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'getblockparamproxy', <<-'},', ], # {
|
2018-06-29 07:56:07 +03:00
|
|
|
def m&b
|
|
|
|
b # here
|
|
|
|
.call
|
|
|
|
end
|
|
|
|
m { true }
|
|
|
|
},
|
|
|
|
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'setspecial', %q{ true if true..true }, ],
|
|
|
|
[ 'getspecial', %q{ $&.nil? }, ],
|
|
|
|
[ 'getspecial', %q{ $`.nil? }, ],
|
|
|
|
[ 'getspecial', %q{ $'.nil? }, ],
|
|
|
|
[ 'getspecial', %q{ $+.nil? }, ],
|
|
|
|
[ 'getspecial', %q{ $1.nil? }, ],
|
|
|
|
[ 'getspecial', %q{ $128.nil? }, ],
|
|
|
|
|
|
|
|
[ 'getglobal', %q{ String === $0 }, ],
|
|
|
|
[ 'getglobal', %q{ $_.nil? }, ],
|
|
|
|
[ 'setglobal', %q{ $0 = "true" }, ],
|
|
|
|
|
|
|
|
[ 'setinstancevariable', %q{ @x = true }, ],
|
|
|
|
[ 'getinstancevariable', %q{ @x = true; @x }, ],
|
|
|
|
|
2020-03-28 01:08:52 +03:00
|
|
|
[ 'setclassvariable', %q{ class A; @@x = true; end }, ],
|
|
|
|
[ 'getclassvariable', %q{ class A; @@x = true; @@x end }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
|
|
|
|
[ 'setconstant', %q{ X = true }, ],
|
|
|
|
[ 'setconstant', %q{ Object::X = true }, ],
|
|
|
|
[ 'getconstant', %q{ X = true; X }, ],
|
|
|
|
[ 'getconstant', %q{ X = true; Object::X }, ],
|
|
|
|
|
|
|
|
[ 'getinlinecache / setinlinecache', %q{ def x; X; end; X = true; x; x; x }, ],
|
|
|
|
|
|
|
|
[ 'putnil', %q{ $~ == nil }, ],
|
|
|
|
[ 'putself', %q{ $~ != self }, ],
|
|
|
|
[ 'putobject INT2FIX(0)', %q{ $~ != 0 }, ],
|
|
|
|
[ 'putobject INT2FIX(1)', %q{ $~ != 1 }, ],
|
|
|
|
[ 'putobject', %q{ $~ != -1 }, ],
|
|
|
|
[ 'putobject', %q{ $~ != /x/ }, ],
|
|
|
|
[ 'putobject', %q{ $~ != :x }, ],
|
|
|
|
[ 'putobject', %q{ $~ != (1..2) }, ],
|
|
|
|
[ 'putobject', %q{ $~ != true }, ],
|
|
|
|
[ 'putobject', %q{ /(?<x>x)/ =~ "x"; x == "x" }, ],
|
|
|
|
|
|
|
|
[ 'putspecialobject', %q{ {//=>true}[//] }, ],
|
|
|
|
[ 'putstring', %q{ "true" }, ],
|
|
|
|
[ 'tostring / concatstrings', %q{ "#{true}" }, ],
|
2017-05-27 16:54:09 +03:00
|
|
|
[ 'toregexp', %q{ /#{true}/ =~ "true" && $~ }, ],
|
2018-06-29 07:56:07 +03:00
|
|
|
[ 'intern', %q{ :"#{true}" }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
|
|
|
|
[ 'newarray', %q{ ["true"][0] }, ],
|
Add pushtoarraykwsplat instruction to avoid unnecessary array allocation
This is designed to replace the newarraykwsplat instruction, which is
no longer used in the parse.y compiler after this commit. This avoids
an unnecessary array allocation in the case where ARGSCAT is followed
by LIST with keyword:
```ruby
a = []
kw = {}
[*a, 1, **kw]
```
Previous Instructions:
```
0000 newarray 0 ( 1)[Li]
0002 setlocal_WC_0 a@0
0004 newhash 0 ( 2)[Li]
0006 setlocal_WC_0 kw@1
0008 getlocal_WC_0 a@0 ( 3)[Li]
0010 splatarray true
0012 putobject_INT2FIX_1_
0013 putspecialobject 1
0015 newhash 0
0017 getlocal_WC_0 kw@1
0019 opt_send_without_block <calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE>
0021 newarraykwsplat 2
0023 concattoarray
0024 leave
```
New Instructions:
```
0000 newarray 0 ( 1)[Li]
0002 setlocal_WC_0 a@0
0004 newhash 0 ( 2)[Li]
0006 setlocal_WC_0 kw@1
0008 getlocal_WC_0 a@0 ( 3)[Li]
0010 splatarray true
0012 putobject_INT2FIX_1_
0013 pushtoarray 1
0015 putspecialobject 1
0017 newhash 0
0019 getlocal_WC_0 kw@1
0021 opt_send_without_block <calldata!mid:core#hash_merge_kwd, argc:2, ARGS_SIMPLE>
0023 pushtoarraykwsplat
0024 leave
```
pushtoarraykwsplat is designed to be simpler than newarraykwsplat.
It does not take a variable number of arguments from the stack, it
pops the top of the stack, and appends it to the second from the top,
unless the top of the stack is an empty hash.
During this work, I found the ARGSPUSH followed by HASH with keyword
did not compile correctly, as it pushed the generated hash to the
array even if the hash was empty. This fixes the behavior, to use
pushtoarraykwsplat instead of pushtoarray in that case:
```ruby
a = []
kw = {}
[*a, **kw]
[{}] # Before
[] # After
```
This does not remove the newarraykwsplat instruction, as it is still
referenced in the prism compiler (which should be updated similar
to this), YJIT (only in the bindings, it does not appear to be
implemented), and RJIT (in a couple comments). After those are
updated, the newarraykwsplat instruction should be removed.
2024-01-30 21:31:27 +03:00
|
|
|
[ 'pushtoarraykwsplat', %q{ [**{x:'true'}][0][:x] }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'duparray', %q{ [ true ][0] }, ],
|
|
|
|
[ 'expandarray', %q{ y = [ true, false, nil ]; x, = y; x }, ],
|
|
|
|
[ 'expandarray', %q{ y = [ true, false, nil ]; x, *z = y; x }, ],
|
|
|
|
[ 'expandarray', %q{ y = [ true, false, nil ]; x, *z, w = y; x }, ],
|
|
|
|
[ 'splatarray', %q{ x, = *(y = true), false; x }, ],
|
|
|
|
[ 'concatarray', %q{ ["t", "r", *x = "u", "e"].join }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'concatarray', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class X; def to_a; ['u']; end; end
|
|
|
|
['t', 'r', *X.new, 'e'].join
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'concatarray', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
r = false
|
|
|
|
t = [true, nil]
|
|
|
|
q, w, e = r, *t # here
|
|
|
|
w
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'newhash', %q{ x = {}; x[x] = true }, ],
|
|
|
|
[ 'newhash', %q{ x = true; { x => x }[x] }, ],
|
2018-10-27 11:17:01 +03:00
|
|
|
[ 'newhashfromarray', %q{ { a: true }[:a] }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'newrange', %q{ x = 1; [*(0..x)][0] == 0 }, ],
|
|
|
|
[ 'newrange', %q{ x = 1; [*(0...x)][0] == 0 }, ],
|
|
|
|
|
|
|
|
[ 'pop', %q{ def x; true; end; x }, ],
|
|
|
|
[ 'dup', %q{ x = y = true; x }, ],
|
|
|
|
[ 'dupn', %q{ Object::X ||= true }, ],
|
|
|
|
[ 'reverse', %q{ q, (w, e), r = 1, [2, 3], 4; e == 3 }, ],
|
2021-10-29 19:31:31 +03:00
|
|
|
[ 'swap', %q{ !!defined?([[]]) }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'swap', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
x = [[false, true]]
|
|
|
|
for i, j in x # here
|
|
|
|
;
|
|
|
|
end
|
|
|
|
j
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'topn', %q{ x, y = [], 0; x[*y], = [true, false]; x[0] }, ],
|
|
|
|
[ 'setn', %q{ x, y = [], 0; x[*y] = true ; x[0] }, ],
|
|
|
|
[ 'adjuststack', %q{ x = [true]; x[0] ||= nil; x[0] }, ],
|
|
|
|
|
|
|
|
[ 'defined', %q{ !defined?(x) }, ],
|
|
|
|
[ 'checkkeyword', %q{ def x x:rand;x end; x x: true }, ],
|
2018-06-29 07:56:07 +03:00
|
|
|
[ 'checktype', %q{ x = true; "#{x}" }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'checkmatch', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
x = y = true
|
|
|
|
case x
|
|
|
|
when false
|
|
|
|
y = false
|
|
|
|
when true # here
|
|
|
|
y = nil
|
|
|
|
end
|
|
|
|
y == nil
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'checkmatch', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
x, y = true, [false]
|
|
|
|
case x
|
|
|
|
when *y # here
|
|
|
|
z = false
|
|
|
|
else
|
|
|
|
z = true
|
|
|
|
end
|
|
|
|
z
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'checkmatch', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
x = false
|
|
|
|
begin
|
|
|
|
raise
|
|
|
|
rescue # here
|
|
|
|
x = true
|
|
|
|
end
|
|
|
|
x
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'defineclass', %q{ module X; true end }, ],
|
|
|
|
[ 'defineclass', %q{ X = Module.new; module X; true end }, ],
|
|
|
|
[ 'defineclass', %q{ class X; true end }, ],
|
|
|
|
[ 'defineclass', %q{ X = Class.new; class X; true end }, ],
|
|
|
|
[ 'defineclass', %q{ X = Class.new; class Y < X; true end }, ],
|
|
|
|
[ 'defineclass', %q{ X = Class.new; class << X; true end }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'defineclass', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
X = Class.new
|
|
|
|
Y = Class.new(X)
|
|
|
|
class Y < X
|
|
|
|
true
|
|
|
|
end
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'opt_send_without_block', %q{ true.to_s }, ],
|
|
|
|
[ 'send', %q{ true.tap {|i| i.to_s } }, ],
|
|
|
|
[ 'leave', %q{ def x; true; end; x }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'invokesuper', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class X < String
|
|
|
|
def empty?
|
|
|
|
super # here
|
|
|
|
end
|
|
|
|
end
|
|
|
|
X.new.empty?
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'invokeblock', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
def x
|
|
|
|
return yield self # here
|
|
|
|
end
|
|
|
|
x do
|
|
|
|
true
|
|
|
|
end
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'opt_str_freeze', %q{ 'true'.freeze }, ],
|
2019-08-01 14:34:10 +03:00
|
|
|
[ 'opt_nil_p', %q{ nil.nil? }, ],
|
2019-09-05 09:02:07 +03:00
|
|
|
[ 'opt_nil_p', %q{ !Object.nil? }, ],
|
|
|
|
[ 'opt_nil_p', %q{ Class.new{def nil?; true end}.new.nil? }, ],
|
2017-03-28 16:47:43 +03:00
|
|
|
[ 'opt_str_uminus', %q{ -'true' }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_str_freeze', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class String
|
|
|
|
def freeze
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
'true'.freeze
|
|
|
|
},
|
|
|
|
|
Emit special instruction for array literal + .(hash|min|max)
This commit introduces a new instruction `opt_newarray_send` which is
used when there is an array literal followed by either the `hash`,
`min`, or `max` method.
```
[a, b, c].hash
```
Will emit an `opt_newarray_send` instruction. This instruction falls
back to a method call if the "interested" method has been monkey
patched.
Here are some examples of the instructions generated:
```
$ ./miniruby --dump=insns -e '[@a, @b].max'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :max
0009 leave
$ ./miniruby --dump=insns -e '[@a, @b].min'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :min
0009 leave
$ ./miniruby --dump=insns -e '[@a, @b].hash'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :hash
0009 leave
```
[Feature #18897] [ruby-core:109147]
Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-06-07 03:27:56 +03:00
|
|
|
[ 'opt_newarray_send', %q{ ![ ].hash.nil? }, ],
|
|
|
|
|
|
|
|
[ 'opt_newarray_send', %q{ [ ].max.nil? }, ],
|
|
|
|
[ 'opt_newarray_send', %q{ [1, x = 2, 3].max == 3 }, ],
|
|
|
|
[ 'opt_newarray_send', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class Array
|
|
|
|
def max
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
[1, x = 2, 3].max
|
|
|
|
},
|
Emit special instruction for array literal + .(hash|min|max)
This commit introduces a new instruction `opt_newarray_send` which is
used when there is an array literal followed by either the `hash`,
`min`, or `max` method.
```
[a, b, c].hash
```
Will emit an `opt_newarray_send` instruction. This instruction falls
back to a method call if the "interested" method has been monkey
patched.
Here are some examples of the instructions generated:
```
$ ./miniruby --dump=insns -e '[@a, @b].max'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :max
0009 leave
$ ./miniruby --dump=insns -e '[@a, @b].min'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,12)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :min
0009 leave
$ ./miniruby --dump=insns -e '[@a, @b].hash'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 getinstancevariable :@a, <is:0> ( 1)[Li]
0003 getinstancevariable :@b, <is:1>
0006 opt_newarray_send 2, :hash
0009 leave
```
[Feature #18897] [ruby-core:109147]
Co-authored-by: John Hawthorn <jhawthorn@github.com>
2022-06-07 03:27:56 +03:00
|
|
|
[ 'opt_newarray_send', %q{ [ ].min.nil? }, ],
|
|
|
|
[ 'opt_newarray_send', %q{ [3, x = 2, 1].min == 1 }, ],
|
|
|
|
[ 'opt_newarray_send', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class Array
|
|
|
|
def min
|
|
|
|
true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
[3, x = 2, 1].min
|
|
|
|
},
|
Expand opt_newarray_send to support Array#pack with buffer keyword arg
Use an enum for the method arg instead of needing to add an id
that doesn't map to an actual method name.
$ ruby --dump=insns -e 'b = "x"; [v].pack("E*", buffer: b)'
before:
```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring "x" ( 1)[Li]
0002 setlocal_WC_0 b@0
0004 putself
0005 opt_send_without_block <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 newarray 1
0009 putchilledstring "E*"
0011 getlocal_WC_0 b@0
0013 opt_send_without_block <calldata!mid:pack, argc:2, kw:[#<Symbol:0x000000000023110c>], KWARG>
0015 leave
```
after:
```
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,34)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] b@0
0000 putchilledstring "x" ( 1)[Li]
0002 setlocal_WC_0 b@0
0004 putself
0005 opt_send_without_block <calldata!mid:v, argc:0, FCALL|VCALL|ARGS_SIMPLE>
0007 putchilledstring "E*"
0009 getlocal b@0, 0
0012 opt_newarray_send 3, 5
0015 leave
```
2024-07-20 20:03:02 +03:00
|
|
|
[ 'opt_newarray_send', %q{ v = 1.23; [v, v*2].pack("E*").unpack("E*") == [v, v*2] }, ],
|
|
|
|
[ 'opt_newarray_send', %q{ v = 4.56; b = +"x"; [v, v*2].pack("E*", buffer: b); b[1..].unpack("E*") == [v, v*2] }, ],
|
|
|
|
[ 'opt_newarray_send', <<-'},', ], # {
|
|
|
|
v = 7.89;
|
|
|
|
b = +"x";
|
|
|
|
class Array
|
|
|
|
alias _pack pack
|
|
|
|
def pack(s, buffer: nil, prefix: "y")
|
|
|
|
buffer ||= +"b"
|
|
|
|
buffer << prefix
|
|
|
|
_pack(s, buffer: buffer)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
tests = []
|
|
|
|
|
|
|
|
ret = [v].pack("E*", prefix: "z")
|
|
|
|
tests << (ret[0..1] == "bz")
|
|
|
|
tests << (ret[2..].unpack("E*") == [v])
|
|
|
|
|
|
|
|
ret = [v].pack("E*")
|
|
|
|
tests << (ret[0..1] == "by")
|
|
|
|
tests << (ret[2..].unpack("E*") == [v])
|
|
|
|
|
|
|
|
[v, v*2, v*3].pack("E*", buffer: b)
|
|
|
|
tests << (b[0..1] == "xy")
|
|
|
|
tests << (b[2..].unpack("E*") == [v, v*2, v*3])
|
|
|
|
|
|
|
|
class Array
|
|
|
|
def pack(_fmt, buffer:) = buffer
|
|
|
|
end
|
|
|
|
|
|
|
|
b = nil
|
|
|
|
tests << [v].pack("E*", buffer: b).nil?
|
|
|
|
|
|
|
|
class Array
|
|
|
|
def pack(_fmt, **kw) = kw.empty?
|
|
|
|
end
|
|
|
|
|
|
|
|
tests << [v].pack("E*") == true
|
|
|
|
|
|
|
|
tests.all? or puts tests
|
|
|
|
},
|
2017-01-23 05:47:16 +03:00
|
|
|
|
|
|
|
[ 'throw', %q{ false.tap { break true } }, ],
|
|
|
|
[ 'branchif', %q{ x = nil; x ||= true }, ],
|
|
|
|
[ 'branchif', %q{ x = true; x ||= nil; x }, ],
|
|
|
|
[ 'branchunless', %q{ x = 1; x &&= true }, ],
|
|
|
|
[ 'branchunless', %q{ x = nil; x &&= true; x.nil? }, ],
|
|
|
|
[ 'branchnil', %q{ x = true; x&.to_s }, ],
|
|
|
|
[ 'branchnil', %q{ x = nil; (x&.to_s).nil? }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'jump', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
y = 1
|
|
|
|
x = if y == 0 then nil elsif y == 1 then true else nil end
|
|
|
|
x
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'jump', <<-'},', ], # {
|
2017-03-28 16:47:45 +03:00
|
|
|
# ultra complicated situation: this ||= assignment only generates
|
2017-01-23 05:47:16 +03:00
|
|
|
# 15 instructions, not including the class definition.
|
|
|
|
class X; attr_accessor :x; end
|
|
|
|
x = X.new
|
|
|
|
x&.x ||= true # here
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'once', %q{ /#{true}/o =~ "true" && $~ }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'once', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
def once expr
|
|
|
|
return /#{expr}/o # here
|
|
|
|
end
|
|
|
|
x = once(true); x = once(false); x = once(nil);
|
|
|
|
x =~ "true" && $~
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'once', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
# recursive once
|
|
|
|
def once n
|
|
|
|
return %r/#{
|
|
|
|
if n == 0
|
|
|
|
true
|
|
|
|
else
|
|
|
|
once(n-1) # here
|
|
|
|
end
|
|
|
|
}/ox
|
|
|
|
end
|
|
|
|
x = once(128); x = once(7); x = once(16);
|
|
|
|
x =~ "true" && $~
|
|
|
|
},
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'once', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
# inter-thread lockup situation
|
|
|
|
def once n
|
|
|
|
return Thread.start n do |m|
|
|
|
|
Thread.pass
|
|
|
|
next %r/#{
|
|
|
|
sleep m # here
|
|
|
|
true
|
|
|
|
}/ox
|
|
|
|
end
|
|
|
|
end
|
|
|
|
x = once(1); y = once(0.1); z = y.value
|
|
|
|
z =~ "true" && $~
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'opt_case_dispatch', %q{ case 0 when 1.1 then false else true end }, ],
|
|
|
|
[ 'opt_case_dispatch', %q{ case 1.0 when 1.1 then false else true end }, ],
|
|
|
|
|
|
|
|
[ 'opt_plus', %q{ 1 + 1 == 2 }, ],
|
2017-01-30 13:12:18 +03:00
|
|
|
if defined? $FIXNUM_MAX then
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'opt_plus', %Q{ #{ $FIXNUM_MAX } + 1 == #{ $FIXNUM_MAX + 1 } }, ]
|
|
|
|
end,
|
|
|
|
[ 'opt_plus', %q{ 1.0 + 1.0 == 2.0 }, ],
|
|
|
|
[ 'opt_plus', %q{ x = +0.0.next_float; x + x >= x }, ],
|
|
|
|
[ 'opt_plus', %q{ 't' + 'rue' }, ],
|
|
|
|
[ 'opt_plus', %q{ ( ['t'] + ['r', ['u', ['e'], ], ] ).join }, ],
|
|
|
|
[ 'opt_plus', %q{ Time.at(1) + 1 == Time.at(2) }, ],
|
|
|
|
[ 'opt_minus', %q{ 1 - 1 == 0 }, ],
|
2017-01-30 13:12:18 +03:00
|
|
|
if defined? $FIXNUM_MIN then
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'opt_minus', %Q{ #{ $FIXNUM_MIN } - 1 == #{ $FIXNUM_MIN - 1 } }, ]
|
|
|
|
end,
|
|
|
|
[ 'opt_minus', %q{ 1.0 - 1.0 == 0.0 }, ],
|
|
|
|
[ 'opt_minus', %q{ x = -0.0.prev_float; x - x == 0.0 }, ],
|
|
|
|
[ 'opt_minus', %q{ ( [false, true] - [false] )[0] }, ],
|
|
|
|
[ 'opt_mult', %q{ 1 * 1 == 1 }, ],
|
|
|
|
[ 'opt_mult', %q{ 1.0 * 1.0 == 1.0 }, ],
|
|
|
|
[ 'opt_mult', %q{ x = +0.0.next_float; x * x <= x }, ],
|
|
|
|
[ 'opt_mult', %q{ ( "ruet" * 3 )[7,4] }, ],
|
|
|
|
[ 'opt_div', %q{ 1 / 1 == 1 }, ],
|
|
|
|
[ 'opt_div', %q{ 1.0 / 1.0 == 1.0 }, ],
|
|
|
|
[ 'opt_div', %q{ x = +0.0.next_float; x / x >= x }, ],
|
|
|
|
[ 'opt_div', %q{ x = 1/2r; x / x == 1 }, ],
|
|
|
|
[ 'opt_mod', %q{ 1 % 1 == 0 }, ],
|
|
|
|
[ 'opt_mod', %q{ 1.0 % 1.0 == 0.0 }, ],
|
|
|
|
[ 'opt_mod', %q{ x = +0.0.next_float; x % x == 0.0 }, ],
|
|
|
|
[ 'opt_mod', %q{ '%s' % [ true ] }, ],
|
|
|
|
|
|
|
|
[ 'opt_eq', %q{ 1 == 1 }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_eq', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class X; def == other; true; end; end
|
|
|
|
X.new == true
|
|
|
|
},
|
|
|
|
[ 'opt_neq', %q{ 1 != 0 }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_neq', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class X; def != other; true; end; end
|
|
|
|
X.new != true
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'opt_lt', %q{ -1 < 0 }, ],
|
|
|
|
[ 'opt_lt', %q{ -1.0 < 0.0 }, ],
|
|
|
|
[ 'opt_lt', %q{ -0.0.prev_float < 0.0 }, ],
|
|
|
|
[ 'opt_lt', %q{ ?a < ?z }, ],
|
|
|
|
[ 'opt_le', %q{ -1 <= 0 }, ],
|
|
|
|
[ 'opt_le', %q{ -1.0 <= 0.0 }, ],
|
|
|
|
[ 'opt_le', %q{ -0.0.prev_float <= 0.0 }, ],
|
|
|
|
[ 'opt_le', %q{ ?a <= ?z }, ],
|
|
|
|
[ 'opt_gt', %q{ 1 > 0 }, ],
|
|
|
|
[ 'opt_gt', %q{ 1.0 > 0.0 }, ],
|
|
|
|
[ 'opt_gt', %q{ +0.0.next_float > 0.0 }, ],
|
|
|
|
[ 'opt_gt', %q{ ?z > ?a }, ],
|
|
|
|
[ 'opt_ge', %q{ 1 >= 0 }, ],
|
|
|
|
[ 'opt_ge', %q{ 1.0 >= 0.0 }, ],
|
|
|
|
[ 'opt_ge', %q{ +0.0.next_float >= 0.0 }, ],
|
|
|
|
[ 'opt_ge', %q{ ?z >= ?a }, ],
|
|
|
|
|
2024-03-13 14:50:11 +03:00
|
|
|
[ 'opt_ltlt', %q{ +'' << 'true' }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'opt_ltlt', %q{ ([] << 'true').join }, ],
|
|
|
|
[ 'opt_ltlt', %q{ (1 << 31) == 2147483648 }, ],
|
|
|
|
|
|
|
|
[ 'opt_aref', %q{ ['true'][0] }, ],
|
|
|
|
[ 'opt_aref', %q{ { 0 => 'true'}[0] }, ],
|
|
|
|
[ 'opt_aref', %q{ 'true'[0] == ?t }, ],
|
|
|
|
[ 'opt_aset', %q{ [][0] = true }, ],
|
|
|
|
[ 'opt_aset', %q{ {}[0] = true }, ],
|
2024-03-13 14:50:11 +03:00
|
|
|
[ 'opt_aset', %q{ x = +'frue'; x[0] = 't'; x }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_aset', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
# opt_aref / opt_aset mixup situation
|
|
|
|
class X; def x; {}; end; end
|
|
|
|
x = X.new
|
|
|
|
x&.x[true] ||= true # here
|
|
|
|
},
|
|
|
|
|
|
|
|
[ 'opt_aref_with', %q{ { 'true' => true }['true'] }, ],
|
|
|
|
[ 'opt_aref_with', %q{ Struct.new(:nil).new['nil'].nil? }, ],
|
|
|
|
[ 'opt_aset_with', %q{ {}['true'] = true }, ],
|
|
|
|
[ 'opt_aset_with', %q{ Struct.new(:true).new['true'] = true }, ],
|
|
|
|
|
|
|
|
[ 'opt_length', %q{ 'true' .length == 4 }, ],
|
|
|
|
[ 'opt_length', %q{ :true .length == 4 }, ],
|
|
|
|
[ 'opt_length', %q{ [ 'true' ] .length == 1 }, ],
|
|
|
|
[ 'opt_length', %q{ { 'true' => 1 }.length == 1 }, ],
|
|
|
|
[ 'opt_size', %q{ 'true' .size == 4 }, ],
|
|
|
|
[ 'opt_size', %q{ 1.size >= 4 }, ],
|
|
|
|
[ 'opt_size', %q{ [ 'true' ] .size == 1 }, ],
|
|
|
|
[ 'opt_size', %q{ { 'true' => 1 }.size == 1 }, ],
|
|
|
|
[ 'opt_empty_p', %q{ ''.empty? }, ],
|
|
|
|
[ 'opt_empty_p', %q{ [].empty? }, ],
|
|
|
|
[ 'opt_empty_p', %q{ {}.empty? }, ],
|
2021-06-28 17:01:53 +03:00
|
|
|
[ 'opt_empty_p', %q{ Thread::Queue.new.empty? }, ],
|
2017-01-23 05:47:16 +03:00
|
|
|
|
|
|
|
[ 'opt_succ', %q{ 1.succ == 2 }, ],
|
2017-01-30 13:12:18 +03:00
|
|
|
if defined? $FIXNUM_MAX then
|
2017-01-23 05:47:16 +03:00
|
|
|
[ 'opt_succ',%Q{ #{ $FIXNUM_MAX }.succ == #{ $FIXNUM_MAX + 1 } }, ]
|
|
|
|
end,
|
|
|
|
[ 'opt_succ', %q{ '1'.succ == '2' }, ],
|
|
|
|
|
|
|
|
[ 'opt_not', %q{ ! false }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_neq', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class X; def !; true; end; end
|
|
|
|
! X.new
|
|
|
|
},
|
|
|
|
|
2019-09-02 08:33:29 +03:00
|
|
|
[ 'opt_regexpmatch2', %q{ /true/ =~ 'true' && $~ }, ],
|
|
|
|
[ 'opt_regexpmatch2', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class Regexp; def =~ other; true; end; end
|
|
|
|
/true/ =~ 'true'
|
|
|
|
},
|
|
|
|
[ 'opt_regexpmatch2', %q{ 'true' =~ /true/ && $~ }, ],
|
2019-01-19 10:49:47 +03:00
|
|
|
[ 'opt_regexpmatch2', <<-'},', ], # {
|
2017-01-23 05:47:16 +03:00
|
|
|
class String; def =~ other; true; end; end
|
|
|
|
'true' =~ /true/
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
2018-06-29 07:56:07 +03:00
|
|
|
# normal path
|
2019-08-20 22:29:51 +03:00
|
|
|
tests.compact.each do |(insn, expr, *a)|
|
|
|
|
if a.last.is_a?(Hash)
|
|
|
|
a = a.dup
|
|
|
|
kw = a.pop
|
|
|
|
assert_equal 'true', expr, insn, *a, **kw
|
|
|
|
else
|
|
|
|
assert_equal 'true', expr, insn, *a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-06-29 07:56:07 +03:00
|
|
|
# with trace
|
|
|
|
tests.compact.each {|(insn, expr, *a)|
|
|
|
|
progn = "set_trace_func(proc{})\n" + expr
|
2019-08-20 22:29:51 +03:00
|
|
|
if a.last.is_a?(Hash)
|
|
|
|
a = a.dup
|
|
|
|
kw = a.pop
|
|
|
|
assert_equal 'true', progn, 'trace_' + insn, *a, **kw
|
|
|
|
else
|
|
|
|
assert_equal 'true', progn, 'trace_' + insn, *a
|
|
|
|
end
|
2018-06-29 07:56:07 +03:00
|
|
|
}
|
2020-04-15 08:27:38 +03:00
|
|
|
|
|
|
|
assert_normal_exit("#{<<-"begin;"}\n#{<<-'end;'}")
|
|
|
|
begin;
|
|
|
|
RubyVM::InstructionSequence.compile("", debug_level: 5)
|
|
|
|
end;
|