You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@freemarker.apache.org by "Kleine, Moritz" <Mo...@coremedia.com> on 2015/09/10 15:53:13 UTC

template inclusions cause stackoverflowerror

Hi,

we’re building a website using a view-agnostic document store and a spring-based webapp that is capable of rendering views for documents that include views of linked documents. The views are typically small freemarker templates (at most some tenths of lines) using a custom include directive that determines the view for the included  document and – if it is a freemarker template — tells that template to process itself.

Now, we’re frequently running into stackoverflowerrors which exhibit the level of inclusion and the whole structure of the freemarker templates from the root of up to the custom include. The stacktrace fragment shown below ([1]) is taken from such a stackoverflowerror. I’d suggest to adjust the visitor pattern based implementation so that the accept calls do not invoke Environment#visit again but return the tempate elements to be visited. That way we obtain stacktraces that look as shown further down below ([2]). The patched code resides here: https://github.com/mkleine/incubator-freemarker/tree/avoid-too-much-recursion-when-visiting-template-elements

Are we misusing freemarker or is the approach to reduction of stackframes welcome?

Kind regards,
Moritz


[1]
     at freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.MethodCall._eval(MethodCall.java:62) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Expression.eval(Expression.java:78) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Expression.evalAndCoerceToString(Expression.java:82) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.BuiltInForString._eval(BuiltInForString.java:26) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Expression.eval(Expression.java:78) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Expression.evalAndCoerceToString(Expression.java:82) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.DollarVariable.accept(DollarVariable.java:40) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitAndTransform(Environment.java:413) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.CompressedBlock.accept(CompressedBlock.java:37) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Macro$Context.runMacro(Macro.java:178) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.invoke(Environment.java:700) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:148) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitIteratorBlock(Environment.java:559) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.IteratorBlock.accept(IteratorBlock.java:67) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.EscapeBlock.accept(EscapeBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.core.Environment.process(Environment.java:290) [freemarker-2.3.22.jar:2.3.22]
        at freemarker.template.Template.process(Template.java:312) [freemarker-2.3.22.jar:2.3.22]

[2]

at freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
at freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
at freemarker.core.MethodCall._eval(MethodCall.java:62)
at freemarker.core.Expression.eval(Expression.java:78)
at freemarker.core.ReturnInstruction.accept(ReturnInstruction.java:35)
at freemarker.core.Environment.visit(Environment.java:336)
at freemarker.core.Macro$Context.runMacro(Macro.java:186)
at freemarker.core.Environment.invoke(Environment.java:705)
at freemarker.core.MethodCall._eval(MethodCall.java:74)
at freemarker.core.Expression.eval(Expression.java:78)
at freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
at freemarker.core.DollarVariable.accept(DollarVariable.java:41)
at freemarker.core.Environment.visit(Environment.java:336)
at freemarker.core.Environment.visit(Environment.java:341)
at freemarker.core.Environment.renderElementToString(Environment.java:2200)
at freemarker.core.StringLiteral.evalAndCoerceToString(StringLiteral.java:96)
at freemarker.core.StringLiteral._eval(StringLiteral.java:73)
at freemarker.core.Expression.eval(Expression.java:78)
at freemarker.core.Environment.setMacroContextLocalsFromArguments(Environment.java:743)
at freemarker.core.Environment.invoke(Environment.java:693)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:85)
at freemarker.core.Environment.visit(Environment.java:336)
at freemarker.core.Environment.visit(Environment.java:341)
at freemarker.core.Environment.visit(Environment.java:341)
at freemarker.core.Environment.process(Environment.java:302)
at freemarker.template.Template.process(Template.java:325)

Re: template inclusions cause stackoverflowerror

Posted by Daniel Dekany <dd...@freemail.hu>.
Just let you know that this patch has been merged into the 2.3.x
branch (which will be 2.3.24). Thanks for the contribution!

Afterwards I did some refactoring in the long existing architecture to
get rid of some unnecessary complexity (which I have noticed during
reviewing this patch), and thus I was able to replace
TemplateElementsToVisit with a simple TemplateElement[], also those
TemplateElement[] are now created during parsing, not for each
executions of the elements.

-- 
Thanks,
 Daniel Dekany


Wednesday, October 14, 2015, 9:18:01 PM, Daniel Dekany wrote:

> Thanks, I will review it more closely as soon as I get to it... like
> there's a slight hope that it will happen this weekend.
>
> Others, please look at this patch and tell your opinions, if you find
> the time! As of me, I have finally decided to let this change in
> (unless I will find some new issue I haven't realized yet).
>
>
> Wednesday, October 14, 2015, 2:35:40 PM, Kleine, Moritz wrote:
>
>> Hi, here¹s the 2.3-gae pull request
>> https://github.com/apache/incubator-freemarker/pull/6
>> Regards,
>> Moritz
>>
>> On 9/11/15, 7:49 PM, "Daniel Dekany" <dd...@freemail.hu> wrote:
>>
>>>It surely consumes less stack this way, though stack usage doesn't
>>>used to be an concern (barring FTL sequence concatenations in a loop,
>>>but that's another topic). While less stack is just better regardless,
>>>with this change the code becomes a bit more convoluted (especially if
>>>you had to do something after the child elements ran; though if I saw
>>>well, surprisingly, we don't have such case so far). So I would like
>>>to understand why does your application need so deep stack. I would
>>>think that you had to have some ridiculously high number of things
>>>nested into each other to run into this, but maybe I'm wrong.
>>>
>>>
>>>Thursday, September 10, 2015, 3:53:13 PM, Kleine, Moritz wrote:
>>>
>>>> Hi,
>>>>
>>>> we¹re building a website using a view-agnostic document store and a
>>>> spring-based webapp that is capable of rendering views for documents
>>>> that include views of linked documents. The views are typically
>>>> small freemarker templates (at most some tenths of lines) using a
>>>> custom include directive that determines the view for the included
>>>> document and ­ if it is a freemarker template ‹ tells that template to
>>>>process itself.
>>>>
>>>> Now, we¹re frequently running into stackoverflowerrors which
>>>> exhibit the level of inclusion and the whole structure of the
>>>> freemarker templates from the root of up to the custom include. The
>>>> stacktrace fragment shown below ([1]) is taken from such a
>>>> stackoverflowerror. I¹d suggest to adjust the visitor pattern based
>>>> implementation so that the accept calls do not invoke
>>>> Environment#visit again but return the tempate elements to be
>>>> visited. That way we obtain stacktraces that look as shown further
>>>> down below ([2]). The patched code resides here:
>>>> 
>>>>https://github.com/mkleine/incubator-freemarker/tree/avoid-too-much-recur
>>>>sion-when-visiting-template-elements
>>>>
>>>> Are we misusing freemarker or is the approach to reduction of
>>>>stackframes welcome?
>>>>
>>>> Kind regards,
>>>> Moritz
>>>>
>>>>
>>>> [1]
>>>>      at
>>>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.MethodCall._eval(MethodCall.java:62)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Expression.eval(Expression.java:78)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.BuiltInForString._eval(BuiltInForString.java:26)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Expression.eval(Expression.java:78)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.DollarVariable.accept(DollarVariable.java:40)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitAndTransform(Environment.java:413)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.CompressedBlock.accept(CompressedBlock.java:37)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Macro$Context.runMacro(Macro.java:178)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.invoke(Environment.java:700)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:148)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitIteratorBlock(Environment.java:559)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.IteratorBlock.accept(IteratorBlock.java:67)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.EscapeBlock.accept(EscapeBlock.java:48)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at
>>>> freemarker.core.Environment.process(Environment.java:290)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>         at freemarker.template.Template.process(Template.java:312)
>>>>[freemarker-2.3.22.jar:2.3.22]
>>>>
>>>> [2]
>>>>
>>>> at
>>>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>>>> at
>>>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>>>> at freemarker.core.MethodCall._eval(MethodCall.java:62)
>>>> at freemarker.core.Expression.eval(Expression.java:78)
>>>> at freemarker.core.ReturnInstruction.accept(ReturnInstruction.java:35)
>>>> at freemarker.core.Environment.visit(Environment.java:336)
>>>> at freemarker.core.Macro$Context.runMacro(Macro.java:186)
>>>> at freemarker.core.Environment.invoke(Environment.java:705)
>>>> at freemarker.core.MethodCall._eval(MethodCall.java:74)
>>>> at freemarker.core.Expression.eval(Expression.java:78)
>>>> at
>>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>>> at freemarker.core.DollarVariable.accept(DollarVariable.java:41)
>>>> at freemarker.core.Environment.visit(Environment.java:336)
>>>> at freemarker.core.Environment.visit(Environment.java:341)
>>>> at
>>>> freemarker.core.Environment.renderElementToString(Environment.java:2200)
>>>> at
>>>> 
>>>>freemarker.core.StringLiteral.evalAndCoerceToString(StringLiteral.java:96
>>>>)
>>>> at freemarker.core.StringLiteral._eval(StringLiteral.java:73)
>>>> at freemarker.core.Expression.eval(Expression.java:78)
>>>> at
>>>> 
>>>>freemarker.core.Environment.setMacroContextLocalsFromArguments(Environmen
>>>>t.java:743)
>>>> at freemarker.core.Environment.invoke(Environment.java:693)
>>>> at freemarker.core.UnifiedCall.accept(UnifiedCall.java:85)
>>>> at freemarker.core.Environment.visit(Environment.java:336)
>>>> at freemarker.core.Environment.visit(Environment.java:341)
>>>> at freemarker.core.Environment.visit(Environment.java:341)
>>>> at freemarker.core.Environment.process(Environment.java:302)
>>>> at freemarker.template.Template.process(Template.java:325)
>>>
>>>-- 
>>>Thanks,
>>> Daniel Dekany
>>>
>>
>>
>


---
This email has been checked for viruses by Avast antivirus software.
https://www.avast.com/antivirus


Re: template inclusions cause stackoverflowerror

Posted by Daniel Dekany <dd...@freemail.hu>.
Thanks, I will review it more closely as soon as I get to it... like
there's a slight hope that it will happen this weekend.

Others, please look at this patch and tell your opinions, if you find
the time! As of me, I have finally decided to let this change in
(unless I will find some new issue I haven't realized yet).


Wednesday, October 14, 2015, 2:35:40 PM, Kleine, Moritz wrote:

> Hi, here¹s the 2.3-gae pull request
> https://github.com/apache/incubator-freemarker/pull/6
> Regards,
> Moritz
>
> On 9/11/15, 7:49 PM, "Daniel Dekany" <dd...@freemail.hu> wrote:
>
>>It surely consumes less stack this way, though stack usage doesn't
>>used to be an concern (barring FTL sequence concatenations in a loop,
>>but that's another topic). While less stack is just better regardless,
>>with this change the code becomes a bit more convoluted (especially if
>>you had to do something after the child elements ran; though if I saw
>>well, surprisingly, we don't have such case so far). So I would like
>>to understand why does your application need so deep stack. I would
>>think that you had to have some ridiculously high number of things
>>nested into each other to run into this, but maybe I'm wrong.
>>
>>
>>Thursday, September 10, 2015, 3:53:13 PM, Kleine, Moritz wrote:
>>
>>> Hi,
>>>
>>> we¹re building a website using a view-agnostic document store and a
>>> spring-based webapp that is capable of rendering views for documents
>>> that include views of linked documents. The views are typically
>>> small freemarker templates (at most some tenths of lines) using a
>>> custom include directive that determines the view for the included
>>> document and ­ if it is a freemarker template ‹ tells that template to
>>>process itself.
>>>
>>> Now, we¹re frequently running into stackoverflowerrors which
>>> exhibit the level of inclusion and the whole structure of the
>>> freemarker templates from the root of up to the custom include. The
>>> stacktrace fragment shown below ([1]) is taken from such a
>>> stackoverflowerror. I¹d suggest to adjust the visitor pattern based
>>> implementation so that the accept calls do not invoke
>>> Environment#visit again but return the tempate elements to be
>>> visited. That way we obtain stacktraces that look as shown further
>>> down below ([2]). The patched code resides here:
>>> 
>>>https://github.com/mkleine/incubator-freemarker/tree/avoid-too-much-recur
>>>sion-when-visiting-template-elements
>>>
>>> Are we misusing freemarker or is the approach to reduction of
>>>stackframes welcome?
>>>
>>> Kind regards,
>>> Moritz
>>>
>>>
>>> [1]
>>>      at
>>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.MethodCall._eval(MethodCall.java:62)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Expression.eval(Expression.java:78)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.BuiltInForString._eval(BuiltInForString.java:26)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Expression.eval(Expression.java:78)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.DollarVariable.accept(DollarVariable.java:40)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitAndTransform(Environment.java:413)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.CompressedBlock.accept(CompressedBlock.java:37)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Macro$Context.runMacro(Macro.java:178)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.invoke(Environment.java:700)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:148)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitIteratorBlock(Environment.java:559)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.IteratorBlock.accept(IteratorBlock.java:67)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.EscapeBlock.accept(EscapeBlock.java:48)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.core.Environment.visit(Environment.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at
>>> freemarker.core.Environment.process(Environment.java:290)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>         at freemarker.template.Template.process(Template.java:312)
>>>[freemarker-2.3.22.jar:2.3.22]
>>>
>>> [2]
>>>
>>> at
>>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>>> at
>>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>>> at freemarker.core.MethodCall._eval(MethodCall.java:62)
>>> at freemarker.core.Expression.eval(Expression.java:78)
>>> at freemarker.core.ReturnInstruction.accept(ReturnInstruction.java:35)
>>> at freemarker.core.Environment.visit(Environment.java:336)
>>> at freemarker.core.Macro$Context.runMacro(Macro.java:186)
>>> at freemarker.core.Environment.invoke(Environment.java:705)
>>> at freemarker.core.MethodCall._eval(MethodCall.java:74)
>>> at freemarker.core.Expression.eval(Expression.java:78)
>>> at
>>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>> at freemarker.core.DollarVariable.accept(DollarVariable.java:41)
>>> at freemarker.core.Environment.visit(Environment.java:336)
>>> at freemarker.core.Environment.visit(Environment.java:341)
>>> at
>>> freemarker.core.Environment.renderElementToString(Environment.java:2200)
>>> at
>>> 
>>>freemarker.core.StringLiteral.evalAndCoerceToString(StringLiteral.java:96
>>>)
>>> at freemarker.core.StringLiteral._eval(StringLiteral.java:73)
>>> at freemarker.core.Expression.eval(Expression.java:78)
>>> at
>>> 
>>>freemarker.core.Environment.setMacroContextLocalsFromArguments(Environmen
>>>t.java:743)
>>> at freemarker.core.Environment.invoke(Environment.java:693)
>>> at freemarker.core.UnifiedCall.accept(UnifiedCall.java:85)
>>> at freemarker.core.Environment.visit(Environment.java:336)
>>> at freemarker.core.Environment.visit(Environment.java:341)
>>> at freemarker.core.Environment.visit(Environment.java:341)
>>> at freemarker.core.Environment.process(Environment.java:302)
>>> at freemarker.template.Template.process(Template.java:325)
>>
>>-- 
>>Thanks,
>> Daniel Dekany
>>
>
>

-- 
Thanks,
 Daniel Dekany


Re: template inclusions cause stackoverflowerror

Posted by "Kleine, Moritz" <Mo...@coremedia.com>.
Hi, here¹s the 2.3-gae pull request
https://github.com/apache/incubator-freemarker/pull/6
Regards,
Moritz

On 9/11/15, 7:49 PM, "Daniel Dekany" <dd...@freemail.hu> wrote:

>It surely consumes less stack this way, though stack usage doesn't
>used to be an concern (barring FTL sequence concatenations in a loop,
>but that's another topic). While less stack is just better regardless,
>with this change the code becomes a bit more convoluted (especially if
>you had to do something after the child elements ran; though if I saw
>well, surprisingly, we don't have such case so far). So I would like
>to understand why does your application need so deep stack. I would
>think that you had to have some ridiculously high number of things
>nested into each other to run into this, but maybe I'm wrong.
>
>
>Thursday, September 10, 2015, 3:53:13 PM, Kleine, Moritz wrote:
>
>> Hi,
>>
>> we¹re building a website using a view-agnostic document store and a
>> spring-based webapp that is capable of rendering views for documents
>> that include views of linked documents. The views are typically
>> small freemarker templates (at most some tenths of lines) using a
>> custom include directive that determines the view for the included
>> document and ­ if it is a freemarker template ‹ tells that template to
>>process itself.
>>
>> Now, we¹re frequently running into stackoverflowerrors which
>> exhibit the level of inclusion and the whole structure of the
>> freemarker templates from the root of up to the custom include. The
>> stacktrace fragment shown below ([1]) is taken from such a
>> stackoverflowerror. I¹d suggest to adjust the visitor pattern based
>> implementation so that the accept calls do not invoke
>> Environment#visit again but return the tempate elements to be
>> visited. That way we obtain stacktraces that look as shown further
>> down below ([2]). The patched code resides here:
>> 
>>https://github.com/mkleine/incubator-freemarker/tree/avoid-too-much-recur
>>sion-when-visiting-template-elements
>>
>> Are we misusing freemarker or is the approach to reduction of
>>stackframes welcome?
>>
>> Kind regards,
>> Moritz
>>
>>
>> [1]
>>      at
>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.MethodCall._eval(MethodCall.java:62)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Expression.eval(Expression.java:78)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.BuiltInForString._eval(BuiltInForString.java:26)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Expression.eval(Expression.java:78)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.DollarVariable.accept(DollarVariable.java:40)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitAndTransform(Environment.java:413)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.CompressedBlock.accept(CompressedBlock.java:37)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Macro$Context.runMacro(Macro.java:178)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.invoke(Environment.java:700)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:148)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitIteratorBlock(Environment.java:559)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.IteratorBlock.accept(IteratorBlock.java:67)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.visitByHiddingParent(Environment.java:333)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.MixedContent.accept(MixedContent.java:62)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.EscapeBlock.accept(EscapeBlock.java:48)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.core.Environment.visit(Environment.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at
>> freemarker.core.Environment.process(Environment.java:290)
>>[freemarker-2.3.22.jar:2.3.22]
>>         at freemarker.template.Template.process(Template.java:312)
>>[freemarker-2.3.22.jar:2.3.22]
>>
>> [2]
>>
>> at
>> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
>> at
>> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
>> at freemarker.core.MethodCall._eval(MethodCall.java:62)
>> at freemarker.core.Expression.eval(Expression.java:78)
>> at freemarker.core.ReturnInstruction.accept(ReturnInstruction.java:35)
>> at freemarker.core.Environment.visit(Environment.java:336)
>> at freemarker.core.Macro$Context.runMacro(Macro.java:186)
>> at freemarker.core.Environment.invoke(Environment.java:705)
>> at freemarker.core.MethodCall._eval(MethodCall.java:74)
>> at freemarker.core.Expression.eval(Expression.java:78)
>> at
>> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
>> at freemarker.core.DollarVariable.accept(DollarVariable.java:41)
>> at freemarker.core.Environment.visit(Environment.java:336)
>> at freemarker.core.Environment.visit(Environment.java:341)
>> at
>> freemarker.core.Environment.renderElementToString(Environment.java:2200)
>> at
>> 
>>freemarker.core.StringLiteral.evalAndCoerceToString(StringLiteral.java:96
>>)
>> at freemarker.core.StringLiteral._eval(StringLiteral.java:73)
>> at freemarker.core.Expression.eval(Expression.java:78)
>> at
>> 
>>freemarker.core.Environment.setMacroContextLocalsFromArguments(Environmen
>>t.java:743)
>> at freemarker.core.Environment.invoke(Environment.java:693)
>> at freemarker.core.UnifiedCall.accept(UnifiedCall.java:85)
>> at freemarker.core.Environment.visit(Environment.java:336)
>> at freemarker.core.Environment.visit(Environment.java:341)
>> at freemarker.core.Environment.visit(Environment.java:341)
>> at freemarker.core.Environment.process(Environment.java:302)
>> at freemarker.template.Template.process(Template.java:325)
>
>-- 
>Thanks,
> Daniel Dekany
>


Re: template inclusions cause stackoverflowerror

Posted by Daniel Dekany <dd...@freemail.hu>.
It surely consumes less stack this way, though stack usage doesn't
used to be an concern (barring FTL sequence concatenations in a loop,
but that's another topic). While less stack is just better regardless,
with this change the code becomes a bit more convoluted (especially if
you had to do something after the child elements ran; though if I saw
well, surprisingly, we don't have such case so far). So I would like
to understand why does your application need so deep stack. I would
think that you had to have some ridiculously high number of things
nested into each other to run into this, but maybe I'm wrong.


Thursday, September 10, 2015, 3:53:13 PM, Kleine, Moritz wrote:

> Hi,
>
> we’re building a website using a view-agnostic document store and a
> spring-based webapp that is capable of rendering views for documents
> that include views of linked documents. The views are typically
> small freemarker templates (at most some tenths of lines) using a
> custom include directive that determines the view for the included 
> document and – if it is a freemarker template — tells that template to process itself.
>
> Now, we’re frequently running into stackoverflowerrors which
> exhibit the level of inclusion and the whole structure of the
> freemarker templates from the root of up to the custom include. The
> stacktrace fragment shown below ([1]) is taken from such a
> stackoverflowerror. I’d suggest to adjust the visitor pattern based
> implementation so that the accept calls do not invoke
> Environment#visit again but return the tempate elements to be
> visited. That way we obtain stacktraces that look as shown further
> down below ([2]). The patched code resides here:
> https://github.com/mkleine/incubator-freemarker/tree/avoid-too-much-recursion-when-visiting-template-elements
>
> Are we misusing freemarker or is the approach to reduction of stackframes welcome?
>
> Kind regards,
> Moritz
>
>
> [1]
>      at
> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.MethodCall._eval(MethodCall.java:62) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Expression.eval(Expression.java:78) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.BuiltInForString._eval(BuiltInForString.java:26) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Expression.eval(Expression.java:78) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.DollarVariable.accept(DollarVariable.java:40) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitAndTransform(Environment.java:413) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.CompressedBlock.accept(CompressedBlock.java:37) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Macro$Context.runMacro(Macro.java:178) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.invoke(Environment.java:700) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.UnifiedCall.accept(UnifiedCall.java:84) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.IteratorBlock$Context.runLoop(IteratorBlock.java:148) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitIteratorBlock(Environment.java:559) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.IteratorBlock.accept(IteratorBlock.java:67) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.visitByHiddingParent(Environment.java:333) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.MixedContent.accept(MixedContent.java:62) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.EscapeBlock.accept(EscapeBlock.java:48) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.core.Environment.visit(Environment.java:312) [freemarker-2.3.22.jar:2.3.22]
>         at
> freemarker.core.Environment.process(Environment.java:290) [freemarker-2.3.22.jar:2.3.22]
>         at freemarker.template.Template.process(Template.java:312) [freemarker-2.3.22.jar:2.3.22]
>
> [2]
>
> at
> freemarker.ext.beans.BeansWrapper.invokeMethod(BeansWrapper.java:1458)
> at
> freemarker.ext.beans.SimpleMethodModel.exec(SimpleMethodModel.java:71)
> at freemarker.core.MethodCall._eval(MethodCall.java:62)
> at freemarker.core.Expression.eval(Expression.java:78)
> at freemarker.core.ReturnInstruction.accept(ReturnInstruction.java:35)
> at freemarker.core.Environment.visit(Environment.java:336)
> at freemarker.core.Macro$Context.runMacro(Macro.java:186)
> at freemarker.core.Environment.invoke(Environment.java:705)
> at freemarker.core.MethodCall._eval(MethodCall.java:74)
> at freemarker.core.Expression.eval(Expression.java:78)
> at
> freemarker.core.Expression.evalAndCoerceToString(Expression.java:82)
> at freemarker.core.DollarVariable.accept(DollarVariable.java:41)
> at freemarker.core.Environment.visit(Environment.java:336)
> at freemarker.core.Environment.visit(Environment.java:341)
> at
> freemarker.core.Environment.renderElementToString(Environment.java:2200)
> at
> freemarker.core.StringLiteral.evalAndCoerceToString(StringLiteral.java:96)
> at freemarker.core.StringLiteral._eval(StringLiteral.java:73)
> at freemarker.core.Expression.eval(Expression.java:78)
> at
> freemarker.core.Environment.setMacroContextLocalsFromArguments(Environment.java:743)
> at freemarker.core.Environment.invoke(Environment.java:693)
> at freemarker.core.UnifiedCall.accept(UnifiedCall.java:85)
> at freemarker.core.Environment.visit(Environment.java:336)
> at freemarker.core.Environment.visit(Environment.java:341)
> at freemarker.core.Environment.visit(Environment.java:341)
> at freemarker.core.Environment.process(Environment.java:302)
> at freemarker.template.Template.process(Template.java:325)

-- 
Thanks,
 Daniel Dekany