PageRenderTime 28ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/ruby/core/kernel/eval_spec.rb

https://github.com/rklemme/ruby
Ruby | 416 lines | 386 code | 29 blank | 1 comment | 18 complexity | 55c8d9efc39200d06672fdd52410e233 MD5 | raw file
  1. require_relative '../../spec_helper'
  2. require_relative 'fixtures/classes'
  3. EvalSpecs::A.new.c
  4. describe "Kernel#eval" do
  5. it "is a private method" do
  6. Kernel.should have_private_instance_method(:eval)
  7. end
  8. it "is a module function" do
  9. Kernel.respond_to?(:eval).should == true
  10. end
  11. it "evaluates the code within" do
  12. eval("2 + 3").should == 5
  13. end
  14. it "coerces an object to string" do
  15. eval(EvalSpecs::CoercedObject.new).should == 5
  16. end
  17. it "evaluates within the scope of the eval" do
  18. EvalSpecs::A::B.name.should == "EvalSpecs::A::B"
  19. end
  20. it "evaluates such that constants are scoped to the class of the eval" do
  21. EvalSpecs::A::C.name.should == "EvalSpecs::A::C"
  22. end
  23. it "finds a local in an enclosing scope" do
  24. a = 1
  25. eval("a").should == 1
  26. end
  27. it "updates a local in an enclosing scope" do
  28. a = 1
  29. eval("a = 2")
  30. a.should == 2
  31. end
  32. it "updates a local in a surrounding block scope" do
  33. EvalSpecs.new.f do
  34. a = 1
  35. eval("a = 2")
  36. a.should == 2
  37. end
  38. end
  39. it "updates a local in a scope above a surrounding block scope" do
  40. a = 1
  41. EvalSpecs.new.f do
  42. eval("a = 2")
  43. a.should == 2
  44. end
  45. a.should == 2
  46. end
  47. it "updates a local in a scope above when modified in a nested block scope" do
  48. a = 1
  49. es = EvalSpecs.new
  50. eval("es.f { es.f { a = 2 } }")
  51. a.should == 2
  52. end
  53. it "finds locals in a nested eval" do
  54. eval('test = 10; eval("test")').should == 10
  55. end
  56. it "does not share locals across eval scopes" do
  57. code = fixture __FILE__, "eval_locals.rb"
  58. ruby_exe(code).chomp.should == "NameError"
  59. end
  60. it "doesn't accept a Proc object as a binding" do
  61. x = 1
  62. bind = proc {}
  63. -> { eval("x", bind) }.should raise_error(TypeError)
  64. end
  65. it "does not make Proc locals visible to evaluated code" do
  66. bind = proc { inner = 4 }
  67. -> { eval("inner", bind.binding) }.should raise_error(NameError)
  68. end
  69. # REWRITE ME: This obscures the real behavior of where locals are stored
  70. # in eval bindings.
  71. it "allows a binding to be captured inside an eval" do
  72. outer_binding = binding
  73. level1 = eval("binding", outer_binding)
  74. level2 = eval("binding", level1)
  75. eval("x = 2", outer_binding)
  76. eval("y = 3", level1)
  77. eval("w=1", outer_binding)
  78. eval("w", outer_binding).should == 1
  79. eval("w=1", level1).should == 1
  80. eval("w", level1).should == 1
  81. eval("w=1", level2).should == 1
  82. eval("w", level2).should == 1
  83. eval("x", outer_binding).should == 2
  84. eval("x=2", level1)
  85. eval("x", level1).should == 2
  86. eval("x=2", level2)
  87. eval("x", level2).should == 2
  88. eval("y=3", outer_binding)
  89. eval("y", outer_binding).should == 3
  90. eval("y=3", level1)
  91. eval("y", level1).should == 3
  92. eval("y=3", level2)
  93. eval("y", level2).should == 3
  94. end
  95. it "uses the same scope for local variables when given the same binding" do
  96. outer_binding = binding
  97. eval("if false; a = 1; end", outer_binding)
  98. eval("a", outer_binding).should be_nil
  99. end
  100. it "allows creating a new class in a binding" do
  101. bind = proc {}
  102. eval("class EvalBindingProcA; end; EvalBindingProcA.name", bind.binding).should =~ /EvalBindingProcA$/
  103. end
  104. it "allows creating a new class in a binding created by #eval" do
  105. bind = eval "binding"
  106. eval("class EvalBindingA; end; EvalBindingA.name", bind).should =~ /EvalBindingA$/
  107. end
  108. it "includes file and line information in syntax error" do
  109. expected = 'speccing.rb'
  110. -> {
  111. eval('if true',TOPLEVEL_BINDING, expected)
  112. }.should raise_error(SyntaxError) { |e|
  113. e.message.should =~ /#{expected}:1:.+/
  114. }
  115. end
  116. it "evaluates string with given filename and negative linenumber" do
  117. expected_file = 'speccing.rb'
  118. -> {
  119. eval('if true',TOPLEVEL_BINDING, expected_file, -100)
  120. }.should raise_error(SyntaxError) { |e|
  121. e.message.should =~ /#{expected_file}:-100:.+/
  122. }
  123. end
  124. it "sets constants at the toplevel from inside a block" do
  125. # The class Object bit is needed to workaround some mspec oddness
  126. class Object
  127. [1].each { eval "Const = 1"}
  128. Const.should == 1
  129. remove_const :Const
  130. end
  131. end
  132. ruby_version_is ""..."3.0" do
  133. it "uses the filename of the binding if none is provided" do
  134. eval("__FILE__").should == "(eval)"
  135. suppress_warning {eval("__FILE__", binding)}.should == __FILE__
  136. eval("__FILE__", binding, "success").should == "success"
  137. suppress_warning {eval("eval '__FILE__', binding")}.should == "(eval)"
  138. suppress_warning {eval("eval '__FILE__', binding", binding)}.should == __FILE__
  139. suppress_warning {eval("eval '__FILE__', binding", binding, 'success')}.should == 'success'
  140. end
  141. end
  142. ruby_version_is "3.0" do
  143. it "uses (eval) filename if none is provided" do
  144. eval("__FILE__").should == "(eval)"
  145. eval("__FILE__", binding).should == "(eval)"
  146. eval("__FILE__", binding, "success").should == "success"
  147. eval("eval '__FILE__', binding").should == "(eval)"
  148. eval("eval '__FILE__', binding", binding).should == "(eval)"
  149. eval("eval '__FILE__', binding", binding, 'success').should == '(eval)'
  150. eval("eval '__FILE__', binding, 'success'", binding).should == 'success'
  151. end
  152. end
  153. # Found via Rubinius bug github:#149
  154. it "does not alter the value of __FILE__ in the binding" do
  155. first_time = EvalSpecs.call_eval
  156. second_time = EvalSpecs.call_eval
  157. # This bug is seen by calling the method twice and comparing the values
  158. # of __FILE__ each time. If the bug is present, calling eval will set the
  159. # value of __FILE__ to the eval's "filename" argument.
  160. second_time.should_not == "(eval)"
  161. first_time.should == second_time
  162. end
  163. it "can be aliased" do
  164. alias aliased_eval eval
  165. x = 2
  166. aliased_eval('x += 40')
  167. x.should == 42
  168. end
  169. # See http://jira.codehaus.org/browse/JRUBY-5163
  170. it "uses the receiver as self inside the eval" do
  171. eval("self").should equal(self)
  172. Kernel.eval("self").should equal(Kernel)
  173. end
  174. it "does not pass the block to the method being eval'ed" do
  175. -> {
  176. eval('KernelSpecs::EvalTest.call_yield') { "content" }
  177. }.should raise_error(LocalJumpError)
  178. end
  179. it "returns from the scope calling #eval when evaluating 'return'" do
  180. -> { eval("return :eval") }.call.should == :eval
  181. end
  182. it "unwinds through a Proc-style closure and returns from a lambda-style closure in the closure chain" do
  183. code = fixture __FILE__, "eval_return_with_lambda.rb"
  184. ruby_exe(code).chomp.should == "a,b,c,eval,f"
  185. end
  186. it "raises a LocalJumpError if there is no lambda-style closure in the chain" do
  187. code = fixture __FILE__, "eval_return_without_lambda.rb"
  188. ruby_exe(code).chomp.should == "a,b,c,e,LocalJumpError,f"
  189. end
  190. # See language/magic_comment_spec.rb for more magic comments specs
  191. describe "with a magic encoding comment" do
  192. it "uses the magic comment encoding for the encoding of literal strings" do
  193. code = "# encoding: UTF-8\n'é'.encoding".b
  194. code.encoding.should == Encoding::BINARY
  195. eval(code).should == Encoding::UTF_8
  196. end
  197. it "uses the magic comment encoding for parsing constants" do
  198. code = <<CODE.b
  199. # encoding: UTF-8
  200. class EvalSpecs
  201. = 3.14
  202. end
  203. CODE
  204. code.encoding.should == Encoding::BINARY
  205. eval(code)
  206. EvalSpecs.constants(false).should include(:"Vπ")
  207. EvalSpecs::.should == 3.14
  208. end
  209. it "allows an emacs-style magic comment encoding" do
  210. code = <<CODE.b
  211. # -*- encoding: UTF-8 -*-
  212. class EvalSpecs
  213. Vπemacs = 3.14
  214. end
  215. CODE
  216. code.encoding.should == Encoding::BINARY
  217. eval(code)
  218. EvalSpecs.constants(false).should include(:"Vπemacs")
  219. EvalSpecs::Vπemacs.should == 3.14
  220. end
  221. it "allows spaces before the magic encoding comment" do
  222. code = <<CODE.b
  223. \t \t # encoding: UTF-8
  224. class EvalSpecs
  225. Vπspaces = 3.14
  226. end
  227. CODE
  228. code.encoding.should == Encoding::BINARY
  229. eval(code)
  230. EvalSpecs.constants(false).should include(:"Vπspaces")
  231. EvalSpecs::Vπspaces.should == 3.14
  232. end
  233. it "allows a shebang line before the magic encoding comment" do
  234. code = <<CODE.b
  235. #!/usr/bin/env ruby
  236. # encoding: UTF-8
  237. class EvalSpecs
  238. Vπshebang = 3.14
  239. end
  240. CODE
  241. code.encoding.should == Encoding::BINARY
  242. eval(code)
  243. EvalSpecs.constants(false).should include(:"Vπshebang")
  244. EvalSpecs::Vπshebang.should == 3.14
  245. end
  246. it "allows a shebang line and some spaces before the magic encoding comment" do
  247. code = <<CODE.b
  248. #!/usr/bin/env ruby
  249. # encoding: UTF-8
  250. class EvalSpecs
  251. Vπshebang_spaces = 3.14
  252. end
  253. CODE
  254. code.encoding.should == Encoding::BINARY
  255. eval(code)
  256. EvalSpecs.constants(false).should include(:"Vπshebang_spaces")
  257. EvalSpecs::Vπshebang_spaces.should == 3.14
  258. end
  259. it "allows a magic encoding comment and a subsequent frozen_string_literal magic comment" do
  260. # Make sure frozen_string_literal is not default true
  261. eval("'foo'".b).frozen?.should be_false
  262. code = <<CODE.b
  263. # encoding: UTF-8
  264. # frozen_string_literal: true
  265. class EvalSpecs
  266. Vπstring = "frozen"
  267. end
  268. CODE
  269. code.encoding.should == Encoding::BINARY
  270. eval(code)
  271. EvalSpecs.constants(false).should include(:"Vπstring")
  272. EvalSpecs::Vπstring.should == "frozen"
  273. EvalSpecs::Vπstring.encoding.should == Encoding::UTF_8
  274. EvalSpecs::Vπstring.frozen?.should be_true
  275. end
  276. it "allows a magic encoding comment and a frozen_string_literal magic comment on the same line in emacs style" do
  277. code = <<CODE.b
  278. # -*- encoding: UTF-8; frozen_string_literal: true -*-
  279. class EvalSpecs
  280. Vπsame_line = "frozen"
  281. end
  282. CODE
  283. code.encoding.should == Encoding::BINARY
  284. eval(code)
  285. EvalSpecs.constants(false).should include(:"Vπsame_line")
  286. EvalSpecs::Vπsame_line.should == "frozen"
  287. EvalSpecs::Vπsame_line.encoding.should == Encoding::UTF_8
  288. EvalSpecs::Vπsame_line.frozen?.should be_true
  289. end
  290. it "ignores the magic encoding comment if it is after a frozen_string_literal magic comment" do
  291. code = <<CODE.b
  292. # frozen_string_literal: true
  293. # encoding: UTF-8
  294. class EvalSpecs
  295. Vπfrozen_first = "frozen"
  296. end
  297. CODE
  298. code.encoding.should == Encoding::BINARY
  299. eval(code)
  300. EvalSpecs.constants(false).should_not include(:"Vπfrozen_first")
  301. binary_constant = "Vπfrozen_first".b.to_sym
  302. EvalSpecs.constants(false).should include(binary_constant)
  303. value = EvalSpecs.const_get(binary_constant)
  304. value.should == "frozen"
  305. value.encoding.should == Encoding::BINARY
  306. value.frozen?.should be_true
  307. end
  308. it "ignores the frozen_string_literal magic comment if it appears after a token and warns if $VERBOSE is true" do
  309. code = <<CODE
  310. some_token_before_magic_comment = :anything
  311. # frozen_string_literal: true
  312. class EvalSpecs
  313. Vπstring_not_frozen = "not frozen"
  314. end
  315. CODE
  316. -> { eval(code) }.should complain(/warning: `frozen_string_literal' is ignored after any tokens/, verbose: true)
  317. EvalSpecs::Vπstring_not_frozen.frozen?.should be_false
  318. EvalSpecs.send :remove_const, :Vπstring_not_frozen
  319. -> { eval(code) }.should_not complain(verbose: false)
  320. EvalSpecs::Vπstring_not_frozen.frozen?.should be_false
  321. EvalSpecs.send :remove_const, :Vπstring_not_frozen
  322. end
  323. end
  324. it "activates refinements from the eval scope" do
  325. refinery = Module.new do
  326. refine EvalSpecs::A do
  327. def foo
  328. "bar"
  329. end
  330. end
  331. end
  332. result = nil
  333. Module.new do
  334. using refinery
  335. result = eval "EvalSpecs::A.new.foo"
  336. end
  337. result.should == "bar"
  338. end
  339. it "activates refinements from the binding" do
  340. refinery = Module.new do
  341. refine EvalSpecs::A do
  342. def foo
  343. "bar"
  344. end
  345. end
  346. end
  347. b = nil
  348. m = Module.new do
  349. using refinery
  350. b = binding
  351. end
  352. result = eval "EvalSpecs::A.new.foo", b
  353. result.should == "bar"
  354. end
  355. end