Skip to content
Permalink
Browse files

add `@Locals` macro for obtaining dictionary of local vars and values

implements #29366
  • Loading branch information...
JeffBezanson committed Oct 19, 2018
1 parent a50d5af commit c7f7422fd107dcf96f6541e88c9e8e636ddf565b
Showing with 93 additions and 1 deletion.
  1. +2 −1 NEWS.md
  2. +36 −0 base/reflection.jl
  3. +22 −0 src/julia-syntax.scm
  4. +33 −0 test/reflection.jl
@@ -5,7 +5,8 @@ New language features
---------------------

* An *exception stack* is maintained on each task to make exception handling more robust and enable root cause analysis using `catch_stack` ([#28878]).

* The experimental macro `Base.@locals` returns a dictionary of current local variable names
and values ([#29733]).

Language changes
----------------
@@ -250,6 +250,42 @@ macro isdefined(s::Symbol)
return Expr(:isdefined, esc(s))
end

"""
@locals()
Construct a dictionary of the names (as symbols) and values of all local
variables defined as of the call site.
# Examples
```jldoctest
julia> let x = 1, y = 2
Base.@locals
end
Dict{Symbol,Any} with 2 entries:
:y => 2
:x => 1
julia> function f(x)
local y
show(Base.@locals); println()
for i = 1:1
show(Base.@locals); println()
end
y = 2
show(Base.@locals); println()
nothing
end;
julia> f(42)
Dict{Symbol,Any}(:x=>42)
Dict{Symbol,Any}(:i=>1,:x=>42)
Dict{Symbol,Any}(:y=>2,:x=>42)
```
"""
macro locals()
return Expr(:locals)
end

"""
objectid(x)
@@ -2442,6 +2442,27 @@
(if (not (memq (cadr e) env))
(error "no outer local variable declaration exists for \"for outer\""))
'(null))
((eq? (car e) 'locals)
(let* ((names (filter (lambda (v)
(and (not (gensym? v))
(not (length= (string-split (string v) "#") 2))
(let ((r (assq v renames)))
(or (atom? r)
(let ((mapping (cdr r)))
(not (and (pair? mapping)
(eq? (car mapping) 'outerref))))))))
env))
(names (delete-duplicates
(filter (lambda (v) (not (eq? v '||)))
(map unmangled-name names))))
(d (make-ssavalue)))
`(block (= ,d (call (call (core apply_type) (top Dict) (core Symbol) (core Any))))
,@(map (lambda (v)
(let ((var (resolve-scopes- v env implicitglobals lam renames newlam sp)))
`(if (isdefined ,var)
(call (top setindex!) ,d ,var (quote ,v)))))
names)
,d)))
((eq? (car e) 'lambda)
(let* ((lv (lam:vars e))
(env (append lv env))
@@ -2494,6 +2515,7 @@
(new-renames (append (map cons need-rename renamed) ;; map from definition name -> gensym name
(map cons need-rename-def renamed-def)
(map (lambda (g) (cons g `(outerref ,g))) new-iglo)
(map (lambda (g) (cons g `(outerref ,g))) glob)
(filter (lambda (ren) ;; old renames list, with anything in vars removed
(let ((var (car ren)))
(not (or (memq var all-vars) ;; remove anything new
@@ -813,3 +813,36 @@ f20872(::Val, ::Val) = false
module M29962 end
# make sure checking if a binding is deprecated does not resolve it
@test !Base.isdeprecated(M29962, :sin) && !Base.isbindingresolved(M29962, :sin)

# @locals
using Base: @locals
let
local x, y
global z
@test isempty(keys(@locals))
x = 1
@test @locals() == Dict{Symbol,Any}(:x=>1)
y = ""
@test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"")
for i = 8:8
@test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"",:i=>8)
end
for i = 42:42
local x
@test @locals() == Dict{Symbol,Any}(:y=>"",:i=>42)
end
@test @locals() == Dict{Symbol,Any}(:x=>1,:y=>"")
x = (y,)
@test @locals() == Dict{Symbol,Any}(:x=>("",),:y=>"")
end

function _test_at_locals1(::Any, ::Any)
x = 1
@test @locals() == Dict{Symbol,Any}(:x=>1)
end
_test_at_locals1(1,1)
function _test_at_locals2(a::Any, ::Any)
x = 2
@test @locals() == Dict{Symbol,Any}(:x=>2,:a=>a)
end
_test_at_locals2(1,1)

0 comments on commit c7f7422

Please sign in to comment.
You can’t perform that action at this time.