[ruby/prism] Remove Debug module
https://github.com/ruby/prism/commit/4d8929ff6a
This commit is contained in:
parent
745a948b6d
commit
f8b750370e
@ -13,7 +13,6 @@ module Prism
|
|||||||
|
|
||||||
autoload :BasicVisitor, "prism/visitor"
|
autoload :BasicVisitor, "prism/visitor"
|
||||||
autoload :Compiler, "prism/compiler"
|
autoload :Compiler, "prism/compiler"
|
||||||
autoload :Debug, "prism/debug"
|
|
||||||
autoload :DesugarCompiler, "prism/desugar_compiler"
|
autoload :DesugarCompiler, "prism/desugar_compiler"
|
||||||
autoload :Dispatcher, "prism/dispatcher"
|
autoload :Dispatcher, "prism/dispatcher"
|
||||||
autoload :DotVisitor, "prism/dot_visitor"
|
autoload :DotVisitor, "prism/dot_visitor"
|
||||||
@ -32,7 +31,6 @@ module Prism
|
|||||||
# Some of these constants are not meant to be exposed, so marking them as
|
# Some of these constants are not meant to be exposed, so marking them as
|
||||||
# private here.
|
# private here.
|
||||||
|
|
||||||
private_constant :Debug
|
|
||||||
private_constant :LexCompat
|
private_constant :LexCompat
|
||||||
private_constant :LexRipper
|
private_constant :LexRipper
|
||||||
|
|
||||||
|
@ -1,206 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
module Prism
|
|
||||||
# This module is used for testing and debugging and is not meant to be used by
|
|
||||||
# consumers of this library.
|
|
||||||
module Debug
|
|
||||||
# A wrapper around a RubyVM::InstructionSequence that provides a more
|
|
||||||
# convenient interface for accessing parts of the iseq.
|
|
||||||
class ISeq # :nodoc:
|
|
||||||
attr_reader :parts
|
|
||||||
|
|
||||||
def initialize(parts)
|
|
||||||
@parts = parts
|
|
||||||
end
|
|
||||||
|
|
||||||
def type
|
|
||||||
parts[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
def local_table
|
|
||||||
parts[10]
|
|
||||||
end
|
|
||||||
|
|
||||||
def instructions
|
|
||||||
parts[13]
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_child
|
|
||||||
instructions.each do |instruction|
|
|
||||||
# Only look at arrays. Other instructions are line numbers or
|
|
||||||
# tracepoint events.
|
|
||||||
next unless instruction.is_a?(Array)
|
|
||||||
|
|
||||||
instruction.each do |opnd|
|
|
||||||
# Only look at arrays. Other operands are literals.
|
|
||||||
next unless opnd.is_a?(Array)
|
|
||||||
|
|
||||||
# Only look at instruction sequences. Other operands are literals.
|
|
||||||
next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
|
|
||||||
|
|
||||||
yield ISeq.new(opnd)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private_constant :ISeq
|
|
||||||
|
|
||||||
# :call-seq:
|
|
||||||
# Debug::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.
|
|
||||||
def self.cruby_locals(source)
|
|
||||||
verbose, $VERBOSE = $VERBOSE, nil
|
|
||||||
|
|
||||||
begin
|
|
||||||
locals = [] #: Array[Array[Symbol | Integer]]
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
AnonymousLocal = Object.new
|
|
||||||
private_constant :AnonymousLocal
|
|
||||||
|
|
||||||
# :call-seq:
|
|
||||||
# Debug::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.
|
|
||||||
def self.prism_locals(source)
|
|
||||||
locals = [] #: Array[Array[Symbol | Integer]]
|
|
||||||
stack = [Prism.parse(source).value] #: Array[Prism::node]
|
|
||||||
|
|
||||||
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),
|
|
||||||
]
|
|
||||||
|
|
||||||
sorted << AnonymousLocal if params.keywords.any?
|
|
||||||
|
|
||||||
if params.keyword_rest.is_a?(ForwardingParameterNode)
|
|
||||||
sorted.push(:*, :**, :&, :"...")
|
|
||||||
elsif params.keyword_rest.is_a?(KeywordRestParameterNode)
|
|
||||||
sorted << (params.keyword_rest.name || :**)
|
|
||||||
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(MultiTargetNode).reverse
|
|
||||||
while (param = param_stack.pop)
|
|
||||||
case param
|
|
||||||
when MultiTargetNode
|
|
||||||
param_stack.concat(param.rights.reverse)
|
|
||||||
param_stack << param.rest if param.rest&.expression && !sorted.include?(param.rest.expression.name)
|
|
||||||
param_stack.concat(param.lefts.reverse)
|
|
||||||
when RequiredParameterNode
|
|
||||||
sorted << param.name
|
|
||||||
when SplatNode
|
|
||||||
sorted << param.expression.name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if params.block
|
|
||||||
sorted << (params.block.name || :&)
|
|
||||||
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
|
|
||||||
|
|
||||||
# :call-seq:
|
|
||||||
# Debug::newlines(source) -> Array
|
|
||||||
#
|
|
||||||
# For the given source string, return the byte offsets of every newline in
|
|
||||||
# the source.
|
|
||||||
def self.newlines(source)
|
|
||||||
Prism.parse(source).source.offsets
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -394,7 +394,7 @@ module Prism
|
|||||||
|
|
||||||
template << "L"
|
template << "L"
|
||||||
if (encoding = options[:encoding])
|
if (encoding = options[:encoding])
|
||||||
name = encoding.name
|
name = encoding.is_a?(Encoding) ? encoding.name : encoding
|
||||||
values.push(name.bytesize, name.b)
|
values.push(name.bytesize, name.b)
|
||||||
template << "A*"
|
template << "A*"
|
||||||
else
|
else
|
||||||
|
@ -70,7 +70,6 @@ Gem::Specification.new do |spec|
|
|||||||
"include/prism/version.h",
|
"include/prism/version.h",
|
||||||
"lib/prism.rb",
|
"lib/prism.rb",
|
||||||
"lib/prism/compiler.rb",
|
"lib/prism/compiler.rb",
|
||||||
"lib/prism/debug.rb",
|
|
||||||
"lib/prism/desugar_compiler.rb",
|
"lib/prism/desugar_compiler.rb",
|
||||||
"lib/prism/dispatcher.rb",
|
"lib/prism/dispatcher.rb",
|
||||||
"lib/prism/dot_visitor.rb",
|
"lib/prism/dot_visitor.rb",
|
||||||
|
@ -6,7 +6,7 @@ module Prism
|
|||||||
class CommentsTest < TestCase
|
class CommentsTest < TestCase
|
||||||
def test_comment_inline
|
def test_comment_inline
|
||||||
source = "# comment"
|
source = "# comment"
|
||||||
assert_equal [0], Debug.newlines(source)
|
assert_equal [0], Prism.parse(source).source.offsets
|
||||||
|
|
||||||
assert_comment(
|
assert_comment(
|
||||||
source,
|
source,
|
||||||
|
@ -41,8 +41,8 @@ module Prism
|
|||||||
def assert_locals(filepath)
|
def assert_locals(filepath)
|
||||||
source = File.read(filepath)
|
source = File.read(filepath)
|
||||||
|
|
||||||
expected = Debug.cruby_locals(source)
|
expected = cruby_locals(source)
|
||||||
actual = Debug.prism_locals(source)
|
actual = prism_locals(source)
|
||||||
|
|
||||||
assert_equal(expected, actual)
|
assert_equal(expected, actual)
|
||||||
end
|
end
|
||||||
@ -54,5 +54,186 @@ module Prism
|
|||||||
ensure
|
ensure
|
||||||
$VERBOSE = previous_verbosity
|
$VERBOSE = previous_verbosity
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# A wrapper around a RubyVM::InstructionSequence that provides a more
|
||||||
|
# convenient interface for accessing parts of the iseq.
|
||||||
|
class ISeq
|
||||||
|
attr_reader :parts
|
||||||
|
|
||||||
|
def initialize(parts)
|
||||||
|
@parts = parts
|
||||||
|
end
|
||||||
|
|
||||||
|
def type
|
||||||
|
parts[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_table
|
||||||
|
parts[10]
|
||||||
|
end
|
||||||
|
|
||||||
|
def instructions
|
||||||
|
parts[13]
|
||||||
|
end
|
||||||
|
|
||||||
|
def each_child
|
||||||
|
instructions.each do |instruction|
|
||||||
|
# Only look at arrays. Other instructions are line numbers or
|
||||||
|
# tracepoint events.
|
||||||
|
next unless instruction.is_a?(Array)
|
||||||
|
|
||||||
|
instruction.each do |opnd|
|
||||||
|
# Only look at arrays. Other operands are literals.
|
||||||
|
next unless opnd.is_a?(Array)
|
||||||
|
|
||||||
|
# Only look at instruction sequences. Other operands are literals.
|
||||||
|
next unless opnd[0] == "YARVInstructionSequence/SimpleDataFormat"
|
||||||
|
|
||||||
|
yield ISeq.new(opnd)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
AnonymousLocal = Object.new
|
||||||
|
|
||||||
|
# For the given source, compiles with CRuby and returns a list of all of the
|
||||||
|
# sets of local variables that were encountered.
|
||||||
|
def cruby_locals(source)
|
||||||
|
verbose, $VERBOSE = $VERBOSE, nil
|
||||||
|
|
||||||
|
begin
|
||||||
|
locals = [] #: Array[Array[Symbol | Integer]]
|
||||||
|
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
|
||||||
|
|
||||||
|
# For the given source, parses with prism and returns a list of all of the
|
||||||
|
# sets of local variables that were encountered.
|
||||||
|
def prism_locals(source)
|
||||||
|
locals = [] #: Array[Array[Symbol | Integer]]
|
||||||
|
stack = [Prism.parse(source).value] #: Array[Prism::node]
|
||||||
|
|
||||||
|
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),
|
||||||
|
]
|
||||||
|
|
||||||
|
sorted << AnonymousLocal if params.keywords.any?
|
||||||
|
|
||||||
|
if params.keyword_rest.is_a?(ForwardingParameterNode)
|
||||||
|
sorted.push(:*, :**, :&, :"...")
|
||||||
|
elsif params.keyword_rest.is_a?(KeywordRestParameterNode)
|
||||||
|
sorted << (params.keyword_rest.name || :**)
|
||||||
|
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(MultiTargetNode).reverse
|
||||||
|
while (param = param_stack.pop)
|
||||||
|
case param
|
||||||
|
when MultiTargetNode
|
||||||
|
param_stack.concat(param.rights.reverse)
|
||||||
|
param_stack << param.rest if param.rest&.expression && !sorted.include?(param.rest.expression.name)
|
||||||
|
param_stack.concat(param.lefts.reverse)
|
||||||
|
when RequiredParameterNode
|
||||||
|
sorted << param.name
|
||||||
|
when SplatNode
|
||||||
|
sorted << param.expression.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if params.block
|
||||||
|
sorted << (params.block.name || :&)
|
||||||
|
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
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -10,6 +10,7 @@ module Prism
|
|||||||
filepaths = Dir["*.rb", base: base] - %w[
|
filepaths = Dir["*.rb", base: base] - %w[
|
||||||
encoding_test.rb
|
encoding_test.rb
|
||||||
errors_test.rb
|
errors_test.rb
|
||||||
|
locals_test.rb
|
||||||
parser_test.rb
|
parser_test.rb
|
||||||
regexp_test.rb
|
regexp_test.rb
|
||||||
static_literals_test.rb
|
static_literals_test.rb
|
||||||
|
@ -265,7 +265,7 @@ module Prism
|
|||||||
# Next, assert that the newlines are in the expected places.
|
# Next, assert that the newlines are in the expected places.
|
||||||
expected_newlines = [0]
|
expected_newlines = [0]
|
||||||
source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
|
source.b.scan("\n") { expected_newlines << $~.offset(0)[0] + 1 }
|
||||||
assert_equal expected_newlines, Debug.newlines(source)
|
assert_equal expected_newlines, Prism.parse(source).source.offsets
|
||||||
|
|
||||||
if ripper_should_match
|
if ripper_should_match
|
||||||
# Finally, assert that we can lex the source and get the same tokens as
|
# Finally, assert that we can lex the source and get the same tokens as
|
||||||
|
Loading…
x
Reference in New Issue
Block a user