class SyntaxTree::YARV::InstructionSequence

This class is meant to mirror RubyVM::InstructionSequence. It contains a list of instructions along with the metadata pertaining to them. It also functions as a builder for the instruction sequence.

Constants

MAGIC

Attributes

argument_options[R]
argument_size[RW]

This is the list of information about the arguments to this instruction sequence.

catch_table[R]

The catch table for this instruction sequence.

file[R]

The source location of the instruction sequence.

inline_storages[R]

The hash of names of instance and class variables pointing to the index of their associated inline storage.

insns[R]

The list of instructions for this instruction sequence.

line[R]

The source location of the instruction sequence.

local_table[R]

The table of local variables.

name[R]

The name of the instruction sequence.

options[R]

These are various compilation options provided.

parent_iseq[R]

The parent instruction sequence, if there is one.

stack[R]

An object that will track the current size of the stack and the maximum size of the stack for this instruction sequence.

storage_index[R]

The index of the next inline storage that will be created.

type[R]

The type of the instruction sequence.

Public Class Methods

from(source, options = Compiler::Options.new, parent_iseq = nil) click to toggle source

This method will create a new instruction sequence from a serialized RubyVM::InstructionSequence object.

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 1015
def self.from(source, options = Compiler::Options.new, parent_iseq = nil)
  iseq =
    new(source[5], source[6], source[8], source[9], parent_iseq, options)

  # set up the labels object so that the labels are shared between the
  # location in the instruction sequence and the instructions that
  # reference them
  labels = Hash.new { |hash, name| hash[name] = Label.new(name) }

  # set up the correct argument size
  iseq.argument_size = source[4][:arg_size]

  # set up all of the locals
  source[10].each { |local| iseq.local_table.plain(local) }

  # set up the argument options
  iseq.argument_options.merge!(source[11])
  if iseq.argument_options[:opt]
    iseq.argument_options[:opt].map! { |opt| labels[opt] }
  end

  # track the child block iseqs so that our catch table can point to the
  # correctly created iseqs
  block_iseqs = []

  # set up all of the instructions
  source[13].each do |insn|
    # add line numbers
    if insn.is_a?(Integer)
      iseq.push(insn)
      next
    end

    # add events and labels
    if insn.is_a?(Symbol)
      if insn.start_with?("label_")
        iseq.push(labels[insn])
      else
        iseq.push(insn)
      end
      next
    end

    # add instructions, mapped to our own instruction classes
    type, *opnds = insn

    case type
    when :adjuststack
      iseq.adjuststack(opnds[0])
    when :anytostring
      iseq.anytostring
    when :branchif
      iseq.branchif(labels[opnds[0]])
    when :branchnil
      iseq.branchnil(labels[opnds[0]])
    when :branchunless
      iseq.branchunless(labels[opnds[0]])
    when :checkkeyword
      iseq.checkkeyword(iseq.local_table.size - opnds[0] + 2, opnds[1])
    when :checkmatch
      iseq.checkmatch(opnds[0])
    when :checktype
      iseq.checktype(opnds[0])
    when :concatarray
      iseq.concatarray
    when :concatstrings
      iseq.concatstrings(opnds[0])
    when :defineclass
      iseq.defineclass(opnds[0], from(opnds[1], options, iseq), opnds[2])
    when :defined
      iseq.defined(opnds[0], opnds[1], opnds[2])
    when :definedivar
      iseq.definedivar(opnds[0], opnds[1], opnds[2])
    when :definemethod
      iseq.definemethod(opnds[0], from(opnds[1], options, iseq))
    when :definesmethod
      iseq.definesmethod(opnds[0], from(opnds[1], options, iseq))
    when :dup
      iseq.dup
    when :duparray
      iseq.duparray(opnds[0])
    when :duphash
      iseq.duphash(opnds[0])
    when :dupn
      iseq.dupn(opnds[0])
    when :expandarray
      iseq.expandarray(opnds[0], opnds[1])
    when :getblockparam, :getblockparamproxy, :getlocal, :getlocal_WC_0,
         :getlocal_WC_1, :setblockparam, :setlocal, :setlocal_WC_0,
         :setlocal_WC_1
      current = iseq
      level = 0

      case type
      when :getlocal_WC_1, :setlocal_WC_1
        level = 1
      when :getblockparam, :getblockparamproxy, :getlocal, :setblockparam,
           :setlocal
        level = opnds[1]
      end

      level.times { current = current.parent_iseq }
      index = current.local_table.size - opnds[0] + 2

      case type
      when :getblockparam
        iseq.getblockparam(index, level)
      when :getblockparamproxy
        iseq.getblockparamproxy(index, level)
      when :getlocal, :getlocal_WC_0, :getlocal_WC_1
        iseq.getlocal(index, level)
      when :setblockparam
        iseq.setblockparam(index, level)
      when :setlocal, :setlocal_WC_0, :setlocal_WC_1
        iseq.setlocal(index, level)
      end
    when :getclassvariable
      iseq.push(GetClassVariable.new(opnds[0], opnds[1]))
    when :getconstant
      iseq.getconstant(opnds[0])
    when :getglobal
      iseq.getglobal(opnds[0])
    when :getinstancevariable
      iseq.push(GetInstanceVariable.new(opnds[0], opnds[1]))
    when :getspecial
      iseq.getspecial(opnds[0], opnds[1])
    when :intern
      iseq.intern
    when :invokeblock
      iseq.invokeblock(CallData.from(opnds[0]))
    when :invokesuper
      block_iseq = opnds[1] ? from(opnds[1], options, iseq) : nil
      iseq.invokesuper(CallData.from(opnds[0]), block_iseq)
    when :jump
      iseq.jump(labels[opnds[0]])
    when :leave
      iseq.leave
    when :newarray
      iseq.newarray(opnds[0])
    when :newarraykwsplat
      iseq.newarraykwsplat(opnds[0])
    when :newhash
      iseq.newhash(opnds[0])
    when :newrange
      iseq.newrange(opnds[0])
    when :nop
      iseq.nop
    when :objtostring
      iseq.objtostring(CallData.from(opnds[0]))
    when :once
      iseq.once(from(opnds[0], options, iseq), opnds[1])
    when :opt_and, :opt_aref, :opt_aset, :opt_div, :opt_empty_p, :opt_eq,
         :opt_ge, :opt_gt, :opt_le, :opt_length, :opt_lt, :opt_ltlt,
         :opt_minus, :opt_mod, :opt_mult, :opt_nil_p, :opt_not, :opt_or,
         :opt_plus, :opt_regexpmatch2, :opt_send_without_block, :opt_size,
         :opt_succ
      iseq.send(CallData.from(opnds[0]), nil)
    when :opt_aref_with
      iseq.opt_aref_with(opnds[0], CallData.from(opnds[1]))
    when :opt_aset_with
      iseq.opt_aset_with(opnds[0], CallData.from(opnds[1]))
    when :opt_case_dispatch
      hash =
        opnds[0]
          .each_slice(2)
          .to_h
          .transform_values { |value| labels[value] }
      iseq.opt_case_dispatch(hash, labels[opnds[1]])
    when :opt_getconstant_path
      iseq.opt_getconstant_path(opnds[0])
    when :opt_getinlinecache
      iseq.opt_getinlinecache(labels[opnds[0]], opnds[1])
    when :opt_newarray_max
      iseq.newarray(opnds[0])
      iseq.send(YARV.calldata(:max))
    when :opt_newarray_min
      iseq.newarray(opnds[0])
      iseq.send(YARV.calldata(:min))
    when :opt_newarray_send
      iseq.newarray(opnds[0])
      iseq.send(CallData.new(opnds[1]))
    when :opt_neq
      iseq.push(
        OptNEq.new(CallData.from(opnds[0]), CallData.from(opnds[1]))
      )
    when :opt_setinlinecache
      iseq.opt_setinlinecache(opnds[0])
    when :opt_str_freeze
      iseq.putstring(opnds[0])
      iseq.send(YARV.calldata(:freeze))
    when :opt_str_uminus
      iseq.putstring(opnds[0])
      iseq.send(YARV.calldata(:-@))
    when :pop
      iseq.pop
    when :putnil
      iseq.putnil
    when :putobject
      iseq.putobject(opnds[0])
    when :putobject_INT2FIX_0_
      iseq.putobject(0)
    when :putobject_INT2FIX_1_
      iseq.putobject(1)
    when :putself
      iseq.putself
    when :putstring
      iseq.putstring(opnds[0])
    when :putspecialobject
      iseq.putspecialobject(opnds[0])
    when :send
      block_iseq = opnds[1] ? from(opnds[1], options, iseq) : nil
      block_iseqs << block_iseq if block_iseq
      iseq.send(CallData.from(opnds[0]), block_iseq)
    when :setclassvariable
      iseq.push(SetClassVariable.new(opnds[0], opnds[1]))
    when :setconstant
      iseq.setconstant(opnds[0])
    when :setglobal
      iseq.setglobal(opnds[0])
    when :setinstancevariable
      iseq.push(SetInstanceVariable.new(opnds[0], opnds[1]))
    when :setn
      iseq.setn(opnds[0])
    when :setspecial
      iseq.setspecial(opnds[0])
    when :splatarray
      iseq.splatarray(opnds[0])
    when :swap
      iseq.swap
    when :throw
      iseq.throw(opnds[0])
    when :topn
      iseq.topn(opnds[0])
    when :toregexp
      iseq.toregexp(opnds[0], opnds[1])
    else
      raise "Unknown instruction type: #{type}"
    end
  end

  # set up the catch table
  source[12].each do |entry|
    case entry[0]
    when :break
      if entry[1]
        break_iseq =
          block_iseqs.find do |block_iseq|
            block_iseq.name == entry[1][5] &&
              block_iseq.file == entry[1][6] &&
              block_iseq.line == entry[1][8]
          end

        iseq.catch_break(
          break_iseq || from(entry[1], options, iseq),
          labels[entry[2]],
          labels[entry[3]],
          labels[entry[4]],
          entry[5]
        )
      else
        iseq.catch_break(
          nil,
          labels[entry[2]],
          labels[entry[3]],
          labels[entry[4]],
          entry[5]
        )
      end
    when :ensure
      iseq.catch_ensure(
        from(entry[1], options, iseq),
        labels[entry[2]],
        labels[entry[3]],
        labels[entry[4]],
        entry[5]
      )
    when :next
      iseq.catch_next(
        labels[entry[2]],
        labels[entry[3]],
        labels[entry[4]],
        entry[5]
      )
    when :rescue
      iseq.catch_rescue(
        from(entry[1], options, iseq),
        labels[entry[2]],
        labels[entry[3]],
        labels[entry[4]],
        entry[5]
      )
    when :redo
      iseq.catch_redo(
        labels[entry[2]],
        labels[entry[3]],
        labels[entry[4]],
        entry[5]
      )
    when :retry
      iseq.catch_retry(
        labels[entry[2]],
        labels[entry[3]],
        labels[entry[4]],
        entry[5]
      )
    else
      raise "unknown catch type: #{entry[0]}"
    end
  end

  iseq.compile! if iseq.type == :top
  iseq
end
iseq_load(iseq) click to toggle source

This provides a handle to the rb_iseq_load function, which allows you to pass a serialized iseq to Ruby and have it return a RubyVM::InstructionSequence object.

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 13
def self.iseq_load(iseq)
  require "fiddle"

  @iseq_load_function ||=
    Fiddle::Function.new(
      Fiddle::Handle::DEFAULT["rb_iseq_load"],
      [Fiddle::TYPE_VOIDP] * 3,
      Fiddle::TYPE_VOIDP
    )

  Fiddle.dlunwrap(@iseq_load_function.call(Fiddle.dlwrap(iseq), 0, nil))
rescue LoadError
  raise "Could not load the Fiddle library"
rescue NameError
  raise "Unable to find rb_iseq_load"
rescue Fiddle::DLError
  raise "Unable to perform a dynamic load"
end
new( name, file, line, type, parent_iseq = nil, options = Compiler::Options.new ) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 168
def initialize(
  name,
  file,
  line,
  type,
  parent_iseq = nil,
  options = Compiler::Options.new
)
  @name = name
  @file = file
  @line = line
  @type = type
  @parent_iseq = parent_iseq

  @argument_size = 0
  @argument_options = {}
  @catch_table = []

  @local_table = LocalTable.new
  @inline_storages = {}
  @insns = InstructionList.new
  @storage_index = 0
  @stack = Stack.new

  @options = options
end

Public Instance Methods

adjuststack(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 652
def adjuststack(number)
  push(AdjustStack.new(number))
end
anytostring() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 656
def anytostring
  push(AnyToString.new)
end
block_child_iseq(line) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 472
def block_child_iseq(line)
  current = self
  current = current.parent_iseq while current.type == :block
  child_iseq("block in #{current.name}", line, :block)
end
branchif(label) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 660
def branchif(label)
  push(BranchIf.new(label))
end
branchnil(label) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 664
def branchnil(label)
  push(BranchNil.new(label))
end
branchunless(label) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 668
def branchunless(label)
  push(BranchUnless.new(label))
end
catch_break(iseq, begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 565
def catch_break(iseq, begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchBreak.new(
    iseq,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
catch_ensure(iseq, begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 575
def catch_ensure(iseq, begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchEnsure.new(
    iseq,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
catch_next(begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 585
def catch_next(begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchNext.new(
    nil,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
catch_redo(begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 595
def catch_redo(begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchRedo.new(
    nil,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
catch_rescue(iseq, begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 605
def catch_rescue(iseq, begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchRescue.new(
    iseq,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
catch_retry(begin_label, end_label, exit_label, restore_sp) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 615
def catch_retry(begin_label, end_label, exit_label, restore_sp)
  catch_table << CatchRetry.new(
    nil,
    begin_label,
    end_label,
    exit_label,
    restore_sp
  )
end
checkkeyword(keyword_bits_index, keyword_index) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 672
def checkkeyword(keyword_bits_index, keyword_index)
  push(CheckKeyword.new(keyword_bits_index, keyword_index))
end
checkmatch(type) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 676
def checkmatch(type)
  push(CheckMatch.new(type))
end
checktype(type) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 680
def checktype(type)
  push(CheckType.new(type))
end
child_iseq(name, line, type) click to toggle source

Child instruction sequence methods

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 468
def child_iseq(name, line, type)
  InstructionSequence.new(name, file, line, type, self, options)
end
class_child_iseq(name, line) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 478
def class_child_iseq(name, line)
  child_iseq("<class:#{name}>", line, :class)
end
compile!() click to toggle source

This method converts our linked list of instructions into a final array and performs any other compilation steps necessary.

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 305
def compile!
  specialize_instructions! if options.specialized_instruction?

  catch_table.each do |catch_entry|
    if !catch_entry.is_a?(CatchBreak) && catch_entry.iseq
      catch_entry.iseq.compile!
    end
  end

  length = 0
  insns.each do |insn|
    case insn
    when Integer, Symbol
      # skip
    when Label
      insn.patch!(:"label_#{length}")
    when DefineClass
      insn.class_iseq.compile!
      length += insn.length
    when DefineMethod, DefineSMethod
      insn.method_iseq.compile!
      length += insn.length
    when InvokeSuper, Send
      insn.block_iseq.compile! if insn.block_iseq
      length += insn.length
    when Once
      insn.iseq.compile!
      length += insn.length
    else
      length += insn.length
    end
  end

  @insns = insns.to_a
end
concatarray() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 684
def concatarray
  push(ConcatArray.new)
end
concatstrings(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 688
def concatstrings(number)
  push(ConcatStrings.new(number))
end
defineclass(name, class_iseq, flags) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 692
def defineclass(name, class_iseq, flags)
  push(DefineClass.new(name, class_iseq, flags))
end
defined(type, name, message) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 696
def defined(type, name, message)
  push(Defined.new(type, name, message))
end
definedivar(name, cache, message) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 700
def definedivar(name, cache, message)
  if RUBY_VERSION < "3.3"
    push(PutNil.new)
    push(Defined.new(Defined::TYPE_IVAR, name, message))
  else
    push(DefinedIVar.new(name, cache, message))
  end
end
definemethod(name, method_iseq) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 709
def definemethod(name, method_iseq)
  push(DefineMethod.new(name, method_iseq))
end
definesmethod(name, method_iseq) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 713
def definesmethod(name, method_iseq)
  push(DefineSMethod.new(name, method_iseq))
end
disasm() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 292
def disasm
  fmt = Disassembler.new
  fmt.enqueue(self)
  fmt.format!
  fmt.string
end
dup() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 717
def dup
  push(Dup.new)
end
duparray(object) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 721
def duparray(object)
  push(DupArray.new(object))
end
duphash(object) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 725
def duphash(object)
  push(DupHash.new(object))
end
dupn(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 729
def dupn(number)
  push(DupN.new(number))
end
eval() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 232
def eval
  InstructionSequence.iseq_load(to_a).eval
end
event(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 648
def event(name)
  push(name)
end
expandarray(length, flags) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 733
def expandarray(length, flags)
  push(ExpandArray.new(length, flags))
end
getblockparam(index, level) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 737
def getblockparam(index, level)
  push(GetBlockParam.new(index, level))
end
getblockparamproxy(index, level) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 741
def getblockparamproxy(index, level)
  push(GetBlockParamProxy.new(index, level))
end
getclassvariable(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 745
def getclassvariable(name)
  if RUBY_VERSION < "3.0"
    push(Legacy::GetClassVariable.new(name))
  else
    push(GetClassVariable.new(name, inline_storage_for(name)))
  end
end
getconstant(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 753
def getconstant(name)
  push(GetConstant.new(name))
end
getglobal(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 757
def getglobal(name)
  push(GetGlobal.new(name))
end
getinstancevariable(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 761
def getinstancevariable(name)
  if RUBY_VERSION < "3.2"
    push(GetInstanceVariable.new(name, inline_storage_for(name)))
  else
    push(GetInstanceVariable.new(name, inline_storage))
  end
end
getlocal(index, level) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 769
def getlocal(index, level)
  if options.operands_unification?
    # Specialize the getlocal instruction based on the level of the
    # local variable. If it's 0 or 1, then there's a specialized
    # instruction that will look at the current scope or the parent
    # scope, respectively, and requires fewer operands.
    case level
    when 0
      push(GetLocalWC0.new(index))
    when 1
      push(GetLocalWC1.new(index))
    else
      push(GetLocal.new(index, level))
    end
  else
    push(GetLocal.new(index, level))
  end
end
getspecial(key, type) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 788
def getspecial(key, type)
  push(GetSpecial.new(key, type))
end
inline_storage() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 207
def inline_storage
  storage = storage_index
  @storage_index += 1
  storage
end
inline_storage_for(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 213
def inline_storage_for(name)
  inline_storages[name] = inline_storage unless inline_storages.key?(name)

  inline_storages[name]
end
inspect() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 299
def inspect
  "#<ISeq:#{name}@<compiled>:1 (#{line},0)-(#{line},0)>"
end
intern() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 792
def intern
  push(Intern.new)
end
invokeblock(calldata) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 796
def invokeblock(calldata)
  push(InvokeBlock.new(calldata))
end
invokesuper(calldata, block_iseq) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 800
def invokesuper(calldata, block_iseq)
  push(InvokeSuper.new(calldata, block_iseq))
end
jump(label) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 804
def jump(label)
  push(Jump.new(label))
end
label() click to toggle source

Instruction push methods

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 629
def label
  Label.new
end
leave() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 808
def leave
  push(Leave.new)
end
length() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 219
def length
  insns
    .each
    .inject(0) do |sum, insn|
      case insn
      when Integer, Label, Symbol
        sum
      else
        sum + insn.length
      end
    end
end
local_variable(name, level = 0) click to toggle source

Query methods

# File lib/syntax_tree/yarv/instruction_sequence.rb, line 199
def local_variable(name, level = 0)
  if (lookup = local_table.find(name, level))
    lookup
  elsif parent_iseq
    parent_iseq.local_variable(name, level + 1)
  end
end
method_child_iseq(name, line) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 482
def method_child_iseq(name, line)
  child_iseq(name, line, :method)
end
module_child_iseq(name, line) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 486
def module_child_iseq(name, line)
  child_iseq("<module:#{name}>", line, :class)
end
newarray(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 812
def newarray(number)
  push(NewArray.new(number))
end
newarraykwsplat(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 816
def newarraykwsplat(number)
  push(NewArrayKwSplat.new(number))
end
newhash(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 820
def newhash(number)
  push(NewHash.new(number))
end
newrange(exclude_end) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 824
def newrange(exclude_end)
  push(NewRange.new(exclude_end))
end
nop() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 828
def nop
  push(Nop.new)
end
objtostring(calldata) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 832
def objtostring(calldata)
  push(ObjToString.new(calldata))
end
once(iseq, cache) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 836
def once(iseq, cache)
  push(Once.new(iseq, cache))
end
opt_aref_with(object, calldata) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 840
def opt_aref_with(object, calldata)
  push(OptArefWith.new(object, calldata))
end
opt_aset_with(object, calldata) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 844
def opt_aset_with(object, calldata)
  push(OptAsetWith.new(object, calldata))
end
opt_case_dispatch(case_dispatch_hash, else_label) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 848
def opt_case_dispatch(case_dispatch_hash, else_label)
  push(OptCaseDispatch.new(case_dispatch_hash, else_label))
end
opt_getconstant_path(names) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 852
def opt_getconstant_path(names)
  if RUBY_VERSION < "3.2" || !options.inline_const_cache?
    cache = nil
    cache_filled_label = nil

    if options.inline_const_cache?
      cache = inline_storage
      cache_filled_label = label
      opt_getinlinecache(cache_filled_label, cache)

      if names[0] == :""
        names.shift
        pop
        putobject(Object)
      end
    elsif names[0] == :""
      names.shift
      putobject(Object)
    else
      putnil
    end

    names.each_with_index do |name, index|
      putobject(index == 0)
      getconstant(name)
    end

    if options.inline_const_cache?
      opt_setinlinecache(cache)
      push(cache_filled_label)
    end
  else
    push(OptGetConstantPath.new(names))
  end
end
opt_getinlinecache(label, cache) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 888
def opt_getinlinecache(label, cache)
  push(Legacy::OptGetInlineCache.new(label, cache))
end
opt_setinlinecache(cache) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 892
def opt_setinlinecache(cache)
  push(Legacy::OptSetInlineCache.new(cache))
end
pop() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 896
def pop
  push(Pop.new)
end
push(value) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 633
def push(value)
  node = insns.push(value)

  case value
  when Array, Integer, Symbol
    value
  when Label
    value.node = node
    value
  else
    stack.change_by(-value.pops + value.pushes)
    value
  end
end
putnil() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 900
def putnil
  push(PutNil.new)
end
putobject(object) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 904
def putobject(object)
  if options.operands_unification?
    # Specialize the putobject instruction based on the value of the
    # object. If it's 0 or 1, then there's a specialized instruction
    # that will push the object onto the stack and requires fewer
    # operands.
    if object.eql?(0)
      push(PutObjectInt2Fix0.new)
    elsif object.eql?(1)
      push(PutObjectInt2Fix1.new)
    else
      push(PutObject.new(object))
    end
  else
    push(PutObject.new(object))
  end
end
putself() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 922
def putself
  push(PutSelf.new)
end
putspecialobject(object) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 926
def putspecialobject(object)
  push(PutSpecialObject.new(object))
end
putstring(object) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 930
def putstring(object)
  push(PutString.new(object))
end
send(calldata, block_iseq = nil) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 934
def send(calldata, block_iseq = nil)
  push(Send.new(calldata, block_iseq))
end
setblockparam(index, level) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 938
def setblockparam(index, level)
  push(SetBlockParam.new(index, level))
end
setclassvariable(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 942
def setclassvariable(name)
  if RUBY_VERSION < "3.0"
    push(Legacy::SetClassVariable.new(name))
  else
    push(SetClassVariable.new(name, inline_storage_for(name)))
  end
end
setconstant(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 950
def setconstant(name)
  push(SetConstant.new(name))
end
setglobal(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 954
def setglobal(name)
  push(SetGlobal.new(name))
end
setinstancevariable(name) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 958
def setinstancevariable(name)
  if RUBY_VERSION < "3.2"
    push(SetInstanceVariable.new(name, inline_storage_for(name)))
  else
    push(SetInstanceVariable.new(name, inline_storage))
  end
end
setlocal(index, level) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 966
def setlocal(index, level)
  if options.operands_unification?
    # Specialize the setlocal instruction based on the level of the
    # local variable. If it's 0 or 1, then there's a specialized
    # instruction that will write to the current scope or the parent
    # scope, respectively, and requires fewer operands.
    case level
    when 0
      push(SetLocalWC0.new(index))
    when 1
      push(SetLocalWC1.new(index))
    else
      push(SetLocal.new(index, level))
    end
  else
    push(SetLocal.new(index, level))
  end
end
setn(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 985
def setn(number)
  push(SetN.new(number))
end
setspecial(key) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 989
def setspecial(key)
  push(SetSpecial.new(key))
end
singleton_class_child_iseq(line) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 490
def singleton_class_child_iseq(line)
  child_iseq("singleton class", line, :class)
end
specialize_instructions!() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 341
def specialize_instructions!
  insns.each_node do |node, value|
    case value
    when NewArray
      next unless node.next_node

      next_node = node.next_node
      next unless next_node.value.is_a?(Send)
      next if next_node.value.block_iseq

      calldata = next_node.value.calldata
      next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
      next unless calldata.argc == 0

      case calldata.method
      when :min
        node.value =
          if RUBY_VERSION < "3.3"
            Legacy::OptNewArrayMin.new(value.number)
          else
            OptNewArraySend.new(value.number, :min)
          end

        node.next_node = next_node.next_node
      when :max
        node.value =
          if RUBY_VERSION < "3.3"
            Legacy::OptNewArrayMax.new(value.number)
          else
            OptNewArraySend.new(value.number, :max)
          end

        node.next_node = next_node.next_node
      when :hash
        next if RUBY_VERSION < "3.3"
        node.value = OptNewArraySend.new(value.number, :hash)
        node.next_node = next_node.next_node
      end
    when PutObject, PutString
      next unless node.next_node
      next if value.is_a?(PutObject) && !value.object.is_a?(String)

      next_node = node.next_node
      next unless next_node.value.is_a?(Send)
      next if next_node.value.block_iseq

      calldata = next_node.value.calldata
      next unless calldata.flags == CallData::CALL_ARGS_SIMPLE
      next unless calldata.argc == 0

      case calldata.method
      when :freeze
        node.value = OptStrFreeze.new(value.object, calldata)
        node.next_node = next_node.next_node
      when :-@
        node.value = OptStrUMinus.new(value.object, calldata)
        node.next_node = next_node.next_node
      end
    when Send
      calldata = value.calldata

      if !value.block_iseq &&
           !calldata.flag?(CallData::CALL_ARGS_BLOCKARG)
        # Specialize the send instruction. If it doesn't have a block
        # attached, then we will replace it with an opt_send_without_block
        # and do further specializations based on the called method and
        # the number of arguments.
        node.value =
          case [calldata.method, calldata.argc]
          when [:length, 0]
            OptLength.new(calldata)
          when [:size, 0]
            OptSize.new(calldata)
          when [:empty?, 0]
            OptEmptyP.new(calldata)
          when [:nil?, 0]
            OptNilP.new(calldata)
          when [:succ, 0]
            OptSucc.new(calldata)
          when [:!, 0]
            OptNot.new(calldata)
          when [:+, 1]
            OptPlus.new(calldata)
          when [:-, 1]
            OptMinus.new(calldata)
          when [:*, 1]
            OptMult.new(calldata)
          when [:/, 1]
            OptDiv.new(calldata)
          when [:%, 1]
            OptMod.new(calldata)
          when [:==, 1]
            OptEq.new(calldata)
          when [:!=, 1]
            OptNEq.new(YARV.calldata(:==, 1), calldata)
          when [:=~, 1]
            OptRegExpMatch2.new(calldata)
          when [:<, 1]
            OptLT.new(calldata)
          when [:<=, 1]
            OptLE.new(calldata)
          when [:>, 1]
            OptGT.new(calldata)
          when [:>=, 1]
            OptGE.new(calldata)
          when [:<<, 1]
            OptLTLT.new(calldata)
          when [:[], 1]
            OptAref.new(calldata)
          when [:&, 1]
            OptAnd.new(calldata)
          when [:|, 1]
            OptOr.new(calldata)
          when [:[]=, 2]
            OptAset.new(calldata)
          else
            OptSendWithoutBlock.new(calldata)
          end
      end
    end
  end
end
splatarray(flag) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 993
def splatarray(flag)
  push(SplatArray.new(flag))
end
swap() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 997
def swap
  push(Swap.new)
end
throw(type) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 1001
def throw(type)
  push(Throw.new(type))
end
to_a() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 236
def to_a
  versions = RUBY_VERSION.split(".").map(&:to_i)

  # Dump all of the instructions into a flat list.
  dumped =
    insns.map do |insn|
      case insn
      when Integer, Symbol
        insn
      when Label
        insn.name
      else
        insn.to_a(self)
      end
    end

  dumped_options = argument_options.dup
  dumped_options[:opt].map!(&:name) if dumped_options[:opt]

  # Next, return the instruction sequence as an array.
  [
    MAGIC,
    versions[0],
    versions[1],
    1,
    {
      arg_size: argument_size,
      local_size: local_table.size,
      stack_max: stack.maximum_size,
      node_id: -1,
      node_ids: [-1] * insns.length
    },
    name,
    file,
    "<compiled>",
    line,
    type,
    local_table.names,
    dumped_options,
    catch_table.map(&:to_a),
    dumped
  ]
end
to_cfg() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 280
def to_cfg
  ControlFlowGraph.compile(self)
end
to_dfg() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 284
def to_dfg
  to_cfg.to_dfg
end
to_son() click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 288
def to_son
  to_dfg.to_son
end
topn(number) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 1005
def topn(number)
  push(TopN.new(number))
end
toregexp(options, length) click to toggle source
# File lib/syntax_tree/yarv/instruction_sequence.rb, line 1009
def toregexp(options, length)
  push(ToRegExp.new(options, length))
end