mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
0cd9120f17
Make it lazy and add a hash specialization in addition to the array specialization.
450 lines
6.4 KiB
Ruby
450 lines
6.4 KiB
Ruby
# BOP redefined methods work when JIT compiled
|
|
assert_equal 'false', %q{
|
|
def less_than x
|
|
x < 10
|
|
end
|
|
|
|
class Integer
|
|
def < x
|
|
false
|
|
end
|
|
end
|
|
|
|
less_than 2
|
|
less_than 2
|
|
less_than 2
|
|
}
|
|
|
|
# BOP redefinition works on Integer#<
|
|
assert_equal 'false', %q{
|
|
def less_than x
|
|
x < 10
|
|
end
|
|
|
|
less_than 2
|
|
less_than 2
|
|
|
|
class Integer
|
|
def < x
|
|
false
|
|
end
|
|
end
|
|
|
|
less_than 2
|
|
}
|
|
|
|
# Putobject, less-than operator, fixnums
|
|
assert_equal '2', %q{
|
|
def check_index(index)
|
|
if 0x40000000 < index
|
|
raise "wat? #{index}"
|
|
end
|
|
index
|
|
end
|
|
check_index 2
|
|
check_index 2
|
|
}
|
|
|
|
# foo leaves a temp on the stack before the call
|
|
assert_equal '6', %q{
|
|
def bar
|
|
return 5
|
|
end
|
|
|
|
def foo
|
|
return 1 + bar
|
|
end
|
|
|
|
foo()
|
|
retval = foo()
|
|
}
|
|
|
|
# Method with one arguments
|
|
# foo leaves a temp on the stack before the call
|
|
assert_equal '7', %q{
|
|
def bar(a)
|
|
return a + 1
|
|
end
|
|
|
|
def foo
|
|
return 1 + bar(5)
|
|
end
|
|
|
|
foo()
|
|
retval = foo()
|
|
}
|
|
|
|
# Method with two arguments
|
|
# foo leaves a temp on the stack before the call
|
|
assert_equal '0', %q{
|
|
def bar(a, b)
|
|
return a - b
|
|
end
|
|
|
|
def foo
|
|
return 1 + bar(1, 2)
|
|
end
|
|
|
|
foo()
|
|
retval = foo()
|
|
}
|
|
|
|
# Recursive Ruby-to-Ruby calls
|
|
assert_equal '21', %q{
|
|
def fib(n)
|
|
if n < 2
|
|
return n
|
|
end
|
|
|
|
return fib(n-1) + fib(n-2)
|
|
end
|
|
|
|
r = fib(8)
|
|
}
|
|
|
|
# Ruby-to-Ruby call and C call
|
|
assert_normal_exit %q{
|
|
def bar
|
|
puts('hi!')
|
|
end
|
|
|
|
def foo
|
|
bar
|
|
end
|
|
|
|
foo()
|
|
foo()
|
|
}
|
|
|
|
# The hash method is a C function and uses the self argument
|
|
assert_equal 'true', %q{
|
|
def lehashself
|
|
hash
|
|
end
|
|
|
|
a = lehashself
|
|
b = lehashself
|
|
a == b
|
|
}
|
|
|
|
# Method redefinition (code invalidation) test
|
|
assert_equal '1', %q{
|
|
def ret1
|
|
return 1
|
|
end
|
|
|
|
klass = Class.new do
|
|
def alias_then_hash(klass, method_to_redefine)
|
|
# Redefine the method to be ret1
|
|
klass.alias_method(method_to_redefine, :ret1)
|
|
hash
|
|
end
|
|
end
|
|
|
|
instance = klass.new
|
|
|
|
i = 0
|
|
while i < 12
|
|
if i < 11
|
|
# Redefine the bar method
|
|
instance.alias_then_hash(klass, :bar)
|
|
else
|
|
# Redefine the hash method to be ret1
|
|
retval = instance.alias_then_hash(klass, :hash)
|
|
end
|
|
i += 1
|
|
end
|
|
|
|
retval
|
|
}
|
|
|
|
# Method redefinition (code invalidation) and GC
|
|
assert_equal '7', %q{
|
|
def bar()
|
|
return 5
|
|
end
|
|
|
|
def foo()
|
|
bar()
|
|
end
|
|
|
|
foo()
|
|
foo()
|
|
|
|
def bar()
|
|
return 7
|
|
end
|
|
|
|
4.times { GC.start }
|
|
|
|
foo()
|
|
foo()
|
|
}
|
|
|
|
# Method redefinition with two block versions
|
|
assert_equal '7', %q{
|
|
def bar()
|
|
return 5
|
|
end
|
|
|
|
def foo(n)
|
|
return ((n < 5)? 5:false), bar()
|
|
end
|
|
|
|
foo(4)
|
|
foo(4)
|
|
foo(10)
|
|
foo(10)
|
|
|
|
def bar()
|
|
return 7
|
|
end
|
|
|
|
4.times { GC.start }
|
|
|
|
foo(4)
|
|
foo(4)[1]
|
|
}
|
|
|
|
# Test for GC safety. Don't invalidate dead iseqs.
|
|
assert_normal_exit %q{
|
|
Class.new do
|
|
def foo
|
|
itself
|
|
end
|
|
|
|
new.foo
|
|
new.foo
|
|
new.foo
|
|
new.foo
|
|
end
|
|
|
|
4.times { GC.start }
|
|
def itself
|
|
self
|
|
end
|
|
}
|
|
|
|
# Test getinstancevariable and inline caches
|
|
assert_equal '6', %q{
|
|
class Foo
|
|
def initialize
|
|
@x1 = 1
|
|
@x2 = 1
|
|
@x2 = 1
|
|
@x3 = 1
|
|
@x4 = 3
|
|
end
|
|
|
|
def bar
|
|
x = 1
|
|
@x4 + @x4
|
|
end
|
|
end
|
|
|
|
f = Foo.new
|
|
f.bar
|
|
f.bar
|
|
}
|
|
|
|
# Test that getinstancevariable codegen checks for extended table size
|
|
assert_equal "nil\n", %q{
|
|
class A
|
|
def read
|
|
@ins1000
|
|
end
|
|
end
|
|
|
|
ins = A.new
|
|
other = A.new
|
|
10.times { other.instance_variable_set(:"@otr#{_1}", 'value') }
|
|
1001.times { ins.instance_variable_set(:"@ins#{_1}", 'value') }
|
|
|
|
ins.read
|
|
ins.read
|
|
ins.read
|
|
|
|
p other.read
|
|
}
|
|
|
|
# Test that opt_aref checks the class of the receiver
|
|
assert_equal 'special', %q{
|
|
def foo(array)
|
|
array[30]
|
|
end
|
|
|
|
foo([])
|
|
foo([])
|
|
|
|
special = []
|
|
def special.[](idx)
|
|
'special'
|
|
end
|
|
|
|
foo(special)
|
|
}
|
|
|
|
# Test that object references in generated code get marked and moved
|
|
assert_equal "good", %q{
|
|
def bar
|
|
"good"
|
|
end
|
|
|
|
def foo
|
|
bar
|
|
end
|
|
|
|
foo
|
|
foo
|
|
|
|
GC.verify_compaction_references(double_heap: true, toward: :empty)
|
|
|
|
foo
|
|
}
|
|
|
|
# Test polymorphic getinstancevariable. T_OBJECT -> T_STRING
|
|
assert_equal 'ok', %q{
|
|
@hello = @h1 = @h2 = @h3 = @h4 = 'ok'
|
|
str = ""
|
|
str.instance_variable_set(:@hello, 'ok')
|
|
|
|
public def get
|
|
@hello
|
|
end
|
|
|
|
get
|
|
get
|
|
str.get
|
|
str.get
|
|
}
|
|
|
|
# Test polymorphic getinstancevariable, two different classes
|
|
assert_equal 'ok', %q{
|
|
class Embedded
|
|
def initialize
|
|
@ivar = 0
|
|
end
|
|
|
|
def get
|
|
@ivar
|
|
end
|
|
end
|
|
|
|
class Extended < Embedded
|
|
def initialize
|
|
@v1 = @v2 = @v3 = @v4 = @ivar = 'ok'
|
|
end
|
|
end
|
|
|
|
embed = Embedded.new
|
|
extend = Extended.new
|
|
|
|
embed.get
|
|
embed.get
|
|
extend.get
|
|
extend.get
|
|
}
|
|
|
|
# Test megamorphic getinstancevariable
|
|
assert_equal 'ok', %q{
|
|
parent = Class.new do
|
|
def initialize
|
|
@hello = @h1 = @h2 = @h3 = @h4 = 'ok'
|
|
end
|
|
|
|
def get
|
|
@hello
|
|
end
|
|
end
|
|
|
|
subclasses = 300.times.map { Class.new(parent) }
|
|
subclasses.each { _1.new.get }
|
|
parent.new.get
|
|
}
|
|
|
|
# Test polymorphic opt_aref. array -> hash
|
|
assert_equal '[42, :key]', %q{
|
|
def index(obj, idx)
|
|
obj[idx]
|
|
end
|
|
|
|
index([], 0) # get over compilation threshold
|
|
|
|
[
|
|
index([42], 0),
|
|
index({0=>:key}, 0),
|
|
]
|
|
}
|
|
|
|
# Test polymorphic opt_aref. hash -> array -> custom class
|
|
assert_equal '[nil, nil, :custom]', %q{
|
|
def index(obj, idx)
|
|
obj[idx]
|
|
end
|
|
|
|
custom = Object.new
|
|
def custom.[](_idx)
|
|
:custom
|
|
end
|
|
|
|
index({}, 0) # get over compilation threshold
|
|
|
|
[
|
|
index({}, 0),
|
|
index([], 0),
|
|
index(custom, 0)
|
|
]
|
|
}
|
|
|
|
# Test polymorphic opt_aref. array -> custom class
|
|
assert_equal '[42, :custom]', %q{
|
|
def index(obj, idx)
|
|
obj[idx]
|
|
end
|
|
|
|
custom = Object.new
|
|
def custom.[](_idx)
|
|
:custom
|
|
end
|
|
|
|
index([], 0) # get over compilation threshold
|
|
|
|
[
|
|
index([42], 0),
|
|
index(custom, 0)
|
|
]
|
|
}
|
|
|
|
# Test custom hash method with opt_aref
|
|
assert_equal '[nil, :ok]', %q{
|
|
def index(obj, idx)
|
|
obj[idx]
|
|
end
|
|
|
|
custom = Object.new
|
|
def custom.hash
|
|
42
|
|
end
|
|
|
|
h = {custom => :ok}
|
|
|
|
[
|
|
index(h, 0),
|
|
index(h, custom)
|
|
]
|
|
}
|
|
|
|
# Test default value block for Hash with opt_aref
|
|
assert_equal '[42, :default]', %q{
|
|
def index(obj, idx)
|
|
obj[idx]
|
|
end
|
|
|
|
h = Hash.new { :default }
|
|
h[0] = 42
|
|
|
|
[
|
|
index(h, 0),
|
|
index(h, 1)
|
|
]
|
|
}
|