Skip to content

Commit c8ffd42

Browse files
committed
fix eval_code when variables are captured in closures
1 parent 926dd5b commit c8ffd42

File tree

2 files changed

+68
-4
lines changed

2 files changed

+68
-4
lines changed

src/utils.jl

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -368,8 +368,56 @@ end
368368
eval_code(frame::Frame, code::Union{String, Expr})
369369
370370
Evaluate `code` in the context of `frame`, updating any local variables
371-
(including type parameters) that are reassigned in `code`. New local variables
371+
(including type parameters) that are reassigned in `code`, however, new local variables
372372
cannot be introduced.
373+
374+
```jldoctest; setup=(using JuliaInterpreter; empty!(JuliaInterpreter.junk))
375+
julia> foo(x, y) = x + y;
376+
377+
julia> frame = JuliaInterpreter.enter_call(foo, 1, 3);
378+
379+
julia> JuliaInterpreter.eval_code(frame, "x + y")
380+
4
381+
382+
julia> JuliaInterpreter.eval_code(frame, "x = 5");
383+
384+
julia> JuliaInterpreter.finish_and_return!(frame)
385+
8
386+
````
387+
388+
When variables are captured in closures (and thus gets wrapped in a `Core.Box`)
389+
they will be automatically unwrapped and rewrapped upon evaluating them:
390+
391+
```jldoctest
392+
julia> function capture()
393+
x = 1
394+
f = ()->(x = 2) # x captured in closure and is thus a Core.Box
395+
f()
396+
x
397+
end;
398+
399+
julia> frame = JuliaInterpreter.enter_call(capture);
400+
401+
julia> JuliaInterpreter.step_expr!(frame);
402+
403+
julia> JuliaInterpreter.step_expr!(frame);
404+
405+
julia> JuliaInterpreter.locals(frame)
406+
2-element Array{JuliaInterpreter.Variable,1}:
407+
#self# = capture
408+
x = Core.Box(1)
409+
410+
julia> JuliaInterpreter.eval_code(frame, "x")
411+
1
412+
413+
julia> JuliaInterpreter.eval_code(frame, "x = 2")
414+
2
415+
416+
julia> JuliaInterpreter.locals(frame)
417+
2-element Array{JuliaInterpreter.Variable,1}:
418+
#self# = capture
419+
x = Core.Box(2)
420+
```
373421
"""
374422
function eval_code end
375423

@@ -386,7 +434,7 @@ function eval_code(frame::Frame, expr)
386434
vars = filter(v -> v.name != Symbol(""), locals(frame))
387435
res = gensym()
388436
eval_expr = Expr(:let,
389-
Expr(:block, map(x->Expr(:(=), x...), [(v.name, maybe_quote(v.value)) for v in vars])...),
437+
Expr(:block, map(x->Expr(:(=), x...), [(v.name, maybe_quote(v.value isa Core.Box ? v.value.contents : v.value)) for v in vars])...),
390438
Expr(:block,
391439
Expr(:(=), res, expr),
392440
Expr(:tuple, res, Expr(:tuple, [v.name for v in vars]...))
@@ -398,7 +446,7 @@ function eval_code(frame::Frame, expr)
398446
frame.framedata.sparams[j] = res[i]
399447
j += 1
400448
else
401-
frame.framedata.locals[frame.framedata.last_reference[v.name]] = Some{Any}(res[i])
449+
frame.framedata.locals[frame.framedata.last_reference[v.name]] = Some{Any}(v.value isa Core.Box ? Core.Box(res[i]) : res[i])
402450
end
403451
end
404452
eval_res

test/eval_code.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,20 @@ JuliaInterpreter.step_expr!(frame)
3939
frame = JuliaInterpreter.enter_call(evalfoo1, 1, 2)
4040
@test eval_code(frame, "x = 1; y = 2") == 2
4141
@test eval_code(frame, "x") == 1
42-
@test eval_code(frame, "y") == 2
42+
@test eval_code(frame, "y") == 2
43+
44+
# https://github.com/JuliaDebug/Debugger.jl/issues/177
45+
function f()
46+
x = 1
47+
f = ()->(x = 2)
48+
f()
49+
x
50+
end
51+
frame = JuliaInterpreter.enter_call(f)
52+
JuliaInterpreter.step_expr!(frame)
53+
JuliaInterpreter.step_expr!(frame)
54+
@test eval_code(frame, "x") == 1
55+
eval_code(frame, "x = 3")
56+
@test eval_code(frame, "x") == 3
57+
JuliaInterpreter.finish!(frame)
58+
@test JuliaInterpreter.get_return(frame) == 2

0 commit comments

Comments
 (0)