@@ -131,9 +131,12 @@ def baz():
131131from sys import stderr
132132
133133from .regutil import register_decorator
134- from .lazyutil import islazy , mark_lazy , force , lazycall
134+ from .lazyutil import islazy , mark_lazy , lazycall
135+ from .dynassign import dyn
135136
136- @mark_lazy
137+ # In principle, jump should have @mark_lazy, but for performance reasons
138+ # it doesn't. "force(target)" is slow, so strict code shouldn't have to do that.
139+ # This is handled by a special case in lazycall.
137140def jump (target , * args , ** kwargs ):
138141 """A jump (noun, not verb).
139142
@@ -151,7 +154,7 @@ def jump(target, *args, **kwargs):
151154 **kwargs:
152155 Named arguments to be passed to `target`.
153156 """
154- return _jump (force ( target ) , args , kwargs )
157+ return _jump (target , args , kwargs )
155158
156159class _jump :
157160 """The actual class representing a jump.
@@ -234,35 +237,63 @@ def trampolined(function):
234237 to perform optimized tail calls. (*Optimized* in the sense of not
235238 increasing the call stack depth, not for speed.)
236239 """
237- @wraps (function )
238- def trampoline (* args , ** kwargs ):
239- f = function
240- while True :
241- if callable (f ): # general case
242- v = lazycall (f , * args , ** kwargs )
243- else : # inert-data return value from call_ec or similar
244- v = f
245- if isinstance (v , _jump ):
246- f = v .target
247- if not callable (f ): # protect against jump() to inert data from call_ec or similar
248- raise RuntimeError ("Cannot jump into a non-callable value '{}'" .format (f ))
249- args = v .args
250- kwargs = v .kwargs
251- v ._claimed = True
252- else : # final result, exit trampoline
253- return v
254- # Work together with call_ec and other do-it-now decorators.
255- #
256- # The function has already been replaced by its return value. E.g. call_ec
257- # must work that way, because the ec is only valid during the dynamic extent
258- # of the call_ec. OTOH, the trampoline must be **outside**, to be able to
259- # catch a jump() from the result of the call_ec. So we treat a non-callable
260- # "function" as an inert-data return value.
261- if callable (function ):
262- # fortunately functions in Python are just objects; stash for jump constructor
263- trampoline ._entrypoint = function
264- if islazy (function ):
265- trampoline = mark_lazy (trampoline )
266- return trampoline
267- else : # return value from call_ec or similar do-it-now decorator
268- return trampoline ()
240+ if not dyn ._build_lazy_trampoline :
241+ # building a trampoline for regular strict code
242+ @wraps (function )
243+ def trampoline (* args , ** kwargs ):
244+ f = function
245+ while True :
246+ if callable (f ): # general case
247+ v = f (* args , ** kwargs )
248+ else : # inert-data return value from call_ec or similar
249+ v = f
250+ if isinstance (v , _jump ):
251+ f = v .target
252+ if not callable (f ): # protect against jump() to inert data from call_ec or similar
253+ raise RuntimeError ("Cannot jump into a non-callable value '{}'" .format (f ))
254+ args = v .args
255+ kwargs = v .kwargs
256+ v ._claimed = True
257+ else : # final result, exit trampoline
258+ return v
259+ # Work together with call_ec and other do-it-now decorators.
260+ #
261+ # The function has already been replaced by its return value. E.g. call_ec
262+ # must work that way, because the ec is only valid during the dynamic extent
263+ # of the call_ec. OTOH, the trampoline must be **outside**, to be able to
264+ # catch a jump() from the result of the call_ec. So we treat a non-callable
265+ # "function" as an inert-data return value.
266+ if callable (function ):
267+ # fortunately functions in Python are just objects; stash for jump constructor
268+ trampoline ._entrypoint = function
269+ return trampoline
270+ else : # return value from call_ec or similar do-it-now decorator
271+ return trampoline ()
272+ else :
273+ # Exact same code as above, except has the lazify-aware stuff.
274+ # This is to avoid a drastic (~10x) performance hit in trampolines
275+ # built for regular strict code.
276+ @wraps (function )
277+ def trampoline (* args , ** kwargs ):
278+ f = function
279+ while True :
280+ if callable (f ):
281+ v = lazycall (f , * args , ** kwargs ) # <-- this causes the performance hit
282+ else :
283+ v = f
284+ if isinstance (v , _jump ):
285+ f = v .target
286+ if not callable (f ):
287+ raise RuntimeError ("Cannot jump into a non-callable value '{}'" .format (f ))
288+ args = v .args
289+ kwargs = v .kwargs
290+ v ._claimed = True
291+ else : # final result, exit trampoline
292+ return v
293+ if callable (function ):
294+ trampoline ._entrypoint = function
295+ if islazy (function ): # <--
296+ trampoline = mark_lazy (trampoline ) # <--
297+ return trampoline
298+ else :
299+ return trampoline ()
0 commit comments