Analysis and introspection
Module bindings
The public names for Module
are available through the function names(m::Module)
, which returns an array of elements Symbol
representing public bindings. The names(m::Module, all = true)
function returns characters for all bindings in m
, regardless of the public availability status.
DataType fields
The names of the DataType
fields can be requested using the function fieldnames
. For example, for the following type, fieldnames(Point)
returns an array of objects Symbol
representing field names:
julia> struct Point
x::Int
y
end
julia> fieldnames(Point)
(:x, :y)
The type of each field in the Point
object is stored in the types
field of the Point
variable itself:
julia> Point.types
svec(Int64, Any)
Although the variable x
is marked as Int
, the annotation is removed from y
in the type definition, so y
has the type Any
by default.
The types themselves are represented as a DataType
structure:
julia> typeof(Point)
DataType
Note that the fieldnames(DataType)
function returns the names of each field of the DataType
structure itself, and one of these fields is the types
field shown in the example above.
Subtypes
A list of mediate subtypes of any type of DataType
can be obtained using the function subtypes
. For example, for the abstract type DataType
AbstractFloat
four (specific) subtypes:
julia> InteractiveUtils.subtypes(AbstractFloat)
5-element Vector{Any}:
BigFloat
Core.BFloat16
Float16
Float32
Float64
This list also includes any abstract subtype, but not further subtypes; to explore the entire type tree, you can use subtypes
recursively.
Please note that subtypes'
is located inside `InteractiveUtils, but is automatically exported when using REPL.
The DataType structure
The internal representation of DataType' is extremely important when interacting with C code. Several functions are available for its analysis. `isbitstype(T::DataType)
returns true if the type T
is stored with an alignment compatible with C. fieldoffset(T::DataType, i::Integer)
returns the offset (in bytes) for the i field relative to the beginning of the type.
Function methods
The list of methods of any universal function can be obtained using the function methods
. You can search for methods that accept a specific type in the method dispatch table using the function methodswith
.
Expansion and downgrade
As mentioned in the chapter Metaprogramming, function 'macroexpand` returns the interpolated form of the expression (Expr
) without quotes for this macro. To use `macroexpand', enclose the expression block itself in quotation marks (`quote') (otherwise, the macro itself will be evaluated and its result will be transmitted). For example:
julia> InteractiveUtils.macroexpand(@__MODULE__, :(@edit println("")) )
:(InteractiveUtils.edit(println, (Base.typesof)("")))
The functions Base.Meta.show_sexpr
and dump
are used to represent expressions in symbolic form and display information about the deeply embedded contents of any expression.
Finally, the function Meta.lower
returns the lowered
form of any expression and is especially useful for understanding how language constructs map to primitive operations such as assignment, branching, and calls.:
julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] ))
:($(Expr(:thunk, CodeInfo(
@ none within `top-level scope`
1 ─ %1 = 1 + 2
│ %2 = sin(0.5)
│ %3 = Base.vect(%1, %2)
└── return %3
))))
Intermediate and compiled representations
Analysis of the reduced form of functions requires the selection of specific methods for displaying, since universal functions can have many methods with different type signatures. For this purpose, it is possible to downgrade the code for a specific method by code_lowered
, as well as a variant with type inference by code_typed
. code_warntype
adds highlighting to the output data code_typed
.
At a level closer to the machine level, an intermediate representation of the LLVM function can be derived using code_llvm'
, and the compiled machine code is available via `code_native (this initiates JIT compilation or code generation for any functions that have not been called before).
For convenience, there are versions of the above functions in the form of macros that accept standard function calls and automatically expand argument types.:
julia> @code_llvm +(1,1)
; @ int.jl:87 within `+`
; Function Attrs: sspstrong uwtable
define i64 @"julia_+_476"(i64 signext %0, i64 signext %1) #0 {
top:
%2 = add i64 %1, %0
ret i64 %2
}
For more information, see the description @code_lowered
, @code_typed
, @code_warntype
, @code_llvm
and @code_native
.
Debugging information output
The functions and macros mentioned above take the named debuginfo
argument, which defines the level of debugging information output.
julia> InteractiveUtils.@code_typed debuginfo=:source +(1,1)
CodeInfo(
@ int.jl:87 within `+`
1 ─ %1 = Base.add_int(x, y)::Int64
└── return %1
) => Int64
Possible values for debuginfo
are: :none', `:source
, and :default'. By default, debugging information is not displayed. To change this behavior, set `Base.IRShow.default_debuginfo[] = :source
.