Change soft-fail to use the config, rather than env
[rbx.git] / kernel / core / method_context.rb
blob15bd3f772d6bda852c03aafeb76a543427239995
1 # depends on: class.rb
3 ##
4 # Stores information about a running method:
6 # sender::          the MethodContext calling this one
7 # block::           block argument passed in
8 # CompiledMethod::  the method being run
9 # locals::          locals for the callee
10 # defining module:: which module (or class) the CompiledMethod was defined in
11 # receiver::        object this CompiledMethod was sent too.
12 # CPU registers::   VM state for running this CompiledMethod
14 # == Life of a MethodContext
16 # Given a CompiledMethod "m" defined on a module or class K, if the
17 # CompiledMethod has no primitive
19 # When calling a method, a MethodContext "mc" is created if the CompiledMethod
20 # representing the method either has no primitive, or the primitive failed.
22 # mc is filled in with various details from the CompiledMethod.  The VM state
23 # is then saved into the active MethodContext and the VM state from mc is
24 # coppied into the VM.  The VM will run the CompiledMethod's bytecode until
25 # encountering a return instruction (:ret).
27 # Upon encountering the return instruction, the VM pops off the top of the
28 # stack for use as the return value, restores the previously running
29 # MethodContext's VM state and pushes the return value back onto the stack.
30 #--
31 # Hey! Be careful with this! This is used by backtrace and if it doesn't work,
32 # you can get recursive exceptions being raised (THATS BAD, BTW).
34 class MethodContext
36   attr_accessor :last_match
38   def from_eval?
39     false
40   end
42   def normalized_name
43     if method_module.is_a?(MetaClass)
44       begin
45         "#{method_module.attached_instance.inspect}.#{name}"
46       rescue Object
47         "#{method_module.attached_instance.class}##{name}"
48       end
49     else
50       if method_module
51         "#{method_module.name}##{name}"
52       else
53         "#{receiver}.#{name}"
54       end
55     end
56   end
58   def position_info
59     if [:__script__, :__block__].include?(self.name)
60       "#{self.file}:#{self.line}"
61     else
62       "#{self.file}:#{self.line}:in `#{self.name}'"
63     end
64   end
65   ##
66   # The Nth group of the last regexp match.
68   def nth_ref(n)
69     if lm = @last_match
70       return lm[n]
71     end
73     return nil
74   end
76   ##
77   # One of the special globals $&, $`, $' or $+.
79   def back_ref(kind)
80     if lm = @last_match
81       res = case kind
82       when :&
83         lm[0]
84       when :"`"
85         lm.pre_match
86       when :"'"
87         lm.post_match
88       when :+
89         lm.captures.last
90       end
92       return res
93     end
95     return nil
96   end
98   def to_s
99     "#<#{self.class}:0x#{self.object_id.to_s(16)} #{receiver}##{name} #{file}:#{line}>"
100   end
101   alias_method :inspect, :to_s
102   # File in which associated method defined.
103   def file
104     return "(unknown)" unless self.method
105     method.file
106   end
108   # See CompiledMethod#lines
109   def lines
110     return [] unless self.method
111     method.lines
112   end
114   # Current line being executed by the VM.
115   def line
116     return 0 unless self.method
117     # We subtract 1 because the ip is actually set to what it should do
118     # next, not what it's currently doing (unless we are at the start of
119     # a new context).
120     ip = self.ip - 1
121     ip = 0 if ip < 0
122     self.method.line_from_ip(ip)
123   end
125   ##
126   # Copies context. If locals is true local variable values are also copied
127   # into new context.
129   def copy(locals=false)
130     d = self.dup
131     return d unless locals
133     i = 0
134     lc = self.locals
135     tot = lc.fields
136     nl = Tuple.new(tot)
137     while i < tot
138       nl.put i, lc.at(i)
139       i += 1
140     end
142     # d.put 10, nl
144     return d
145   end
147   ##
148   # Place in the source that this method was created at.
150   def location
151     l = line()
152     if l == 0
153       "#{file}+#{ip-1}"
154     else
155       "#{file}:#{line}"
156     end
157   end
159   def disable_long_return!
160     # 12 => fc->flags
161     # CTX_FLAG_NO_LONG_RETURN => 1
162     _set_field(12, 1)
163   end
165   def context_stack
166     ret = []
167     frame = self
168     while frame
169       ret << frame
170       # If this context's env was created from a Proc binding
171       # then we duplicate the frame and reset its instruction pointer
172       # in order to show the first line of the block as the active
173       # line in stack trace output
174       if frame.__kind_of__(BlockContext) and frame.env.from_proc?
175         frame = frame.context_from_proc
176       else
177         frame = frame.sender
178       end
179     end
180     ret
181   end
183   # Get the first IP value in the 'home_block' that is on the
184   # first line of the method representing the Proc and return
185   # a copy of the Proc environment's home block initialized with
186   # this IP
187   def context_from_proc
188     frame = self.env.proc_environment.home_block.dup
190     first_line = self.env.proc_environment.method.first_line
191     frame.ip = frame.method.first_ip_on_line(first_line) + 1
193     return frame
194   end
196   def stack_trace_starting_at(start=1)
197     ret = []
198     trace = self.context_stack
199     return nil if start > trace.size
200     trace.each_with_index do |frame, i|
201       next if i < start
202       ret << frame.position_info
203     end
204     ret
205   end
207   ##
208   # Desrcibes the execution state of this context.  Produces the message you
209   # would see in a backtrace print-out.
211   def describe
212     if method_module.equal?(Kernel)
213       str = "Kernel."
214     elsif method_module.kind_of?(MetaClass)
215       str = "#{receiver}."
216     elsif method_module and method_module != receiver.__class__
217       str = "#{method_module}(#{receiver.__class__})#"
218     else
219       str = "#{receiver.__class__}#"
220     end
222     if kind_of? BlockContext
223       str << "#{name} {}"
224     elsif name == method.name
225       str << "#{name}"
226     else
227       str << "#{name} (#{method.name})"
228     end
229   end
231   def const_defined?(name)
232     scope = method.staticscope
233     while scope and scope.module != Object
234       return true if scope.module.const_defined?(name)
235       scope = scope.parent
236     end
238     return Object.const_defined?(name)
239   end
241   def const_path_defined?(path)
242     if path.prefix? "::"
243       return Object.const_path_defined? path[2..-1]
244     end
246     parts = path.split("::")
247     top = parts.shift
249     scope = method.staticscope
251     while scope
252       mod = top.to_s !~ /self/ ? scope.module.__send__(:recursive_const_get, top, false) : scope.module
253       return mod.const_path_defined? parts.join("::") if mod
255       scope = scope.parent
256     end
258     return Object.const_path_defined? parts.join("::")
259   end
261   def class_variable_get(name)
262     return current_scope.class_variable_get(name)
263   end
265   def class_variable_set(name, val)
266     return current_scope.class_variable_set(name, val)
267   end
269   def class_variable_defined?(name)
270     return current_scope.class_variable_defined?(name)
271   end
273   def current_scope
274     if ss = method.staticscope
275       return ss.module
276     else
277       return method_module
278     end
279   end
281   ##
282   # Safely dups this MethodContext's method for manipulation.
284   def make_independent
285     self.method = method.dup
286   end
288   def send_private?
289     @send_private
290   end
292   ##
293   # Look up the staticscope chain to find the one with a Script object
294   # attached to it. Return that object.
296   def script_object
297     if ss = method.staticscope
298       while ss and !ss.script
299         ss = ss.parent
300       end
302       return ss.script if ss
303     end
305     return nil
306   end
308   ##
309   # Used to implement __FILE__ properly. kernel/core/compile.rb stashes
310   # the path used to load this file in the Script object located in
311   # the top staticscope.
313   def active_path
314     if script = script_object()
315       if path = script.path
316         return path.dup
317       end
318     end
320     # If for some reason that didn't work, return the compile time filename.
321     method.file.to_s
322   end
324   ##
325   # Used to set the module body toggles
327   attr_accessor :method_scope
329   def alias_method(name, original)
330     scope = MethodContext.current.sender.current_scope
331     scope.__send__(:alias_method, name, original)
332   end
334   # This version is trivial, and is meant to match the API of BlockContext
335   def __const_set__(name, value)
336     const_scope = MethodContext.current.sender.receiver
337     const_scope.__send__(:__const_set__, name, value)
338   end
340   ##
341   # Called when 'def name' is used in userland
343   def __add_method__(name, obj)
344     s = MethodContext.current.sender
345     scope = s.method_scope || :public
347     if name == :initialize or scope == :module
348       visibility = :private
349     else
350       visibility = scope
351     end
353     # All userland added methods start out with a serial of 1.
354     obj.serial = 1
356     # Push the scoping down.
357     obj.staticscope = s.method.staticscope
359     Rubinius::VM.reset_method_cache(name)
361     obj.staticscope.module.method_table[name] = Tuple[visibility, obj]
363     if scope == :module
364       s.current_scope.module_function name
365     end
367     if s.current_scope.respond_to? :method_added
368       s.current_scope.method_added(name)
369     end
371     # Return value here is the return value of the 'def' expression
372     return obj
373   end
378 # Stores all the information about a running NativeMethod.
380 class NativeMethodContext
381   def location
382     "#{file}"
383   end