@@ -10,13 +10,15 @@ export @RuntimeGeneratedFunction
1010
1111This type should be constructed via the macro @RuntimeGeneratedFunction.
1212"""
13- struct RuntimeGeneratedFunction{moduletag,id,argnames}
13+ mutable struct RuntimeGeneratedFunction{moduletag,id,argnames}
14+ body:: Expr
1415 function RuntimeGeneratedFunction (moduletag, ex)
16+ id = expr2bytes (ex)
1517 def = splitdef (ex)
1618 args, body = normalize_args (def[:args ]), def[:body ]
17- id = expr2bytes (body)
18- _cache_body (moduletag, id, body )
19- new {moduletag,id,Tuple(args)} ()
19+ f = new {moduletag,id,Tuple(args)} (body)
20+ _cache_self (moduletag, id, f )
21+ return f
2022 end
2123end
2224
@@ -51,31 +53,30 @@ macro RuntimeGeneratedFunction(ex)
5153end
5254
5355function Base. show (io:: IO , f:: RuntimeGeneratedFunction{moduletag, id, argnames} ) where {moduletag,id,argnames}
54- body = _lookup_body (moduletag, id)
5556 mod = parentmodule (moduletag)
56- func_expr = Expr (:-> , Expr (:tuple , argnames... ), body)
57+ func_expr = Expr (:-> , Expr (:tuple , argnames... ), f . body)
5758 print (io, " RuntimeGeneratedFunction(#=in $mod =#, " , repr (func_expr), " )" )
5859end
5960
6061(f:: RuntimeGeneratedFunction )(args:: Vararg{Any,N} ) where N = generated_callfunc (f, args... )
6162
6263@inline @generated function generated_callfunc (f:: RuntimeGeneratedFunction{moduletag, id, argnames} , __args... ) where {moduletag,id,argnames}
6364 setup = (:($ (argnames[i]) = @inbounds __args[$ i]) for i in 1 : length (argnames))
65+ f_value = _lookup_self (moduletag, id)
6466 quote
6567 $ (setup... )
66- $ (_lookup_body (moduletag, id) )
68+ $ (f_value . body )
6769 end
6870end
6971
70- # ## Function body caching and lookup
72+ # ## Function caching and lookup
7173#
72- # Caching the body of a RuntimeGeneratedFunction is a little complicated
73- # because we want the `id=>body` mapping to survive precompilation. This means
74- # we need to store the cache of mappings which are created by a module in that
75- # module itself.
74+ # Looking up a RuntimeGeneratedFunction based on the id is a little complicated
75+ # because we want the `id=>func` mapping to survive precompilation. This means
76+ # we need to store the mapping created by a module in that module itself.
7677#
7778# For that, we need a way to lookup the correct module from an instance of
78- # RuntimeGeneratedFunction. Modules can't be type parameters, but we can use
79+ # RuntimeGeneratedFunction. Modules can't be type parameters, but we can use
7980# any type which belongs to the module as a proxy "tag" for the module.
8081#
8182# (We could even abuse `typeof(__module__.eval)` for the tag, though this is a
8586_cachename = Symbol (" #_RuntimeGeneratedFunctions_cache" )
8687_tagname = Symbol (" #_RuntimeGeneratedFunctions_ModTag" )
8788
88- function _cache_body (moduletag, id, body)
89- getfield (parentmodule (moduletag), _cachename)[id] = body
89+ function _cache_self (moduletag, id, f)
90+ # Use a WeakRef to allow `f` to be garbage collected. (After GC the cache
91+ # will still contain an empty entry with key `id`.)
92+ getfield (parentmodule (moduletag), _cachename)[id] = WeakRef (f)
9093end
9194
92- function _lookup_body (moduletag, id)
93- getfield (parentmodule (moduletag), _cachename)[id]
95+ function _lookup_self (moduletag, id)
96+ getfield (parentmodule (moduletag), _cachename)[id]. value
9497end
9598
9699function _ensure_cache_exists! (mod)
97100 if ! isdefined (mod, _cachename)
98101 mod. eval (quote
99- const $ _cachename = Dict {Tuple,Expr} ()
102+ const $ _cachename = Dict ()
100103 struct $ _tagname
101104 end
102105 end )
0 commit comments