How to Use JET.jl

Note

JET's analysis entry points follow the naming conventions below:

  • report_xxx: runs analysis, and then prints the collected error points
  • analyze_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.

Warning

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_fileFunction
report_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 JET
  • res.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.

Tip

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)
Note

This function will enable the toplevel logger by default with the default logging level (see Logging Configurations for more details).

source
JET.report_and_watch_fileFunction
report_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 included files reachable from filename, and it will re-trigger analysis if there is code update detected in any of the included 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.

Note

Like report_file, this function will enable the toplevel logger by default with the default logging level (see Logging Configurations for more details).

source
JET.report_textFunction
report_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 JET
  • res.any_reported::Bool: indicates if there was any error point reported

The following optional positional arguments can be specified:

  • filename: the file containing text (if exists)
  • mod::Module: the module context in which the top-level execution will be simulated
source

Testing, Interactive Usage

There are utilities for checking JET analysis in a running Julia session like REPL or such.

Warning

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_callFunction
report_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.

source
JET.@report_callMacro
@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)
source
JET.analyze_callFunction
analyze_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 such
  • frame::InferenceFrame, which is the final state of the abstract interpretation
source
JET.@analyze_callMacro
@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)
source