反射 与 自我检查

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

模块绑定

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

DateType 字段

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

  1. julia> struct Point
  2. x::Int
  3. y
  4. end
  5. julia> fieldnames(Point)
  6. (:x, :y)

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

  1. julia> Point.types
  2. svec(Int64, Any)

虽然 x 被注释为 Int,但 y 在类型定义里没有注释,因此 y 默认为 Any 类型。

类型本身表示为一个叫做 DataType 的结构:

  1. julia> typeof(Point)
  2. DataType

注意 fieldnames(DataType) 给出了 DataType 本身的每个字段的名称,其中的一个字段是上面示例中提到的 types 字段。

子类型

任何 DataType直接子类型都可以通过使用 subtypes 来列出。 例如抽象 DataType AbstractFloat 有四个(具体的)子类型:

  1. julia> subtypes(AbstractFloat)
  2. 4-element Array{Any,1}:
  3. BigFloat
  4. Float16
  5. Float32
  6. Float64

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

DataType 布局

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

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

函数方法

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

扩展和更底层

Metaprogramming 部分讨论的那样, macroexpand 函数提供不带 quote 和 插值表达式 (Expr) 的形式。若要使用 macroexpand, quote 表达式代码块本身,(不然宏将被执行并替换为结果)。例如: 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:

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

函数 Base.Meta.show_sexprdump 用来展示 S-表达式样式预览 并且对任何表达式显示深层网络结构细节。

最终, Meta.lower 函数对任何表达式提供 底层 的形式,并且是令人感兴趣的是理解 语言的结构 映射到 原始的操作比如赋值、分支以及调用:

  1. julia> Meta.lower(@__MODULE__, :([1+2, sin(0.5)]) )
  2. :($(Expr(:thunk, CodeInfo(
  3. 1 %1 = 1 + 2
  4. %2 = sin(0.5)
  5. %3 = (Base.vect)(%1, %2)
  6. └── return %3
  7. ))))

中间表示和编译后表示

检查函数的底层形式 需要选择所要显示的特定方法,因为泛型函数可能会有许多具有不同类型签名的方法。为此, 用 code_lowered 可以指定代码底层中的方法。 并且可以用 code_typed 来进行类型推断。 code_warntype 增加 code_typed 输出的高亮。

更加接近于机器, 一个函数的 LLVM-IR 可以通过使用 code_llvm 打印出。 最终编译的机器码使用 code_native 查看(这将触发 之前未调用过的任何函数的 JIT 编译/代码生成)。

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

  1. julia> @code_llvm +(1,1)
  2. ; Function Attrs: sspreq
  3. define i64 @"julia_+_130862"(i64, i64) #0 {
  4. top:
  5. %2 = add i64 %1, %0, !dbg !8
  6. ret i64 %2, !dbg !8
  7. }

See @code_lowered, @code_typed, @code_warntype, @code_llvm, and @code_native.