Function reference
Running the interpreter
JuliaInterpreter.@interpret
— Macro@interpret 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)
8
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 Frame
s 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)
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.
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 = 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.
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 = Float64
See 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 GlobalRef
s (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.Compiled
— TypeCompiled
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.
JuliaInterpreter.step_expr!
— Functionpc = 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
.
JuliaInterpreter.finish!
— Functionpc = 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.
JuliaInterpreter.finish_and_return!
— Functionret = finish_and_return!(recurse, frame, istoplevel::Bool=false)
ret = finish_and_return!(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!(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.
JuliaInterpreter.get_return
— Functionret = get_return(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, 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
.
JuliaInterpreter.maybe_next_until!
— Functionpc = maybe_next_until!(predicate, recurse, frame, istoplevel=false)
pc = maybe_next_until!(predicate, frame, istoplevel=false)
Like next_until!
except checks predicate
before executing the current statment.
JuliaInterpreter.through_methoddef_or_done!
— Functionpc = 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.
JuliaInterpreter.evaluate_call!
— Functionret = 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!
.
JuliaInterpreter.evaluate_foreigncall
— Functionret = evaluate_foreigncall(recurse, 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(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.
JuliaInterpreter.next_call!
— Functionpc = next_call!(recurse, frame, istoplevel=false)
pc = next_call!(frame, istoplevel=false)
Execute the current statement. Continue stepping through frame
until the next ReturnNode
or :call
expression.
JuliaInterpreter.maybe_next_call!
— Functionpc = 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 ReturnNode
or :call
expression. Otherwise, step through the statements of frame
until the next ReturnNode
or :call
expression.
JuliaInterpreter.next_line!
— Functionpc = 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.
JuliaInterpreter.until_line!
— Functionpc = 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.
JuliaInterpreter.maybe_reset_frame!
— Functionret = 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
).
JuliaInterpreter.maybe_step_through_wrapper!
— Functioncframe = 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.
JuliaInterpreter.maybe_step_through_kwprep!
— Functionframe = 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.
JuliaInterpreter.handle_err
— Functionloc = 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 offrame
; - if
frame
doesn't catch the error, butbreak_on_error[]
istrue
,loc
is aBreakpointRef
; - otherwise,
err
gets rethrown.
JuliaInterpreter.debug_command
— Functionret = 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 intof
if the line isf(g(h(x)))
).:sr
step until the current function will return:until
: advance the frame to lineline
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!
.
Breakpoints
JuliaInterpreter.@breakpoint
— Macro@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
.
JuliaInterpreter.@bp
— Macro@bp
Insert 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.dummy_breakpoint
— Functionbpref = 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.
Types
JuliaInterpreter.Frame
— TypeFrame
represents the current execution state in a particular call frame. Fields:
framecode
: theFrameCode
for this frame.framedata
: theFrameData
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, 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 Frame
s.
Important fields:
scope
: theMethod
orModule
in which this frame is to be evaluated.src
: theCodeInfo
object storing (optimized) lowered source code.methodtables
: a vector, each entry potentially stores a "local method table" for the corresponding:call
expression insrc
(undefined entries correspond to statements that do not contain:call
expressions).used
: aBitSet
storing 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 theslotnames
of the src. Uselocals
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., forf(x::Vector{T}) where T
this would store the value ofT
given the particular inputx
.exception_frames
: a list of indexes tocatch
blocks for handling exceptions within the current frame. The active handler is the last one on the list.last_exception
: the exceptionthrow
n by this frame or one of its callees.
JuliaInterpreter._INACTIVE_EXCEPTION
— Type_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.
JuliaInterpreter.FrameInstance
— TypeFrameInstance
represents a method specialized for particular argument types.
Fields:
framecode
: theFrameCode
for 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 enable
d or disable
d. 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 .nothing
means 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 theBreakpointRef
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.
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}
: iff
is 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 Compiled
rather than recursed into via the interpreter.
JuliaInterpreter.compiled_modules
— Constantmod ∈ compiled_modules
indicates that any method in mod
should be run using Compiled
rather than recursed into via the interpreter.
JuliaInterpreter.interpreted_methods
— Constantmeth ∈ interpreted_methods
indicates that meth
should not be run using Compiled
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)
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.
JuliaInterpreter.@lookup
— Macrorhs = @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.
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 exampleT
inf(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)
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.
This feature is experimental, and may be modified or removed in a minor release.
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.
This feature is experimental, and may be modified or removed in a minor release.