反射 与 自我检查

Julia 提供了多种运行时的反射功能。

模块绑定

Module 导出的名称可用 names(m::Module) 获得,它会返回一个元素为 Symbol 的数组来表示模块导出的绑定。不管导出状态如何,names(m::Module, all = true) 返回 m 中所有绑定的符号。

DateType 字段

DataType 的所有字段名称可以使用 fieldnames 来获取。例如,对于下面给定的类型,fieldnames(Point) 会返回一个表示字段名称的 Symbol 元组:

julia> struct Point
           x::Int
           y
       end

julia> fieldnames(Point)
(:x, :y)

Point 对象中每个字段的类型存储在 Point 本身的 types 变量中:

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 Array{Any,1}:
 BigFloat
 Float16
 Float32
 Float64

任何抽象子类型也包括此列表中,但子类型的子类型不在其中。递归使用 subtypes 可以遍历出整个类型树。

DataType 布局

用 C 代码接口时,DataType 的内部表现非常重要。有几个函数可以检查这些细节。

isbits(T::DataType) 如果 T 类型是以 C 兼容的对齐方式存储,则为 true。 fieldoffset(T::DataType, i::Integer) 返回字段 i 相对于类型开始的 (字节) 偏移量。

函数方法

任何泛型函数的方法都可以使用 methods 来列出。用 methodswith 搜索 方法调度表 来查找 接收给定类型的方法。

扩展和更底层

As discussed in the Metaprogramming section, the macroexpand function gives the unquoted and interpolated expression (Expr) form for a given macro. To use macroexpand, quote the expression block itself (otherwise, the macro will be evaluated and the result will be passed instead!). For example:

julia> macroexpand(@__MODULE__, :(@edit println("")) )
:(InteractiveUtils.edit(println, (Base.typesof)("")))

The functions Base.Meta.show_sexpr and dump are used to display S-expr style views and depth-nested detail views for any expression.

Finally, the Meta.lower function gives the lowered form of any expression and is of particular interest for understanding how language constructs map to primitive operations such as assignments, branches, and calls:

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-IR 可以通过使用 code_llvm 打印出。 最终编译的机器码使用 code_native 查看(这将触发 之前未调用过的任何函数的 JIT 编译/代码生成)。

为方便起见,上述函数有 宏的版本,它们接受标准函数调用并自动展开参数类型:

julia> @code_llvm +(1,1)

define i64 @"julia_+_130862"(i64, i64) {
top:
    %2 = add i64 %1, %0
    ret i64 %2
}

For more informations see @code_lowered, @code_typed, @code_warntype, @code_llvm, and @code_native.

Printing of debug information

The aforementioned functions and macros take the keyword argument debuginfo that controls the level debug information printed.

julia> @code_typed debuginfo=:source +(1,1)
CodeInfo(
    @ int.jl:53 within `+'
1 ─ %1 = Base.add_int(x, y)::Int64
└──      return %1
) => Int64

Possible values for debuginfo are: :none, :source, and:default. Per default debug information is not printed, but that can be changed by setting Base.IRShow.default_debuginfo[] = :source.