summaryrefslogtreecommitdiff
path: root/test/ruby/test_eval.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/ruby/test_eval.rb')
-rw-r--r--test/ruby/test_eval.rb565
1 files changed, 422 insertions, 143 deletions
diff --git a/test/ruby/test_eval.rb b/test/ruby/test_eval.rb
index e79d1c82ec..d2145bec5d 100644
--- a/test/ruby/test_eval.rb
+++ b/test/ruby/test_eval.rb
@@ -1,3 +1,4 @@
+# frozen_string_literal: false
require 'test/unit'
class TestEval < Test::Unit::TestCase
@@ -7,6 +8,15 @@ class TestEval < Test::Unit::TestCase
$gvar__eval = 14
Const = 15
+ def ruby(*args)
+ args = ['-e', '$>.write($<.read)'] if args.empty?
+ ruby = EnvUtil.rubybin
+ f = IO.popen([ruby] + args, 'r+')
+ yield(f)
+ ensure
+ f.close unless !f || f.closed?
+ end
+
def test_eval_basic
assert_equal nil, eval("nil")
assert_equal true, eval("true")
@@ -117,119 +127,223 @@ class TestEval < Test::Unit::TestCase
}
end
- def forall_TYPE(mid)
- objects = [Object.new, [], nil, true, false, 77, ] #:sym] # TODO: check
+ def test_module_eval_block_symbol
+ assert_equal "Math", Math.module_eval(&:to_s)
+ end
+
+ def forall_TYPE
+ objects = [Object.new, [], nil, true, false] # TODO: check
objects.each do |obj|
- obj.instance_variable_set :@ivar, 12
- obj.class.class_variable_set :@@cvar, 13
- # Use same value with env. See also test_instance_variable_cvar.
- obj.class.const_set :Const, 15 unless obj.class.const_defined?(:Const)
- funcall mid, obj
+ obj.instance_variable_set :@ivar, 12 unless obj.frozen?
+ yield obj
end
end
def test_instance_eval_string_basic
- forall_TYPE :instance_eval_string_basic_i
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval("nil")
+ assert_equal true, o.instance_eval("true")
+ assert_equal false, o.instance_eval("false")
+ assert_equal o, o.instance_eval("self")
+ assert_equal 1, o.instance_eval("1")
+ assert_equal :sym, o.instance_eval(":sym")
+
+ assert_equal 11, o.instance_eval("11")
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ assert_equal 16, o.instance_eval("7 + 9")
+ assert_equal 17, o.instance_eval("17.to_i")
+ assert_equal "18", o.instance_eval(%q("18"))
+ assert_equal "19", o.instance_eval(%q("1#{9}"))
+
+ 1.times {
+ assert_equal 12, o.instance_eval("@ivar") unless o.frozen?
+ assert_equal 13, o.instance_eval("@@cvar")
+ assert_equal 14, o.instance_eval("$gvar__eval")
+ assert_equal 15, o.instance_eval("Const")
+ }
+ end
end
- def instance_eval_string_basic_i(o)
- assert_equal nil, o.instance_eval("nil")
- assert_equal true, o.instance_eval("true")
- assert_equal false, o.instance_eval("false")
- assert_equal o, o.instance_eval("self")
- assert_equal 1, o.instance_eval("1")
- assert_equal :sym, o.instance_eval(":sym")
+ def test_instance_eval_block_basic
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_eval { nil }
+ assert_equal true, o.instance_eval { true }
+ assert_equal false, o.instance_eval { false }
+ assert_equal o, o.instance_eval { self }
+ assert_equal 1, o.instance_eval { 1 }
+ assert_equal :sym, o.instance_eval { :sym }
+
+ assert_equal 11, o.instance_eval { 11 }
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ assert_equal 16, o.instance_eval { 7 + 9 }
+ assert_equal 17, o.instance_eval { 17.to_i }
+ assert_equal "18", o.instance_eval { "18" }
+ assert_equal "19", o.instance_eval { "1#{9}" }
+
+ 1.times {
+ assert_equal 12, o.instance_eval { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_eval { @@cvar }
+ assert_equal 14, o.instance_eval { $gvar__eval }
+ assert_equal 15, o.instance_eval { Const }
+ }
+ end
+ end
- assert_equal 11, o.instance_eval("11")
- assert_equal 12, o.instance_eval("@ivar")
- begin
- assert_equal 13, o.instance_eval("@@cvar")
- rescue => err
- assert false, "cannot get cvar from #{o.class}"
+ def test_instance_eval_block_self
+ # instance_eval(&block)'s self must not be sticky (jruby/jruby#2060)
+ pr = proc { self }
+ assert_equal self, pr.call
+ o = Object.new
+ assert_equal o, o.instance_eval(&pr)
+ assert_equal self, pr.call
+ end
+
+ def test_instance_eval_block_symbol
+ forall_TYPE do |o|
+ assert_equal o.to_s, o.instance_eval(&:to_s)
end
- assert_equal 14, o.instance_eval("$gvar__eval")
- assert_equal 15, o.instance_eval("Const")
- assert_equal 16, o.instance_eval("7 + 9")
- assert_equal 17, o.instance_eval("17.to_i")
- assert_equal "18", o.instance_eval(%q("18"))
- assert_equal "19", o.instance_eval(%q("1#{9}"))
+ end
- 1.times {
- assert_equal 12, o.instance_eval("@ivar")
- assert_equal 13, o.instance_eval("@@cvar")
- assert_equal 14, o.instance_eval("$gvar__eval")
- assert_equal 15, o.instance_eval("Const")
- }
+ def test_instance_eval_cvar
+ [Object.new, [], 7, :sym, true, false, nil].each do |obj|
+ assert_equal(13, obj.instance_eval("@@cvar"))
+ assert_equal(13, obj.instance_eval{@@cvar})
+ # assert_raise(NameError){obj.instance_eval("@@cvar")}
+ # assert_raise(NameError){obj.instance_eval{@@cvar}}
+ end
end
- def test_instance_eval_block_basic
- forall_TYPE :instance_eval_block_basic_i
- end
-
- def instance_eval_block_basic_i(o)
- assert_equal nil, o.instance_eval { nil }
- assert_equal true, o.instance_eval { true }
- assert_equal false, o.instance_eval { false }
- assert_equal o, o.instance_eval { self }
- assert_equal 1, o.instance_eval { 1 }
- assert_equal :sym, o.instance_eval { :sym }
-
- assert_equal 11, o.instance_eval { 11 }
- assert_equal 12, o.instance_eval { @ivar }
- assert_equal 13, o.instance_eval { @@cvar }
- assert_equal 14, o.instance_eval { $gvar__eval }
- assert_equal 15, o.instance_eval { Const }
- assert_equal 16, o.instance_eval { 7 + 9 }
- assert_equal 17, o.instance_eval { 17.to_i }
- assert_equal "18", o.instance_eval { "18" }
- assert_equal "19", o.instance_eval { "1#{9}" }
+ def test_instance_exec_cvar
+ [Object.new, [], 7, :sym, true, false, nil].each do |obj|
+ assert_equal(13, obj.instance_exec{@@cvar})
+ end
+ end
- 1.times {
- assert_equal 12, o.instance_eval { @ivar }
- assert_equal 13, o.instance_eval { @@cvar }
- assert_equal 14, o.instance_eval { $gvar__eval }
- assert_equal 15, o.instance_eval { Const }
- }
+ def test_instance_eval_method
+ bug2788 = '[ruby-core:28324]'
+ [Object.new, [], nil, true, false].each do |o|
+ assert_nothing_raised(TypeError, "#{bug2788} (#{o.inspect})") do
+ o.instance_eval {
+ def defd_using_instance_eval() :ok end
+ }
+ end
+ assert_equal(:ok, o.defd_using_instance_eval)
+ class << o
+ remove_method :defd_using_instance_eval
+ end
+ end
end
- def test_instance_eval_cvar
- env = @@cvar
- cls = "class"
- [Object.new, [], 7, ].each do |obj| # TODO: check :sym
- obj.class.class_variable_set :@@cvar, cls
- assert_equal env, obj.instance_eval("@@cvar")
- assert_equal env, obj.instance_eval { @@cvar }
+ def test_instance_eval_on_argf_singleton_class
+ bug8188 = '[ruby-core:53839] [Bug #8188]'
+ assert_warning('', bug8188) do
+ ARGF.singleton_class.instance_eval{}
+ end
+ end
+
+ class Foo
+ Bar = 2
+ end
+
+ def test_instance_eval_const
+ bar = nil
+ assert_nothing_raised(NameError) do
+ bar = Foo.new.instance_eval("Bar")
end
- [true, false, nil].each do |obj|
- obj.class.class_variable_set :@@cvar, cls
- assert_equal cls, obj.instance_eval("@@cvar")
- assert_equal cls, obj.instance_eval { @@cvar }
+ assert_equal(2, bar)
+ end
+
+ def test_instance_exec_block_basic
+ forall_TYPE do |o|
+ assert_equal nil, o.instance_exec { nil }
+ assert_equal true, o.instance_exec { true }
+ assert_equal false, o.instance_exec { false }
+ assert_equal o, o.instance_exec { self }
+ assert_equal 1, o.instance_exec { 1 }
+ assert_equal :sym, o.instance_exec { :sym }
+
+ assert_equal 11, o.instance_exec { 11 }
+ assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_exec { @@cvar }
+ assert_equal 14, o.instance_exec { $gvar__eval }
+ assert_equal 15, o.instance_exec { Const }
+ assert_equal 16, o.instance_exec { 7 + 9 }
+ assert_equal 17, o.instance_exec { 17.to_i }
+ assert_equal "18", o.instance_exec { "18" }
+ assert_equal "19", o.instance_exec { "1#{9}" }
+
+ 1.times {
+ assert_equal 12, o.instance_exec { @ivar } unless o.frozen?
+ assert_equal 13, o.instance_exec { @@cvar }
+ assert_equal 14, o.instance_exec { $gvar__eval }
+ assert_equal 15, o.instance_exec { Const }
+ }
end
end
- #
+ def test_instance_exec_method_definition
+ klass = Class.new
+ o = klass.new
+
+ o.instance_exec do
+ def foo
+ :foo_result
+ end
+ end
+
+ assert_respond_to o, :foo
+ refute_respond_to klass, :foo
+ refute_respond_to klass.new, :foo
+
+ assert_equal :foo_result, o.foo
+ end
+
+ def test_instance_exec_eval_method_definition
+ klass = Class.new
+ o = klass.new
+
+ o.instance_exec do
+ eval %{
+ def foo
+ :foo_result
+ end
+ }
+ end
+
+ assert_respond_to o, :foo
+ refute_respond_to klass, :foo
+ refute_respond_to klass.new, :foo
+
+ assert_equal :foo_result, o.foo
+ end
+
+ #
# From ruby/test/ruby/test_eval.rb
#
- def test_ev
- local1 = "local1"
+ def make_test_binding
+ local1 = local1 = "local1"
lambda {
- local2 = "local2"
+ local2 = local2 = "local2"
return binding
}.call
end
def test_eval_orig
assert_nil(eval(""))
- $bad=false
- eval 'while false; $bad = true; print "foo\n" end'
- assert(!$bad)
+ bad=false
+ eval 'while false; bad = true; print "foo\n" end'
+ assert(!bad)
- assert(eval('TRUE'))
+ assert(eval('Object'))
assert(eval('true'))
- assert(!eval('NIL'))
assert(!eval('nil'))
- assert(!eval('FALSE'))
assert(!eval('false'))
$foo = 'assert(true)'
@@ -241,53 +355,40 @@ class TestEval < Test::Unit::TestCase
assert_equal('assert(true)', eval("$foo"))
assert_equal(true, eval("true"))
- i = 5
+ i = i = 5
assert(eval("i == 5"))
assert_equal(5, eval("i"))
assert(eval("defined? i"))
- $x = test_ev
- assert_equal("local1", eval("local1", $x)) # normal local var
- assert_equal("local2", eval("local2", $x)) # nested local var
- $bad = true
+ x = make_test_binding
+ assert_equal("local1", eval("local1", x)) # normal local var
+ assert_equal("local2", eval("local2", x)) # nested local var
+ bad = true
begin
p eval("local1")
rescue NameError # must raise error
- $bad = false
+ bad = false
end
- assert(!$bad)
+ assert(!bad)
# !! use class_eval to avoid nested definition
- self.class.class_eval %q(
+ x = self.class.class_eval %q(
module EvTest
EVTEST1 = 25
evtest2 = 125
- $x = binding
+ evtest2 = evtest2
+ binding
end
)
- assert_equal(25, eval("EVTEST1", $x)) # constant in module
- assert_equal(125, eval("evtest2", $x)) # local var in module
- $bad = true
+ assert_equal(25, eval("EVTEST1", x)) # constant in module
+ assert_equal(125, eval("evtest2", x)) # local var in module
+ bad = true
begin
eval("EVTEST1")
rescue NameError # must raise error
- $bad = false
- end
- assert(!$bad)
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- x = proc{}
- eval "i4 = 1", x
- assert_equal(1, eval("i4", x))
- x = proc{proc{}}.call
- eval "i4 = 22", x
- assert_equal(22, eval("i4", x))
- $x = []
- x = proc{proc{}}.call
- eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
- assert_equal(8, $x[4].call)
+ bad = false
end
+ assert(!bad)
x = binding
eval "i = 1", x
@@ -295,10 +396,10 @@ class TestEval < Test::Unit::TestCase
x = proc{binding}.call
eval "i = 22", x
assert_equal(22, eval("i", x))
- $x = []
+ t = []
x = proc{binding}.call
- eval "(0..9).each{|i5| $x[i5] = proc{i5*2}}", x
- assert_equal(8, $x[4].call)
+ eval "(0..9).each{|i5| t[i5] = proc{i5*2}}", x
+ assert_equal(8, t[4].call)
x = proc{binding}.call
eval "for i6 in 1..1; j6=i6; end", x
assert(eval("defined? i6", x))
@@ -308,63 +409,48 @@ class TestEval < Test::Unit::TestCase
p = binding
eval "foo11 = 1", p
foo22 = 5
- proc{foo11=22}.call
+ proc{foo11=22;foo11}.call
proc{foo22=55}.call
# assert_equal(eval("foo11"), eval("foo11", p))
# assert_equal(1, eval("foo11"))
assert_equal(eval("foo22"), eval("foo22", p))
assert_equal(55, eval("foo22"))
+ assert_equal(55, foo22)
}.call
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- assert_equal(0, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert(!defined?(i7))
- end
-
- if false
- # Ruby 2.0 doesn't see Proc as Binding
- p1 = proc{i7 = 0; proc{i7}}.call
- i7 = nil
- assert_equal(0, p1.call)
- eval "i7=1", p1
- assert_equal(1, p1.call)
- eval "i7=5", p1
- assert_equal(5, p1.call)
- assert_nil(i7)
+ self.class.class_eval do
+ remove_const :EvTest
end
end
- def test_nil_instance_eval_cvar # [ruby-dev:24103]
+ def test_nil_instance_eval_cvar
def nil.test_binding
binding
end
bb = eval("nil.instance_eval \"binding\"", nil.test_binding)
- assert_raise(NameError) { eval("@@a", bb) }
+ assert_raise(NameError, "[ruby-dev:24103]") { eval("@@a", bb) }
class << nil
remove_method :test_binding
end
end
- def test_fixnum_instance_eval_cvar # [ruby-dev:24213]
- assert_raise(NameError) { 1.instance_eval "@@a" }
+ def test_fixnum_instance_eval_cvar
+ assert_raise(NameError, "[ruby-dev:24213]") { 1.instance_eval "@@a" }
end
- def test_cvar_scope_with_instance_eval # [ruby-dev:24223]
- Fixnum.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229]
+ def test_cvar_scope_with_instance_eval
+ # TODO: check
+ Integer.class_eval "@@test_cvar_scope_with_instance_eval = 1" # depends on [ruby-dev:24229]
@@test_cvar_scope_with_instance_eval = 4
- assert_equal(4, 1.instance_eval("@@test_cvar_scope_with_instance_eval"))
- Fixnum.__send__(:remove_class_variable, :@@test_cvar_scope_with_instance_eval)
+ assert_equal(4, 1.instance_eval("@@test_cvar_scope_with_instance_eval"), "[ruby-dev:24223]")
+ Integer.__send__(:remove_class_variable, :@@test_cvar_scope_with_instance_eval)
end
- def test_eval_and_define_method # [ruby-dev:24228]
- assert_nothing_raised {
+ def test_eval_and_define_method
+ assert_nothing_raised("[ruby-dev:24228]") {
def temporally_method_for_test_eval_and_define_method(&block)
lambda {
- class << Object.new; self end.funcall(:define_method, :zzz, &block)
+ class << Object.new; self end.send(:define_method, :zzz, &block)
}
end
v = eval("temporally_method_for_test_eval_and_define_method {}")
@@ -373,4 +459,197 @@ class TestEval < Test::Unit::TestCase
}
end
+ def test_define_method_block
+ cc = Class.new do
+ define_method(:foo) {|&block|
+ block.call if block
+ }
+ end
+
+ c = cc.new
+ x = "ng"
+ c.foo() {x = "ok"}
+ assert_equal("ok", x)
+ end
+
+ def test_define_method_toplevel
+ feature6609 = '[ruby-core:45715]'
+ main = eval("self", TOPLEVEL_BINDING)
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_block") {feature6609}
+ end
+ end
+ assert_equal(feature6609, feature6609_block)
+
+ assert_nothing_raised(NoMethodError, feature6609) do
+ main.instance_eval do
+ define_method("feature6609_method", Object.instance_method(:feature6609_block))
+ end
+ end
+ assert_equal(feature6609, feature6609_method)
+ ensure
+ Object.undef_method(:feature6609_block) rescue nil
+ Object.undef_method(:feature6609_method) rescue nil
+ end
+
+ def test_eval_using_integer_as_binding
+ assert_raise(TypeError) { eval("", 1) }
+ end
+
+ def test_eval_raise
+ assert_raise(RuntimeError) { eval("raise ''") }
+ end
+
+ def test_eval_with_toplevel_binding # [ruby-dev:37142]
+ ruby("-e", "x = 0; eval('p x', TOPLEVEL_BINDING)") do |f|
+ f.close_write
+ assert_equal("0", f.read.chomp)
+ end
+ end
+
+ def test_eval_ascii_incompatible
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-16be"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-16le"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32be"))}
+ assert_raise(ArgumentError) {eval("__ENCODING__".encode("utf-32le"))}
+ end
+
+ def test_instance_eval_method_proc
+ bug3860 = Class.new do
+ def initialize(a);
+ @a=a
+ end
+ def get(*args)
+ @a
+ end
+ end
+ foo = bug3860.new 1
+ foo_pr = foo.method(:get).to_proc
+ result = foo.instance_eval(&foo_pr)
+ assert_equal(1, result, 'Bug #3786, Bug #3860, [ruby-core:32501]')
+ end
+
+ def test_file_encoding
+ fname = "\u{3042}".encode("euc-jp")
+ assert_equal(fname, eval("__FILE__", nil, fname, 1))
+ end
+
+ def test_eval_invalid_block_exit_bug_20597
+ assert_raise(SyntaxError){eval("break if false")}
+ assert_raise(SyntaxError){eval("next if false")}
+ assert_raise(SyntaxError){eval("redo if false")}
+ end
+
+ def test_eval_location_fstring
+ o = Object.new
+ o.instance_eval "def foo() end", "generated code"
+ o.instance_eval "def bar() end", "generated code"
+
+ a, b = o.method(:foo).source_location[0],
+ o.method(:bar).source_location[0]
+
+ assert_same a, b
+ end
+
+ def test_eval_location_binding
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", nil))
+ assert_equal(["(eval at #{__FILE__}:#{__LINE__})", 1], eval("[__FILE__, __LINE__]", binding))
+ assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", nil, 'foo'))
+ assert_equal(['foo', 1], eval("[__FILE__, __LINE__]", binding, 'foo'))
+ assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", nil, 'foo', 2))
+ assert_equal(['foo', 2], eval("[__FILE__, __LINE__]", binding, 'foo', 2))
+ end
+
+ def test_fstring_instance_eval
+ bug = "[ruby-core:78116] [Bug #12930]".freeze
+ assert_same bug, (bug.instance_eval {self})
+ assert_raise(FrozenError) {
+ bug.instance_eval {@ivar = true}
+ }
+ end
+
+ def test_gced_binding_block
+ assert_normal_exit %q{
+ def m
+ binding
+ end
+ GC.stress = true
+ b = nil
+ tap do
+ b = m {}
+ end
+ 0.times.to_a
+ b.eval('yield')
+ }, '[Bug #10368]'
+ end
+
+ def test_gced_eval_location
+ Dir.mktmpdir do |d|
+ File.write("#{d}/2.rb", "")
+ File.write("#{d}/1.rb", "require_relative '2'\n""__FILE__\n")
+ file = "1.rb"
+ path = File.expand_path(file, d)
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ assert_equal(path, eval(File.read(path), nil, File.expand_path(file, d)))
+ end
+ end
+
+ def orphan_proc
+ proc {eval("return :ng")}
+ end
+
+ def orphan_lambda
+ lambda {eval("return :ok")}
+ end
+
+ def test_return_in_eval_proc
+ x = orphan_proc
+ assert_raise(LocalJumpError) {x.call}
+ end
+
+ def test_return_in_eval_lambda
+ x = orphan_lambda
+ assert_equal(:ok, x.call)
+ end
+
+ def test_syntax_error_no_memory_leak
+ assert_no_memory_leak([], "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
+ begin;
+ 100_000.times do
+ eval("/[/=~s")
+ rescue SyntaxError
+ else
+ raise "Expected SyntaxError to be raised"
+ end
+ end;
+
+ assert_no_memory_leak([], "#{<<~'begin;'}", "#{<<~'end;'}", rss: true)
+ begin;
+ a = 1
+
+ 100_000.times do
+ eval("if a in [0, 0] | [0, a]; end")
+ rescue SyntaxError
+ else
+ raise "Expected SyntaxError to be raised"
+ end
+ end;
+ end
+
+ def test_outer_local_variable_under_gc_compact_stress
+ omit "compaction is not supported on this platform" unless GC.respond_to?(:compact)
+ omit "compaction is not supported on s390x" if /s390x/ =~ RUBY_PLATFORM
+
+ assert_separately([], <<~RUBY)
+ o = Object.new
+ def o.m = 1
+
+ GC.verify_compaction_references(expand_heap: true, toward: :empty)
+
+ EnvUtil.under_gc_compact_stress do
+ assert_equal(1, eval("o.m"))
+ end
+ RUBY
+ end
end