Description
Many things use "downward funargs", that is, things that pass a function down into another function. Classic example is the scala map function on collections, but any use of call-by-name also seems to generate these closures.
This results in heap allocation of a closure object, and so takes code that would not allocate, and makes it allocate things.
To fix this either one must crank up optimizations so more aggressive inlining takes place (which interferes with debugging), or convert these places to macros instead of higher-order functions.
(Edited: see later comment - this macro approach doesn't work in scala 2.11 for map/foreach because the function object passed takes an argument. Where a zero-argument body expression is passed this approach works, but not if an actual function object is passed.)
Places where this is needed:
Assert.usage and Assert.invariant
Logging (see DFDL1242)
all the "with body" functions. Search code for "def with.*" to pull up all the with-body definitions.
OnStack and LocalStack idioms that pop the stack and push the stack automatically.
(Note: OnStack and LocalStack take function arguments that need an argument. We avoid allocation there by aggressive inlining, not by way of macros)
Maybe.map (Note: uses inlining,not macros)
EntityReplacer (note: uses inlining not macros)
To avoid code-size blow-ups, some refactoring may help:
def withFooBar(body: => Bar) = {
|
// bunch of preliminary stuff
|
try {
|
// protected stuff
|
body // call the body function
|
// more protected stuff
|
} catch {
|
/// bunch of catch cases
|
} finally {
|
/// finally stuff
|
}
|
/// wrap up.
|
}
|
Imagine that where the comments are above, there are blocks of code that are fairly long
and complex.
As a macro, it will want to avoid repetition of these code blocks, so like:
def withFooBar(body : => Bar) = macro (body) => q"""
|
{
|
preliminaryStuff()
|
try {
|
protected1Stuff()
|
$body // call the body function
|
protected2Stuff()
|
} catch {
|
case th: Throwable => handleThrow(th)
|
} finally {
|
handleFinally()
|
}
|
wrapUp()
|
}"""
|
In the above, the blocks of code that were in the original definition are factored out into callable methods (if they contain any significant quantity of code. Otherwise they can stay as is.)
This avoids repetition of more than just the callouts and try/catch/finally structure.
Note that any time a function is defined on a final class it can be inlined so long as it is not used polymorphically. This can be preferable to converting it into a macro.