Skip to content

Commit d047188

Browse files
committed
Make tests pass
1 parent 97d6ffa commit d047188

File tree

7 files changed

+269
-58
lines changed

7 files changed

+269
-58
lines changed

src/ASTInterpreter2.jl

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ struct JuliaStackFrame
2828
# for which a reference was last encountered.
2929
last_reference::Dict{Symbol, Int}
3030
wrapper::Bool
31+
generator::Bool
3132
end
32-
function JuliaStackFrame(frame::JuliaStackFrame, pc::JuliaProgramCounter; wrapper = false)
33+
function JuliaStackFrame(frame::JuliaStackFrame, pc::JuliaProgramCounter; wrapper = frame.wrapper, generator=frame.generator)
3334
JuliaStackFrame(frame.meth, frame.code, frame.locals,
34-
frame.ssavalues, frame.sparams, pc, frame.last_reference, wrapper)
35+
frame.ssavalues, frame.sparams, pc, frame.last_reference, wrapper, generator)
3536
end
3637

3738
is_loc_meta(expr, kind) = isexpr(expr, :meta) && length(expr.args) >= 1 && expr.args[1] === kind
@@ -59,7 +60,7 @@ function determine_line_and_file(frame, highlight::Int=0)
5960
npops = 1
6061
while npops >= 1
6162
i -= 1
62-
expr = exprtree.args[i]
63+
expr = frame.code.code[i]
6364
is_loc_meta(expr, :pop_loc) && (npops += 1)
6465
is_loc_meta(expr, :push_loc) && (npops -= 1)
6566
end
@@ -232,7 +233,7 @@ end
232233
function JuliaStackFrame(meth::Method)
233234
JuliaStackFrame(meth, Vector{Nullable{Any}}(),
234235
Vector{Any}(), Vector{Any}(), Vector{Any}(),
235-
Dict{Symbol, Int}(), false)
236+
Dict{Symbol, Int}(), false, false)
236237
end
237238

238239
function DebuggerFramework.debug(meth::Method, args...)
@@ -298,7 +299,7 @@ function determine_method_for_expr(expr; enter_generated = false)
298299
# If we're stepping into a staged function, we need to use
299300
# the specialization, rather than stepping thorugh the
300301
# unspecialized method.
301-
code = Core.Inference.specialize_method(method, argtypes, lenv, false)
302+
code = Core.Inference.get_staged(Core.Inference.code_for_method(method, argtypes, lenv, typemax(UInt), false))
302303
else
303304
if method.isstaged
304305
args = map(_Typeof, args)
@@ -317,7 +318,7 @@ function get_source(meth)
317318
end
318319
end
319320

320-
function prepare_locals(meth, code, argvals = ())
321+
function prepare_locals(meth, code, argvals = (), generator = false)
321322
code = deepcopy(code)
322323
linearize!(code)
323324
# Construct the environment from the arguments
@@ -337,15 +338,15 @@ function prepare_locals(meth, code, argvals = ())
337338
for i = (meth.nargs+1):length(code.slotnames)
338339
locals[i] = Nullable{Any}()
339340
end
340-
JuliaStackFrame(meth, code, locals, ssavalues, sparams, JuliaProgramCounter(2), Dict{Symbol,Int}(), false)
341+
JuliaStackFrame(meth, code, locals, ssavalues, sparams, JuliaProgramCounter(2), Dict{Symbol,Int}(), false, generator)
341342
end
342343

343344

344345
function enter_call_expr(expr; enter_generated = false)
345346
r = determine_method_for_expr(expr; enter_generated = enter_generated)
346347
if r !== nothing
347348
code, method, args, lenv = r
348-
frame = prepare_locals(method, code, args)
349+
frame = prepare_locals(method, code, args, enter_generated)
349350
# Add static parameters to environment
350351
for i = 1:length(lenv)
351352
frame.sparams[i] = lenv[i]

src/commands.jl

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,29 @@ function perform_return!(state)
55
returning_expr = pc_expr(returning_frame)
66
@assert isexpr(returning_expr, :return)
77
val = lookup_var_if_var(returning_frame, returning_expr.args[1])
8-
do_assignment!(calling_frame, pc_expr(calling_frame).args[1], val)
9-
state.stack[2] = JuliaStackFrame(calling_frame, maybe_next_call!(calling_frame,
10-
calling_frame.pc + 1))
8+
if returning_frame.generator
9+
# Don't do anything here, just return us to where we were
10+
else
11+
if isexpr(pc_expr(calling_frame), :(=))
12+
do_assignment!(calling_frame, pc_expr(calling_frame).args[1], val)
13+
end
14+
state.stack[2] = JuliaStackFrame(calling_frame, maybe_next_call!(calling_frame,
15+
calling_frame.pc + 1))
16+
end
1117
end
1218
shift!(state.stack)
19+
if !isempty(state.stack) && state.stack[1].wrapper
20+
state.stack[1] = JuliaStackFrame(state.stack[1], finish!(state.stack[1]))
21+
perform_return!(state)
22+
end
1323
end
1424

1525
function DebuggerFramework.execute_command(state, frame::JuliaStackFrame, ::Union{Val{:ns},Val{:nc},Val{:n},Val{:se}}, command)
1626
if (pc = command == "ns" ? next_statement!(frame) :
1727
command == "nc" ? next_call!(frame) :
18-
command == "n" ? next_line!(frame; state = state) :
28+
command == "n" ? next_line!(frame, state.stack) :
1929
!step_expr(frame)) != nothing #= command == "se" =#
20-
state.stack[1] = JuliaStackFrame(frame, pc)
30+
state.stack[1] = JuliaStackFrame(state.stack[1], pc)
2131
return true
2232
end
2333
perform_return!(state)
@@ -33,16 +43,25 @@ function DebuggerFramework.execute_command(state, frame::JuliaStackFrame, cmd::U
3343
if is_call(expr)
3444
isexpr(expr, :(=)) && (expr = expr.args[2])
3545
expr = Expr(:call, map(x->lookup_var_if_var(frame, x), expr.args)...)
46+
ok = true
3647
if !isa(expr.args[1], Union{Core.Builtin, Core.IntrinsicFunction})
3748
new_frame = enter_call_expr(expr;
3849
enter_generated = command == "sg")
3950
if (cmd == Val{:s}() || cmd == Val{:sg}())
4051
new_frame = JuliaStackFrame(new_frame, maybe_next_call!(new_frame))
4152
end
42-
state.stack[1] = JuliaStackFrame(frame, pc)
43-
unshift!(state.stack, new_frame)
44-
return true
53+
# Don't step into Core.Inference
54+
if new_frame.meth.module == Core.Inference
55+
ok = false
56+
else
57+
state.stack[1] = JuliaStackFrame(frame, pc)
58+
unshift!(state.stack, new_frame)
59+
return true
60+
end
4561
else
62+
ok = false
63+
end
64+
if !ok
4665
# It's confusing if we step into the next call, so just go there
4766
# and then return
4867
state.stack[1] = JuliaStackFrame(frame, next_call!(frame, pc))

src/interpret.jl

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,45 @@ function finish!(frame)
1717
end
1818
end
1919

20+
instantiate_type_in_env(arg, spsig, spvals) =
21+
ccall(:jl_instantiate_type_in_env, Any, (Any, Any, Ptr{Any}), arg, spsig, spvals)
22+
2023
function evaluate_call(frame, call_expr)
21-
# Don't go through eval since this may have unqouted, symbols and
22-
# exprs
23-
f = to_function(lookup_var(frame, call_expr.args[1]))
24-
args = Array{Any}(length(call_expr.args)-1)
24+
args = Array{Any}(length(call_expr.args))
2525
for i = 1:length(args)
26-
arg = call_expr.args[i+1]
27-
args[i] = isa(arg, Union{SSAValue, GlobalRef, Slot}) ? lookup_var(frame, arg) :
28-
arg
26+
arg = call_expr.args[i]
27+
if isa(arg, QuoteNode)
28+
args[i] = arg.value
29+
elseif isa(arg, Union{SSAValue, GlobalRef, Slot})
30+
args[i] = lookup_var(frame, arg)
31+
elseif isexpr(arg, :&)
32+
args[i] = Expr(:&, lookup_var(frame, arg.args[1]))
33+
else
34+
args[i] = arg
35+
end
2936
end
30-
if isa(f, CodeInfo)
31-
ret = finish!(enter_call_expr(frame, call_expr))
37+
# Don't go through eval since this may have unqouted, symbols and
38+
# exprs
39+
if isexpr(call_expr, :foreigncall)
40+
args = map(args) do arg
41+
isa(arg, Symbol) ? QuoteNode(arg) : arg
42+
end
43+
if !isempty(frame.sparams)
44+
args[2] = instantiate_type_in_env(args[2], frame.meth.sig, frame.sparams)
45+
args[3] = Core.svec(map(args[3]) do arg
46+
instantiate_type_in_env(arg, frame.meth.sig, frame.sparams)
47+
end...)
48+
end
49+
ret = eval(frame.meth.module, Expr(:foreigncall, args...))
3250
else
33-
# Don't go through eval since this may have unqouted, symbols and
34-
# exprs
35-
ret = f(args...)
51+
f = to_function(args[1])
52+
if isa(f, CodeInfo)
53+
ret = finish!(enter_call_expr(frame, call_expr))
54+
else
55+
# Don't go through eval since this may have unqouted, symbols and
56+
# exprs
57+
ret = f(args[2:end]...)
58+
end
3659
end
3760
return ret
3861
end
@@ -56,8 +79,13 @@ function _step_expr(frame, pc)
5679
if isa(node, Expr)
5780
if node.head == :(=)
5881
lhs = node.args[1]
59-
rhs = isexpr(node.args[2], :call) ? evaluate_call(frame, node.args[2]) :
60-
lookup_var(frame, node.args[2])
82+
if isexpr(node.args[2], :new)
83+
new_expr = Expr(:new, map(x->lookup_var_if_var(frame, x), node.args[2].args)...)
84+
rhs = eval(frame.meth.module, new_expr)
85+
else
86+
rhs = (isexpr(node.args[2], :call) || isexpr(node.args[2], :foreigncall)) ? evaluate_call(frame, node.args[2]) :
87+
lookup_var_if_var(frame, node.args[2])
88+
end
6189
do_assignment!(frame, lhs, rhs)
6290
# Special case hack for readability.
6391
# ret = rhs
@@ -66,19 +94,19 @@ function _step_expr(frame, pc)
6694
ret = node
6795
elseif node.head == :gotoifnot
6896
ret = node
69-
arg = lookup_var(frame, node.args[1])
97+
arg = node.args[1]
98+
arg = isa(arg, Bool) ? arg : lookup_var(frame, arg)
7099
if !isa(arg, Bool)
71100
throw(TypeError(frame.meth.name, "if", Bool, node.args[1]))
72101
end
73102
if !arg
74103
return JuliaProgramCounter(node.args[2])
75104
end
76-
elseif node.head == :call
105+
elseif node.head == :call || node.head == :foreigncall
77106
evaluate_call(frame, node)
78107
elseif node.head == :static_typeof
79108
ret = Any
80-
elseif node.head == :type_goto
81-
ret = nothing
109+
elseif node.head == :type_goto || node.head == :inbounds
82110
elseif node.head == :enter
83111
push!(interp.exception_frames, node.args[1])
84112
ret = node
@@ -95,7 +123,7 @@ function _step_expr(frame, pc)
95123
ret = eval(node)
96124
end
97125
elseif isa(node, GotoNode)
98-
return JuliaProgramCounter(node.args[1])
126+
return JuliaProgramCounter(node.label)
99127
elseif isa(node, QuoteNode)
100128
ret = node.value
101129
else
@@ -154,20 +182,9 @@ isgotonode(node) = isa(node, GotoNode) || isexpr(node, :gotoifnot)
154182
Determine whether we are calling a function for which the current function
155183
is a wrapper (either because of optional arguments or becaue of keyword arguments).
156184
"""
157-
function iswrappercall(interp, expr)
158-
!isexpr(expr, :call) && return false
159-
r = determine_method_for_expr(interp, expr; enter_generated = false)
160-
if r !== nothing
161-
linfo, method, args, _ = r
162-
ours, theirs = interp.linfo.def, method
163-
# Check if this a method of the same function that shares a definition line/file.
164-
# If so, we're likely in an automatically generated wrapper.
165-
if ours.sig.parameters[1] == theirs.sig.parameters[1] &&
166-
ours.line == theirs.line && ours.file == theirs.file
167-
return true
168-
end
169-
end
170-
return false
185+
function iswrappercall(expr)
186+
isexpr(expr, :(=)) && (expr = expr.args[2])
187+
isexpr(expr, :call) && any(x->x==SlotNumber(1), expr.args)
171188
end
172189

173190
pc_expr(frame, pc) = frame.code.code[pc.next_stmt]
@@ -181,7 +198,7 @@ function maybe_next_call!(frame, pc)
181198
end
182199
maybe_next_call!(frame) = maybe_next_call!(frame, frame.pc)
183200

184-
function next_line!(frame; state = nothing)
201+
function next_line!(frame, stack = nothing)
185202
didchangeline = false
186203
fls = determine_line_and_file(frame, frame.pc.next_stmt)
187204
line = fls[1][2]
@@ -198,9 +215,13 @@ function next_line!(frame; state = nothing)
198215
pc == nothing && return nothing
199216
fls = determine_line_and_file(frame, pc.next_stmt)
200217
didchangeline = line != fls[1][2]
201-
elseif iswrappercall(frame, pc_expr(frame, pc))
202-
interp.did_wrappercall = true
203-
frame = enter_call_expr(frame, pc_expr(frame, pc))
218+
elseif stack !== nothing && iswrappercall(pc_expr(frame, pc))
219+
stack[1] = JuliaStackFrame(frame, pc; wrapper = true)
220+
call_expr = pc_expr(frame, pc)
221+
isexpr(call_expr, :(=)) && (call_expr = call_expr.args[2])
222+
frame = enter_call_expr(Expr(:call, map(x->lookup_var_if_var(frame, x), call_expr.args)...))
223+
unshift!(stack, frame)
224+
pc = frame.pc
204225
elseif isa(pc_expr(frame, pc), LineNumberNode)
205226
line != pc_expr(frame, pc).line && break
206227
pc = _step_expr(frame, pc)

test/evaling.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@ function evalfoo1(x,y)
55
x+y
66
end
77
frame = ASTInterpreter2.enter_call_expr(:($(evalfoo1)(1,2)))
8-
ok, res = DebuggerFramework.eval_code(nothing, frame, "x")
8+
res = DebuggerFramework.eval_code(nothing, frame, "x")
99
@assert res == 1
1010

11-
ok, res = DebuggerFramework.eval_code(state, "y")
11+
res = DebuggerFramework.eval_code(nothing, frame, "y")
1212
@assert res == 2
1313

1414
# Evaling with sparams
1515
function evalsparams{T}(x::T)
1616
x
1717
end
1818
frame = ASTInterpreter2.enter_call_expr(:($(evalsparams)(1)))
19-
ok, res = DebuggerFramework.eval_code(nothing, frame, "x")
19+
res = DebuggerFramework.eval_code(nothing, frame, "x")
2020
@assert res == 1
2121

22-
ok, res = DebuggerFramework.eval_code(nothing, frame, "T")
22+
res = DebuggerFramework.eval_code(nothing, frame, "T")
2323
@assert res == Int

test/interpret.jl

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using ASTInterpreter2
2+
using ASTInterpreter2: enter_call_expr
3+
using Base.Test
4+
5+
function CallTest()
6+
UnitRange{Int64}(2,2)
7+
end
8+
9+
step_through(enter_call_expr(:($(CallTest)())))
10+
11+
# Properly handle :meta annotations
12+
function MetaTest()
13+
@Base._pure_meta
14+
0
15+
end
16+
17+
step_through(enter_call_expr(:($(MetaTest)())))
18+
19+
# Test Vararg handling
20+
function VATest(x...)
21+
x
22+
end
23+
callVA() = VATest()
24+
25+
step_through(enter_call_expr(:($(VATest)())))
26+
27+
# Test Loops
28+
function LoopTest()
29+
x = Int[]
30+
for i = 1:2
31+
push!(x, i)
32+
end
33+
x
34+
end
35+
36+
step_through(enter_call_expr(:($(LoopTest)())))
37+
38+
# Test continue
39+
function ContinueTest()
40+
x = Int[]
41+
for i = 1:3
42+
if true
43+
push!(x, i)
44+
continue
45+
end
46+
error("Fell through")
47+
end
48+
x
49+
end
50+
51+
step_through(enter_call_expr(:($(ContinueTest)())))
52+
53+
#foo() = 1+1
54+
function foo(n)
55+
x = n+1
56+
((BigInt[1 1; 1 0])^x)[2,1]
57+
end
58+
59+
60+
step_through(enter_call_expr(:($(foo)(20))))

test/runtests.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using ASTInterpreter2
22
using Base.Test
33

4-
include("evaling.jl")
4+
include("evaling.jl")
5+
include("stepping.jl")
6+
include("interpret.jl")

0 commit comments

Comments
 (0)