@@ -11,7 +11,8 @@ using UUIDs
11
11
using Random. DSFMT
12
12
using InteractiveUtils
13
13
14
- export @enter , @make_stack , @interpret , Compiled, JuliaStackFrame
14
+ export @enter , @make_stack , @interpret , Compiled, JuliaStackFrame,
15
+ Breakpoints, breakpoint, @breakpoint , breakpoints, enable, disable, remove
15
16
16
17
module CompiledCalls
17
18
# This module is for handling intrinsics that must be compiled (llvmcall)
@@ -40,14 +41,39 @@ isless(x::JuliaProgramCounter, y::Integer) = isless(x.next_stmt, y)
40
41
41
42
Base. show (io:: IO , pc:: JuliaProgramCounter ) = print (io, " JuliaProgramCounter(" , pc. next_stmt, ' )' )
42
43
44
+ # Breakpoint support
45
+ truecondition (frame) = true
46
+ falsecondition (frame) = false
47
+ const break_on_error = Ref (false )
48
+
49
+ """
50
+ BreakpointState(isactive=true, condition=JuliaInterpreter.truecondition)
51
+
52
+ `BreakpointState` represents a breakpoint at a particular statement in
53
+ a `JuliaFrameCode`. `isactive` indicates whether the breakpoint is currently
54
+ [`enable`](@ref)d or [`disable`](@ref)d. `condition` is a function that accepts
55
+ a single `JuliaStackFrame`, and `condition(frame)` must return either
56
+ `true` or `false`. Execution will stop at a breakpoint only if `isactive`
57
+ and `condition(frame)` both evaluate as `true`. The default `condition` always
58
+ returns `true`.
59
+
60
+ To create these objects, see [`breakpoint`](@ref).
61
+ """
62
+ struct BreakpointState
63
+ isactive:: Bool
64
+ condition:: Function
65
+ end
66
+ BreakpointState (isactive:: Bool ) = BreakpointState (isactive, truecondition)
67
+ BreakpointState () = BreakpointState (true )
68
+
43
69
# A type used transiently in renumbering CodeInfo SSAValues (to distinguish a new SSAValue from an old one)
44
70
struct NewSSAValue
45
71
id:: Int
46
72
end
47
73
48
74
"""
49
75
`JuliaFrameCode` holds static information about a method or toplevel code.
50
- One `JuliaFrameCode` can be shared by many `JuliaFrameState ` calling frames.
76
+ One `JuliaFrameCode` can be shared by many `JuliaStackFrame ` calling frames.
51
77
52
78
Important fields:
53
79
- `scope`: the `Method` or `Module` in which this frame is to be evaluated
@@ -61,6 +87,7 @@ struct JuliaFrameCode
61
87
scope:: Union{Method,Module}
62
88
code:: CodeInfo
63
89
methodtables:: Vector{Union{Compiled,TypeMapEntry}} # line-by-line method tables for generic-function :call Exprs
90
+ breakpoints:: Vector{BreakpointState}
64
91
used:: BitSet
65
92
wrapper:: Bool
66
93
generator:: Bool
@@ -69,7 +96,7 @@ struct JuliaFrameCode
69
96
end
70
97
71
98
function JuliaFrameCode (frame:: JuliaFrameCode ; wrapper = frame. wrapper, generator= frame. generator, fullpath= frame. fullpath)
72
- JuliaFrameCode (frame. scope, frame. code, frame. methodtables, frame. used,
99
+ JuliaFrameCode (frame. scope, frame. code, frame. methodtables, frame. breakpoints, frame . used,
73
100
wrapper, generator, fullpath)
74
101
end
75
102
@@ -80,8 +107,9 @@ function JuliaFrameCode(scope, code::CodeInfo; wrapper=false, generator=false, f
80
107
code = copy_codeinfo (code)
81
108
methodtables = Vector {Union{Compiled,TypeMapEntry}} (undef, length (code. code))
82
109
end
110
+ breakpoints = Vector {BreakpointState} (undef, length (code. code))
83
111
used = find_used (code)
84
- return JuliaFrameCode (scope, code, methodtables, used, wrapper, generator, fullpath)
112
+ return JuliaFrameCode (scope, code, methodtables, breakpoints, used, wrapper, generator, fullpath)
85
113
end
86
114
87
115
"""
@@ -211,6 +239,16 @@ function is_generated(meth)
211
239
isdefined (meth, :generator )
212
240
end
213
241
242
+ function sparam_syms (meth:: Method )
243
+ s = Symbol[]
244
+ sig = meth. sig
245
+ while sig isa UnionAll
246
+ push! (s, Symbol (sig. var. name))
247
+ sig = sig. body
248
+ end
249
+ return s
250
+ end
251
+
214
252
function namedtuple (kwargs)
215
253
names, types, vals = Symbol[], [], []
216
254
for pr in kwargs
@@ -269,6 +307,58 @@ function prepare_args(@nospecialize(f), allargs, kwargs)
269
307
return f, allargs
270
308
end
271
309
310
+ function prepare_framecode (method:: Method , argtypes; enter_generated= false )
311
+ sig = method. sig
312
+ isa (method, TypeMapEntry) && (method = method. func)
313
+ if method. module == Core. Compiler || method. module == Base. Threads || method ∈ compiled_methods
314
+ return Compiled ()
315
+ end
316
+ # Get static parameters
317
+ (ti, lenv:: SimpleVector ) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
318
+ argtypes, sig):: SimpleVector
319
+ enter_generated &= is_generated (method)
320
+ if is_generated (method) && ! enter_generated
321
+ framecode = get (genframedict, (method, argtypes), nothing )
322
+ else
323
+ framecode = get (framedict, method, nothing )
324
+ end
325
+ if framecode === nothing
326
+ if is_generated (method) && ! enter_generated
327
+ # If we're stepping into a staged function, we need to use
328
+ # the specialization, rather than stepping through the
329
+ # unspecialized method.
330
+ code = Core. Compiler. get_staged (Core. Compiler. code_for_method (method, argtypes, lenv, typemax (UInt), false ))
331
+ code === nothing && return nothing
332
+ generator = false
333
+ else
334
+ if is_generated (method)
335
+ code = get_source (method. generator)
336
+ generator = true
337
+ else
338
+ code = get_source (method)
339
+ generator = false
340
+ end
341
+ end
342
+ framecode = JuliaFrameCode (method, code; generator= generator)
343
+ if is_generated (method) && ! enter_generated
344
+ genframedict[(method, argtypes)] = framecode
345
+ else
346
+ framedict[method] = framecode
347
+ end
348
+ end
349
+ return framecode, lenv
350
+ end
351
+
352
+ function get_framecode (method)
353
+ framecode = get (framedict, method, nothing )
354
+ if framecode === nothing
355
+ code = get_source (method)
356
+ framecode = JuliaFrameCode (method, code; generator= false )
357
+ framedict[method] = framecode
358
+ end
359
+ return framecode
360
+ end
361
+
272
362
function whichtt (tt)
273
363
m = ccall (:jl_gf_invoke_lookup , Any, (Any, UInt), tt, typemax (UInt))
274
364
m === nothing && return nothing
@@ -328,47 +418,17 @@ function prepare_call(@nospecialize(f), allargs; enter_generated = false)
328
418
f (allargs[2 : end ]. .. )
329
419
end
330
420
args = allargs
331
- sig = method. sig
332
- isa (method, TypeMapEntry) && (method = method. func)
333
- if method. module == Core. Compiler || method. module == Base. Threads || method ∈ compiled_methods
334
- return Compiled ()
335
- end
336
- # Get static parameters
337
- (ti, lenv:: SimpleVector ) = ccall (:jl_type_intersection_with_env , Any, (Any, Any),
338
- argtypes, sig):: SimpleVector
339
- enter_generated &= is_generated (method)
340
- if is_generated (method) && ! enter_generated
341
- framecode = get (genframedict, (method, argtypes), nothing )
342
- else
343
- framecode = get (framedict, method, nothing )
421
+ ret = prepare_framecode (method, argtypes; enter_generated= enter_generated)
422
+ # Exceptional returns
423
+ if ret === nothing
424
+ # The generator threw an error. Let's generate the same error by calling it.
425
+ f (allargs[2 : end ]. .. )
344
426
end
345
- if framecode === nothing
346
- if is_generated (method) && ! enter_generated
347
- # If we're stepping into a staged function, we need to use
348
- # the specialization, rather than stepping through the
349
- # unspecialized method.
350
- code = Core. Compiler. get_staged (Core. Compiler. code_for_method (method, argtypes, lenv, typemax (UInt), false ))
351
- generator = false
352
- if code === nothing
353
- # The generator threw an error. Let's generate the same error by calling it.
354
- f (allargs[2 : end ]. .. )
355
- end
356
- else
357
- if is_generated (method)
358
- args = Any[_Typeof (a) for a in args]
359
- code = get_source (method. generator)
360
- generator = true
361
- else
362
- code = get_source (method)
363
- generator = false
364
- end
365
- end
366
- framecode = JuliaFrameCode (method, code; generator= generator)
367
- if is_generated (method) && ! enter_generated
368
- genframedict[(method, argtypes)] = framecode
369
- else
370
- framedict[method] = framecode
371
- end
427
+ isa (ret, Compiled) && return ret
428
+ # Typical return
429
+ framecode, lenv = ret
430
+ if is_generated (method) && enter_generated
431
+ args = Any[_Typeof (a) for a in args]
372
432
end
373
433
return framecode, args, lenv, argtypes
374
434
end
@@ -795,6 +855,7 @@ function prepare_locals(framecode, argvals::Vector{Any})
795
855
pc = Ref (JuliaProgramCounter (1 ))
796
856
end
797
857
for i = 1 : meth. nargs
858
+ last_reference[framecode. code. slotnames[i]] = i
798
859
if meth. isva && i == meth. nargs
799
860
locals[i] = nargs < i ? Some {Any} (()) : (let i= i; Some {Any} (ntuple (k-> argvals[i+ k- 1 ], nargs- i+ 1 )); end )
800
861
break
@@ -1060,8 +1121,10 @@ macro interpret(arg)
1060
1121
if frame === nothing
1061
1122
return eval (Expr (:call , map (QuoteNode, theargs)... ))
1062
1123
end
1063
- empty! (framedict) # start fresh each time; kind of like bumping the world age at the REPL prompt
1064
- empty! (genframedict)
1124
+ if shouldbreak (frame, 1 )
1125
+ push! (stack, frame)
1126
+ return stack, BreakpointRef (frame. code, 1 )
1127
+ end
1065
1128
finish_and_return! (stack, frame)
1066
1129
end
1067
1130
end
@@ -1085,6 +1148,10 @@ function __init__()
1085
1148
set_compiled_methods ()
1086
1149
end
1087
1150
1151
+ include (" breakpoints.jl" )
1152
+ using . Breakpoints
1153
+ using . Breakpoints: shouldbreak, BreakpointRef
1154
+
1088
1155
include (" precompile.jl" )
1089
1156
_precompile_ ()
1090
1157
0 commit comments