Updated RubySpec submodule to 9f66d0b1.
[rbx.git] / kernel / core / context.rb
blobbade639d5f8963852aaf28c762f4785a0ac91b7b
1 # depends on: class.rb
3 ##
4 # Stores all the information about a running method.
5 #--
6 # Hey! Be careful with this! This is used by backtrace and if it doesn't work,
7 # you can get recursive exceptions being raised (THATS BAD, BTW).
9 class MethodContext
11   attr_accessor :last_match
13   ##
14   # The Nth group of the last regexp match.
16   def nth_ref(n)
17     if lm = @last_match
18       return lm[n]
19     end
21     return nil
22   end
24   ##
25   # One of the special globals $&, $`, $' or $+.
27   def back_ref(kind)
28     if lm = @last_match
29       res = case kind
30       when :&
31         lm[0]
32       when :"`"
33         lm.pre_match
34       when :"'"
35         lm.post_match
36       when :+
37         lm.captures.last
38       end
40       return res
41     end
43     return nil
44   end
46   def to_s
47     "#<#{self.class}:0x#{self.object_id.to_s(16)} #{receiver}##{name} #{file}:#{line}>"
48   end
49   alias_method :inspect, :to_s
50   # File in which associated method defined.
51   def file
52     return "(unknown)" unless self.method
53     method.file
54   end
56   # See CompiledMethod#lines
57   def lines
58     return [] unless self.method
59     method.lines
60   end
62   # Current line being executed by the VM.
63   def line
64     return 0 unless self.method
65     # We subtract 1 because the ip is actually set to what it should do
66     # next, not what it's currently doing (unless we are at the start of
67     # a new context).
68     return self.method.line_from_ip(self.ip == 0 ? self.ip : self.ip - 1)
69   end
71   # Copies context. If locals is true
72   # local variable values are also
73   # copied into new context.
74   def copy(locals=false)
75     d = self.dup
76     return d unless locals
78     i = 0
79     lc = self.locals
80     tot = lc.fields
81     nl = Tuple.new(tot)
82     while i < tot
83       nl.put i, lc.at(i)
84       i += 1
85     end
87     # d.put 10, nl
89     return d
90   end
92   def location
93     l = line()
94     if l == 0
95       "#{file}+#{ip-1}"
96     else
97       "#{file}:#{line}"
98     end
99   end
101   def disable_long_return!
102     # 12 => fc->flags
103     # CTX_FLAG_NO_LONG_RETURN => 1
104     _set_field(12, 1)
105   end
107   def calling_hierarchy(start=1)
108     ret = []
109     ctx = self
110     i = 0
111     until ctx.nil?
112       if i >= start
113         if ctx.method.name
114           ret << "#{ctx.file}:#{ctx.line}:in `#{ctx.method.name}'"
115         else
116           ret << "#{ctx.file}:#{ctx.line}"
117         end
119         # In a backtrace, an eval'd context's binding shows up
120         if ctx.kind_of?(BlockContext) and ctx.env.from_eval? then
121           home = ctx.env.home
122           ret << "#{home.file}:#{home.line} in `#{home.method.name}'"
123         end
125       end
127       i += 1
128       ctx = ctx.sender
129     end
131     return nil if start > i + 1
132     ret
133   end
135   def describe
136     if method_module.equal?(Kernel)
137       str = "Kernel."
138     elsif method_module.kind_of?(MetaClass)
139       str = "#{receiver}."
140     elsif method_module and method_module != receiver.class
141       str = "#{method_module}(#{receiver.class})#"
142     else
143       str = "#{receiver.class}#"
144     end
146     if kind_of? BlockContext
147       str << "#{name} {}"
148     elsif name == method.name
149       str << "#{name}"
150     else
151       str << "#{name} (#{method.name})"
152     end
153   end
155   def const_defined?(name)
156     scope = method.staticscope
157     while scope
158       return true if scope.module.const_defined?(name)
159       scope = scope.parent
160     end
162     return Object.const_defined?(name)
163   end
165   def const_path_defined?(path)
166     scope = method.staticscope
167     scope.module.const_path_defined?(path)
168   end
170   def class_variable_get(name)
171     return current_scope.class_variable_get(name)
172   end
174   def class_variable_set(name, val)
175     if receiver.kind_of? Module
176       return receiver.class_variable_set(name, val)
177     end
179     return current_scope.class_variable_set(name, val)
180   end
182   def class_variable_defined?(name)
183     return current_scope.class_variable_defined?(name)
184   end
186   def current_scope
187     if ss = method.staticscope
188       return ss.module
189     else
190       return method_module
191     end
192   end
194   def make_independent
195     self.method = method.dup
196   end
198   def send_private?
199     @send_private
200   end
202   ##
203   # Look up the staticscope chain to find the one with a Script object
204   # attached to it. Return that object.
206   def script_object
207     if ss = method.staticscope
208       while ss and !ss.script
209         ss = ss.parent
210       end
212       return ss.script if ss
213     end
215     return nil
216   end
218   ##
219   # Used to implement __FILE__ properly. kernel/core/compile.rb stashes
220   # the path used to load this file in the Script object located in
221   # the top staticscope.
223   def active_path
224     if script = script_object()
225       if path = script.path
226         return path.dup
227       end
228     end
230     # If for some reason that didn't work, return the compile time filename.
231     method.file.to_s
232   end
234   ##
235   # Used to set the module body toggles
237   attr_accessor :method_scope
239   def alias_method(name, original)
240     scope = MethodContext.current.sender.current_scope
241     scope.__send__(:alias_method, name, original)
242   end
244   # This version is trivial, and is meant to match the API of BlockContext
245   def __const_set__(name, value)
246     const_scope = MethodContext.current.sender.receiver
247     const_scope.__send__(:__const_set__, name, value)
248   end
250   ##
251   # Called when 'def name' is used in userland
253   def __add_method__(name, obj)
254     s = MethodContext.current.sender
255     scope = s.method_scope || :public
257     if name == :initialize or scope == :module
258       visibility = :private
259     else
260       visibility = scope
261     end
263     # All userland added methods start out with a serial of 1.
264     obj.serial = 1
266     # Push the scoping down.
267     obj.staticscope = s.method.staticscope
269     Rubinius::VM.reset_method_cache(name)
271     obj.staticscope.module.method_table[name] = Tuple[visibility, obj]
273     if scope == :module
274       s.current_scope.module_function name
275     end
277     if s.current_scope.respond_to? :method_added
278       s.current_scope.method_added(name)
279     end
281     # Return value here is the return value of the 'def' expression
282     return obj
283   end
288 # Stores all the information about a running NativeMethod.
290 class NativeMethodContext
291   def location
292     "#{file}"
293   end
297 # Stores all information about a running Block.
299 # Block context has no own receiver,
300 # static lexical scope and is unnamed
301 # so it uses receiver, scope and name
302 # of home method context, that is,
303 # method context that started it's execution.
304 class BlockContext
306   def last_match
307     home.last_match
308   end
310   def last_match=(match)
311     home.last_match = match
312   end
314   def nth_ref(idx)
315     home.nth_ref(idx)
316   end
318   def back_ref(idx)
319     home.back_ref(idx)
320   end
322   def method_scope
323     @method_scope || env.home_block.method_scope
324   end
326   # Active context (instance of MethodContext) that started
327   # execution of this block context.
328   def home
329     env.home
330   end
332   # Name of home method.
333   def name
334     home.name
335   end
337   # Block context has no receiver thus uses
338   # receiver from it's home method context.
339   def receiver
340     home.receiver
341   end
343   # Block context has no own module thus uses
344   # module from it's home method context.
345   def method_module
346     home.method_module
347   end
349   # Static scope of home method context.
350   def current_scope
351     if ss = method.staticscope
352       return ss.module
353     else
354       home.current_scope
355     end
356   end
358   # instance_eval needs alternate const behavior
359   def __const_set__(name, value)
360     const_scope = env.constant_scope.module
361     const_scope.__send__(:__const_set__, name, value)
362   end
366 # Describes the environment a Block was created in.  BlockEnvironment is used
367 # to create a BlockContext.
369 class BlockEnvironment
370   ivar_as_index :__ivars__ => 0, :home => 1, :initial_ip => 2, :last_ip => 3,
371     :post_send => 4, :home_block => 5, :local_count => 6, :metadata_container => 7, :method => 8
372   def __ivars__   ; @__ivars__   ; end
373   def home        ; @home        ; end
374   def initial_ip  ; @initial_ip  ; end
375   def last_ip     ; @last_ip     ; end
376   def post_send   ; @post_send   ; end
377   def home_block  ; @home_block  ; end
378   def local_count ; @local_count ; end
379   def method      ; @method      ; end
381   def metadata_container
382     @metadata_container
383   end
385   def under_context(home, cmethod)
386     if home.kind_of? BlockContext
387       home_block = home
388       home = home.home
389     else
390       home_block = home
391     end
393     @home = home
394     @initial_ip = 0
395     @last_ip = 0x10000000 # 2**28
396     @post_send = 0
397     @home_block = home_block
398     @method = cmethod
399     @local_count = cmethod.local_count
400     return self
401   end
403   ##
404   # Holds a Tuple of additional metadata.
405   # First field of the tuple holds a boolean indicating if the context is from
406   # eval
407   def metadata_container=(tup)
408     @metadata_container = tup
409   end
411   def from_eval?
412     @metadata_container and @metadata_container[0]
413   end
415   def from_eval!
416     @metadata_container = Tuple.new(1) unless @metadata_container
417     @metadata_container[0] = true
418   end
420   ##
421   # The CompiledMethod object that we were called from
423   def method=(tup)
424     @method = tup
425   end
427   ##
428   #--
429   # These should be safe since I'm unsure how you'd have a BlockContext
430   # and have a nil CompiledMethod (something that can (and has) happened
431   # with MethodContexts)
433   def file
434     method.file
435   end
437   def line
438     method.line_from_ip(initial_ip)
439   end
441   def home=(home)
442     @home = home
443   end
445   def scope=(tup)
446     @scope = tup
447   end
449   def make_independent
450     @home = @home.dup
451     @home_block = @home_block.dup
452     @method = @method.dup
453   end
455   def redirect_to(obj)
456     env = dup
457     env.make_independent
458     env.home.receiver = obj
459     return env
460   end
462   def call_on_instance(obj, *args)
463     obj = redirect_to(obj)
464     obj.call(*args)
465   end
467   def disable_long_return!
468     @post_send = nil
469   end
471   def arity
472     method.required
473   end
475   # Static scope for constant lookup
476   def constant_scope=(scope)
477     @constant_scope = scope
478   end
480   def constant_scope
481     @constant_scope ||= @method.staticscope
482   end
486 # Contains stack frame objects
488 class Backtrace
489   include Enumerable
491   attr_reader :frames
493   attr_accessor :first_color
494   attr_accessor :kernel_color
495   attr_accessor :eval_color
497   def initialize
498     @frames = []
499     @top_context = nil
500     @first_color = "\033[0;31m"
501     @kernel_color = "\033[0;34m"
502     @eval_color = "\033[0;33m"
503   end
505   def [](index)
506     @frames[index]
507   end
509   def show(sep="\n", colorize = true)
510     first = true
511     color_config = Rubinius::RUBY_CONFIG["rbx.colorize_backtraces"]
512     if color_config == "no" or color_config == "NO"
513       colorize = false
514       color = ""
515       clear = ""
516     else
517       clear = "\033[0m"
518     end
520     fr2 = @frames.map do |ent|
521       recv = ent[0]
522       loc = ent[1]
523       color = color_from_loc(loc, first) if colorize
524       first = false # special handling for first line
525       times = @max - recv.size
526       times = 0 if times < 0
527       "#{color}    #{' ' * times}#{recv} at #{loc}#{clear}"
528     end
529     return fr2.join(sep)
530   end
532   def join(sep)
533     show
534   end
536   alias_method :to_s, :show
538   def color_from_loc(loc, first)
539     return @first_color if first
540     if loc =~ /kernel/
541       @kernel_color
542     elsif loc =~ /\(eval\)/
543       @eval_color
544     else
545       ""
546     end
547   end
549   attr_reader :top_context
551   MAX_WIDTH = 40
553   def fill_from(ctx)
554     @top_context = ctx
556     @max = 0
557     while ctx
558       unless ctx.method
559         ctx = ctx.sender
560         next
561       end
563       str = ctx.describe
565       if str.size > @max
566         @max = str.size
567       end
569       @frames << [str, ctx.location]
570       ctx = ctx.sender
571     end
572     @max = MAX_WIDTH if @max > MAX_WIDTH
573   end
575   def self.backtrace(ctx=nil)
576     obj = new()
577     unless ctx
578       ctx = MethodContext.current.sender
579     end
580     obj.fill_from ctx
581     return obj
582   end
584   def each
585     @frames.each { |f| yield f.last }
586     self
587   end
589   def to_mri
590     return @top_context.calling_hierarchy(0)
591   end