module YARP::Debug

This module is used for testing and debugging and is not meant to be used by consumers of this library.

Public Class Methods

cruby_locals(source) click to toggle source

For the given source, compiles with CRuby and returns a list of all of the sets of local variables that were encountered.

# File yarp.rb, line 427
def self.cruby_locals(source)
  verbose = $VERBOSE
  $VERBOSE = nil

  begin
    locals = []
    stack = [ISeq.new(RubyVM::InstructionSequence.compile(source).to_a)]

    while (iseq = stack.pop)
      if iseq.type != :once
        names = iseq.local_table

        # CRuby will push on a special local variable when there are keyword
        # arguments. We get rid of that here.
        names = names.grep_v(Integer)

        # TODO: We don't support numbered local variables yet, so we get rid
        # of those here.
        names = names.grep_v(/^_\d$/)

        # For some reason, CRuby occasionally pushes this special local
        # variable when there are splat arguments. We get rid of that here.
        names = names.grep_v(:"#arg_rest")

        # Now push them onto the list of locals.
        locals << names
      end

      iseq.each_child { |child| stack << child }
    end

    locals
  ensure
    $VERBOSE = verbose
  end
end
newlines(source) click to toggle source
# File yarp.rb, line 533
def self.newlines(source)
  YARP.parse(source).source.offsets
end
parse_serialize_file(filepath) click to toggle source
# File yarp.rb, line 537
def self.parse_serialize_file(filepath)
  parse_serialize_file_metadata(filepath, [filepath.bytesize, filepath.b, 0].pack("LA*L"))
end
yarp_locals(source) click to toggle source

For the given source, parses with YARP and returns a list of all of the sets of local variables that were encountered.

# File yarp.rb, line 466
def self.yarp_locals(source)
  locals = []
  stack = [YARP.parse(source).value]

  while (node = stack.pop)
    case node
    when BlockNode, DefNode, LambdaNode
      names = node.locals

      params = node.parameters
      params = params&.parameters unless node.is_a?(DefNode)

      # YARP places parameters in the same order that they appear in the
      # source. CRuby places them in the order that they need to appear
      # according to their own internal calling convention. We mimic that
      # order here so that we can compare properly.
      if params
        sorted = [
          *params.requireds.grep(RequiredParameterNode).map(&:name),
          *params.optionals.map(&:name),
          *((params.rest.name || :*) if params.rest && params.rest.operator != ","),
          *params.posts.grep(RequiredParameterNode).map(&:name),
          *params.keywords.reject(&:value).map(&:name),
          *params.keywords.select(&:value).map(&:name)
        ]

        # TODO: When we get a ... parameter, we should be pushing * and &
        # onto the local list. We don't do that yet, so we need to add them
        # in here.
        if params.keyword_rest.is_a?(ForwardingParameterNode)
          sorted.push(:*, :&, :"...")
        end

        # Recurse down the parameter tree to find any destructured
        # parameters and add them after the other parameters.
        param_stack = params.requireds.concat(params.posts).grep(RequiredDestructuredParameterNode).reverse
        while (param = param_stack.pop)
          case param
          when RequiredDestructuredParameterNode
            param_stack.concat(param.parameters.reverse)
          when RequiredParameterNode
            sorted << param.name
          when SplatNode
            sorted << param.expression.name if param.expression
          end
        end

        names = sorted.concat(names - sorted)
      end

      locals << names
    when ClassNode, ModuleNode, ProgramNode, SingletonClassNode
      locals << node.locals
    when ForNode
      locals << []
    when PostExecutionNode
      locals.push([], [])
    when InterpolatedRegularExpressionNode
      locals << [] if node.once?
    end

    stack.concat(node.child_nodes.compact)
  end

  locals
end