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.