class SystemNavigation::InstructionStream::Decoder

Public Class Methods

new(scanner) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 4
def initialize(scanner)
  @scanner = scanner
end

Public Instance Methods

cvar_read_scan(cvar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 57
def cvar_read_scan(cvar)
  self.select_instructions(literal: cvar) do |_prev_prev, prev, instruction|
    next instruction if instruction.reads_cvar?(cvar)
  end
end
cvar_write_scan(cvar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 63
def cvar_write_scan(cvar)
  self.select_instructions(literal: cvar) do |prev_prev, prev, instruction|
    next instruction if instruction.writes_cvar?(cvar)
  end
end
gvar_read_scan(gvar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 45
def gvar_read_scan(gvar)
  self.select_instructions(literal: gvar) do |_prev_prev, prev, instruction|
    next instruction if instruction.reads_gvar?(gvar)
  end
end
gvar_write_scan(gvar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 51
def gvar_write_scan(gvar)
  self.select_instructions(literal: gvar) do |prev_prev, prev, instruction|
    next instruction if instruction.writes_gvar?(gvar)
  end
end
ivar_read_scan(ivar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 25
def ivar_read_scan(ivar)
  self.select_instructions(literal: ivar) do |_prev_prev, prev, instruction|
    next instruction if instruction.reads_ivar?(ivar)

    if instruction.dynamically_reads_ivar? && prev.putobjects?(ivar)
      next instruction
    end
  end
end
ivar_write_scan(ivar) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 35
def ivar_write_scan(ivar)
  self.select_instructions(literal: ivar) do |prev_prev, prev, instruction|
    next instruction if instruction.writes_ivar?(ivar)

    if instruction.dynamically_writes_ivar? && prev_prev.putobjects?(ivar)
      next instruction
    end
  end
end
literal_scan(literal) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 69
def literal_scan(literal)
  name = @scanner.method.original_name

  self.select_instructions(method_name: name, literal: literal) do |_prev_prev, prev, instruction|
    if instruction.putobjects?(literal) ||
       instruction.putnils?(literal) ||
       instruction.duparrays?(literal) ||
       instruction.putstrings?(literal)
      next instruction
    end
  end
end
msg_send_scan(message) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 82
def msg_send_scan(message)
  self.select_instructions(literal: message) do |_prev_prev, prev, instruction|
    next instruction if instruction.sends_msg?(message)
  end
end
scan_for_sent_messages() click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 88
def scan_for_sent_messages
  @scanner.iseqs(nil).map do |instruction|
    instruction.find_message
  end.compact
end
select_instructions(literal:, method_name: nil, &block) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 8
def select_instructions(literal:, method_name: nil, &block)
  instructions = @scanner.iseqs(method_name || literal)

  instructions.select.with_index do |instruction, i|
    prev = instructions[i - 1]
    prev_prev = instructions[i - 2]

    returned = block.call(prev_prev, prev, instruction)
    next instruction if returned

    next if !(instruction.evals? && prev.putstrings?(literal))

    self.class.new(iseq_from_eval(prev, @scanner.method)).
      __send__(block.binding.eval('__method__'), literal).any?
  end
end

Private Instance Methods

iseq_from_eval(instruction, method = nil) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 96
def iseq_from_eval(instruction, method = nil)
  # Avoid segfault if evaling_str is nil.
  # See: https://bugs.ruby-lang.org/issues/11159
  uncompiled = unwind_eval(instruction.evaling_str || nil.to_s)
  uncompiled.gsub!(/\\n/, ?\n)

  iseq = RubyVM::InstructionSequence.compile(uncompiled).disasm
  InstructionStream.new(method: method, iseq: iseq)
end
sanitize_newlines(eval_string) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 110
def sanitize_newlines(eval_string)
  eval_string.gsub(/\\n/, ?\n)
end
unwind_eval(eval_string) click to toggle source
# File lib/system_navigation/instruction_stream/decoder.rb, line 106
def unwind_eval(eval_string)
  eval_string.sub(/\A(eval\(\\?["'])*/, '').sub(/(\\?["']\))*\z/, '')
end