Документация Engee

Анализ и интроспекция

Julia предоставляет различные возможности анализа во время выполнения.

Привязки модулей

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