How to Use JET.jl
JET's analysis entry points follow the naming conventions below:
report_xxx
: runs analysis, and then prints the collected error pointsanalyze_xxx
: just runs analysis, and returns the final state of the analysis
The report_xxx
entries are for general users, while analyze_xxx
is mainly for internal usages or debugging purposes.
Entry Points into Analysis
JET can analyze your "top-level" code. This means your can just give your Julia file or code to JET and get error reports. report_file
, report_and_watch_file
and report_text
are the main entry points for that.
JET will analyze your code "half-statically" – JET will selectively interpret "top-level definitions" (like a function definition) and try to simulate Julia's top-level code execution, while it tries to avoid executing any other parts of code like function calls, but analyze them using abstract interpretation (this is a part where JET "statically" analyzes your code). If you're interested in how JET selects "top-level definitions", please see JET.virtual_process
.
Because JET will actually interpret "top-level definitions" in your code, it certainly runs your code. So we should note that JET can cause some side effects from your code; for example JET will try to expand all the macros used in your code, and so the side effects involved with macro expansions will also happen in JET's analysis process.
JET.report_file
— Functionreport_file([io::IO = stdout],
filename::AbstractString,
mod::Module = Main;
toplevel_logger::Union{Nothing,IO} = IOContext(io, :JET_LOGGER_LEVEL => 0),
jetconfigs...) -> res::ReportResult
Analyzes filename
, prints the collected error reports to the io
stream, and finally returns res::ReportResult
res.included_files::Set{String}
: files analyzed by JETres.any_reported::Bool
: indicates if there was any error point reported
The following optional positional arguments can be specified:
mod::Module
: the module context in which the top-level execution will be simulated
This function will look for .JET.toml
configuration file in the directory of filename
, and search up the file tree until any .JET.toml
is (or isn't) found. When found, the configurations specified in the file will overwrite the given jetconfigs
. See Configuration File for more details.
When you want to analyze your package, but any file using it isn't available, the analyze_from_definitions
option can be useful (see ToplevelConfig
's analyze_from_definitions
option).
For example, JET can analyze JET itself like below:
# from the root directory of JET.jl
julia> report_file("src/JET";
analyze_from_definitions = true)
This function will enable the toplevel logger by default with the default logging level (see Logging Configurations for more details).
JET.report_and_watch_file
— Functionreport_and_watch_file([io::IO = stdout],
filename::AbstractString,
mod::Module = Main;
toplevel_logger::Union{Nothing,IO} = IOContext(io, :JET_LOGGER_LEVEL => 0),
jetconfigs...)
Watches filename
and keeps re-triggering analysis with report_file
on code update. JET will try to analyze all the include
d files reachable from filename
, and it will re-trigger analysis if there is code update detected in any of the include
d files.
This function internally uses Revise.jl to track code updates. Revise also offers possibilities to track changes in files that are not directly analyzed by JET, or even changes in Base
files. See Watch Configurations for more details.
Like report_file
, this function will look for .JET.toml
configuration file in the directory of filename
, and search up the file tree until any .JET.toml
is (or isn't) found. When found, the configurations specified in the file will overwrite the given jetconfigs
. See Configuration File for more details.
Like report_file
, this function will enable the toplevel logger by default with the default logging level (see Logging Configurations for more details).
JET.report_text
— Functionreport_text([io::IO = stdout],
text::AbstractString,
filename::AbstractString = "top-level",
mod::Module = Main;
jetconfigs...) -> res::ReportResult
Analyzes text
, prints the collected error reports to the io
stream, and finally returns res::ReportResult
res.included_files::Set{String}
: files analyzed by JETres.any_reported::Bool
: indicates if there was any error point reported
The following optional positional arguments can be specified:
filename
: the file containingtext
(if exists)mod::Module
: the module context in which the top-level execution will be simulated
Testing, Interactive Usage
There are utilities for checking JET analysis in a running Julia session like REPL or such.
They are supposed to be used for testing of JET or some quick check, and you're not expected to use JET in the same Julia session where you "seriously" run your code. This is because JET analysis itself will create code cache, which isn't necessarily same as the code that Julia's naitve compiler does. In particular, JET currently disables inlining for reasons and it can have a potent impact on the performance of your code in actual execution.
JET in the future will offer a more "proper" UI to render analysis result to users, whose process isn't supposed to interact with actual user's runtime in any way (I'm thinking of IDE/CI integration or such). Then these utilities will only be used for testing of JET itself, and not be really user-facing.
JET.report_call
— Functionreport_call(f, types = Tuple{}; jetconfigs...) -> result_type::Any
Analyzes the generic function call with the given type signature, and then prints collected error points to stdout
, and finally returns the result type of the call.
JET.@report_call
— Macro@report_call [jetconfigs...] f(args...)
Evaluates the arguments to the function call, determines its types, and then calls report_call
on the resulting expression. As with @code_typed
and its family, any of JET configurations can be given as the optional arguments like this:
# reports `rand(::Type{Bool})` with `aggressive_constant_propagation` configuration turned off
julia> @report_call aggressive_constant_propagation=false rand(Bool)
JET.analyze_call
— Functionanalyze_call(f, types = Tuple{}; jetconfigs...) -> (interp::JETInterpreter, frame::InferenceFrame)
Analyzes the generic function call with the given type signature, and returns:
interp::JETInterpreter
, which contains analyzed error reports and suchframe::InferenceFrame
, which is the final state of the abstract interpretation
JET.@analyze_call
— Macro@analyze_call [jetconfigs...] f(args...)
Evaluates the arguments to the function call, determines its types, and then calls analyze_call
on the resulting expression. As with @code_typed
and its family, any of JET configurations can be given as the optional arguments like this:
# analyzes `rand(::Type{Bool})` with `aggressive_constant_propagation` configuration turned off
julia> @analyze_call aggressive_constant_propagation=false rand(Bool)