Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interpreter: let pry see closured vars #12169

Merged
merged 3 commits into from
Jul 3, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/compiler/crystal/interpreter/closure_context.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
class Crystal::Repl
# Information about closured variables in a given context.
class ClosureContext
# The variables closures in the closest context
getter vars : Hash(String, {Int32, Type})

# The self type, if captured, otherwise nil.
# Comes after vars, at the end of the closure (this closure never has a parent closure).
getter self_type : Type?

# The parent context, if any, where more closured variables might be reached
getter parent : ClosureContext?

# The total bytesize to hold all the immediate closure data.
# If this context has a parent context, it will come at the end of this
# data and occupy 8 bytes.
getter bytesize : Int32

def initialize(
@vars : Hash(String, {Int32, Type}),
@self_type : Type?,
@parent : ClosureContext?,
@bytesize : Int32
)
end
end
end
9 changes: 5 additions & 4 deletions src/compiler/crystal/interpreter/compiled_block.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ class Crystal::Repl
# The bytecode to execute the block.
getter instructions : CompiledInstructions

# Local variables for the block (they might reference variables outside of the block)
getter local_vars : LocalVars

# How many bytes occupy the block args
getter args_bytesize : Int32

Expand All @@ -21,8 +18,12 @@ class Crystal::Repl
# What's the byte offset where the local vars of this block end
getter locals_bytesize_end : Int32

# Local variables for the block (they might reference variables outside of the block)
property! local_vars : LocalVars

property closure_context : ClosureContext?

def initialize(@block : Block,
@local_vars : LocalVars,
@args_bytesize : Int32,
@locals_bytesize_start : Int32,
@locals_bytesize_end : Int32)
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/crystal/interpreter/compiled_def.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class Crystal::Repl
# How many bytes occupy the method arguments.
getter args_bytesize : Int32

property closure_context : ClosureContext?

def initialize(
context : Context,
@def : Def,
Expand Down
57 changes: 21 additions & 36 deletions src/compiler/crystal/interpreter/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,6 @@ class Crystal::Repl::Compiler < Crystal::Visitor
# and possibly parent context, to reach the variable with the given type.
record ClosuredVar, indexes : Array(Int32), type : Type

# Information about closured variables in a given context.
class ClosureContext
# The variables closures in the closest context
getter vars : Hash(String, {Int32, Type})

# The self type, if captured, otherwise nil.
# Comes after vars, at the end of the closure (this closure never has a parent closure).
getter self_type : Type?

# The parent context, if any, where more closured variables might be reached
getter parent : ClosureContext?

# The total bytesize to hold all the immediate closure data.
# If this context has a parent context, it will come at the end of this
# data and occupy 8 bytes.
getter bytesize : Int32

def initialize(
@vars : Hash(String, {Int32, Type}),
@self_type : Type?,
@parent : ClosureContext?,
@bytesize : Int32
)
end
end

# What's `self` when compiling a node.
private getter scope : Type

Expand Down Expand Up @@ -103,7 +77,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
# ```
property block_level = 0

@closure_context : ClosureContext?
property closure_context : ClosureContext?

def initialize(
@context : Context,
Expand Down Expand Up @@ -170,7 +144,9 @@ class Crystal::Repl::Compiler < Crystal::Visitor

# Compile bytecode instructions for the given block, where `target_def`
# is the method that will yield to the block.
def compile_block(node : Block, target_def : Def, parent_closure_context : ClosureContext?) : Nil
def compile_block(compiled_block : CompiledBlock, target_def : Def, parent_closure_context : ClosureContext?) : Nil
node = compiled_block.block

prepare_closure_context(node, parent_closure_context: parent_closure_context)

@compiling_block = CompilingBlock.new(node, target_def)
Expand Down Expand Up @@ -211,10 +187,17 @@ class Crystal::Repl::Compiler < Crystal::Visitor

# Use a dummy node so that pry stops at `end`
leave aligned_sizeof_type(node), node: Nop.new.at(node.end_location)

# Keep a copy of the local vars before exiting the block.
# Otherwise we'll lose reference to the block's vars (useful for pry)
compiled_block.local_vars = @local_vars.dup
compiled_block.closure_context = @closure_context
end

# Compile bytecode instructions for the given method.
def compile_def(node : Def, parent_closure_context : ClosureContext? = nil, closure_owner = node) : Nil
def compile_def(compiled_def : CompiledDef, parent_closure_context : ClosureContext? = nil, closure_owner = compiled_def.def) : Nil
node = compiled_def.def

prepare_closure_context(
node,
closure_owner: closure_owner,
Expand Down Expand Up @@ -260,6 +243,8 @@ class Crystal::Repl::Compiler < Crystal::Visitor
# Use a dummy node so that pry stops at `end`
leave aligned_sizeof_type(final_type), node: Nop.new.at(node.end_location)

compiled_def.closure_context = @closure_context

@instructions
end

Expand Down Expand Up @@ -1116,7 +1101,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
declare_local_vars(fake_def, compiled_def.local_vars, @context.program)

compiler = Compiler.new(@context, compiled_def, top_level: true)
compiler.compile_def(fake_def, closure_owner: @context.program)
compiler.compile_def(compiled_def, closure_owner: @context.program)

{% if Debug::DECOMPILE %}
puts "=== #{def_name} ==="
Expand Down Expand Up @@ -1396,7 +1381,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
declare_local_vars(fake_def, compiled_def.local_vars)

compiler = Compiler.new(@context, compiled_def, top_level: true)
compiler.compile_def(fake_def)
compiler.compile_def(compiled_def)

{% if Debug::DECOMPILE %}
puts "=== #{const} ==="
Expand Down Expand Up @@ -1976,7 +1961,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
compiler.compiled_block = compiled_block

begin
compiler.compile_def(target_def)
compiler.compile_def(compiled_def)
rescue ex : Crystal::CodeError
node.raise "compiling #{node}", inner: ex
end
Expand Down Expand Up @@ -2030,7 +2015,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor

block_args_bytesize = block.args.sum { |arg| aligned_sizeof_type(arg) }

compiled_block = CompiledBlock.new(block, @local_vars,
compiled_block = CompiledBlock.new(block,
args_bytesize: block_args_bytesize,
locals_bytesize_start: bytesize_before_block_local_vars,
locals_bytesize_end: bytesize_after_block_local_vars,
Expand All @@ -2045,7 +2030,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
compiler.compiled_block = @compiled_block
compiler.block_level = block_level + 1

compiler.compile_block(block, target_def, @closure_context)
compiler.compile_block(compiled_block, target_def, @closure_context)

{% if Debug::DECOMPILE %}
puts "=== #{target_def.owner}##{target_def.name}#block ==="
Expand Down Expand Up @@ -2651,7 +2636,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor

compiler = Compiler.new(@context, compiled_def, top_level: false)
begin
compiler.compile_def(target_def, is_closure ? @closure_context : nil)
compiler.compile_def(compiled_def, is_closure ? @closure_context : nil)
rescue ex : Crystal::CodeError
node.raise "compiling #{node}", inner: ex
end
Expand Down Expand Up @@ -2949,7 +2934,7 @@ class Crystal::Repl::Compiler < Crystal::Visitor
declare_local_vars(file_module, compiled_def.local_vars)

compiler = Compiler.new(@context, compiled_def, top_level: true)
compiler.compile_def(a_def, closure_owner: file_module)
compiler.compile_def(compiled_def, closure_owner: file_module)

@context.add_gc_reference(compiled_def)

Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/interpreter/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class Crystal::Repl::Context
end

compiler = Compiler.new(self, compiled_def, top_level: false)
compiler.compile_def(a_def)
compiler.compile_def(compiled_def)

{% if Debug::DECOMPILE %}
puts "=== #{a_def.name} ==="
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/crystal/interpreter/disassembler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module Crystal::Repl::Disassembler
disassemble(context, compiled_def.instructions, compiled_def.local_vars)
end

def self.disassemble(context : Context, compiled_block : CompiledBlock) : String
disassemble(context, compiled_block.instructions, compiled_block.local_vars)
end

def self.disassemble(context : Context, instructions : CompiledInstructions, local_vars : LocalVars) : String
String.build do |io|
exception_handlers = instructions.exception_handlers
Expand Down
Loading