Skip to content

Commit 735c3fc

Browse files
authored
Merge branch 'master' into issue-#432
2 parents 95ab44a + 2030a00 commit 735c3fc

19 files changed

+507
-193
lines changed

Project.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "JuliaInterpreter"
22
uuid = "aa1ae85d-cabe-5617-a682-6adf51b2e16a"
3-
version = "0.9.0"
3+
version = "0.9.10"
44

55
[deps]
66
CodeTracking = "da1fd8a2-8d9e-5ec2-8556-3022fb5608a2"
@@ -16,8 +16,10 @@ julia = "1.6"
1616
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
1717
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1818
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
19+
FunctionWrappers = "069b7b12-0de2-55c6-9aab-29f3d0a68a2e"
1920
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
2021
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
22+
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
2123
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
2224
PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0"
2325
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
@@ -26,4 +28,4 @@ Tensors = "48a634ad-e948-5137-8d70-aa71f2a747f4"
2628
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2729

2830
[targets]
29-
test = ["DataFrames", "Dates", "Distributed", "HTTP", "LinearAlgebra", "Mmap", "PyCall", "SHA", "SparseArrays", "Tensors", "Test"]
31+
test = ["DataFrames", "Dates", "Distributed", "FunctionWrappers", "HTTP", "LinearAlgebra", "Logging", "Mmap", "PyCall", "SHA", "SparseArrays", "Tensors", "Test"]

docs/src/dev_reference.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,6 @@ JuliaInterpreter.is_doc_expr
105105
JuliaInterpreter.is_global_ref
106106
CodeTracking.whereis
107107
JuliaInterpreter.linenumber
108-
JuliaInterpreter.statementnumber
109108
JuliaInterpreter.Variable
110109
JuliaInterpreter.locals
111110
JuliaInterpreter.whichtt

src/breakpoints.jl

Lines changed: 39 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
using Base: Callable
2+
13
const _breakpoints = AbstractBreakpoint[]
24

35
"""
@@ -60,11 +62,20 @@ function add_to_existing_framecodes(bp::AbstractBreakpoint)
6062
end
6163
end
6264

63-
function add_breakpoint_if_match!(framecode::FrameCode, bp::AbstractBreakpoint)
65+
function add_breakpoint_if_match!(framecode::FrameCode, bp::BreakpointSignature)
6466
if framecode_matches_breakpoint(framecode, bp)
65-
stmtidx = bp.line === 0 ? 1 : statementnumber(framecode, bp.line)
66-
breakpoint!(framecode, stmtidx, bp.condition, bp.enabled[])
67-
push!(bp.instances, BreakpointRef(framecode, stmtidx))
67+
scope = framecode.scope
68+
matching_file = if scope isa Method
69+
scope.file
70+
else
71+
# TODO: make more precise?
72+
first(framecode.src.linetable).file
73+
end
74+
stmtidxs = bp.line === 0 ? [1] : statementnumbers(framecode, bp.line, matching_file::Symbol)
75+
stmtidxs === nothing && return
76+
breakpoint!(framecode, stmtidxs, bp.condition, bp.enabled[])
77+
foreach(stmtidx -> push!(bp.instances, BreakpointRef(framecode, stmtidx)), stmtidxs)
78+
return
6879
end
6980
end
7081

@@ -111,10 +122,10 @@ end
111122
breakpoint(radius2, Tuple{Int,Int}, :(y > x))
112123
```
113124
"""
114-
function breakpoint(f::Union{Method, Function}, sig=nothing, line::Integer=0, condition::Condition=nothing)
115-
if sig !== nothing && f isa Function
125+
function breakpoint(f::Union{Method, Callable}, sig=nothing, line::Integer=0, condition::Condition=nothing)
126+
if sig !== nothing && f isa Callable
116127
sig = Base.to_tuple_type(sig)
117-
sig = Tuple{typeof(f), sig.parameters...}
128+
sig = Tuple{_Typeof(f), sig.parameters...}
118129
end
119130
bp = BreakpointSignature(f, sig, line, condition, Ref(true), BreakpointRef[])
120131
add_to_existing_framecodes(bp)
@@ -129,9 +140,9 @@ function breakpoint(f::Union{Method, Function}, sig=nothing, line::Integer=0, co
129140
firehooks(breakpoint, bp)
130141
return bp
131142
end
132-
breakpoint(f::Union{Method, Function}, sig, condition::Condition) = breakpoint(f, sig, 0, condition)
133-
breakpoint(f::Union{Method, Function}, line::Integer, condition::Condition=nothing) = breakpoint(f, nothing, line, condition)
134-
breakpoint(f::Union{Method, Function}, condition::Condition) = breakpoint(f, nothing, 0, condition)
143+
breakpoint(f::Union{Method, Callable}, sig, condition::Condition) = breakpoint(f, sig, 0, condition)
144+
breakpoint(f::Union{Method, Callable}, line::Integer, condition::Condition=nothing) = breakpoint(f, nothing, line, condition)
145+
breakpoint(f::Union{Method, Callable}, condition::Condition) = breakpoint(f, nothing, 0, condition)
135146

136147

137148
"""
@@ -154,29 +165,24 @@ function breakpoint(file::AbstractString, line::Integer, condition::Condition=no
154165
return bp
155166
end
156167

157-
function framecode_matches_breakpoint(framecode::FrameCode, bp::BreakpointFileLocation)
158-
if framecode.scope isa Method
159-
meth = framecode.scope::Method
160-
methpath = CodeTracking.maybe_fix_path(String(meth.file))
161-
ispath(methpath) && (methpath = realpath(methpath))
162-
if bp.abspath == methpath || endswith(methpath, bp.path)
163-
return method_contains_line(meth, bp.line)
164-
else
165-
return false
166-
end
167-
else
168-
w = whereis(framecode, 1)
169-
w === nothing && return false
170-
path, _ = w
171-
path = CodeTracking.maybe_fix_path(path)
172-
ispath(path) && (path = realpath(path))
173-
174-
if bp.abspath == path || endswith(path, bp.path)
175-
return toplevel_code_contains_line(framecode, bp.line)
176-
else
177-
return false
168+
function add_breakpoint_if_match!(framecode::FrameCode, bp::BreakpointFileLocation)
169+
framecode_contains_file = false
170+
matching_file = nothing
171+
for file in framecode.unique_files
172+
filepath = CodeTracking.maybe_fix_path(String(file))
173+
if Base.samefile(bp.abspath, filepath) || endswith(filepath, bp.path)
174+
framecode_contains_file = true
175+
matching_file = file
176+
break
178177
end
179178
end
179+
framecode_contains_file || return nothing
180+
181+
stmtidxs = bp.line === 0 ? [1] : statementnumbers(framecode, bp.line, matching_file::Symbol)
182+
stmtidxs === nothing && return
183+
breakpoint!(framecode, stmtidxs, bp.condition, bp.enabled[])
184+
foreach(stmtidx -> push!(bp.instances, BreakpointRef(framecode, stmtidx)), stmtidxs)
185+
return
180186
end
181187

182188
function shouldbreak(frame::Frame, pc::Int)
@@ -238,6 +244,8 @@ function breakpoint!(framecode::FrameCode, pc, condition::Condition=nothing, ena
238244
framecode.breakpoints[stmtidx] = BreakpointState(enabled, Core.eval(mod, fex))
239245
end
240246
end
247+
breakpoint!(framecode::FrameCode, pcs::AbstractArray, condition::Condition=nothing, enabled=true) =
248+
foreach(pc -> breakpoint!(framecode, pc, condition, enabled), pcs)
241249
breakpoint!(frame::Frame, pc=frame.pc, condition::Condition=nothing) =
242250
breakpoint!(frame.framecode, pc, condition)
243251

src/builtins.jl

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ end
1313
const kwinvoke_name = isdefined(Core, Symbol("#kw##invoke")) ? Symbol("#kw##invoke") : Symbol("##invoke")
1414
const kwinvoke_instance = getfield(Core, kwinvoke_name).instance
1515

16+
17+
function maybe_recurse_expanded_builtin(frame, new_expr)
18+
f = new_expr.args[1]
19+
if isa(f, Core.Builtin) || isa(f, Core.IntrinsicFunction)
20+
return maybe_evaluate_builtin(frame, new_expr, true)
21+
else
22+
return new_expr
23+
end
24+
end
25+
1626
"""
1727
ret = maybe_evaluate_builtin(frame, call_expr, expand::Bool)
1828
@@ -22,8 +32,6 @@ Otherwise, return `call_expr`.
2232
If `expand` is true, `Core._apply` calls will be resolved as a call to the applied function.
2333
"""
2434
function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
25-
# By having each call appearing statically in the "switch" block below,
26-
# each gets call-site optimized.
2735
args = call_expr.args
2836
nargs = length(args) - 1
2937
fex = args[1]
@@ -32,13 +40,11 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
3240
else
3341
f = @lookup(frame, fex)
3442
end
35-
# Builtins and intrinsics have empty method tables. We can circumvent
36-
# a long "switch" check by looking for this.
37-
mt = typeof(f).name.mt
38-
if isa(mt, Core.MethodTable)
39-
isempty(mt) || return call_expr
43+
if !(isa(f, Core.Builtin) || isa(f, Core.IntrinsicFunction))
44+
return call_expr
4045
end
41-
# Builtins
46+
# By having each call appearing statically in the "switch" block below,
47+
# each gets call-site optimized.
4248
if f === <:
4349
if nargs == 2
4450
return Some{Any}(<:(@lookup(frame, args[2]), @lookup(frame, args[3])))
@@ -62,7 +68,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
6268
for x in argsflat
6369
push!(new_expr.args, (isa(x, Symbol) || isa(x, Expr) || isa(x, QuoteNode)) ? QuoteNode(x) : x)
6470
end
65-
return new_expr
71+
return maybe_recurse_expanded_builtin(frame, new_expr)
6672
elseif @static isdefined(Core, :_call_latest) ? f === Core._call_latest : false
6773
args = getargs(args, frame)
6874
if !expand
@@ -73,7 +79,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
7379
for x in args
7480
push!(new_expr.args, (isa(x, Symbol) || isa(x, Expr) || isa(x, QuoteNode)) ? QuoteNode(x) : x)
7581
end
76-
return new_expr
82+
return maybe_recurse_expanded_builtin(frame, new_expr)
7783
elseif @static isdefined(Core, :_apply_latest) ? f === Core._apply_latest : false
7884
args = getargs(args, frame)
7985
if !expand
@@ -84,7 +90,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
8490
for x in args
8591
push!(new_expr.args, (isa(x, Symbol) || isa(x, Expr) || isa(x, QuoteNode)) ? QuoteNode(x) : x)
8692
end
87-
return new_expr
93+
return maybe_recurse_expanded_builtin(frame, new_expr)
8894
elseif @static isdefined(Core, :_apply_iterate) ? f === Core._apply_iterate : false
8995
argswrapped = getargs(args, frame)
9096
if !expand
@@ -99,7 +105,7 @@ function maybe_evaluate_builtin(frame, call_expr, expand::Bool)
99105
for x in argsflat
100106
push!(new_expr.args, (isa(x, Symbol) || isa(x, Expr) || isa(x, QuoteNode)) ? QuoteNode(x) : x)
101107
end
102-
return new_expr
108+
return maybe_recurse_expanded_builtin(frame, new_expr)
103109
elseif f === Core._apply_pure
104110
return Some{Any}(Core._apply_pure(getargs(args, frame)...))
105111
elseif f === Core._expr

src/commands.jl

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,18 @@ function maybe_step_through_wrapper!(@nospecialize(recurse), frame::Frame)
218218
length(stmts) < 2 && return frame
219219
last = stmts[end-1]
220220
isexpr(last, :(=)) && (last = last.args[2])
221-
is_kw = isa(scope, Method) && endswith(String(Base.unwrap_unionall(Base.unwrap_unionall(scope.sig).parameters[1]).name.name), "#kw")
221+
222+
is_kw = false
223+
if isa(scope, Method)
224+
unwrap1 = Base.unwrap_unionall(scope.sig)
225+
if unwrap1 isa DataType
226+
param1 = Base.unwrap_unionall(unwrap1.parameters[1])
227+
if param1 isa DataType
228+
is_kw = endswith(String(param1.name.name), "#kw")
229+
end
230+
end
231+
end
232+
222233
has_selfarg = isexpr(last, :call) && any(isequal(SlotNumber(1)), last.args)
223234
issplatcall, _callee = unpack_splatcall(last)
224235
if is_kw || has_selfarg || (issplatcall && is_bodyfunc(_callee))
@@ -389,6 +400,18 @@ function maybe_step_through_nkw_meta!(frame)
389400
end
390401

391402

403+
function more_calls_on_current_line(frame)
404+
_, curr_line = whereis(frame)
405+
curr_pc = frame.pc + 1
406+
while curr_pc <= length(frame.framecode.src.code)
407+
_, new_line = whereis(frame, curr_pc)
408+
new_line == curr_line || return false
409+
is_call(pc_expr(frame, curr_pc)) && return true
410+
curr_pc += 1
411+
end
412+
return false
413+
end
414+
392415
"""
393416
ret = debug_command(recurse, frame, cmd, rootistoplevel=false; line=nothing)
394417
ret = debug_command(frame, cmd, rootistoplevel=false; line=nothing)
@@ -398,6 +421,7 @@ Perform one "debugger" command. The keyword arguments are not used for all debug
398421
399422
- `:n`: advance to the next line
400423
- `:s`: step into the next call
424+
- `:sl` step into the last call on the current line (e.g. steps into `f` if the line is `f(g(h(x)))`).
401425
- `:until`: advance the frame to line `line` if given, otherwise advance to the line after the current line
402426
- `:c`: continue execution until termination or reaching a breakpoint
403427
- `:finish`: finish the current frame and return to the parent
@@ -433,7 +457,12 @@ function debug_command(@nospecialize(recurse), frame::Frame, cmd::Symbol, rootis
433457
cmd === :n && return maybe_reset_frame!(recurse, frame, next_line!(recurse, frame, istoplevel), rootistoplevel)
434458
cmd === :se && return maybe_reset_frame!(recurse, frame, step_expr!(recurse, frame, istoplevel), rootistoplevel)
435459
cmd === :until && return maybe_reset_frame!(recurse, frame, until_line!(recurse, frame, line, istoplevel), rootistoplevel)
436-
460+
if cmd === :sl
461+
while more_calls_on_current_line(frame)
462+
next_call!(recurse, frame, istoplevel)
463+
end
464+
return debug_command(recurse, frame, :s, rootistoplevel; line)
465+
end
437466
enter_generated = false
438467
if cmd === :sg
439468
enter_generated = true

src/construct.jl

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ const compiled_modules = Set{Module}()
3535

3636
const junk_framedata = FrameData[] # to allow re-use of allocated memory (this is otherwise a bottleneck)
3737
const junk_frames = Frame[]
38-
debug_recycle() = false
38+
debug_mode() = false
3939
@noinline function _check_frame_not_in_junk(frame)
4040
@assert frame.framedata junk_framedata
4141
@assert frame junk_frames
4242
end
4343

4444
@inline function recycle(frame)
45-
debug_recycle() && _check_frame_not_in_junk(frame)
45+
debug_mode() && _check_frame_not_in_junk(frame)
4646
push!(junk_framedata, frame.framedata)
4747
push!(junk_frames, frame)
4848
end
@@ -435,10 +435,12 @@ function push_modex!(iter::ExprSplitter, mod::Module, ex::Expr)
435435
if ex.head === :toplevel || ex.head === :block
436436
# Issue #427
437437
modifies_scope = false
438-
for a in ex.args
439-
if isa(a, Expr) && a.head (:local, :global)
440-
modifies_scope = true
441-
break
438+
if ex.head === :block
439+
for a in ex.args
440+
if isa(a, Expr) && a.head (:local, :global)
441+
modifies_scope = true
442+
break
443+
end
442444
end
443445
end
444446
push!(iter.index, modifies_scope ? 0 : 1)

src/interpret.jl

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,9 +165,24 @@ function evaluate_foreigncall(@nospecialize(recurse), frame::Frame, call_expr::E
165165
if !isempty(data.sparams) && scope isa Method
166166
sig = scope.sig
167167
args[2] = instantiate_type_in_env(args[2], sig, data.sparams)
168-
args[3] = Core.svec(map(args[3]) do arg
169-
instantiate_type_in_env(arg, sig, data.sparams)
170-
end...)
168+
@static if VERSION < v"1.7.0"
169+
arg3 = args[3]
170+
if arg3 isa Core.SimpleVector
171+
args[3] = Core.svec(map(arg3) do arg
172+
instantiate_type_in_env(arg, sig, data.sparams)
173+
end...)
174+
else
175+
args[3] = instantiate_type_in_env(arg3, sig, data.sparams)
176+
args[4] = Core.svec(map(args[4]::Core.SimpleVector) do arg
177+
instantiate_type_in_env(arg, sig, data.sparams)
178+
end...)
179+
end
180+
else
181+
args[3] = instantiate_type_in_env(args[3], sig, data.sparams)
182+
args[4] = Core.svec(map(args[4]::Core.SimpleVector) do arg
183+
instantiate_type_in_env(arg, sig, data.sparams)
184+
end...)
185+
end
171186
end
172187
return Core.eval(moduleof(frame), Expr(head, args...))
173188
end
@@ -462,14 +477,14 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
462477
return (frame.pc = node.args[2]::Int)
463478
end
464479
elseif node.head === :enter
465-
rhs = node.args[1]
480+
rhs = node.args[1]::Int
466481
push!(data.exception_frames, rhs)
467482
elseif node.head === :leave
468483
for _ = 1:node.args[1]::Int
469484
pop!(data.exception_frames)
470485
end
471486
elseif node.head === :pop_exception
472-
n = lookup_var(frame, node.args[1])
487+
n = lookup_var(frame, node.args[1]::SSAValue)::Int
473488
deleteat!(data.exception_frames, n+1:length(data.exception_frames))
474489
elseif node.head === :return
475490
return nothing
@@ -489,7 +504,7 @@ function step_expr!(@nospecialize(recurse), frame, @nospecialize(node), istoplev
489504
end
490505
Core.eval(mod, Expr(:const, name))
491506
elseif node.head === :thunk
492-
newframe = Frame(moduleof(frame), node.args[1])
507+
newframe = Frame(moduleof(frame), node.args[1]::CodeInfo)
493508
if isa(recurse, Compiled)
494509
finish!(recurse, newframe, true)
495510
else

0 commit comments

Comments
 (0)