From 939c0d7c6a4923459b95fbd83bce403dec40195f Mon Sep 17 00:00:00 2001 From: Kristoffer Carlsson Date: Sat, 9 May 2020 11:34:08 +0200 Subject: [PATCH] add closed over variables to locals and fix code evaluation on them (#388) --- src/types.jl | 7 ++++++- src/utils.jl | 18 +++++++++++++++++- test/eval_code.jl | 19 ++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/types.jl b/src/types.jl index fd1b829a1c2a74..d6a065be070b9f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -263,15 +263,20 @@ Important fields: - `value::Any`: the value of the local variable. - `name::Symbol`: the name of the variable as given in the source code. - `isparam::Bool`: if the variable is a type parameter, for example `T` in `f(x::T) where {T} = x`. +- `is_captured_closure::Bool`: if the variable has been captured by a closure """ struct Variable value::Any name::Symbol isparam::Bool + is_captured_closure::Bool end +Variable(value, name) = Variable(value, name, false, false) +Variable(value, name, isparam) = Variable(value, name, isparam, false) Base.show(io::IO, var::Variable) = (print(io, var.name, " = "); show(io,var.value)) Base.isequal(var1::Variable, var2::Variable) = - var1.value == var2.value && var1.name == var2.name && var1.isparam == var2.isparam + var1.value == var2.value && var1.name == var2.name && var1.isparam == var2.isparam && + var1.is_captured_closure == var2.is_captured_closure # A type that is unique to this package for which there are no valid operations struct Unassigned end diff --git a/src/utils.jl b/src/utils.jl index ee3bc7ee0a131c..0286c9d05110af 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -371,7 +371,7 @@ function locals(frame::Frame) slotnames = code.src.slotnames::SlotNamesType for (sym, counter, val) in zip(slotnames, data.last_reference, data.locals) counter == 0 && continue - var = Variable(something(val), sym, false) + var = Variable(something(val), sym) idx = get(varlookup, sym, 0) if idx > 0 if counter > var_counter[idx] @@ -392,6 +392,13 @@ function locals(frame::Frame) end end end + for var in vars + if var.name == Symbol("#self#") + for field in fieldnames(typeof(var.value)) + push!(vars, Variable(getfield(var.value, field), field, false, true)) + end + end + end return vars end @@ -484,6 +491,15 @@ function eval_code(frame::Frame, expr) if v.isparam data.sparams[j] = res[i] j += 1 + elseif v.is_captured_closure + selfidx = findfirst(v -> v.name === Symbol("#self#"), vars) + @assert selfidx !== nothing + self = vars[selfidx].value + closed_over_var = getfield(self, v.name) + if closed_over_var isa Core.Box + setfield!(closed_over_var, :contents, res[i]) + end + # We cannot rebind closed over variables that the frontend identified as constant else slot_indices = code.slotnamelists[v.name] idx = argmax(data.last_reference[slot_indices]) diff --git a/test/eval_code.jl b/test/eval_code.jl index e57092f60b6fc3..e585553b7dd98a 100644 --- a/test/eval_code.jl +++ b/test/eval_code.jl @@ -55,4 +55,21 @@ JuliaInterpreter.step_expr!(frame) eval_code(frame, "x = 3") @test eval_code(frame, "x") == 3 JuliaInterpreter.finish!(frame) -@test JuliaInterpreter.get_return(frame) == 2 \ No newline at end of file +@test JuliaInterpreter.get_return(frame) == 2 + +function debugfun(non_accessible_variable) + garbage = ones(10) + map(1:10) do i + 1+1 + a = 5 + @bp + garbage[i] + non_accessible_variable[i] + non_accessible_variable = 2 + end +end +fr = JuliaInterpreter.enter_call(debugfun, [1,2]) +fr, bp = debug_command(fr, :c) +@test eval_code(fr, "non_accessible_variable") == [1,2] +@test eval_code(fr, "garbage") == ones(10) +eval_code(fr, "non_accessible_variable = 5.0") +@test eval_code(fr, "non_accessible_variable") == 5.0