How does one programmatically examine the contents of a function?

I would like to examine a user defined function to create a variable

example function

function foo(var)
  out = var.a.a1 .+ var.b.c3
  return out
end

in this case I would like to examine the code to identify that var has keys “a” and “b” and create the tuple (“a”,“b”)

julia> e = :(function foo(var)
         out = var.a.a1 .+ var.b.c3
         return out
       end)
:(function foo(var)
      #= REPL[47]:1 =#
      #= REPL[47]:2 =#
      out = var.a.a1 .+ var.b.c3
      #= REPL[47]:3 =#
      return out
  end)

julia> sum_expr = e.args[2].args[3].args[2]
:(var.a.a1 .+ var.b.c3)

julia> sum_expr.args
3-element Vector{Any}:
 :.+
 :(var.a.a1)
 :(var.b.c3)

julia> sum_expr.args[2].args[1].args[2].value
:a

julia> sum_expr.args[3].args[1].args[2].value
:b

julia> (sum_expr.args[2].args[1].args[2].value, sum_expr.args[3].args[1].args[2].value)
(:a, :b)

julia> string.((sum_expr.args[2].args[1].args[2].value, sum_expr.args[3].args[1].args[2].value))
("a", "b")

We could turn it into a macro as follows.

julia> macro metafoo(e)
           sum_expr = e.args[2].args[3].args[2]
           string.((sum_expr.args[2].args[1].args[2].value, sum_expr.args[3].args[1].args[2].value))
       ("a", "b")
       end
@metafoo (macro with 1 method)

julia> @metafoo function foo(var)
         out = var.a.a1 .+ var.b.c3
         return out
       end
("a", "b")

Perhaps you could not convince the user to use a macro. Then you might be able to parse through the lowered code as follows.

julia> lowered = code_lowered(foo, Tuple{NamedTuple})[1]
CodeInfo(
1 ─ %1 = Main.:+
│   %2 = Base.getproperty(var, :a)
│   %3 = Base.getproperty(%2, :a1)
│   %4 = Base.getproperty(var, :b)
│   %5 = Base.getproperty(%4, :c3)
│   %6 = Base.broadcasted(%1, %3, %5)
│        out = Base.materialize(%6)
└──      return out
)

julia> lowered.code[2].args[3].value
:a

julia> lowered.code[4].args[3].value
:b

julia> lowered.code[2].args[3].value, lowered.code[4].args[3].value
(:a, :b)

julia> string.((lowered.code[2].args[3].value, lowered.code[4].args[3].value))
("a", "b")
3 Likes