123456789_123456789_123456789_123456789_123456789_

Module: Prism::Debug

Relationships & Source Files
Namespace Children
Classes:
Defined in: lib/prism/debug.rb,
prism/extension.c

Overview

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

Constant Summary

  • AnonymousLocal = private

    Used to hold the place of a local that will be in the local table but cannot be accessed directly from the source code. For example, the iteration variable in a for loop or the positional parameter on a method definition that is destructured.

    # File 'lib/prism/debug.rb', line 90
    Object.new

Class Method Summary

Class Method Details

.cruby_locals(source) ⇒ Array

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

[ GitHub ]

  
# File 'lib/prism/debug.rb', line 54

def self.cruby_locals(source)
  verbose, $VERBOSE = $VERBOSE, nil

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

    while (iseq = stack.pop)
      names = [*iseq.local_table]
      names.map!.with_index do |name, index|
        # When an anonymous local variable is present in the iseq's local
        # table, it is represented as the stack offset from the top.
        # However, when these are dumped to binary and read back in, they
        # are replaced with the symbol :#arg_rest. To consistently handle
        # this, we replace them here with their index.
        if name == :"#arg_rest"
          names.length - index + 1
        else
          name
        end
      end

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

    locals
  ensure
    $VERBOSE = verbose
  end
end

.inspect_node(source) ⇒ inspected

Inspect the AST that represents the given source using the prism pretty print as opposed to the Ruby implementation.

[ GitHub ]

  
# File 'prism/extension.c', line 951

static VALUE
inspect_node(VALUE self, VALUE source) {
    pm_string_t input;
    input_load_string(&input, source);

    pm_parser_t parser;
    pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), NULL);

    pm_node_t *node = pm_parse(&parser);
    pm_buffer_t buffer = { 0 };

    pm_prettyprint(&buffer, &parser, node);

    rb_encoding *encoding = rb_enc_find(parser.encoding->name);
    VALUE string = rb_enc_str_new(pm_buffer_value(&buffer), pm_buffer_length(&buffer), encoding);

    pm_buffer_free(&buffer);
    pm_node_destroy(&parser, node);
    pm_parser_free(&parser);

    return string;
}

.memsize(source) ⇒ { length: xx, memsize: xx, node_count: xx }

Return a hash of information about the given source string’s memory usage.

[ GitHub ]

  
# File 'prism/extension.c', line 895

static VALUE
memsize(VALUE self, VALUE string) {
    pm_parser_t parser;
    size_t length = RSTRING_LEN(string);
    pm_parser_init(&parser, (const uint8_t *) RSTRING_PTR(string), length, NULL);

    pm_node_t *node = pm_parse(&parser);
    pm_memsize_t memsize;
    pm_node_memsize(node, &memsize);

    pm_node_destroy(&parser, node);
    pm_parser_free(&parser);

    VALUE result = rb_hash_new();
    rb_hash_aset(result, ID2SYM(rb_intern("length")), INT2FIX(length));
    rb_hash_aset(result, ID2SYM(rb_intern("memsize")), INT2FIX(memsize.memsize));
    rb_hash_aset(result, ID2SYM(rb_intern("node_count")), INT2FIX(memsize.node_count));
    return result;
}

.named_captures(source) ⇒ Array

Returns an array of strings corresponding to the named capture groups in the given source string. If prism was unable to parse the regular expression, this function returns nil.

[ GitHub ]

  
# File 'prism/extension.c', line 870

static VALUE
named_captures(VALUE self, VALUE source) {
    pm_string_list_t string_list = { 0 };

    if (!pm_regexp_named_capture_group_names((const uint8_t *) RSTRING_PTR(source), RSTRING_LEN(source), &string_list, false, PM_ENCODING_UTF_8_ENTRY)) {
        pm_string_list_free(&string_list);
        return Qnil;
    }

    VALUE names = rb_ary_new();
    for (size_t index = 0; index < string_list.length; index++) {
        const pm_string_t *string = &string_list.strings[index];
        rb_ary_push(names, rb_str_new((const char *) pm_string_source(string), pm_string_length(string)));
    }

    pm_string_list_free(&string_list);
    return names;
}

.newlines(source) ⇒ Array

For the given source string, return the byte offsets of every newline in the source.

[ GitHub ]

  
# File 'lib/prism/debug.rb', line 196

def self.newlines(source)
  Prism.parse(source).source.offsets
end

.prism_locals(source) ⇒ Array

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

[ GitHub ]

  
# File 'lib/prism/debug.rb', line 98

def self.prism_locals(source)
  locals = []
  stack = [Prism.parse(source).value]

  while (node = stack.pop)
    case node
    when BlockNode, DefNode, LambdaNode
      names = node.locals
      params =
        if node.is_a?(DefNode)
          node.parameters
        elsif node.parameters.is_a?(NumberedParametersNode)
          nil
        else
          node.parameters&.parameters
        end

      # prism 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.map do |required|
            if required.is_a?(RequiredParameterNode)
              required.name
            else
              AnonymousLocal
            end
          end,
          *params.optionals.map(&:name),
          *((params.rest.name || :*) if params.rest && !params.rest.is_a?(ImplicitRestNode)),
          *params.posts.map do |post|
            if post.is_a?(RequiredParameterNode)
              post.name
            else
              AnonymousLocal
            end
          end,
          *params.keywords.grep(RequiredKeywordParameterNode).map(&:name),
          *params.keywords.grep(OptionalKeywordParameterNode).map(&:name),
        ]

        if params.keyword_rest.is_a?(ForwardingParameterNode)
          sorted.push(:*, :&, :"...")
        end

        sorted << AnonymousLocal if params.keywords.any?

        # 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(MultiTargetNode).reverse
        while (param = param_stack.pop)
          case param
          when MultiTargetNode
            param_stack.concat(param.rights.reverse)
            param_stack << param.rest
            param_stack.concat(param.lefts.reverse)
          when RequiredParameterNode
            sorted << param.name
          when SplatNode
            sorted << param.expression.name if param.expression
          end
        end

        names = sorted.concat(names - sorted)
      end

      names.map!.with_index do |name, index|
        if name == AnonymousLocal
          names.length - index + 1
        else
          name
        end
      end

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

    stack.concat(node.compact_child_nodes)
  end

  locals
end

.profile_file(filepath) ⇒ nil

Parse the file, but do nothing with the result. This is used to profile the parser for memory and speed.

[ GitHub ]

  
# File 'prism/extension.c', line 922

static VALUE
profile_file(VALUE self, VALUE filepath) {
    pm_string_t input;

    const char *checked = check_string(filepath);
    if (!pm_string_mapped_init(&input, checked)) return Qnil;

    pm_options_t options = { 0 };
    pm_options_filepath_set(&options, checked);

    pm_parser_t parser;
    pm_parser_init(&parser, pm_string_source(&input), pm_string_length(&input), &options);

    pm_node_t *node = pm_parse(&parser);
    pm_node_destroy(&parser, node);
    pm_parser_free(&parser);
    pm_options_free(&options);
    pm_string_free(&input);

    return Qnil;
}