Reflection and introspection
Julia предоставляет различные возможности отражения во время выполнения.
Module bindings
Общественные имена для Module
доступны с помощью names(m::Module)
, который вернет массив элементов Symbol
, представляющих публичные привязки. names(m::Module, all = true)
возвращает символы для всех привязок в m
, независимо от публичного статуса.
DataType fields
Имена полей 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
, наблюдаемое в приведенном выше примере.
Subtypes
Прямые подтипы любого DataType
могут быть перечислены с использованием subtypes
. Например, абстрактный DataType
AbstractFloat
имеет четыре (конкретных) подтипа:
julia> InteractiveUtils.subtypes(AbstractFloat)
5-element Vector{Any}:
BigFloat
Core.BFloat16
Float16
Float32
Float64
Любой абстрактный подтип также будет включен в этот список, но дальнейшие подтипы не будут; рекурсивное применение subtypes
может быть использовано для проверки полного дерева типов.
Обратите внимание, что subtypes
находится внутри InteractiveUtils
, но автоматически экспортируется при использовании REPL.
DataType layout
Внутреннее представление DataType
имеет критическое значение при взаимодействии с кодом на C, и доступно несколько функций для проверки этих деталей. isbitstype(T::DataType)
возвращает true, если T
хранится с совместимым с C выравниванием. fieldoffset(T::DataType, i::Integer)
возвращает (байтовый) смещение для поля i относительно начала типа.
Function methods
Методы любой обобщенной функции могут быть перечислены с помощью methods
. Таблицу диспетчеризации методов можно просмотреть для поиска методов, принимающих заданный тип, с помощью methodswith
.
Expansion and lowering
Как обсуждалось в разделе Metaprogramming, функция macroexpand
возвращает нецитированное и интерполированное выражение (Expr
) для данной макроса. Чтобы использовать macroexpand
, необходимо quote
сам блок выражения (в противном случае макрос будет оценен, и вместо этого будет передан результат!). Например:
julia> InteractiveUtils.macroexpand(@__MODULE__, :(@edit println("")) )
:(InteractiveUtils.edit(println, (Base.typesof)("")))
Функции Base.Meta.show_sexpr
и dump
используются для отображения представлений в стиле S-экспрессий и детализированных представлений с глубиной вложенности для любого выражения.
Наконец, функция 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
))))
Intermediate and compiled representations
Инспекция пониженной формы для функций требует выбора конкретного метода для отображения, поскольку универсальные функции могут иметь множество методов с разными сигнатурами типов. Для этой цели доступен код, специфичный для метода, с понижением, используя code_lowered
, а форма с выведенным типом доступна с использованием code_typed
. code_warntype
добавляет подсветку к выводу 4d61726b646f776e2e436f64652822222c2022636f64655f74797065642229_40726566
.
Ближе к машине промежуточное представление 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
.
Printing of debug information
Упомянутые функции и макросы принимают аргумент ключевого слова debuginfo
, который управляет уровнем выводимой отладочной информации.
julia> InteractiveUtils.@code_typed debuginfo=:source +(1,1)
CodeInfo(
@ int.jl:87 within `+`
1 ─ %1 = Base.add_int(x, y)::Int64
└── return %1
) => Int64
Возможные значения для debuginfo
следующие: :none
, :source
и :default
. По умолчанию информация отладки не выводится, но это можно изменить, установив Base.IRShow.default_debuginfo[] = :source
.