Анализ и интроспекция
Привязки модулей
Экспортированные имена для Module
доступны посредством функции names(m::Module)
, которая возвращает массив элементов Symbol
, представляющих экспортированные привязки. Функция names(m::Module, all = true)
возвращает символы для всех привязок в m
независимо от состояния экспорта.
Поля DataType
Имена полей DataType
можно запросить с помощью функции fieldnames
. Например, для следующего типа fieldnames(Point)
возвращает массив объектов Symbol
, представляющих имена полей:
julia> struct Point
x::Int
y
end
julia> fieldnames(Point)
(:x, :y)
Тип каждого поля в объекте Point
хранится в поле types
самой переменной Point
:
julia> Point.types
svec(Int64, Any)
Хотя переменная x
помечена как Int
, с y
в определении типа аннотация снята, поэтому y
по умолчанию имеет тип Any
.
Сами типы представлены в виде структуры DataType
:
julia> typeof(Point)
DataType
Обратите внимание, что функция fieldnames(DataType)
возвращает имена каждого поля самой структуры DataType
, и одним из этих полей является поле types
, показанное в примере выше.
Подтипы
Список непосредственных подтипов любого типа DataType
можно получить с помощью функции subtypes
. Например, у абстрактного типа DataType
AbstractFloat
четыре (конкретных) подтипа:
julia> subtypes(AbstractFloat)
4-element Vector{Any}:
BigFloat
Float16
Float32
Float64
В этот список также включается любой абстрактный подтип, но не дальнейшие подтипы; для изучения всего дерева типов можно использовать subtypes
рекурсивно.
Структура DataType
Внутреннее представление DataType
крайне важно при взаимодействии с кодом на C. Для его анализа доступно несколько функций. isbitstype(T::DataType)
возвращает true, если тип T
хранится с выравниванием, совместимым с C. fieldoffset(T::DataType, i::Integer)
возвращает смещение (в байтах) для поля i относительно начала типа.
Методы функции
Список методов любой универсальной функции можно получить с помощью функции methods
. Поиск методов, принимающих определенный тип, в таблице диспетчеризации методов можно выполнить с помощью функции methodswith
.
Расширение и понижение
Как говорилось в главе Метапрограммирование, функция macroexpand
возвращает интерполированную форму выражения (Expr
) без кавычек для данного макроса. Чтобы использовать macroexpand
, заключите в кавычки (quote
) сам блок выражения (иначе будет вычислен сам макрос, и будет передан его результат). Пример:
julia> macroexpand(@__MODULE__, :(@edit println("")) )
:(InteractiveUtils.edit(println, (Base.typesof)("")))
Функции Base.Meta.show_sexpr
и dump
служат для представления выражений в символьной форме и отображения информации о глубоко вложенном содержимом любого выражения.
Наконец, функция Meta.lower
возвращает пониженную (lowered
) форму любого выражения и особенно полезна для понимания того, как конструкции языка сопоставляются с примитивными операциями, такими как присваивание, ветвление и вызовы:
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
))))
Промежуточные и скомпилированные представления
Анализ пониженной формы функций требует выбора конкретных методов для отображения, поскольку универсальные функции могут иметь множество методов с различными сигнатурами типов. С этой целью доступно понижение кода для конкретного метода посредством code_lowered
, а также вариант с выводом типов посредством code_typed
. code_warntype
добавляет выделение к выходным данным code_typed
.
На уровне, более близком к машинному, промежуточное представление LLVM функции можно вывести с помощью code_llvm
, а скомпилированный машинный код доступен посредством code_native
(при этом инициируется JIT-компиляция или формирование кода для любых функций, которые не вызывались ранее).
Для удобства существуют версии приведенных выше функций в виде макросов, которые принимают стандартные вызовы функций и автоматически расширяют типы аргументов:
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
}
Дополнительные сведения см. в описании @code_lowered
, @code_typed
, @code_warntype
, @code_llvm
и @code_native
.
Вывод отладочной информации
Упомянутые выше функции и макросы принимают именованный аргумент debuginfo
, определяющий уровень выводимой отладочной информации.
julia> @code_typed debuginfo=:source +(1,1)
CodeInfo(
@ int.jl:53 within `+'
1 ─ %1 = Base.add_int(x, y)::Int64
└── return %1
) => Int64
Возможные значения debuginfo
: :none
, :source
и :default
. По умолчанию отладочная информация не выводится. Чтобы изменить это поведение, задайте Base.IRShow.default_debuginfo[] = :source
.