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.
Object.new
Class Method Summary
-
.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.
-
.inspect_node(source) ⇒ inspected
Inspect the AST that represents the given source using the prism pretty print as opposed to the Ruby implementation.
-
.memsize(source) ⇒ { length: xx, memsize: xx, node_count: xx }
Return a hash of information about the given source string’s memory usage.
-
.named_captures(source) ⇒ Array
Returns an array of strings corresponding to the named capture groups in the given source string.
-
.newlines(source) ⇒ Array
For the given source string, return the byte offsets of every newline in the source.
-
.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.
-
.profile_file(filepath) ⇒ nil
Parse the file, but do nothing with the result.
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.
# 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.
# 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.
# 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.
# 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.
.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.
# 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.
# 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; }