title: NonStringExceptions consume considerable time when concatenation of hashes occurs
body: <p>We have a lot of FTL code that performs hash concatenation like this:</p>
  
  <pre><code>&lt;#local event_data = event_data + {
     'subaction_name': subactionName
  } /&gt;
  </code></pre>
  
  <p>However, these constructs cause some overhead due to the following code (the freemarker.core.AddConcatExpression class’ _getAsTemplateModel(Environment env) method):</p>
  
  <pre><code>try {
     String s1 = getStringValue(leftModel, left, env);
     if(s1 == null) s1 = "null";
     String s2 = getStringValue(rightModel, right, env);
     if(s2 == null) s2 = "null";
     return new SimpleScalar(s1.concat(s2));
  } catch (NonStringException e) {
     if (leftModel instanceof TemplateHashModel &amp;&amp; rightModel instanceof TemplateHashModel) {
         if (leftModel instanceof TemplateHashModelEx &amp;&amp; rightModel instanceof TemplateHashModelEx) {
             TemplateHashModelEx leftModelEx = (TemplateHashModelEx)leftModel;
             TemplateHashModelEx rightModelEx = (TemplateHashModelEx)rightModel;
             if (leftModelEx.size() == 0) {
                 return rightModelEx;
             } else if (rightModelEx.size() == 0) {
                 return leftModelEx;
             } else {
                 return new ConcatenatedHashEx(leftModelEx, rightModelEx);
             }
         } else {
             return new ConcatenatedHash((TemplateHashModel)leftModel,
                                         (TemplateHashModel)rightModel);
         }
     } else {
         throw e;
     }
  }
  </code></pre>
  
  <p>because the getStringValue method throws NonStringExceptions in such cases. The NonStringException, in its turn, inherits the following constructor logic from the TemplateException:</p>
  
  <pre><code>super(getDescription(description, cause));
  causeException = cause;
  this.env = env;
  if(env != null)
  {
     StringWriter sw = new StringWriter();
     PrintWriter pw = new PrintWriter(sw);
     env.outputInstructionStack(pw);
     pw.flush();
     ftlInstructionStack = sw.toString();
  }
  else
  {
     ftlInstructionStack = "";
  }
  </code></pre>
  
  <p>which fetches a full instruction stack each time regardless further handling.</p>
  
  <p>This leads to up to 50 ms of execution time spent on the exception constructors per page request in our case, which is quite critical for the overall throughput.</p>
  
  <p>Could anyone give some advices on how to avoid these exceptions without touching the FTL code, please? Or, maybe, it’s possible to get some kind of a patch, which would move the TemplateModelHash instanceof check before the catch block in the AddConcatExpression._getAsTemplateModel method?</p>
  
  <p><strong>EDIT:</strong>
  freemarker version 2.3.19</p>