Function reference

Running the interpreter

JuliaInterpreter.@interpretMacro
@interpret f(args; kwargs...)

Evaluate f on the specified arguments using the interpreter.

Example

julia> a = [1, 7]
2-element Vector{Int64}:
1
7

julia> sum(a)
8

julia> @interpret sum(a)
8
source

Frame creation

JuliaInterpreter.FrameMethod
frame = Frame(mod::Module, ex::Expr)

Construct a Frame to evaluate ex in module mod.

This constructor can error, for example if lowering ex results in an :error or :incomplete expression, or if it otherwise fails to return a :thunk.

source
JuliaInterpreter.ExprSplitterType
ExprSplitter(mod::Module, ex::Expr; lnn=nothing)

Create an iterable that returns individual expressions together with their module of evaluation. Optionally supply an initial LineNumberNode lnn.

Example

julia> expr = quote
public(x::Integer) = true
module Private
private(y::String) = false
end
const threshold = 0.1
end;

julia> for (mod, ex) in ExprSplitter(Main, expr)
@show mod ex
end
mod = Main
ex = quote
#= REPL[7]:2 =#
public(x::Integer) = begin
#= REPL[7]:2 =#
true
end
end
mod = Main.Private
ex = quote
#= REPL[7]:4 =#
private(y::String) = begin
#= REPL[7]:4 =#
false
end
end
mod = Main
ex = :($(Expr(:toplevel, :(()), :(const threshold = 0.1)))) Note that Main.Private was created for you so that its internal expressions could be evaluated. ExprSplitter will check to see whether the module already exists and if so return it rather than try to create a new module with the same name. In general each returned expression is a block with two parts: a LineNumberNode followed by a single expression. In some cases the returned expression may be :toplevel, as shown in the const declaration, but otherwise it will be a :block. World age, frame creation, and evaluation The primary purpose of ExprSplitter is to allow sequential return to top-level (e.g., the REPL) after evaluation of each expression. Returning to top-level allows the world age to update, and hence allows one to call methods and use types defined in earlier expressions in a block. For evaluation by JuliaInterpreter, the returned module/expression pairs can be passed directly to the Frame constructor. However, some expressions cannot be converted into Frames and may need special handling: julia> for (mod, ex) in ExprSplitter(Main, expr) if ex.head === :global # global declarations can't be lowered to a CodeInfo. # In this demo we choose to evaluate them, but you can do something else. Core.eval(mod, ex) continue end frame = Frame(mod, ex) debug_command(frame, :c, true) end julia> threshold 0.1 julia> public(3) true If you're parsing package code, ex might be a docstring-expression; you may wish to check for such expressions and take distinct actions. See Frame(mod::Module, ex::Expr) for more information about frame creation. source JuliaInterpreter.enter_callFunction frame = enter_call(f, args...; kwargs...) Build a Frame ready to execute f with the specified positional and keyword arguments. Example julia> mymethod(x) = x+1 mymethod (generic function with 1 method) julia> JuliaInterpreter.enter_call(mymethod, 1) Frame for mymethod(x) in Main at none:1 1* 1 1 ─ %1 = x + 1 2 1 └── return %1 x = 1 julia> mymethod(x::Vector{T}) where T = 1 mymethod (generic function with 2 methods) julia> JuliaInterpreter.enter_call(mymethod, [1.0, 2.0]) Frame for mymethod(x::Vector{T}) where T in Main at none:1 1* 1 1 ─ return 1 x = [1.0, 2.0] T = Float64 For a @generated function you can use enter_call((f, true), args...; kwargs...) to execute the generator of a @generated function, rather than the code that would be created by the generator. See enter_call_expr for a similar approach based on expressions. source JuliaInterpreter.enter_call_exprFunction frame = enter_call_expr(expr; enter_generated=false) Build a Frame ready to execute the expression expr. Set enter_generated=true if you want to execute the generator of a @generated function, rather than the code that would be created by the generator. Example julia> mymethod(x) = x+1 mymethod (generic function with 1 method) julia> JuliaInterpreter.enter_call_expr(:($mymethod(1)))
Frame for mymethod(x) in Main at none:1
1* 1  1 ─ %1 = x + 1
2  1  └──      return %1
x = 1

julia> mymethod(x::Vector{T}) where T = 1
mymethod (generic function with 2 methods)

julia> a = [1.0, 2.0]
2-element Vector{Float64}:
1.0
2.0

julia> JuliaInterpreter.enter_call_expr(:($mymethod($a)))
Frame for mymethod(x::Vector{T}) where T in Main at none:1
1* 1  1 ─     return 1
x = [1.0, 2.0]
T = Float64

See enter_call for a similar approach not based on expressions.

source
JuliaInterpreter.prepare_argsFunction
frun, allargs = prepare_args(fcall, fargs, kwargs)

Prepare the complete argument sequence for a call to fcall. fargs = [fcall, args...] is a list containing both fcall (the #self# slot in lowered code) and the positional arguments supplied to fcall. kwargs is a list of keyword arguments, supplied either as list of expressions :(kwname=kwval) or pairs :kwname=>kwval.

For non-keyword methods, frun === fcall, but for methods with keywords frun will be the keyword-sorter function for fcall.

Example

julia> mymethod(x) = 1
mymethod (generic function with 1 method)

julia> mymethod(x, y; verbose=false) = nothing
mymethod (generic function with 2 methods)

julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 15], ())
(mymethod, Any[mymethod, 15])

julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 1, 2], [:verbose=>true])
(var"#mymethod##kw"(), Any[var"#mymethod##kw"(), (verbose = true,), mymethod, 1, 2])
source
JuliaInterpreter.prepare_callFunction
framecode, frameargs, lenv, argtypes = prepare_call(f, allargs; enter_generated=false)

Prepare all the information needed to execute lowered code for f given arguments allargs. f and allargs are the outputs of prepare_args. For @generated methods, set enter_generated=true if you want to extract the lowered code of the generator itself.

On return framecode is the FrameCode of the method. frameargs contains the actual arguments needed for executing this frame (for generators, this will be the types of allargs); lenv is the "environment", i.e., the static parameters for f given allargs. argtypes is the Tuple-type for this specific call (equivalent to the signature of the MethodInstance).

Example

julia> mymethod(x::Vector{T}) where T = 1
mymethod (generic function with 1 method)

julia> framecode, frameargs, lenv, argtypes = JuliaInterpreter.prepare_call(mymethod, [mymethod, [1.0,2.0]]);

julia> framecode
1  1  1 ─     return 1

julia> frameargs
2-element Vector{Any}:
mymethod (generic function with 1 method)
[1.0, 2.0]

julia> lenv
svec(Float64)

julia> argtypes
Tuple{typeof(mymethod), Vector{Float64}}
source
JuliaInterpreter.get_call_framecodeFunction
framecode, lenv = get_call_framecode(fargs, parentframe::FrameCode, idx::Int)

Return the framecode and environment for a call specified by fargs = [f, args...] (see prepare_args). parentframecode is the caller, and idx is the program-counter index. If possible, framecode will be looked up from the local method tables of parentframe.

source
JuliaInterpreter.optimize!Function
optimize!(code::CodeInfo, mod::Module)

Perform minor optimizations on the lowered AST in code to reduce execution time of the interpreter. Currently it looks up GlobalRefs (for which it needs mod to know the scope in which this will run) and ensures that no statement includes nested :call expressions (splitting them out into multiple SSA-form statements if needed).

source

Frame execution

JuliaInterpreter.CompiledType

Compiled is a trait indicating that any :call expressions should be evaluated using Julia's normal compiled-code evaluation. The alternative is to pass stack=Frame[], which will cause all calls to be evaluated via the interpreter.

source
JuliaInterpreter.step_expr!Function
pc = step_expr!(recurse, frame, istoplevel=false)
pc = step_expr!(frame, istoplevel=false)

Execute the next statement in frame. pc is the new program counter, or nothing if execution terminates, or a BreakpointRef if execution hits a breakpoint.

recurse controls call evaluation; recurse = Compiled() evaluates :call expressions by normal dispatch. The default value recurse = finish_and_return! will use recursive interpretation.

If you are evaluating frame at module scope you should pass istoplevel=true.

source
JuliaInterpreter.finish!Function
pc = finish!(recurse, frame, istoplevel=false)
pc = finish!(frame, istoplevel=false)

Run frame until execution terminates. pc is either nothing (if execution terminates when it hits a return statement) or a reference to a breakpoint. In the latter case, leaf(frame) returns the frame in which it hit the breakpoint.

recurse controls call evaluation; recurse = Compiled() evaluates :call expressions by normal dispatch, whereas the default recurse = finish_and_return! uses recursive interpretation.

source
JuliaInterpreter.finish_stack!Function
ret = finish_stack!(recurse, frame, rootistoplevel=false)
ret = finish_stack!(frame, rootistoplevel=false)

Unwind the callees of frame, finishing each before returning to the caller. frame itself is also finished. rootistoplevel should be true if the root frame is top-level.

ret is typically the returned value. If execution hits a breakpoint, ret will be a reference to the breakpoint.

source
JuliaInterpreter.next_until!Function
pc = next_until!(predicate, recurse, frame, istoplevel=false)
pc = next_until!(predicate, frame, istoplevel=false)

Execute the current statement. Then step through statements of frame until the next statement satisfies predicate(frame). pc will be the index of the statement at which evaluation terminates, nothing (if the frame reached a return), or a BreakpointRef.

source
JuliaInterpreter.through_methoddef_or_done!Function
pc = through_methoddef_or_done!(recurse, frame)
pc = through_methoddef_or_done!(frame)

Runs frame at top level until it either finishes (e.g., hits a return statement) or defines a new method.

source
JuliaInterpreter.evaluate_call!Function
ret = evaluate_call!(Compiled(), frame::Frame, call_expr)
ret = evaluate_call!(recurse,    frame::Frame, call_expr)

Evaluate a :call expression call_expr in the context of frame. The first causes it to be executed using Julia's normal dispatch (compiled code), whereas the second recurses in via the interpreter. recurse has a default value of JuliaInterpreter.finish_and_return!.

source
JuliaInterpreter.evaluate_foreigncallFunction
ret = evaluate_foreigncall(recurse, frame::Frame, call_expr)

Evaluate a :foreigncall (from a ccall) statement callexpr in the context of frame.

source
JuliaInterpreter.maybe_evaluate_builtinFunction
ret = maybe_evaluate_builtin(frame, call_expr, expand::Bool)

If call_expr is to a builtin function, evaluate it, returning the result inside a Some wrapper. Otherwise, return call_expr.

If expand is true, Core._apply_iterate calls will be resolved as a call to the applied function.

source
JuliaInterpreter.next_call!Function
pc = next_call!(recurse, frame, istoplevel=false)
pc = next_call!(frame, istoplevel=false)

Execute the current statement. Continue stepping through frame until the next :return or :call expression.

source
JuliaInterpreter.maybe_next_call!Function
pc = maybe_next_call!(recurse, frame, istoplevel=false)
pc = maybe_next_call!(frame, istoplevel=false)

Return the current program counter of frame if it is a :return or :call expression. Otherwise, step through the statements of frame until the next :return or :call expression.

source
JuliaInterpreter.next_line!Function
pc = next_line!(recurse, frame, istoplevel=false)
pc = next_line!(frame, istoplevel=false)

Execute until reaching the first call of the next line of the source code. Upon return, pc is either the new program counter, nothing if a return is reached, or a BreakpointRef if it encountered a wrapper call. In the latter case, call leaf(frame) to obtain the new execution frame.

source
JuliaInterpreter.until_line!Function
pc = until_line!(recurse, frame, line=nothing istoplevel=false)
pc = until_line!(frame, line=nothing, istoplevel=false)

Execute until the current frame reaches a line greater than line. If line == nothing execute until the current frame reaches any line greater than the current line.

source
JuliaInterpreter.maybe_reset_frame!Function
ret = maybe_reset_frame!(recurse, frame, pc, rootistoplevel)

Perform a return to the caller, or descend to the level of a breakpoint. pc is the return state from the previous command (e.g., next_call! or similar). rootistoplevel should be true if the root frame is top-level.

ret will be nothing if we have just completed a top-level frame. Otherwise,

cframe, cpc = ret

where cframe is the frame from which execution should continue and cpc is the state of cframe (the program counter, a BreakpointRef, or nothing).

source
JuliaInterpreter.maybe_step_through_wrapper!Function
cframe = maybe_step_through_wrapper!(recurse, frame)
cframe = maybe_step_through_wrapper!(frame)

Return the new frame of execution, potentially stepping through "wrapper" methods like those that supply default positional arguments or handle keywords. cframe is the leaf frame from which execution should start.

source
JuliaInterpreter.maybe_step_through_kwprep!Function
frame = maybe_step_through_kwprep!(recurse, frame)
frame = maybe_step_through_kwprep!(frame)

If frame.pc points to the beginning of preparatory work for calling a keyword-argument function, advance forward until the actual call.

source
JuliaInterpreter.handle_errFunction
loc = handle_err(recurse, frame, err)

Deal with an error err that arose while evaluating frame. There are one of three behaviors:

• if frame catches the error, loc is the program counter at which to resume evaluation of frame;
• if frame doesn't catch the error, but break_on_error[] is true, loc is a BreakpointRef;
• otherwise, err gets rethrown.
source
JuliaInterpreter.debug_commandFunction
ret = debug_command(recurse, frame, cmd, rootistoplevel=false; line=nothing)
ret = debug_command(frame, cmd, rootistoplevel=false; line=nothing)

Perform one "debugger" command. The keyword arguments are not used for all debug commands. cmd should be one of:

• :n: advance to the next line
• :s: step into the next call
• :sl step into the last call on the current line (e.g. steps into f if the line is f(g(h(x)))).
• :sr step until the current function will return
• :until: advance the frame to line line if given, otherwise advance to the line after the current line
• :c: continue execution until termination or reaching a breakpoint
• :finish: finish the current frame and return to the parent

or one of the 'advanced' commands

• :nc: step forward to the next call
• :se: execute a single statement
• :si: execute a single statement, stepping in if it's a call
• :sg: step into the generator of a generated function

rootistoplevel and ret are as described for JuliaInterpreter.maybe_reset_frame!.

source

Breakpoints

JuliaInterpreter.@breakpointMacro
@breakpoint f(args...) condition=nothing
@breakpoint f(args...) line condition=nothing

Break upon entry, or at the specified line number, in the method called by f(args...). Optionally supply a condition expressed in terms of the arguments and internal variables of the method. If line is supplied, it must be a literal integer.

Example

Suppose a method mysum is defined as follows, where the numbers to the left are the line number in the file:

12 function mysum(A)
13     s = zero(eltype(A))
14     for a in A
15         s += a
16     end
17     return s
18 end

Then

@breakpoint mysum(A) 15 s>10

would cause execution of the loop to break whenever s>10.

source
JuliaInterpreter.breakpointFunction
breakpoint(f, [sig], [line], [condition])

Add a breakpoint to f with the specified argument types sig.¨ If sig is not given, the breakpoint will apply to all methods of f. If f is a method, the breakpoint will only apply to that method. Optionally specify an absolute line number line in the source file; the default is to break upon entry at the first line of the body. Without condition, the breakpoint will be triggered every time it is encountered; the second only if condition evaluates to true. condition should be written in terms of the arguments and local variables of f.

Example

function radius2(x, y)
return x^2 + y^2
end

breakpoint(radius2, Tuple{Int,Int}, :(y > x))
source
breakpoint(file, line, [condition])

Set a breakpoint in file at line. The argument file can be a filename, a partial path or absolute path. For example, file = foo.jl will match against all files with the name foo.jl, file = src/foo.jl will match against all paths containing src/foo.jl, e.g. both Foo/src/foo.jl and Bar/src/foo.jl. Absolute paths only matches against the file with that exact absolute path.

source
JuliaInterpreter.removeFunction
remove(bp::AbstractBreakpoint)

Remove (delete) breakpoint bp. Removed breakpoints cannot be re-enabled.

source
remove()

Remove all breakpoints.

source
JuliaInterpreter.break_onFunction
break_on(states...)

Turn on automatic breakpoints when any of the conditions described in states occurs. The supported states are:

• :error: trigger a breakpoint any time an uncaught exception is thrown
• :throw : trigger a breakpoint any time a throw is executed (even if it will eventually be caught)
source
JuliaInterpreter.dummy_breakpointFunction
bpref = dummy_breakpoint(recurse, frame::Frame, istoplevel)

Return a fake breakpoint. dummy_breakpoint can be useful as the recurse argument to evaluate_call! (or any of the higher-order commands) to ensure that you return immediately after stepping into a call.

source

Types

JuliaInterpreter.FrameType

Frame represents the current execution state in a particular call frame. Fields:

• framecode: the FrameCode for this frame.
• framedata: the FrameData for this frame.
• pc: the program counter (integer index of the next statment to be evaluated) for this frame.
• caller: the parent caller of this frame, or nothing.
• callee: the frame called by this one, or nothing.

The Base functions show_backtrace and display_error are overloaded such that show_backtrace(io::IO, frame::Frame) and display_error(io::IO, er, frame::Frame) shows a backtrace or error, respectively, in a similar way as to how Base shows them.

source
JuliaInterpreter.FrameCodeType

FrameCode holds static information about a method or toplevel code. One FrameCode can be shared by many calling Frames.

Important fields:

• scope: the Method or Module in which this frame is to be evaluated.
• src: the CodeInfo object storing (optimized) lowered source code.
• methodtables: a vector, each entry potentially stores a "local method table" for the corresponding :call expression in src (undefined entries correspond to statements that do not contain :call expressions).
• used: a BitSet storing the list of SSAValues that get referenced by later statements.
source
JuliaInterpreter.FrameDataType

FrameData holds the arguments, local variables, and intermediate execution state in a particular call frame.

Important fields:

• locals: a vector containing the input arguments and named local variables for this frame. The indexing corresponds to the names in the slotnames of the src. Use locals to extract the current value of local variables.
• ssavalues: a vector containing the Static Single Assignment values produced at the current state of execution.
• sparams: the static type parameters, e.g., for f(x::Vector{T}) where T this would store the value of T given the particular input x.
• exception_frames: a list of indexes to catch blocks for handling exceptions within the current frame. The active handler is the last one on the list.
• last_exception: the exception thrown by this frame or one of its callees.
source
JuliaInterpreter._INACTIVE_EXCEPTIONType
_INACTIVE_EXCEPTION

Represents a case where no exceptions are thrown yet. End users will not see this singleton type, otherwise it usually means there is missing error handling in the interpretation process.

source
JuliaInterpreter.BreakpointStateType
BreakpointState(isactive=true, condition=JuliaInterpreter.truecondition)

BreakpointState represents a breakpoint at a particular statement in a FrameCode. isactive indicates whether the breakpoint is currently enabled or disabled. condition is a function that accepts a single Frame, and condition(frame) must return either true or false. Execution will stop at a breakpoint only if isactive and condition(frame) both evaluate as true. The default condition always returns true.

To create these objects, see breakpoint.

source
JuliaInterpreter.BreakpointRefType
BreakpointRef(framecode, stmtidx)
BreakpointRef(framecode, stmtidx, err)

A reference to a breakpoint at a particular statement index stmtidx in framecode. If the break was due to an error, supply that as well.

Commands that execute complex control-flow (e.g., next_line!) may also return a BreakpointRef to indicate that the execution stack switched frames, even when no breakpoint has been set at the corresponding statement.

source
JuliaInterpreter.AbstractBreakpointType

AbstractBreakpoint is the abstract type that is the supertype for breakpoints. Currently, the concrete breakpoint types BreakpointSignature and BreakpointFileLocation exist.

Common fields shared by the concrete breakpoints:

• condition::Union{Nothing,Expr,Tuple{Module,Expr}}: the condition when the breakpoint applies . nothing means unconditionally, otherwise when the Expr (optionally in Module).
• enabled::Ref{Bool}: If the breakpoint is enabled (should not be directly modified, use enable() or disable()).
• instances::Vector{BreakpointRef}: All the BreakpointRef that the breakpoint has applied to.
• line::Int The line of the breakpoint (equal to 0 if unset).

See BreakpointSignature and BreakpointFileLocation for additional fields in the concrete types.

source
JuliaInterpreter.BreakpointSignatureType

A BreakpointSignature is a breakpoint that is set on methods or functions.

Fields:

• f::Union{Method, Function, Type}: A method or function that the breakpoint should apply to.
• sig::Union{Nothing, Type}: if f is a Method, always equal to nothing. Otherwise, contains the method signature as a tuple type for what methods the breakpoint should apply to.

For common fields shared by all breakpoints, see AbstractBreakpoint.

source
JuliaInterpreter.BreakpointFileLocationType

A BreakpointFileLocation is a breakpoint that is set on a line in a file.

Fields:

• path::String: The literal string that was used to create the breakpoint, e.g. "path/file.jl".
• abspath::String: The absolute path to the file when the breakpoint was created, e.g. "/Users/Someone/path/file.jl".

For common fields shared by all breakpoints, see AbstractBreakpoint.

source

Internal storage

JuliaInterpreter.genframedictConstant

genframedict[(method,argtypes)] returns the FrameCode for a @generated method method, for the particular argument types argtypes.

The framecodes stored in genframedict are for the code returned by the generator (i.e, what will run when you call the method on particular argument types); for the generator itself, its framecode would be stored in framedict.

source

Utilities

JuliaInterpreter.eval_codeFunction
eval_code(frame::Frame, code::Union{String, Expr})

Evaluate code in the context of frame, updating any local variables (including type parameters) that are reassigned in code, however, new local variables cannot be introduced.

julia> foo(x, y) = x + y;

julia> frame = JuliaInterpreter.enter_call(foo, 1, 3);

julia> JuliaInterpreter.eval_code(frame, "x + y")
4

julia> JuliaInterpreter.eval_code(frame, "x = 5");

julia> JuliaInterpreter.finish_and_return!(frame)
8

When variables are captured in closures (and thus gets wrapped in a Core.Box) they will be automatically unwrapped and rewrapped upon evaluating them:

julia> function capture()
x = 1
f = ()->(x = 2) # x captured in closure and is thus a Core.Box
f()
x
end;

julia> frame = JuliaInterpreter.enter_call(capture);

julia> JuliaInterpreter.step_expr!(frame);

julia> JuliaInterpreter.step_expr!(frame);

julia> JuliaInterpreter.locals(frame)
2-element Vector{JuliaInterpreter.Variable}:
#self# = capture
x = Core.Box(1)

julia> JuliaInterpreter.eval_code(frame, "x")
1

julia> JuliaInterpreter.eval_code(frame, "x = 2")
2

julia> JuliaInterpreter.locals(frame)
2-element Vector{JuliaInterpreter.Variable}:
#self# = capture
x = Core.Box(2)

"Special" values like SSA values and slots (shown in lowered code as e.g. %3 and @_4 respectively) can be evaluated using the syntax var"%3" and var"@_4" respectively.

source
JuliaInterpreter.@lookupMacro
rhs = @lookup(frame, node)
rhs = @lookup(mod, frame, node)

This macro looks up previously-computed values referenced as SSAValues, SlotNumbers, GlobalRefs, QuoteNode, sparam or exception reference expression. It will also lookup symbols in moduleof(frame); this can be supplied ahead-of-time via the 3-argument version. If none of the above apply, the value of node will be returned.

source
CodeTracking.whereisFunction
loc = whereis(frame, pc::Int=frame.pc; macro_caller=false)

Return the file and line number for frame at pc. If this cannot be determined, loc == nothing. Otherwise loc == (filepath, line).

By default, any statements expanded from a macro are attributed to the macro definition, but withmacro_caller=true you can obtain the location within the method that issued the macro.

source
JuliaInterpreter.VariableType

Variable is a struct representing a variable with an asigned value. By calling the function locals on a Frame a Vector of Variable's is returned.

Important fields:

• value::Any: the value of the local variable.
• name::Symbol: the name of the variable as given in the source code.
• isparam::Bool: if the variable is a type parameter, for example T in f(x::T) where {T} = x.
• is_captured_closure::Bool: if the variable has been captured by a closure
source
JuliaInterpreter.whichttFunction
method = whichtt(tt)

Like which except it operates on the complete tuple-type tt, and doesn't throw when there is no matching method.

source

Hooks

JuliaInterpreter.on_breakpoints_updatedFunction
on_breakpoints_updated(f)

Register a one-argument function to be called after any update to the set of all breakpoints. This includes their creation, deletion, enabling and disabling.

The function f should take two inputs:

• First argument is the function doing to update, this is provided to allow to dispatch on its type. It will be one:
• ::typeof(breakpoint) for the creation,
• ::typeof(remove) for the deletion.
• ::typeof(update_states) for disable/enable/toggleing
• Second argument is the breakpoint object that was changed.

If only desiring to handle some kinds of update, f should have fallback methods to do nothing in the general case.

Warning

This feature is experimental, and may be modified or removed in a minor release.

source
JuliaInterpreter.firehooksFunction
firehooks(hooked_fun, bp::AbstractBreakpoint)

Trigger all hooks that were registered with on_breakpoints_updated, passing them the hooked_fun and the bp. This should be called whenever the set of breakpoints is updated. hooked_fun is the function doing the update, and bp is the relevent breakpoint being updated after the update is applied.

Warning

This feature is experimental, and may be modified or removed in a minor release.

source