@@ -2,7 +2,7 @@ module RuntimeGeneratedFunctions
22
33using ExprTools: ExprTools, combinedef, splitdef
44using SHA: SHA, SHA1_CTX, update!
5- using Serialization: Serialization, AbstractSerializer, deserialize, serialize
5+ using Serialization: Serialization, AbstractSerializer, deserialize
66
77export RuntimeGeneratedFunction, @RuntimeGeneratedFunction , drop_expr
88
@@ -15,52 +15,53 @@ export RuntimeGeneratedFunction, @RuntimeGeneratedFunction, drop_expr
1515 eval (Expr (:public , :init , :get_expression ))
1616end
1717
18- const _rgf_docs = """
19- @RuntimeGeneratedFunction(function_expression)
20- @RuntimeGeneratedFunction(context_module, function_expression, opaque_closures=true)
18+ """
19+ RuntimeGeneratedFunction(cache_module, context_module, function_expression;
20+ opaque_closures = true)
21+
22+ A callable function compiled from `function_expression` without a world-age
23+ barrier. A `RuntimeGeneratedFunction` is a `Function`, so generic code that
24+ accepts and invokes a `Function` can use it. It is not a named generic function:
25+ it represents one callable method and does not participate in method dispatch.
26+
27+ # Arguments
2128
22- RuntimeGeneratedFunction(cache_module, context_module, function_expression; opaque_closures=true)
29+ - `cache_module::Module`: module that owns the cached expression. It must have
30+ been initialized with [`init`](@ref). During precompilation, pass the module
31+ currently being precompiled so the cache survives precompilation.
32+ - `context_module::Module`: module in which global names in
33+ `function_expression` are resolved. It must have been initialized with
34+ [`init`](@ref).
35+ - `function_expression::Expr`: an anonymous-function or function-definition
36+ expression whose body becomes the callable function.
2337
24- Construct a function from `function_expression` which can be called immediately
25- without world age problems. Somewhat like using `eval(function_expression)` and
26- then calling the resulting function. The differences are:
38+ # Keywords
2739
28- * The result can be called immediately (immune to world age errors)
29- * The result is not a named generic function, and doesn't participate in
30- generic function dispatch; it 's more like a callable method .
40+ - `opaque_closures = true`: convert closures and generators in
41+ `function_expression` to Julia opaque closures. This permits closures in the
42+ generated body, with Julia 's opaque-closure semantics .
3143
32- You need to use `RuntimeGeneratedFunctions.init(your_module)` a single time at
33- the top level of `your_module` before any other uses of the macro.
44+ # Fields
3445
35- If provided, `context_module` is the module in which symbols within
36- `function_expression` will be looked up. By default, this is the module in which
37- `@RuntimeGeneratedFunction` is expanded .
46+ - `body`: cached body expression while it is retained, or `nothing` after
47+ [`drop_expr`](@ref). The function remains callable after dropping its local
48+ expression reference because the cache owns the body .
3849
39- `cache_module` is the module where the expression `code` will be cached. If
40- `RuntimeGeneratedFunction` is used during precompilation, this must be a module
41- which is currently being precompiled. Normally this would be set to
42- `@__MODULE__` using one of the macro constructors.
50+ # Serialization
51+
52+ `Serialization.serialize` preserves a runtime-generated function while its
53+ `body` is retained. Serialize a function before calling [`drop_expr`](@ref): a
54+ dropped function intentionally does not retain the expression needed to restore
55+ its cache in another process.
4356
44- If `opaque_closures` is `true`, all closures in `function_expression` are
45- converted to
46- [opaque closures](https://github.com/JuliaLang/julia/pull/37849#issue-496641229).
47- This allows for the use of closures and generators inside the generated function,
48- but may not work in all cases due to slightly different semantics.
4957# Examples
50- ```
51- RuntimeGeneratedFunctions.init(@__MODULE__) # Required at module top-level
5258
53- function foo()
54- expression = :((x,y)->x+y+1) # May be generated dynamically
55- f = @RuntimeGeneratedFunction(expression)
56- f(1,2) # May be called immediately
57- end
59+ ```julia
60+ RuntimeGeneratedFunctions.init(@__MODULE__)
61+ square = RuntimeGeneratedFunction(@__MODULE__, @__MODULE__, :(x -> x^2))
62+ square(3)
5863```
5964"""
60-
61- """
62- $_rgf_docs
63- """
6465struct RuntimeGeneratedFunction{argnames, cache_tag, context_tag, id, B} <: Function
6566 body:: B
6667 function RuntimeGeneratedFunction (cache_tag, context_tag, ex; opaque_closures = true )
9495"""
9596 drop_expr(rgf::RuntimeGeneratedFunction)
9697
97- Return a new `RuntimeGeneratedFunction` that does not hold a reference to the
98- function body expression. This allows the expression AST to be garbage collected
99- while keeping the function callable.
98+ Return a copy of `rgf` that does not retain its function body expression.
99+ This allows the expression AST to be garbage collected while keeping the
100+ function callable.
101+
102+ # Arguments
103+
104+ - `rgf::RuntimeGeneratedFunction`: generated function whose retained expression
105+ should be released.
106+
107+ # Returns
108+
109+ - A `RuntimeGeneratedFunction` with `body === nothing`. It has the same callable
110+ behavior as `rgf` while its cache entry remains available.
100111
101112The expression can still be retrieved later using [`get_expression`](@ref) as long
102113as at least one `RuntimeGeneratedFunction` with the same body exists.
103114
115+ Serialize a generated function before calling `drop_expr`. A dropped function has
116+ discarded the expression needed to restore its cache in another process.
117+
104118# Examples
105119```julia
106120ex = :((x) -> x^2)
@@ -159,7 +173,34 @@ function RuntimeGeneratedFunction(
159173end
160174
161175"""
162- $_rgf_docs
176+ @RuntimeGeneratedFunction(function_expression)
177+ @RuntimeGeneratedFunction(context_module, function_expression,
178+ opaque_closures = true)
179+
180+ Construct a [`RuntimeGeneratedFunction`](@ref) from a function expression using
181+ the calling module as its cache module. Call [`init`](@ref) at the top level of
182+ that module before expanding this macro.
183+
184+ # Arguments
185+
186+ - `function_expression::Expr`: an anonymous-function or function-definition
187+ expression to compile.
188+ - `context_module::Module`: optional module in which global names in
189+ `function_expression` are resolved. By default, names resolve in the calling
190+ module.
191+
192+ # Keywords
193+
194+ - `opaque_closures = true`: convert closures and generators in the expression to
195+ Julia opaque closures.
196+
197+ # Examples
198+
199+ ```julia
200+ RuntimeGeneratedFunctions.init(@__MODULE__)
201+ increment = @RuntimeGeneratedFunction(:(x -> x + 1))
202+ increment(2)
203+ ```
163204"""
164205macro RuntimeGeneratedFunction (code)
165206 return quote
276317"""
277318 RuntimeGeneratedFunctions.init(mod)
278319
279- Use this at top level to set up your module `mod` before using
280- `@RuntimeGeneratedFunction`.
320+ Initialize `mod` for runtime-generated functions.
321+
322+ # Arguments
323+
324+ - `mod::Module`: module that will cache generated expression bodies and own the
325+ generated call method.
326+
327+ # Rules
328+
329+ Call `init` once at module top level before using
330+ [`@RuntimeGeneratedFunction`](@ref) in `mod`, or before passing `mod` as either
331+ the cache or context module to [`RuntimeGeneratedFunction`](@ref). Repeated calls
332+ are idempotent. Do not call it from a local scope: it defines a generated method
333+ in `mod` so that global names resolve in that module.
334+
335+ # Examples
336+
337+ ```julia
338+ module GeneratedFunctionsExample
339+ using RuntimeGeneratedFunctions
340+ RuntimeGeneratedFunctions.init(@__MODULE__)
341+ end
342+ ```
281343"""
282344function init (mod)
283345 return lock (_cache_lock) do
412474
413475Retrieve the function expression from a `RuntimeGeneratedFunction`.
414476
477+ # Arguments
478+
479+ - `rgf::RuntimeGeneratedFunction`: generated function whose source expression is
480+ requested.
481+
482+ # Returns
483+
484+ - An anonymous-function `Expr` containing the normalized argument names and the
485+ cached body.
486+
415487This works even if [`drop_expr`](@ref) has been called on the function, as long as
416488the expression is still in the cache (i.e., at least one `RuntimeGeneratedFunction`
417489with the same body exists).
@@ -439,33 +511,6 @@ function get_expression(
439511 return func_expr = Expr (:-> , Expr (:tuple , argnames... ), _lookup_body (cache_tag, id))
440512end
441513
442- # We write an explicit serialize() and deserialize() here to manage caching of
443- # the body on a remote node when using Serialization.jl (in Distributed.jl
444- # and elsewhere)
445- function Serialization. serialize (
446- s:: AbstractSerializer ,
447- rgf:: RuntimeGeneratedFunction {
448- argnames, cache_tag,
449- context_tag, id, B,
450- }
451- ) where {
452- argnames,
453- cache_tag,
454- context_tag,
455- id,
456- B,
457- }
458- body = _lookup_body (cache_tag, id)
459- Serialization. serialize_type (
460- s,
461- RuntimeGeneratedFunction{
462- argnames, cache_tag, context_tag,
463- id, B,
464- }
465- )
466- return serialize (s, body)
467- end
468-
469514function Serialization. deserialize (
470515 s:: AbstractSerializer ,
471516 :: Type {
@@ -482,13 +527,18 @@ function Serialization.deserialize(
482527 B,
483528 }
484529 body = deserialize (s)
530+ B === Nothing && throw (
531+ ArgumentError (
532+ " cannot deserialize a dropped RuntimeGeneratedFunction; serialize it before calling drop_expr"
533+ )
534+ )
485535 cached_body = _cache_body (cache_tag, id, body)
486536 f = RuntimeGeneratedFunction {argnames, cache_tag, context_tag, id} (cached_body)
487- return B === Nothing ? drop_expr (f) : f
537+ return f
488538end
489539
490- # achieve deepcopy(f)===f behavior similar to "normal" julia functions
491- Base. deepcopy_internal (f:: RuntimeGeneratedFunction , stackdict :: IdDict ) = f
540+ # Match Julia functions: runtime-generated functions are immutable callable values.
541+ Base. deepcopy (f:: RuntimeGeneratedFunction ) = f
492542
493543@specialize
494544
0 commit comments