1 # depends on: class.rb array.rb
4 # A wrapper for a calling a function in a shared library that has been
5 # attached via rb_define_method().
7 # The primitive slot for a NativeMethod points to the nmethod_call primitive
8 # which dispatches to the underlying C function.
29 # A linked list that details the static, lexical scope the method was created
32 # You can access it this way:
34 # MethodContext.current.method.staticscope
36 # Here is a simple example:
40 # attr_reader :initialize_scope
42 # def initialize(weight)
43 # @initialize_scope = MethodContext.current.method.staticscope
49 # Static scope members are shown below:
51 # irb(main):> pineapple.initialize_scope.script
53 # irb(main):> pineapple.initialize_scope.parent
54 # => #<StaticScope:0x1c9>
55 # irb(main):> pineapple.initialize_scope.module
56 # => Fruits::Pineapple
57 # irb(main):> pineapple.initialize_scope.parent.module
59 # irb(main):> pineapple.initialize_scope.parent.parent.module
61 # irb(main):> pineapple.initialize_scope.parent.parent.parent.module
65 ivar_as_index :__ivars__ => 0, :module => 1, :parent => 2
67 def initialize(mod, par=nil)
74 # Source code of this scope.
79 # Module or class this lexical scope enclosed into.
84 # Static scope object this scope enclosed into.
90 "#<#{self.class.name}:0x#{self.object_id.to_s(16)} parent=#{@parent} module=#{@module}>"
99 # CompiledMethod represents source code method compiled into VM bytecodes.
100 # Its instruction set is then executed by Shotgun's abstraction of CPU.
101 # CompiledMethods are not just sets of instructions though. They carry a lot
102 # of information about method: its lexical scope (static scope), name, file
103 # it has been defined in and so forth.
106 # TODO: Delete/reuse cache (field 14) field from C structure
107 ivar_as_index :__ivars__ => 0,
121 :metadata_container => 15,
125 def __ivars__ ; @__ivars__ ; end
128 # nil if the method does not have a primitive, otherwise the name of the
131 def primitive ; @primitive ; end
133 # number of arguments required by method
134 def required ; @required ; end
136 # Version of method: an incrementing integer.
137 # When you redefine method via re-opening
138 # a class this number is increased.
140 # Kernel methods have serial of 0
142 def serial ; @serial ; end
144 # instructions set that VM executes
145 # instance of InstructionSequence
146 def bytecodes ; @bytecodes ; end
148 # method name as Symbol
149 def name ; @name ; end
151 # file in which this method has been defined
152 def file ; @file ; end
154 # number of local variables method uses
155 # note that locals are stored in slots
156 # in the context this CompiledMethod
158 def local_count; @local_count; end
160 # literals tuple stores literals from
161 # source code like string literals and
162 # some extra stuff like SendSites,
163 # RegExp objects created from
164 # regexp literals and CompiledMethods
166 def literals ; @literals ; end
168 # Tuple holding the arguments defined on a method.
169 # Consists of 3 values:
170 # - a tuple of symbols naming required args (or nil if none)
171 # - a tuple of symbols naming optional args (or nil if none)
172 # - the symbol for any splat arg (or nil if none)
173 def args ; @args ; end
175 # Tuple holding the symbols for all local variable names used in the method.
176 def local_names; @local_names; end
178 # Tuple of tuples. Inner tuples contain
179 # low IP, high IP and IP of exception
182 # When exception is raised this tuple is
183 # looked up by VM using context IP:
185 # Tuple which low/high IP fit in context
186 # IP is picked up and handling continues.
188 # TODO: double check this statement.
189 def exceptions ; @exceptions ; end
191 # Tuple of Tuples. Each inner Tuple
192 # stores the following information:
194 # low IP, high IP and line number as integer.
195 def lines ; @lines ; end
197 # Holds the path of a script CompiledMethod created using eval.
198 # Required for the proper functioning of __FILE__ under eval.
199 def path ; @path ; end
201 # Separate object for storing metadata; this way
202 # the metadata can change without changes to the
204 def metadata_container ; @metadata_container ; end
206 # ByteArray of pointers to optcodes.
207 # This is only populated when CompiledMethod
208 # is loaded into VM and platform specific.
210 # You can think of it as of internal
211 # bytecode representation optimized
212 # for platform Rubinius runs on.
213 def compiled ; @compiled ; end
215 # lexical scope of method in source
216 # instance of StaticScope
217 def staticscope; @staticscope; end
220 # This is runtime hints, added to the method by the VM to indicate how it's
226 "#<#{self.class.name}:0x#{self.object_id.to_s(16)} name=#{@name} file=#{@file}>"
229 def from_string(bc, lcls, req)
233 @literals = Tuple.new(0)
243 def self.from_bytecodes bytecodes, arg_count, local_count, literals, exceptions=nil, lines=nil
244 c = CompiledMethod.new
245 c.bytecodes = InstructionSequence::Encoder.new.encode_stream bytecodes
247 c.local_count = local_count
248 c.required = arg_count
249 c.literals = literals
250 c.lines = lines || Tuple[Tuple[0, bytecodes.size, 0]]
251 c.exceptions = exceptions || []
255 def inherit_scope(other)
256 if ss = other.staticscope
259 @staticscope = StaticScope.new(Object)
263 def staticscope=(val)
264 raise TypeError, "not a static scope: #{val.inspect}" unless val.kind_of? StaticScope
272 def local_count=(val)
312 def metadata_container=(tup)
313 @metadata_container = tup
320 def local_names=(names)
323 unless names.kind_of? Tuple
324 raise ArgumentError, "only accepts a Tuple"
328 unless n.kind_of? Symbol
329 raise ArgumentError, "must be a tuple of symbols: #{n.inspect}"
336 def activate(recv, mod, args, locals=nil, &prc)
344 out = Rubinius.asm(args, block, locals, sz, mod, recv) do |a,b,l,s,m,r|
359 # Accessor for a hash of filenames (as per $" / $LOADED_FEATURES) to the
360 # script CompiledMethod.
365 # Helper function for searching for a CM given a file name; applies similar
366 # search and path expansion rules as load/require, so that the full path to
367 # the file need not be specified.
368 def self.script_for_file(filename)
369 if cm = self.scripts[filename]
373 if filename =~ %r{\A(?:(\.\.?)|(~))?/}
376 return scripts["#{ENV['HOME']}/#{filename}"]
378 return scripts["#{File.expand_path filename}"]
382 scripts = self.scripts
383 $LOAD_PATH.each do |dir|
384 if cm = scripts["#{dir}/#{filename}"]
396 def as_script(script=nil)
397 script ||= CompiledMethod::Script.new
398 yield script if block_given?
400 Rubinius::VM.save_encloser_path
403 ss = StaticScope.new(Object)
408 Rubinius::VM.restore_encloser_path
416 if i >= start and i <= nd
423 # Returns the address (IP) of the first instruction in this CompiledMethod
424 # that is on the specified line, or the address of the first instruction on
425 # the next code line after the specified line if there are no instructions
426 # on the requested line.
427 # This method only looks at instructions within the current CompiledMethod;
428 # see #locate_line for an alternate method that also searches inside the child
431 def first_ip_on_line(line)
441 def bytecodes=(other)
447 return ent[2] if ent[2] > 0
454 @name =~ /__(?:(?:\w|_)+)?block__/
457 # Convenience method to return an array of the child CompiledMethods from
458 # this CompiledMethod's literals.
461 literals.select {|lit| lit.kind_of? CompiledMethod}
464 # Convenience method to return an array of the SendSites from
465 # this CompiledMethod's literals.
468 literals.select {|lit| lit.kind_of? SendSite}
471 # Locates the CompiledMethod and instruction address (IP) of the first
472 # instruction on the specified line. This method recursively examines child
473 # compiled methods until an exact match for the searched line is found.
474 # It returns both the matching CompiledMethod and the IP of the first
475 # instruction on the requested line, or nil if no match for the specified line
478 def locate_line(line, cm=self)
480 if (l = t.at(2)) == line
481 # Found target line - return first IP
487 # Didn't find line in this CM, so check if a contained
488 # CM encompasses the line searched for
489 cm.child_methods.each do |child|
490 if res = locate_line(line, child)
495 # No child method is a match - fail
500 # Decodes the instruction sequence that is represented by this compileed
501 # method. Delegates to InstructionSequence to do the instruction decoding,
502 # but then converts opcode literal arguments to their actual values by looking
503 # them up in the literals tuple.
504 # Takes an optional bytecodes argument representing the bytecode that is to
505 # be decoded using this CompiledMethod's locals and literals. This is provided
506 # for use by the debugger, where the bytecode sequence to be decoded may not
507 # exactly match the bytecode currently held by the CompiledMethod, typically
508 # as a result of substituting yield_debugger instructions into the bytecode.
509 def decode(bytecodes = @bytecodes)
510 stream = bytecodes.decode(false)
513 stream.map! do |inst|
514 instruct = Instruction.new(inst, self, ip, args_reg)
516 if instruct.opcode == :set_args
518 elsif instruct.opcode == :cast_array_for_args
519 args_reg = instruct.args.first
524 # Add a convenience method to the array containing the decoded instructions
525 # to convert an IP address to the index of the corresponding instruction
526 def stream.ip_to_index(ip)
527 if ip < 0 or ip > last.ip
528 raise ArgumentError, "IP address is outside valid range of 0 to #{last.ip} (got #{ip})"
530 each_with_index do |inst, i|
531 return i if ip <= inst.ip
538 # Calculates the minimum stack size required for this method.
540 # Returns two values:
541 # * The minimum size stack required
542 # * A flag indicating whether this is an exact size, or a minimum
547 dc.inject(0) do |sz,op|
548 i,flg = op.stack_produced
551 i,flg = op.stack_consumed
554 high_mark = sz if sz > high_mark
557 return high_mark, exact
560 # Represents virtual machine's CPU instruction.
561 # Instructions are organized into instruction
562 # sequences known as iSeq, forming body
563 # of CompiledMethods.
565 # To generate VM optcodes documentation
566 # use rake doc:vm task.
568 def initialize(inst, cm, ip, args_reg)
571 @args.each_index do |i|
574 @args[i] = cm.literals[@args[i]]
576 # TODO: Blocks should be able to retrieve local names as well,
577 # but need access to method corresponding to home context
578 @args[i] = cm.local_names[args[i]] if cm.local_names and cm.name != :__block__
580 # TODO: Blocks should be able to retrieve enclosing block local names as well,
581 # but need access to static scope
582 @args[i] = cm.local_names[args[i]] if cm.local_names and args[0] == 0
586 @line = cm.line_from_ip(ip)
588 @stack_consumed = calculate_stack_usage(@op.stack_consumed, args_reg)
589 @stack_produced = calculate_stack_usage(@op.stack_produced)
592 # Instruction pointer
597 # Returns the OpCode object
599 # Associated OptCode instance.
605 # Returns the symbol representing the opcode for this instruction.
612 # Returns an array of 0 to 2 arguments, depending on the opcode.
623 # Returns the stack operands consumed by this instruction, as well as a flag
624 # indicating whether this is an exact value (true) or a minimum (false).
631 # Returns the stack operands produced by this instruction, as well as a flag
632 # indicating whether this is an exact value (true) or a minimum (false).
639 # Calculate the stack usage (pushes or pops) of this instruction.
641 def calculate_stack_usage(code, args_reg=0)
649 # Stack usage depends on opcode args
651 mult, code = code.divmod(100)
652 arg, code = code.divmod(10)
653 if arg >= 1 and arg <= 2
654 # Opcode consumes/produces a multiple of the value in the specified
656 usage += mult * args[arg-1]
658 # Opcode consumes number of args specified in args register
659 usage += mult * args_reg
669 str = "%04d: %-27s" % [@ip, opcode]
670 str << @args.map{|a| a.inspect}.join(', ')