class Rubinius::Coverage::CFG

Attributes

basic_blocks[RW]
call_sites[RW]
code[RW]
enter[RW]
exit[RW]

Public Class Methods

new(code, call_sites) click to toggle source
# File lib/rubinius/coverage.rb, line 8
def initialize(code, call_sites)
  @code = code
  @call_sites = call_sites
  @basic_blocks = Hash.new { |h, k| h[k] = BasicBlock.new k }
end

Public Instance Methods

graph() click to toggle source
# File lib/rubinius/coverage.rb, line 14
def graph
  iseq = @code.iseq
  total = iseq.size
  i = 0

  @enter = BasicBlock.new 0
  @enter.left = bb = @basic_blocks[0]

  @exit = @basic_blocks[total]

  while i < total
    opcode = InstructionSet[iseq[i]]

    if @basic_blocks.has_key? i
      bb.exit_ip = i-1
      bb = bb.left = @basic_blocks[i]
    else
      bb.exit_ip = i+opcode.width-1

      case opcode.control_flow
      when :branch
        bb.left = @basic_blocks[iseq[i+1]]
        bb.left.exit_ip = total

        next_bb = @basic_blocks[i+opcode.width]
        bb.right = next_bb if opcode.name != :goto

        bb = next_bb
      when :raise, :return
        bb.left = @exit
        bb = @basic_blocks[i+opcode.width]
      end
    end

    i += opcode.width
  end

  @basic_blocks.values.each do |bb|
    # Split if a jump target intersects our range
    i = bb.enter_ip
    while i < bb.exit_ip
      opcode = InstructionSet[iseq[i]]
      i += opcode.width

      if @basic_blocks.has_key? i
        bb.exit_ip = i-1

        if opcode.control_flow != :branch
          bb.left = @basic_blocks[i]
          bb.right = nil
        end

        break
      end
    end

    bb.coverage @code, @call_sites
  end

  stack = [@enter]
  cycles = {}

  until stack.empty?
    bb = stack.shift

    if left = bb.left
      left.count = bb.count if bb.count > left.count
      unless cycles.has_key? left
        stack << left
        cycles[left] = true
      end
    end

    if right = bb.right
      right.count = bb.count if bb.count > right.count
      unless cycles.has_key? right
        stack << right
        cycles[right] = true
      end
    end
  end

  # Flow coverage counts backwards
  @basic_blocks.values.each do |bb|
    if bb.count == 0
      if (bb.left and bb.left.count > 0) or
         (bb.right and bb.right.count > 0)
        bb.count = 1
      end
    end
  end
end