## Eval家族的那些事儿

• March 30, 2019 23:05
• Posted by lan

## 代码片段的执行者eval

Eval是Ruby语言中比较有意思的一个功能了。其实不仅仅是Ruby，许多语言都开放了这个功能。不过在不同语言里，该功能的命名方式以及侧重点会有所不同。

``````> load("print('Hello World!')")()
Hello World!
``````

``````> 1 + 2
3

> load("1 + 2")()
stdin:1: attempt to call a nil value
stack traceback:
stdin:1: in main chunk
[C]: in ?
``````

``````> f = load("return 1 + 2")
> f()
3
``````

``````In : eval
Out: <function eval>

In : eval('1 + 2')
Out: 3

In : eval('hex(3)')
Out: '0x3'
``````

``````In : eval('print(1 + 2)')
File "<string>", line 1
print(1 + 2)
^
SyntaxError: invalid syntax
``````

``````In : def c_print(name):
....:     print(name)
....:

In : eval("c_print(1 + 2)")
3
``````

``````> eval('print("hello")')
hello => nil
> eval('1 + 2')
=> 3
``````

``````// example.rb
a = 1 + 2 + 3

puts a
``````

``````ruby example.rb

6
``````

``````> content = File.read("example.rb") # 读取文件中的代码片段
=> "a = 1 + 2 + 3\n\nputs a\n"
> eval(content)
6
=> nil
``````

## Eval与上下文

``````> a = 10000
=> 10000
> eval('a + 1')
=> 10001
``````

``````eval('a + 1', binding)
=> 10001
``````

`binding``eval`在当前的作用域中都是私有方法

``````> self
=> main
> self.private_methods.grep(:binding)
=> [:binding]
> self.private_methods.grep(:eval)
=> [:eval]
``````

``````> Kernel.singleton_class.instance_methods(false).grep(:eval)
=> [:eval]
> Kernel.singleton_class.instance_methods(false).grep(:binding)
=> [:binding]

> Kernel.eval('a + 1', Kernel.binding)
=> 10001
``````

``````> def hello(a)
>   binding
> end

> ctx = hello('hello world')
``````
``````> eval('print a') # 打印当前上下文的变量`a`
10000 => nil

> eval('print a', ctx) # 打印`hello`运行时上下文的变量`a`
hello world => nil
``````

``````> ctx.class
=> Binding

> Binding.instance_methods(false).grep /eval/
=> [:eval]
``````

``````ctx.eval('print a')
hello world => nil
``````

## 更多的`eval`变种

#### a. classeval/moduleeval

``````> class A; end
=> nil
> A.class_eval "def set_a; @a = 1000; end"
=> :set_a
> A.module_eval "def set_b; @b = 2000; end"
=> :set_b
> a = A.new
=> #<A:0x00007ff59d955fc0>
> a.set_a
=> 1000
> a.set_b
=> 2000
> a.instance_variable_get('@a')
=> 1000
> a.instance_variable_get('@b')
=> 2000
``````

``````> A.class_eval <<M
> def print_a
>  puts @a
> end
> M
=> :print_a

> a.print_a
1000
``````

``````class A; end

A.class_eval do
def set_a
@a = 1000
end

def set_b
@b = 2000
end

def print_a
puts @a
end
end

i = A.new
i.set_a
i.set_b

puts i
puts i.instance_variable_get('@a')
puts i.instance_variable_get('@b')
i.print_a
``````

``````#<A:0x00007fb75102cb18>
1000
2000
1000
``````

``````> A.class_eval do
>   Module.nesting
> end
=> []

> A.class_eval "Module.nesting"
=> [A]
``````

``````> A.private_methods.grep(:binding)
=> [:binding]
``````

``````> class_a_ctx = A.send(:binding)
=> #<Binding:0x00007f98f910ae70>
``````

``````> class_a_ctx.eval 'def set_c; @c = 3000; end'
=> :set_c

> eval('def set_d; @d = 4000; end', class_a_ctx)
=> :set_d
``````

``````> a = A.new
=> #<A:0x00007f98f923c078>
> a.set_d
=> 4000
> a.set_c
=> 3000
> a.instance_variable_get('@d')
=> 4000
> a.instance_variable_get('@c')
=> 3000
``````

#### b. instance_eval

``````class B
attr_accessor :a, :b, :c, :d, :e
def initialize(a, b, c, d, e)
@a = a
@b = b
@c = c
@d = d
@e = e
end
end
``````

``````> k = B.new(1, 2, 3, 4, 5)
=> #<B:0x00007f999fa2c480 @a=1, @b=2, @c=3, @d=4, @e=5>

> puts k.a + k.b + k.c + k.d + k.e
15

> k.instance_eval do
>   puts a + b + c + d + e
> end

15
``````

``````> k.instance_eval do
>   def sum
>     @a + @b + @c + @d + @e
>   end
> end

> k.sum
=> 15

> B.methods.grep :sum
=> []
``````

## 安全问题

``````# string_handling.rb
def string_handling(method)
code = "'hello world'.#{method}"
puts "Evaluating: #{code}"
eval code
end

loop { p string_handling (gets()) }
``````

``````> ruby string_handling.rb
slice(1)
Evaluating: 'hello world'.slice(1)
"e"

upcase
Evaluating: 'hello world'.upcase
"HELLO WORLD"
``````

But，如果一个恶意的用户输入了下面的内容

``````slice(1); require 'fileutils'; FileUtils.rm_f(Dir.glob("*"))
``````

``````# string_handling_better.rb
def string_handling_better(method, *arg)
'hello world'.send(method, *arg)
end
``````

``````> string_handling_better('slice', 1, 10)
=> "ello world"
``````
{"ft_lang_label":"__label__zh","ft_lang_prob":0.6671702,"math_prob":0.94768333,"size":5576,"snap":"2022-40-2023-06","text_gpt3_token_len":3171,"char_repetition_ratio":0.1137832,"word_repetition_ratio":0.0061919503,"special_character_ratio":0.32855093,"punctuation_ratio":0.19391206,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9956698,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2022-10-04T07:20:19Z\",\"WARC-Record-ID\":\"<urn:uuid:72a61bc7-208c-4c4e-ba5b-f9b26ec15bb4>\",\"Content-Length\":\"20221\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:2a55399a-8659-4e8b-8600-de883db0033c>\",\"WARC-Concurrent-To\":\"<urn:uuid:291b515d-e692-4f3c-bfef-69c285d20c0f>\",\"WARC-IP-Address\":\"50.116.11.225\",\"WARC-Target-URI\":\"https://www.beansmile.com/blog/posts/eval-family-in-ruby\",\"WARC-Payload-Digest\":\"sha1:TCQJQ4SQVPDXYDSLNZFGZCXKDZOKG3IH\",\"WARC-Block-Digest\":\"sha1:KY6FPEHAA3FI7MXX65AYOZFP2ORR2GFN\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2022/CC-MAIN-2022-40/CC-MAIN-2022-40_segments_1664030337480.10_warc_CC-MAIN-20221004054641-20221004084641-00460.warc.gz\"}"}