@@ -2,16 +2,32 @@ module RuntimeGeneratedFunctions
22
33using ExprTools, Serialization, SHA
44
5- export RuntimeGeneratedFunction, @RuntimeGeneratedFunction
5+ export @RuntimeGeneratedFunction
66
77
88"""
9- RuntimeGeneratedFunction(module, function_expression)
9+ RuntimeGeneratedFunction
1010
11- Construct a function from `function_expression` in the scope of `module` which
12- can be called immediately without world age problems. Somewhat like using
13- `eval(function_expression)` and then calling the resulting function. The
14- differences are:
11+ This type should be constructed via the macro @RuntimeGeneratedFunction.
12+ """
13+ struct RuntimeGeneratedFunction{argnames, cache_tag, context_tag, id} <: Function
14+ body:: Expr
15+ function RuntimeGeneratedFunction (cache_tag, context_tag, ex)
16+ def = splitdef (ex)
17+ args, body = normalize_args (def[:args ]), def[:body ]
18+ id = expr_to_id (body)
19+ cached_body = _cache_body (cache_tag, id, body)
20+ new {Tuple(args), cache_tag, context_tag, id} (cached_body)
21+ end
22+ end
23+
24+ """
25+ @RuntimeGeneratedFunction(function_expression)
26+ @RuntimeGeneratedFunction(context_module, function_expression)
27+
28+ Construct a function from `function_expression` which can be called immediately
29+ without world age problems. Somewhat like using `eval(function_expression)` and
30+ then calling the resulting function. The differences are:
1531
1632* The result can be called immediately (immune to world age errors)
1733* The result is not a named generic function, and doesn't participate in
@@ -20,56 +36,59 @@ differences are:
2036You need to use `RuntimeGeneratedFunctions.init(your_module)` a single time at
2137the top level of `your_module` before any other uses of the macro.
2238
39+ If provided, `context_module` is module in which symbols within
40+ `function_expression` will be looked up. By default this is module in which
41+ `@RuntimeGeneratedFunction` is expanded.
42+
2343# Examples
2444```
2545RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
2646
2747function foo()
2848 expression = :((x,y)->x+y+1) # May be generated dynamically
29- f = RuntimeGeneratedFunction(@__MODULE__, expression)
49+ f = @ RuntimeGeneratedFunction(expression)
3050 f(1,2) # May be called immediately
3151end
3252```
3353"""
34- struct RuntimeGeneratedFunction{argnames,moduletag,id} <: Function
35- body:: Expr
36- function RuntimeGeneratedFunction (mod:: Module , ex)
37- if ! isdefined (mod, _tagname)
38- error (""" You must use `RuntimeGeneratedFunctions.init(@__MODULE__)` at module
39- top level before using runtime generated functions""" )
40- end
41- moduletag = getfield (mod, _tagname)
42- def = splitdef (ex)
43- args, body = normalize_args (def[:args ]), def[:body ]
44- id = expr_to_id (body)
45- cached_body = _cache_body (moduletag, id, body)
46- new {Tuple(args),moduletag,id} (cached_body)
47- end
54+ macro RuntimeGeneratedFunction (code)
55+ _RGF_constructor_code (:(@__MODULE__ ), esc (code))
56+ end
57+ macro RuntimeGeneratedFunction (context_module, code)
58+ _RGF_constructor_code (esc (context_module), esc (code))
4859end
4960
50-
51- macro RuntimeGeneratedFunction (ex)
52- Base. depwarn (" `@RuntimeGeneratedFunction(ex)` is deprecated, use `RuntimeGeneratedFunction(@__MODULE__, ex)` instead." , :RuntimeGeneratedFunction )
61+ function _RGF_constructor_code (context_module, code)
5362 quote
54- RuntimeGeneratedFunction (@__MODULE__ , $ (esc (ex)))
63+ code = $ code
64+ cache_module = @__MODULE__
65+ context_module = $ context_module
66+ if #= =# ! isdefined (cache_module, $ (QuoteNode (_tagname))) ||
67+ ! isdefined (context_module, $ (QuoteNode (_tagname)))
68+ init_mods = unique ([context_module, cache_module])
69+ error (""" You must use `RuntimeGeneratedFunctions.init(@__MODULE__)` at module
70+ top level before using runtime generated functions in $init_mods """ )
71+ end
72+ RuntimeGeneratedFunction (cache_module.$ _tagname, context_module.$ _tagname, $ code)
5573 end
5674end
5775
58- function Base. show (io:: IO , f:: RuntimeGeneratedFunction{argnames, moduletag, id} ) where {argnames,moduletag,id}
59- mod = parentmodule (moduletag)
76+ function Base. show (io:: IO , :: MIME"text/plain" , f:: RuntimeGeneratedFunction{argnames, cache_tag, context_tag, id} ) where {argnames,cache_tag,context_tag,id}
77+ cache_mod = parentmodule (cache_tag)
78+ context_mod = parentmodule (context_tag)
6079 func_expr = Expr (:-> , Expr (:tuple , argnames... ), f. body)
61- print (io, " RuntimeGeneratedFunction(#=in $mod =#, " , repr (func_expr), " )" )
80+ print (io, " RuntimeGeneratedFunction(#=in $cache_mod =#, #=using $context_mod =#, " , repr (func_expr), " )" )
6281end
6382
6483(f:: RuntimeGeneratedFunction )(args:: Vararg{Any,N} ) where N = generated_callfunc (f, args... )
6584
6685# We'll generate a method of this function in every module which wants to use
67- # RuntimeGeneratedFunction
86+ # @ RuntimeGeneratedFunction
6887function generated_callfunc end
6988
70- function generated_callfunc_body (argnames, moduletag , id, __args)
89+ function generated_callfunc_body (argnames, cache_tag , id, __args)
7190 setup = (:($ (argnames[i]) = @inbounds __args[$ i]) for i in 1 : length (argnames))
72- body = _lookup_body (moduletag , id)
91+ body = _lookup_body (cache_tag , id)
7392 @assert body != = nothing
7493 quote
7594 $ (setup... )
@@ -99,9 +118,9 @@ _cache_lock = Threads.SpinLock()
99118_cachename = Symbol (" #_RuntimeGeneratedFunctions_cache" )
100119_tagname = Symbol (" #_RGF_ModTag" )
101120
102- function _cache_body (moduletag , id, body)
121+ function _cache_body (cache_tag , id, body)
103122 lock (_cache_lock) do
104- cache = getfield (parentmodule (moduletag ), _cachename)
123+ cache = getfield (parentmodule (cache_tag ), _cachename)
105124 # Caching is tricky when `id` is the same for different AST instances:
106125 #
107126 # Tricky case #1: If a function body with the same `id` was cached
@@ -123,9 +142,9 @@ function _cache_body(moduletag, id, body)
123142 end
124143end
125144
126- function _lookup_body (moduletag , id)
145+ function _lookup_body (cache_tag , id)
127146 lock (_cache_lock) do
128- cache = getfield (parentmodule (moduletag ), _cachename)
147+ cache = getfield (parentmodule (cache_tag ), _cachename)
129148 cache[id]. value
130149 end
131150end
134153 RuntimeGeneratedFunctions.init(mod)
135154
136155Use this at top level to set up your module `mod` before using
137- `RuntimeGeneratedFunction(mod, ...) `.
156+ `@ RuntimeGeneratedFunction`.
138157"""
139158function init (mod)
140159 lock (_cache_lock) do
@@ -155,8 +174,9 @@ function init(mod)
155174 # or so. See:
156175 # https://github.com/JuliaLang/julia/pull/32902
157176 # https://github.com/NHDaly/StagedFunctions.jl/blob/master/src/StagedFunctions.jl#L30
158- @inline @generated function $RuntimeGeneratedFunctions. generated_callfunc (f:: $RuntimeGeneratedFunctions.RuntimeGeneratedFunction{argnames, $_tagname, id} , __args... ) where {argnames,id}
159- $ RuntimeGeneratedFunctions. generated_callfunc_body (argnames, $ _tagname, id, __args)
177+ @inline @generated function $RuntimeGeneratedFunctions. generated_callfunc (
178+ f:: $RuntimeGeneratedFunctions.RuntimeGeneratedFunction{argnames, cache_tag, $_tagname, id} , __args... ) where {argnames, cache_tag, id}
179+ $ RuntimeGeneratedFunctions. generated_callfunc_body (argnames, cache_tag, id, __args)
160180 end
161181 end )
162182 end
0 commit comments