Function reference
Running the interpreter
JuliaInterpreter.@interpret — Macro@interpret [interp] f(args; kwargs...)Evaluate f on the specified arguments using the interpreter.
Example
julia> a = [1, 7];
julia> sum(a)
8
julia> @interpret sum(a)
8Interpreter
JuliaInterpreter.Interpreter — Typeabstract type Interpreter endAn interpreter that subtypes this type can implement its own evaluation strategies, by overloading the certain methods in JuliaInterpreter that are defined for this base type. The default behavior of Interpreter is same as that of RecursiveInterpreter, meaning it will recursively interpret all :call expressions.
JuliaInterpreter.RecursiveInterpreter — TypeRecursiveInterpreter <: InterpreterRecursiveInterpreter is an Interpreter that recursively interprets any :call expressions in the code being interpreted.
With this interpreter, code runs in fully interpreted mode; it will never be compiled for execution.
JuliaInterpreter.NonRecursiveInterpreter — TypeNonRecursiveInterpreter <: InterpreterNonRecursiveInterpreter is an Interpreter that evaluates any :call expressions in the code being interpreted using Julia's normal code execution engine with the native compiler.
JuliaInterpreter.Compiled is aliased to NonRecursiveInterpreter for backward compatibility.
JuliaInterpreter.Compiled — Typeconst Compiled = NonRecursiveInterpreterAs of JuliaInterpreter v0.10, Compiled is now an alias for NonRecursiveInterpreter. This remains for backward compatibility for packages using Compiled, and may be removed or redefined as a completely different type in v0.11 or later.
JuliaInterpreter.method_table — Functionmethod_table(interpreter::Interpreter) -> mt::Union{Nothing,MethodTable}Configures the method table used for method lookups performed by the interpreter. Uses the global method table by default.
Frame creation
JuliaInterpreter.Frame — Methodframe = 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.
JuliaInterpreter.ExprSplitter — TypeExprSplitter(mod::Module, ex::Expr; lnn=nothing)Given a module mod and a top-level expression ex in mod, create an iterable that returns individual expressions together with their module of evaluation. Optionally supply an initial LineNumberNode lnn.
Example
In a fresh session,
julia> expr = quote
public_fn(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_fn(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))))ExprSplitter created 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 preserve its parent's head (e.g., expr.head).
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_fn(3)
trueIf 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.
JuliaInterpreter.enter_call — Functionframe = 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;
julia> JuliaInterpreter.enter_call(mymethod, 1)
Frame for mymethod(x) @ Main none:1
1* 1 1 ─ %1 = x + 1
2 1 └── return %1
x = 1
julia> mymethod(x::Vector{T}) where T = 1;
julia> JuliaInterpreter.enter_call(mymethod, [1.0, 2.0])
Frame for mymethod(x::Vector{T}) where T @ Main none:1
1* 1 1 ─ return 1
x = [1.0, 2.0]
T = Float64For 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.
JuliaInterpreter.enter_call_expr — Functionframe = 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;
julia> JuliaInterpreter.enter_call_expr(:($mymethod(1)))
Frame for mymethod(x) @ Main none:1
1* 1 1 ─ %1 = x + 1
2 1 └── return %1
x = 1
julia> mymethod(x::Vector{T}) where T = 1;
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 @ Main none:1
1* 1 1 ─ return 1
x = [1.0, 2.0]
T = Float64See enter_call for a similar approach not based on expressions.
JuliaInterpreter.prepare_frame — Functionframe = prepare_frame(framecode::FrameCode, frameargs, lenv)Construct a new Frame for framecode, given lowered-code arguments frameargs and static parameters lenv. See JuliaInterpreter.prepare_call for information about how to prepare the inputs.
JuliaInterpreter.determine_method_for_expr — Functionframecode, frameargs, lenv, argtypes = determine_method_for_expr(expr; enter_generated = false)Prepare all the information needed to execute a particular :call expression expr. For example, try JuliaInterpreter.determine_method_for_expr(:($sum([1,2]))). See JuliaInterpreter.prepare_call for information about the outputs.
JuliaInterpreter.prepare_args — Functionfrun, 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;
julia> mymethod(x, y; verbose=false) = nothing;
julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 15], ())
(mymethod, Any[mymethod, 15])
julia> JuliaInterpreter.prepare_args(mymethod, [mymethod, 1, 2], [:verbose=>true])
(Core.kwcall, Any[Core.kwcall, (verbose = true,), mymethod, 1, 2])JuliaInterpreter.prepare_call — Functionframecode, 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;
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}}JuliaInterpreter.get_call_framecode — Functionframecode, 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.
JuliaInterpreter.optimize! — Functionoptimize!(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).
Frame traversal
JuliaInterpreter.root — Functionrframe = root(frame)Return the initial frame in the call stack.
JuliaInterpreter.leaf — Functionlframe = leaf(frame)Return the deepest callee in the call stack.
Frame execution
JuliaInterpreter.step_expr! — Functionpc = step_expr!(interp::Interpreter, 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.
interp controls call evaluation; interp = NonRecursiveInterpreter() evaluates :call expressions by normal dispatch. The default value interp = RecursiveInterpreter() will use recursive interpretation.
If you are evaluating frame at module scope you should pass istoplevel=true.
JuliaInterpreter.finish! — Functionpc = finish!(interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = finish!(frame::Frame, istoplevel::Bool=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.
interp controls call evaluation; interp = NonRecursiveInterpreter() evaluates :call expressions by normal dispatch, whereas the default interp = RecursiveInterpreter() uses recursive interpretation.
JuliaInterpreter.finish_and_return! — Functionret = finish_and_return!(interp::Interpreter, frame::Frame, istoplevel::Bool=false)
ret = finish_and_return!(frame::Frame, istoplevel::Bool=false)Call JuliaInterpreter.finish! and pass back the return value ret. If execution pauses at a breakpoint, ret is the reference to the breakpoint.
JuliaInterpreter.finish_stack! — Functionret = finish_stack!(interp::Interpreter, frame::Frame, rootistoplevel::Bool=false)
ret = finish_stack!(frame::Frame, rootistoplevel::Bool=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.
JuliaInterpreter.get_return — Functionret = get_return(interp, frame)Get the return value of frame. Throws an error if frame.pc does not point to a return expression. frame must have already been executed so that the return value has been computed (see, e.g., JuliaInterpreter.finish!).
JuliaInterpreter.next_until! — Functionpc = next_until!(predicate, interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = next_until!(predicate, frame::Frame, istoplevel::Bool=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.
JuliaInterpreter.maybe_next_until! — Functionpc = maybe_next_until!(predicate, interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = maybe_next_until!(predicate, frame::Frame, istoplevel::Bool=false)Like next_until! except checks predicate before executing the current statment.
JuliaInterpreter.through_methoddef_or_done! — Functionpc = through_methoddef_or_done!(interp::Interpreter, frame::Frame)
pc = through_methoddef_or_done!(frame::Frame)Runs frame at top level until it either finishes (e.g., hits a return statement) or defines a new method.
JuliaInterpreter.evaluate_call! — Functionret = evaluate_call!(interp::Interpreter, frame::Frame, call_expr::Expr, enter_generated::Bool=false)
ret = evaluate_call!(frame::Frame, call_expr::Expr, enter_generated::Bool=false)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. interp has a default value of RecursiveInterpreter.
JuliaInterpreter.evaluate_foreigncall — Functionret = evaluate_foreigncall(interp, frame::Frame, call_expr)Evaluate a :foreigncall (from a ccall) statement callexpr in the context of frame.
JuliaInterpreter.maybe_evaluate_builtin — Functionret = maybe_evaluate_builtin(interp::Interpreter, frame::Frame, call_expr::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.
JuliaInterpreter.next_call! — Functionpc = next_call!(interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = next_call!(frame::Frame, istoplevel::Bool=false)Execute the current statement. Continue stepping through frame until the next ReturnNode or :call expression.
JuliaInterpreter.maybe_next_call! — Functionpc = maybe_next_call!(interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = maybe_next_call!(frame::Frame, istoplevel::Bool=false)Return the current program counter of frame if it is a ReturnNode or :call expression. Otherwise, step through the statements of frame until the next ReturnNode or :call expression.
JuliaInterpreter.next_line! — Functionpc = next_line!(interp::Interpreter, frame::Frame, istoplevel::Bool=false)
pc = next_line!(frame::Frame, istoplevel::Bool=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.
JuliaInterpreter.until_line! — Functionpc = until_line!(interp::Interpreter, 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.
JuliaInterpreter.maybe_reset_frame! — Functionret = maybe_reset_frame!(interp::Interpreter, frame::Frame, pc, rootistoplevel::Bool)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 = retwhere cframe is the frame from which execution should continue and cpc is the state of cframe (the program counter, a BreakpointRef, or nothing).
JuliaInterpreter.maybe_step_through_wrapper! — Functioncframe = maybe_step_through_wrapper!(interp::Interpreter, frame::Frame)
cframe = maybe_step_through_wrapper!(frame::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.
JuliaInterpreter.maybe_step_through_kwprep! — Functionframe = maybe_step_through_kwprep!(interp::Interpreter, frame::Frame)
frame = maybe_step_through_kwprep!(frame::Frame)If frame.pc points to the beginning of preparatory work for calling a keyword-argument function, advance forward until the actual call.
JuliaInterpreter.handle_err — Functionloc = handle_err(interp, frame, err)Deal with an error err that arose while evaluating frame. There are one of three behaviors:
- if
framecatches the error,locis the program counter at which to resume evaluation offrame; - if
framedoesn't catch the error, butbreak_on_error[]istrue,locis aBreakpointRef; - otherwise,
errgets rethrown.
JuliaInterpreter.debug_command — Functionret = debug_command(interp::Interpreter, frame::Frame, cmd::Symbol, rootistoplevel::Bool=false;
line::Union{Nothing,Integer}=nothing)
ret = debug_command(frame::Frame, cmd::Symbol, rootistoplevel::Bool=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:slstep into the last call on the current line (e.g. steps intofif the line isf(g(h(x)))).:srstep until the current function will return:until: advance the frame to linelineif 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!.
Breakpoints
JuliaInterpreter.@breakpoint — Macro@breakpoint f(args...) condition=nothing
@breakpoint f(args...) line condition=nothingBreak 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 endThen
@breakpoint mysum(A) 15 s>10would cause execution of the loop to break whenever s>10.
JuliaInterpreter.@bp — Macro@bpInsert a breakpoint at a location in the source code.
JuliaInterpreter.breakpoint — Functionbreakpoint(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))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.
JuliaInterpreter.enable — Functionenable(bp::AbstractBreakpoint)Enable breakpoint bp.
enable()Enable all breakpoints.
JuliaInterpreter.disable — Functiondisable(bp::AbstractBreakpoint)Disable breakpoint bp. Disabled breakpoints can be re-enabled with enable.
disable()Disable all breakpoints.
JuliaInterpreter.remove — Functionremove(bp::AbstractBreakpoint)Remove (delete) breakpoint bp. Removed breakpoints cannot be re-enabled.
remove()Remove all breakpoints.
JuliaInterpreter.toggle — Functiontoggle(bp::AbstractBreakpoint)Toggle breakpoint bp.
JuliaInterpreter.break_on — Functionbreak_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)
JuliaInterpreter.break_off — Functionbreak_off(states...)Turn off automatic breakpoints when any of the conditions described in states occurs. See break_on for a description of valid states.
JuliaInterpreter.breakpoints — Functionbreakpoints()::Vector{AbstractBreakpoint}Return an array with all breakpoints.
JuliaInterpreter.BreakOnCall — TypeBreakOnCall <: Interpreter
bpref = @invoke evaluate_call!(BreakOnCall()::Interpreter, frame::Frame, istoplevel::Bool)An Interpreter returning a fake breakpoint. This can be useful as the interp argument to evaluate_call! (or any of the higher-order commands) to ensure that you return immediately after stepping into a call.
Types
JuliaInterpreter.Frame — TypeFrame represents the current execution state in a particular call frame. Fields:
framecode: theFrameCodefor this frame.framedata: theFrameDatafor 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, ornothing.callee: the frame called by this one, ornothing.
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.
JuliaInterpreter.FrameCode — TypeFrameCode holds static information about a method or toplevel code. One FrameCode can be shared by many calling Frames.
Important fields:
scope: theMethodorModulein which this frame is to be evaluated.src: theCodeInfoobject storing (optimized) lowered source code.methodtables: a vector, each entry potentially stores a "local method table" for the corresponding:callexpression insrc(undefined entries correspond to statements that do not contain:callexpressions).used: aBitSetstoring the list of SSAValues that get referenced by later statements.
JuliaInterpreter.FrameData — TypeFrameData 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 theslotnamesof the src. Uselocalsto 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., forf(x::Vector{T}) where Tthis would store the value ofTgiven the particular inputx.exception_frames: a list of indexes tocatchblocks for handling exceptions within the current frame. The active handler is the last one on the list.last_exception: the exceptionthrown by this frame or one of its callees.
JuliaInterpreter._INACTIVE_EXCEPTION — Type_INACTIVE_EXCEPTIONRepresents 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.
JuliaInterpreter.FrameInstance — TypeFrameInstance represents a method specialized for particular argument types.
Fields:
framecode: theFrameCodefor the method.sparam_vals: the static parameter values for the method.
JuliaInterpreter.BreakpointState — TypeBreakpointState(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.
JuliaInterpreter.BreakpointRef — TypeBreakpointRef(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.
JuliaInterpreter.AbstractBreakpoint — TypeAbstractBreakpoint 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 .nothingmeans unconditionally, otherwise when theExpr(optionally inModule).enabled::Ref{Bool}: If the breakpoint is enabled (should not be directly modified, useenable()ordisable()).instances::Vector{BreakpointRef}: All theBreakpointRefthat the breakpoint has applied to.line::IntThe line of the breakpoint (equal to 0 if unset).
See BreakpointSignature and BreakpointFileLocation for additional fields in the concrete types.
JuliaInterpreter.BreakpointSignature — TypeA 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}: iffis aMethod, always equal tonothing. 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.
JuliaInterpreter.BreakpointFileLocation — TypeA 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.
Internal storage
JuliaInterpreter.framedict — Constantframedict[method] returns the FrameCode for method. For @generated methods, see genframedict.
JuliaInterpreter.genframedict — Constantgenframedict[(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.
JuliaInterpreter.compiled_methods — Constantmeth ∈ compiled_methods indicates that meth should be run using NonRecursiveInterpreter rather than recursed into via the interpreter.
JuliaInterpreter.compiled_modules — Constantmod ∈ compiled_modules indicates that any method in mod should be run using NonRecursiveInterpreter rather than recursed into via the interpreter.
JuliaInterpreter.interpreted_methods — Constantmeth ∈ interpreted_methods indicates that meth should not be run using NonRecursiveInterpreter and recursed into via the interpreter. This takes precedence over compiled_methods and compiled_modules.
Utilities
JuliaInterpreter.eval_code — Functioneval_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)
8When 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.
JuliaInterpreter.lookup — Functionlookup([interp::Interpreter=RecursiveInterpreter()], frame::Frame, node)Looks up previously-computed values referenced as SSAValue, SlotNumber, GlobalRef, sparam or exception reference expression. It will also lookup Symbols as global reference in the context of moduleof(frame)::Module. If none of the above apply, the value of node will be returned.
JuliaInterpreter.is_wrapper_call — FunctionDetermine whether we are calling a function for which the current function is a wrapper (either because of optional arguments or because of keyword arguments).
JuliaInterpreter.is_doc_expr — Functionis_doc_expr(ex)Test whether expression ex is a @doc expression.
JuliaInterpreter.is_global_ref — Functionis_global_ref(g, mod, name)Tests whether g is equal to GlobalRef(mod, name).
CodeTracking.whereis — Functionloc = 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.
JuliaInterpreter.linenumber — Functionline = linenumber(framecode, pc)Return the "static" line number at statement index pc. The static line number is the location at the time the method was most recently defined. See CodeTracking.whereis for dynamic line information.
JuliaInterpreter.Variable — TypeVariable 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 exampleTinf(x::T) where {T} = x.is_captured_closure::Bool: if the variable has been captured by a closure
JuliaInterpreter.locals — Functionlocal_variables = locals(frame::Frame)::Vector{Variable}Return the local variables as a vector of Variable.
JuliaInterpreter.whichtt — Functionmethod = whichtt(tt, mt = nothing)Like which except it operates on the complete tuple-type tt, and doesn't throw when there is no matching method.
Hooks
JuliaInterpreter.on_breakpoints_updated — Functionon_breakpoints_updated(f)Register a two-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.
JuliaInterpreter.firehooks — Functionfirehooks(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.