Skip to content

Commit 926dd5b

Browse files
committed
add util functionality for evaluating code in a frame
1 parent 4fada2c commit 926dd5b

File tree

4 files changed

+84
-0
lines changed

4 files changed

+84
-0
lines changed

docs/src/dev_reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ JuliaInterpreter.compiled_modules
9696
## Utilities
9797

9898
```@docs
99+
JuliaInterpreter.eval_code
99100
JuliaInterpreter.@lookup
100101
JuliaInterpreter.is_wrapper_call
101102
JuliaInterpreter.is_doc_expr

src/utils.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,46 @@ function print_vars(io::IO, vars::Vector{Variable})
364364
end
365365
end
366366

367+
"""
368+
eval_code(frame::Frame, code::Union{String, Expr})
369+
370+
Evaluate `code` in the context of `frame`, updating any local variables
371+
(including type parameters) that are reassigned in `code`. New local variables
372+
cannot be introduced.
373+
"""
374+
function eval_code end
375+
376+
eval_code(frame::Frame, command::AbstractString) = eval_code(frame, Base.parse_input_line(command))
377+
function eval_code(frame::Frame, expr)
378+
maybe_quote(x) = (isa(x, Expr) || isa(x, Symbol)) ? QuoteNode(x) : x
379+
380+
isexpr(expr, :toplevel) && (expr = expr.args[end])
381+
382+
if isexpr(expr, :toplevel)
383+
expr = Expr(:block, expr.args...)
384+
end
385+
# see https://github.com/JuliaLang/julia/issues/31255 for the Symbol("") check
386+
vars = filter(v -> v.name != Symbol(""), locals(frame))
387+
res = gensym()
388+
eval_expr = Expr(:let,
389+
Expr(:block, map(x->Expr(:(=), x...), [(v.name, maybe_quote(v.value)) for v in vars])...),
390+
Expr(:block,
391+
Expr(:(=), res, expr),
392+
Expr(:tuple, res, Expr(:tuple, [v.name for v in vars]...))
393+
))
394+
eval_res, res = Core.eval(moduleof(frame), eval_expr)
395+
j = 1
396+
for (i, v) in enumerate(vars)
397+
if v.isparam
398+
frame.framedata.sparams[j] = res[i]
399+
j += 1
400+
else
401+
frame.framedata.locals[frame.framedata.last_reference[v.name]] = Some{Any}(res[i])
402+
end
403+
end
404+
eval_res
405+
end
406+
367407
function show_stackloc(io::IO, frame)
368408
indent = ""
369409
fr = root(frame)

test/eval_code.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import JuliaInterpreter.eval_code
2+
3+
# Simple evaling of function argument
4+
function evalfoo1(x,y)
5+
x+y
6+
end
7+
frame = JuliaInterpreter.enter_call(evalfoo1, 1, 2)
8+
@test eval_code(frame, "x") == 1
9+
@test eval_code(frame, "y") == 2
10+
11+
# Evaling with sparams
12+
evalsparams(x::T) where T = x
13+
frame = JuliaInterpreter.enter_call(evalsparams, 1)
14+
@test eval_code(frame, "x") == 1
15+
eval_code(frame, "x = 3")
16+
@test eval_code(frame, "x") == 3
17+
@test eval_code(frame, "T") == Int
18+
eval_code(frame, "T = Float32")
19+
@test eval_code(frame, "T") == Float32
20+
21+
# Evaling with keywords
22+
evalkw(x; bar=true) = x
23+
frame = JuliaInterpreter.enter_call(evalkw, 2)
24+
frame = JuliaInterpreter.maybe_step_through_wrapper!(frame)
25+
@test eval_code(frame, "x") == 2
26+
@test eval_code(frame, "bar") == true
27+
eval_code(frame, "bar = false")
28+
@test eval_code(frame, "bar") == false
29+
30+
# Evaling with symbols
31+
evalsym() = (x = :foo)
32+
frame = JuliaInterpreter.enter_call(evalsym)
33+
# Step until the local actually end up getting defined
34+
JuliaInterpreter.step_expr!(frame)
35+
JuliaInterpreter.step_expr!(frame)
36+
@test eval_code(frame, "x") == :foo
37+
38+
# Evaling multiple statements (https://github.com/JuliaDebug/Debugger.jl/issues/188)
39+
frame = JuliaInterpreter.enter_call(evalfoo1, 1, 2)
40+
@test eval_code(frame, "x = 1; y = 2") == 2
41+
@test eval_code(frame, "x") == 1
42+
@test eval_code(frame, "y") == 2

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ JuliaInterpreter.debug_recycle[] = true
1313
include("interpret.jl")
1414
include("toplevel.jl")
1515
include("limits.jl")
16+
include("eval_code.jl")
1617
include("breakpoints.jl")
1718
remove()
1819
include("debug.jl")

0 commit comments

Comments
 (0)