public
Description: Rubinius, the Ruby VM
Homepage: http://rubini.us
Clone URL: git://github.com/evanphx/rubinius.git
Search Repo:
rubinius / kernel / core / eval.rb
100644 204 lines (169 sloc) 6.392 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# depends on: array.rb proc.rb binding.rb
# (due to alias_method)
 
module Kernel
  
  def local_variables
    ary = []
    ctx = MethodContext.current.sender
    
    while ctx.kind_of? BlockContext
      if names = ctx.method.local_names
        names.each { |n| ary << n.to_s }
      end
      ctx = ctx.home
    end
        
    if names = ctx.method.local_names
      names.each { |n| ary << n.to_s }
    end
    
    return ary
  end
  module_function :local_variables
 
  def binding
    ctx = MethodContext.current.sender
    # If we are here because of eval, fetch the context of
    # the thing that invoked eval
    if ctx.from_eval?
      Binding.setup ctx.sender.sender
    else
      Binding.setup ctx
    end
  end
  module_function :binding
 
  def eval(string, binding=nil, filename='(eval)', lineno=1)
    if !binding
      binding = Binding.setup MethodContext.current.sender
    elsif binding.__kind_of__ Proc
      binding = binding.binding
    elsif !binding.__kind_of__ Binding
      raise ArgumentError, "unknown type of binding"
    end
 
    compiled_method = Compile.compile_string string, binding.context, filename, lineno
    compiled_method.staticscope = binding.context.method.staticscope.dup
 
    # This has to be setup so __FILE__ works in eval.
    script = CompiledMethod::Script.new
    script.path = filename
    compiled_method.staticscope.script = script
 
    be = BlockEnvironment.new
    be.under_context binding.context, compiled_method
 
    # Pass the BlockEnvironment this binding was created from
    # down into the new BlockEnvironment we just created.
    # This indicates the "declaration trace" to the stack trace
    # mechanisms, which can be different from the "call trace"
    # in the case of, say: eval("caller", a_proc_instance)
    if binding.from_proc? then
      be.proc_environment = binding.proc_environment
    end
 
    be.from_eval!
    be.call
  end
  module_function :eval
  private :eval
 
  ##
  # :call-seq:
  # obj.instance_eval(string [, filename [, lineno]] ) => obj
  # obj.instance_eval {| | block } => obj
  #
  # Evaluates a string containing Ruby source code, or the given block, within
  # the context of the receiver +obj+. In order to set the context, the
  # variable +self+ is set to +obj+ while the code is executing, giving the
  # code access to +obj+'s instance variables. In the version of
  # #instance_eval that takes a +String+, the optional second and third
  # parameters supply a filename and starting line number that are used when
  # reporting compilation errors.
  #
  # class Klass
  # def initialize
  # @secret = 99
  # end
  # end
  # k = Klass.new
  # k.instance_eval { @secret } #=> 99
 
  def instance_eval(string = nil, filename = "(eval)", line = 1, modeval = false, binding = nil, &prc)
    if prc
      if string
        raise ArgumentError, 'cannot pass both a block and a string to evaluate'
      end
      # Return a copy of the BlockEnvironment with the receiver set to self
      env = prc.block.redirect_to self
      env.method.staticscope = StaticScope.new(__metaclass__, env.method.staticscope)
      original_scope = prc.block.home.method.staticscope
      env.constant_scope = original_scope
      return env.call(*self)
    elsif string
      string = StringValue(string)
 
      if binding
        context = binding.context
      else
        context = MethodContext.current.sender
      end
 
      compiled_method = Compile.compile_string string, context, filename, line
      compiled_method.inherit_scope context.method
 
      # If this is a module_eval style evaluation, add self to the top of the
      # staticscope chain, so that methods and such are added directly to it.
      if modeval
        compiled_method.staticscope = StaticScope.new(self, compiled_method.staticscope)
      else
 
      # Otherwise add our metaclass, so thats where new methods go.
        compiled_method.staticscope = StaticScope.new(metaclass, compiled_method.staticscope)
      end
 
      # This has to be setup so __FILE__ works in eval.
      script = CompiledMethod::Script.new
      script.path = filename
      compiled_method.staticscope.script = script
 
      be = BlockEnvironment.new
      be.from_eval!
      be.under_context context, compiled_method
      be.call_on_instance(self)
    else
      raise ArgumentError, 'block not supplied'
    end
  end
 
end
 
class Module
 
  #--
  # These have to be aliases, not methods that call instance eval, because we
  # need to pull in the binding of the person that calls them, not the
  # intermediate binding.
  #++
 
  def module_eval(string = Undefined, filename = "(eval)", line = 1, &prc)
    # we have a custom version with the prc, rather than using instance_exec
    # so that we can setup the StaticScope properly.
    if prc
      unless string.equal?(Undefined)
        raise ArgumentError, "cannot pass both string and proc"
      end
 
      env = prc.block.redirect_to self
      env.method.staticscope = StaticScope.new(self, env.method.staticscope)
      return env.call()
    elsif string.equal?(Undefined)
      raise ArgumentError, 'block not supplied'
    end
 
    context = MethodContext.current.sender
 
    string = StringValue(string)
 
    compiled_method = Compile.compile_string string, context, filename, line
 
    # The staticscope of a module_eval CM is the receiver of module_eval
    ss = StaticScope.new(self, context.method.staticscope)
 
    # This has to be setup so __FILE__ works in eval.
    script = CompiledMethod::Script.new
    script.path = filename
    ss.script = script
 
    compiled_method.staticscope = ss
 
    # The gist of this code is that we need the receiver's static scope
    # but the caller's binding to implement the proper constant behavior
    be = BlockEnvironment.new
    be.from_eval!
    be.under_context context, compiled_method
    be.make_independent
    be.home.receiver = self
    be.home.make_independent
    # open_module and friends in the VM use this field to determine scope
    be.home.method.staticscope = ss
    be.call
  end
  alias_method :class_eval, :module_eval
 
  def _eval_under(*args, &block)
    raise "not yet" unless block
 
    env = block.block.redirect_to self
    env.method.staticscope = StaticScope.new(self, env.method.staticscope)
 
    return env.call(*args)
  end
  private :_eval_under
end