Анализ и интроспекция
Привязки модулей
Экспортированные имена для 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.