module YARP::Debug
def self.cruby_locals(source)
For the given source, compiles with CRuby and returns a list of all of the
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
def self.newlines(source)
def self.newlines(source) YARP.parse(source).source.offsets end
def self.parse_serialize_file(filepath)
def self.parse_serialize_file(filepath) parse_serialize_file_metadata(filepath, [filepath.bytesize, filepath.b, 0].pack("LA*L")) end
def self.yarp_locals(source)
For the given source, parses with YARP and returns a list of all of the
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(&:constant_id), *params.optionals.map(&:constant_id), *((params.rest.name ? params.rest.name.to_sym : :*) if params.rest && params.rest.operator != ","), *params.posts.grep(RequiredParameterNode).map(&:constant_id), *params.keywords.reject(&:value).map { |param| param.name.chomp(":").to_sym }, *params.keywords.select(&:value).map { |param| param.name.chomp(":").to_sym } ] # 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.constant_id when SplatNode sorted << param.expression.constant_id 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