You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2015/12/13 17:02:27 UTC

[10/15] incubator-freemarker git commit: [unfinished] Getting rid of nestedBlock VS regulatedChildren

[unfinished] Getting rid of nestedBlock VS regulatedChildren


Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/808ddadd
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/808ddadd
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/808ddadd

Branch: refs/heads/2.3-gae
Commit: 808ddadd382a5ce116b249ee4ae14c683c96ba91
Parents: 83eca1b
Author: ddekany <dd...@apache.org>
Authored: Mon Dec 7 21:56:32 2015 +0100
Committer: ddekany <dd...@apache.org>
Committed: Wed Dec 9 00:00:05 2015 +0100

----------------------------------------------------------------------
 .../freemarker/core/AssignmentInstruction.java  |  14 +-
 src/main/java/freemarker/core/AttemptBlock.java |   6 +-
 src/main/java/freemarker/core/AutoEscBlock.java |  11 +-
 .../java/freemarker/core/BlockAssignment.java   |   9 +-
 src/main/java/freemarker/core/Case.java         |   6 +-
 .../java/freemarker/core/CompressedBlock.java   |  14 +-
 .../java/freemarker/core/ConditionalBlock.java  |   8 +-
 src/main/java/freemarker/core/DebugBreak.java   |  10 +-
 src/main/java/freemarker/core/ElseOfList.java   |   8 +-
 src/main/java/freemarker/core/Environment.java  |  66 ++--
 src/main/java/freemarker/core/EscapeBlock.java  |   8 +-
 src/main/java/freemarker/core/IfBlock.java      |  20 +-
 src/main/java/freemarker/core/Items.java        |   6 +-
 .../java/freemarker/core/IteratorBlock.java     |  40 +--
 .../java/freemarker/core/ListElseContainer.java |  10 +-
 src/main/java/freemarker/core/Macro.java        |  17 +-
 src/main/java/freemarker/core/MixedContent.java |  23 +-
 .../NestedContentNotSupportedException.java     |   5 +-
 .../java/freemarker/core/NoAutoEscBlock.java    |  11 +-
 .../java/freemarker/core/NoEscapeBlock.java     |   6 +-
 .../java/freemarker/core/OutputFormatBlock.java |  11 +-
 .../java/freemarker/core/RecoveryBlock.java     |   8 +-
 .../java/freemarker/core/ReturnInstruction.java |  12 +-
 src/main/java/freemarker/core/Sep.java          |   6 +-
 src/main/java/freemarker/core/SwitchBlock.java  |  12 +-
 .../java/freemarker/core/TemplateElement.java   | 312 ++++++++-----------
 src/main/java/freemarker/core/TextBlock.java    |  32 +-
 ...nterruptionSupportTemplatePostProcessor.java |  33 +-
 .../java/freemarker/core/TransformBlock.java    |   8 +-
 .../java/freemarker/core/TrimInstruction.java   |   2 +-
 src/main/java/freemarker/core/UnifiedCall.java  |  18 +-
 src/test/java/freemarker/core/ASTPrinter.java   |  43 +--
 32 files changed, 348 insertions(+), 447 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/AssignmentInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AssignmentInstruction.java b/src/main/java/freemarker/core/AssignmentInstruction.java
index c3526fb..87a7d56 100644
--- a/src/main/java/freemarker/core/AssignmentInstruction.java
+++ b/src/main/java/freemarker/core/AssignmentInstruction.java
@@ -35,24 +35,24 @@ final class AssignmentInstruction extends TemplateElement {
 
     AssignmentInstruction(int scope) {
         this.scope = scope;
-        setRegulatedChildBufferCapacity(1);
+        setChildBufferCapacity(1);
     }
 
     void addAssignment(Assignment assignment) {
-        addRegulatedChild(assignment);
+        addChild(assignment);
     }
     
     void setNamespaceExp(Expression namespaceExp) {
         this.namespaceExp = namespaceExp;
-        int ln = getRegulatedChildCount();
+        int ln = getChildCount();
         for (int i = 0; i < ln; i++) {
-            ((Assignment) getRegulatedChild(i)).setNamespaceExp(namespaceExp);
+            ((Assignment) getChild(i)).setNamespaceExp(namespaceExp);
         }
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
@@ -62,12 +62,12 @@ final class AssignmentInstruction extends TemplateElement {
         buf.append(Assignment.getDirectiveName(scope));
         if (canonical) {
             buf.append(' ');
-            int ln = getRegulatedChildCount();
+            int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
                 if (i != 0) {
                     buf.append(", ");
                 }
-                Assignment assignment = (Assignment) getRegulatedChild(i);
+                Assignment assignment = (Assignment) getChild(i);
                 buf.append(assignment.getCanonicalForm());
             }
         } else {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/AttemptBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AttemptBlock.java b/src/main/java/freemarker/core/AttemptBlock.java
index 390fc92..586cf05 100644
--- a/src/main/java/freemarker/core/AttemptBlock.java
+++ b/src/main/java/freemarker/core/AttemptBlock.java
@@ -34,9 +34,9 @@ final class AttemptBlock extends TemplateElement {
     AttemptBlock(TemplateElement attemptBlock, RecoveryBlock recoveryBlock) {
         this.attemptBlock = attemptBlock;
         this.recoveryBlock = recoveryBlock;
-        setRegulatedChildBufferCapacity(2);
-        addRegulatedChild(attemptBlock);
-        addRegulatedChild(recoveryBlock);
+        setChildBufferCapacity(2);
+        addChild(attemptBlock);
+        addChild(recoveryBlock);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/AutoEscBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/AutoEscBlock.java b/src/main/java/freemarker/core/AutoEscBlock.java
index 303aba0..5bcc555 100644
--- a/src/main/java/freemarker/core/AutoEscBlock.java
+++ b/src/main/java/freemarker/core/AutoEscBlock.java
@@ -29,19 +29,18 @@ import freemarker.template.TemplateException;
 final class AutoEscBlock extends TemplateElement {
     
     AutoEscBlock(TemplateElement nestedBlock) { 
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            String nested = getNestedBlock() != null ? getNestedBlock().getCanonicalForm() : "";
-            return "<" + getNodeTypeSymbol() + "\">" + nested + "</" + getNodeTypeSymbol() + ">";
+            return "<" + getNodeTypeSymbol() + "\">" + getChildrenCanonicalForm() + "</" + getNodeTypeSymbol() + ">";
         } else {
             return getNodeTypeSymbol();
         }
@@ -68,8 +67,8 @@ final class AutoEscBlock extends TemplateElement {
     }
 
     @Override
-    boolean isIgnorable() {
-        return getNestedBlock() == null || getNestedBlock().isIgnorable();
+    boolean isIgnorable(boolean stripWhitespace) {
+        return getChildCount() == 0;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/BlockAssignment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BlockAssignment.java b/src/main/java/freemarker/core/BlockAssignment.java
index 58f3404..a4f88d1 100644
--- a/src/main/java/freemarker/core/BlockAssignment.java
+++ b/src/main/java/freemarker/core/BlockAssignment.java
@@ -41,7 +41,7 @@ final class BlockAssignment extends TemplateElement {
     private final MarkupOutputFormat<?> markupOutputFormat;
 
     BlockAssignment(TemplateElement nestedBlock, String varName, int scope, Expression namespaceExp, MarkupOutputFormat<?> markupOutputFormat) {
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         this.varName = varName;
         this.namespaceExp = namespaceExp;
         this.scope = scope;
@@ -50,8 +50,9 @@ final class BlockAssignment extends TemplateElement {
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        if (getNestedBlock() != null) {
-            env.visitAndTransform(getNestedBlock(), new CaptureOutput(env), null);
+        TemplateElement[] children = getChildBuffer();
+        if (children != null) {
+            env.visitAndTransform(children, new CaptureOutput(env), null);
         } else {
             TemplateModel value = capturedStringToModel("");
             if (namespaceExp != null) {
@@ -135,7 +136,7 @@ final class BlockAssignment extends TemplateElement {
         }
         if (canonical) {
             sb.append('>');
-            sb.append(getNestedBlock() == null ? "" : getNestedBlock().getCanonicalForm());
+            sb.append(getChildrenCanonicalForm());
             sb.append("</");
             sb.append(getNodeTypeSymbol());
             sb.append('>');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/Case.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Case.java b/src/main/java/freemarker/core/Case.java
index baba698..4b13cfb 100644
--- a/src/main/java/freemarker/core/Case.java
+++ b/src/main/java/freemarker/core/Case.java
@@ -31,12 +31,12 @@ final class Case extends TemplateElement {
 
     Case(Expression matchingValue, TemplateElement nestedBlock) {
         this.condition = matchingValue;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
     TemplateElement[] accept(Environment env) {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
@@ -50,7 +50,7 @@ final class Case extends TemplateElement {
         }
         if (canonical) {
             sb.append('>');
-            if (getNestedBlock() != null) sb.append(getNestedBlock().getCanonicalForm());
+            sb.append(getChildrenCanonicalForm());
         }
         return sb.toString();
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/CompressedBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/CompressedBlock.java b/src/main/java/freemarker/core/CompressedBlock.java
index e405e0f..2ecde74 100644
--- a/src/main/java/freemarker/core/CompressedBlock.java
+++ b/src/main/java/freemarker/core/CompressedBlock.java
@@ -32,13 +32,14 @@ import freemarker.template.utility.StandardCompress;
 final class CompressedBlock extends TemplateElement {
 
     CompressedBlock(TemplateElement nestedBlock) { 
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        if (getNestedBlock() != null) {
-            env.visitAndTransform(getNestedBlock(), StandardCompress.INSTANCE, null);
+        TemplateElement[] childBuffer = getChildBuffer();
+        if (childBuffer != null) {
+            env.visitAndTransform(childBuffer, StandardCompress.INSTANCE, null);
         }
         return null;
     }
@@ -46,8 +47,7 @@ final class CompressedBlock extends TemplateElement {
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            String nested = getNestedBlock() != null ? getNestedBlock().getCanonicalForm() : "";
-            return "<" + getNodeTypeSymbol() + ">" + nested + "</" + getNodeTypeSymbol() + ">";
+            return "<" + getNodeTypeSymbol() + ">" + getChildrenCanonicalForm() + "</" + getNodeTypeSymbol() + ">";
         } else {
             return getNodeTypeSymbol();
         }
@@ -74,8 +74,8 @@ final class CompressedBlock extends TemplateElement {
     }
 
     @Override
-    boolean isIgnorable() {
-        return getNestedBlock() == null || getNestedBlock().isIgnorable();
+    boolean isIgnorable(boolean stripWhitespace) {
+        return getChildCount() == 0 && getParameterCount() == 0;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/ConditionalBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ConditionalBlock.java b/src/main/java/freemarker/core/ConditionalBlock.java
index 97563cb..00dafdc 100644
--- a/src/main/java/freemarker/core/ConditionalBlock.java
+++ b/src/main/java/freemarker/core/ConditionalBlock.java
@@ -39,14 +39,14 @@ final class ConditionalBlock extends TemplateElement {
 
     ConditionalBlock(Expression condition, TemplateElement nestedBlock, int type) {
         this.condition = condition;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         this.type = type;
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
         if (condition == null || condition.evalToBoolean(env)) {
-            return getRegulatedChildren();
+            return getChildBuffer();
         }
         return null;
     }
@@ -62,9 +62,7 @@ final class ConditionalBlock extends TemplateElement {
         }
         if (canonical) {
             buf.append(">");
-            if (getNestedBlock() != null) {
-                buf.append(getNestedBlock().getCanonicalForm());
-            }
+            buf.append(getChildrenCanonicalForm());
             if (!(getParent() instanceof IfBlock)) {
                 buf.append("</#if>");
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/DebugBreak.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DebugBreak.java b/src/main/java/freemarker/core/DebugBreak.java
index cea2b77..2fa1cf3 100644
--- a/src/main/java/freemarker/core/DebugBreak.java
+++ b/src/main/java/freemarker/core/DebugBreak.java
@@ -33,15 +33,15 @@ import freemarker.template.TemplateException;
 @Deprecated
 public class DebugBreak extends TemplateElement {
     public DebugBreak(TemplateElement nestedBlock) {
-        setNestedBlock(nestedBlock);
+        addChild(nestedBlock);
         copyLocationFrom(nestedBlock);
     }
     
     @Override
     protected TemplateElement[] accept(Environment env) throws TemplateException, IOException {
         if (!DebuggerService.suspendEnvironment(
-                env, this.getTemplate().getSourceName(), getNestedBlock().getBeginLine())) {
-            return getNestedBlock().accept(env);
+                env, this.getTemplate().getSourceName(), getChild(0).getBeginLine())) {
+            return getChild(0).accept(env);
         } else {
             throw new StopException(env, "Stopped by debugger");
         }
@@ -53,11 +53,11 @@ public class DebugBreak extends TemplateElement {
             StringBuilder sb = new StringBuilder();
             sb.append("<#-- ");
             sb.append("debug break");
-            if (getNestedBlock() == null) {
+            if (getChildCount() == 0) {
                 sb.append(" /-->");
             } else {
                 sb.append(" -->");
-                sb.append(getNestedBlock().getCanonicalForm());                
+                sb.append(getChild(0).getCanonicalForm());                
                 sb.append("<#--/ debug break -->");
             }
             return sb.toString();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/ElseOfList.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ElseOfList.java b/src/main/java/freemarker/core/ElseOfList.java
index 8aa6462..8e0bbeb 100644
--- a/src/main/java/freemarker/core/ElseOfList.java
+++ b/src/main/java/freemarker/core/ElseOfList.java
@@ -29,12 +29,12 @@ import freemarker.template.TemplateException;
 final class ElseOfList extends TemplateElement {
     
     ElseOfList(TemplateElement block) {
-        setNestedBlock(block);
+        setChildrenFromElement(block);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
@@ -42,9 +42,7 @@ final class ElseOfList extends TemplateElement {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
             buf.append('<').append(getNodeTypeSymbol()).append('>');
-            if (getNestedBlock() != null) {
-                buf.append(getNestedBlock().getCanonicalForm());            
-            }
+            buf.append(getChildrenCanonicalForm());            
             return buf.toString();
         } else {
             return getNodeTypeSymbol();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java
index dcc178a..bf89fbb 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -337,6 +337,24 @@ public final class Environment extends Configurable {
             popElement();
         }
     }
+    
+    /**
+     * @param elementBuffer
+     *            The elements to visit; might contains trailing {@code null}-s. Can be {@code null}.
+     * 
+     * @since 2.3.24
+     */
+    void visit(TemplateElement[] elementBuffer) throws IOException, TemplateException {
+        if (elementBuffer == null) {
+            return;
+        }
+        for (TemplateElement el : elementBuffer) {
+            if (el == null) {
+                break;  // Skip unused trailing buffer capacity 
+            }
+            visit(el);
+        }
+    }
 
     @SuppressFBWarnings(value = "RANGE_ARRAY_INDEX", justification = "Not called when stack is empty")
     private TemplateElement replaceTopElement(TemplateElement element) {
@@ -345,14 +363,24 @@ public final class Environment extends Configurable {
 
     private static final TemplateModel[] NO_OUT_ARGS = new TemplateModel[0];
 
+    /**
+     * @deprecated Should be internal API
+     */
+    @Deprecated
     public void visit(final TemplateElement element,
             TemplateDirectiveModel directiveModel, Map args,
             final List bodyParameterNames) throws TemplateException, IOException {
+        visit(new TemplateElement[] { element }, directiveModel, args, bodyParameterNames);
+    }
+    
+    void visit(final TemplateElement[] childBuffer,
+            TemplateDirectiveModel directiveModel, Map args,
+            final List bodyParameterNames) throws TemplateException, IOException {
         TemplateDirectiveBody nested;
-        if (element == null) {
+        if (childBuffer == null) {
             nested = null;
         } else {
-            nested = new NestedElementTemplateDirectiveBody(element);
+            nested = new NestedElementTemplateDirectiveBody(childBuffer);
         }
         final TemplateModel[] outArgs;
         if (bodyParameterNames == null || bodyParameterNames.isEmpty()) {
@@ -385,14 +413,14 @@ public final class Environment extends Configurable {
     /**
      * "Visit" the template element, passing the output through a TemplateTransformModel
      * 
-     * @param element
-     *            the element to visit through a transform
+     * @param elementBuffer
+     *            the element to visit through a transform; might contains trailing {@code null}-s
      * @param transform
      *            the transform to pass the element output through
      * @param args
      *            optional arguments fed to the transform
      */
-    void visitAndTransform(TemplateElement element,
+    void visitAndTransform(TemplateElement[] elementBuffer,
             TemplateTransformModel transform,
             Map args)
                     throws TemplateException, IOException {
@@ -408,9 +436,7 @@ public final class Environment extends Configurable {
             try {
                 if (tc == null || tc.onStart() != TransformControl.SKIP_BODY) {
                     do {
-                        if (element != null) {
-                            visit(element);
-                        }
+                        visit(elementBuffer);
                     } while (tc != null && tc.afterBody() == TransformControl.REPEAT_EVALUATION);
                 }
             } catch (Throwable t) {
@@ -501,8 +527,8 @@ public final class Environment extends Configurable {
     void invokeNestedContent(BodyInstruction.Context bodyCtx) throws TemplateException, IOException {
         Macro.Context invokingMacroContext = getCurrentMacroContext();
         LocalContextStack prevLocalContextStack = localContextStack;
-        TemplateElement nestedContent = invokingMacroContext.nestedContent;
-        if (nestedContent != null) {
+        TemplateElement[] nestedContentBuffer = invokingMacroContext.nestedContentBuffer;
+        if (nestedContentBuffer != null) {
             this.currentMacroContext = invokingMacroContext.prevMacroContext;
             currentNamespace = invokingMacroContext.nestedContentNamespace;
 
@@ -520,7 +546,7 @@ public final class Environment extends Configurable {
                 pushLocalContext(bodyCtx);
             }
             try {
-                visit(nestedContent);
+                visit(nestedContentBuffer);
             } finally {
                 if (invokingMacroContext.nestedContentParameterNames != null) {
                     localContextStack.pop();
@@ -642,14 +668,14 @@ public final class Environment extends Configurable {
      */
     void invoke(Macro macro,
             Map namedArgs, List positionalArgs,
-            List bodyParameterNames, TemplateElement nestedBlock) throws TemplateException, IOException {
+            List bodyParameterNames, TemplateElement[] childBuffer) throws TemplateException, IOException {
         if (macro == Macro.DO_NOTHING_MACRO) {
             return;
         }
 
         pushElement(macro);
         try {
-            final Macro.Context macroCtx = macro.new Context(this, nestedBlock, bodyParameterNames);
+            final Macro.Context macroCtx = macro.new Context(this, childBuffer, bodyParameterNames);
             setMacroContextLocalsFromArguments(macroCtx, macro, namedArgs, positionalArgs);
 
             final Macro.Context prevMacroCtx = currentMacroContext;
@@ -2662,24 +2688,24 @@ public final class Environment extends Configurable {
 
     final class NestedElementTemplateDirectiveBody implements TemplateDirectiveBody {
 
-        private final TemplateElement element;
+        private final TemplateElement[] childBuffer;
 
-        private NestedElementTemplateDirectiveBody(TemplateElement element) {
-            this.element = element;
+        private NestedElementTemplateDirectiveBody(TemplateElement[] childBuffer) {
+            this.childBuffer = childBuffer;
         }
 
         public void render(Writer newOut) throws TemplateException, IOException {
             Writer prevOut = out;
             out = newOut;
             try {
-                visit(element);
+                visit(childBuffer);
             } finally {
                 out = prevOut;
             }
         }
-
-        public TemplateElement getElement() {
-            return element;
+        
+        TemplateElement[] getChildrenBuffer() {
+            return childBuffer;
         }
 
     }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/EscapeBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/EscapeBlock.java b/src/main/java/freemarker/core/EscapeBlock.java
index a9be235..b5d40ab 100644
--- a/src/main/java/freemarker/core/EscapeBlock.java
+++ b/src/main/java/freemarker/core/EscapeBlock.java
@@ -41,14 +41,14 @@ class EscapeBlock extends TemplateElement {
     }
 
     void setContent(TemplateElement nestedBlock) {
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         // We don't need it anymore at this point
         this.escapedExpr = null;
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     Expression doEscape(Expression expression) {
@@ -64,9 +64,7 @@ class EscapeBlock extends TemplateElement {
                 .append(" as ").append(expr.getCanonicalForm());
         if (canonical) {
             sb.append('>');
-            if (getNestedBlock() != null) {
-                sb.append(getNestedBlock().getCanonicalForm());
-            }
+            sb.append(getChildrenCanonicalForm());
             sb.append("</").append(getNodeTypeSymbol()).append('>');
         }
         return sb.toString();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/IfBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/IfBlock.java b/src/main/java/freemarker/core/IfBlock.java
index 61e046e..42526f3 100644
--- a/src/main/java/freemarker/core/IfBlock.java
+++ b/src/main/java/freemarker/core/IfBlock.java
@@ -31,25 +31,23 @@ import freemarker.template.TemplateException;
 final class IfBlock extends TemplateElement {
 
     IfBlock(ConditionalBlock block) {
-        setRegulatedChildBufferCapacity(1);
+        setChildBufferCapacity(1);
         addBlock(block);
     }
 
     void addBlock(ConditionalBlock block) {
-        addRegulatedChild(block);
+        addChild(block);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        int ln  = getRegulatedChildCount();
+        int ln  = getChildCount();
         for (int i = 0; i < ln; i++) {
-            ConditionalBlock cblock = (ConditionalBlock) getRegulatedChild(i);
+            ConditionalBlock cblock = (ConditionalBlock) getChild(i);
             Expression condition = cblock.condition;
             env.replaceElementStackTop(cblock);
             if (condition == null || condition.evalToBoolean(env)) {
-                if (cblock.getNestedBlock() != null) {
-                    return cblock.getRegulatedChildren();
-                }
+                return cblock.getChildBuffer();
             }
         }
         return null;
@@ -58,8 +56,8 @@ final class IfBlock extends TemplateElement {
     @Override
     TemplateElement postParseCleanup(boolean stripWhitespace)
         throws ParseException {
-        if (getRegulatedChildCount() == 1) {
-            ConditionalBlock cblock = (ConditionalBlock) getRegulatedChild(0);
+        if (getChildCount() == 1) {
+            ConditionalBlock cblock = (ConditionalBlock) getChild(0);
             cblock.setLocation(getTemplate(), cblock, this);
             return cblock.postParseCleanup(stripWhitespace);
         } else {
@@ -71,9 +69,9 @@ final class IfBlock extends TemplateElement {
     protected String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
-            int ln = getRegulatedChildCount();
+            int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
-                ConditionalBlock cblock = (ConditionalBlock) getRegulatedChild(i);
+                ConditionalBlock cblock = (ConditionalBlock) getChild(i);
                 buf.append(cblock.dump(canonical));
             }
             buf.append("</#if>");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/Items.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Items.java b/src/main/java/freemarker/core/Items.java
index d48c013..ccc0223 100644
--- a/src/main/java/freemarker/core/Items.java
+++ b/src/main/java/freemarker/core/Items.java
@@ -32,7 +32,7 @@ class Items extends TemplateElement {
 
     public Items(String loopVariableName, TemplateElement nestedBlock) {
         this.loopVarName = loopVariableName;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
@@ -44,7 +44,7 @@ class Items extends TemplateElement {
                     getNodeTypeSymbol(), " without iteraton in context");
         }
         
-        iterCtx.loopForItemsElement(env, getNestedBlock(), loopVarName);
+        iterCtx.loopForItemsElement(env, getChildBuffer(), loopVarName);
         return null;
     }
 
@@ -62,7 +62,7 @@ class Items extends TemplateElement {
         sb.append(loopVarName);
         if (canonical) {
             sb.append('>');
-            if (getNestedBlock() != null) sb.append(getNestedBlock().getCanonicalForm());
+            sb.append(getChildrenCanonicalForm());
             sb.append("</");
             sb.append(getNodeTypeSymbol());
             sb.append('>');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/IteratorBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/IteratorBlock.java b/src/main/java/freemarker/core/IteratorBlock.java
index 62c6e34..8f629dd 100644
--- a/src/main/java/freemarker/core/IteratorBlock.java
+++ b/src/main/java/freemarker/core/IteratorBlock.java
@@ -58,7 +58,7 @@ final class IteratorBlock extends TemplateElement {
                   boolean isForEach) {
         this.listExp = listExp;
         this.loopVarName = loopVarName;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         this.isForEach = isForEach;
     }
 
@@ -122,9 +122,7 @@ final class IteratorBlock extends TemplateElement {
         }
         if (canonical) {
             buf.append(">");
-            if (getNestedBlock() != null) {
-                buf.append(getNestedBlock().getCanonicalForm());
-            }
+            buf.append(getChildrenCanonicalForm());
             if (!(getParentElement() instanceof ListElseContainer)) {
                 buf.append("</");
                 buf.append(getNodeTypeSymbol());
@@ -199,10 +197,10 @@ final class IteratorBlock extends TemplateElement {
         }
         
         boolean accept(Environment env) throws TemplateException, IOException {
-            return executeNestedBlock(env, getNestedBlock());
+            return executeNestedContent(env, getChildBuffer());
         }
 
-        void loopForItemsElement(Environment env, TemplateElement nestedBlock, String loopVarName)
+        void loopForItemsElement(Environment env, TemplateElement[] childBuffer, String loopVarName)
                     throws NonSequenceOrCollectionException, TemplateModelException, InvalidReferenceException,
                     TemplateException, IOException {
             try {
@@ -212,7 +210,7 @@ final class IteratorBlock extends TemplateElement {
                 }
                 alreadyEntered = true;
                 this.loopVarName = loopVarName;
-                executeNestedBlock(env, nestedBlock);
+                executeNestedContent(env, childBuffer);
             } finally {
                 this.loopVarName = null;
             }
@@ -222,13 +220,7 @@ final class IteratorBlock extends TemplateElement {
          * Executes the given block for the {@link #listValue}: if {@link #loopVarName} is non-{@code null}, then for
          * each list item once, otherwise once if {@link #listValue} isn't empty.
          */
-        private boolean executeNestedBlock(Environment env, TemplateElement nestedBlock)
-                throws TemplateModelException, TemplateException, IOException,
-                NonSequenceOrCollectionException, InvalidReferenceException {
-            return executeNestedBlockInner(env, nestedBlock);
-        }
-
-        private boolean executeNestedBlockInner(Environment env, TemplateElement nestedBlock)
+        private boolean executeNestedContent(Environment env, TemplateElement[] childBuffer)
                 throws TemplateModelException, TemplateException, IOException, NonSequenceOrCollectionException,
                 InvalidReferenceException {
             final boolean listNotEmpty;
@@ -244,9 +236,7 @@ final class IteratorBlock extends TemplateElement {
                             while (hasNext) {
                                 loopVar = iterModel.next();
                                 hasNext = iterModel.hasNext();
-                                if (nestedBlock != null) {
-                                    env.visit(nestedBlock);
-                                }
+                                env.visit(childBuffer);
                                 index++;
                             }
                         } catch (BreakInstruction.Break br) {
@@ -257,9 +247,7 @@ final class IteratorBlock extends TemplateElement {
                         // We must reuse this later, because TemplateCollectionModel-s that wrap an Iterator only
                         // allow one iterator() call.
                         openedIteratorModel = iterModel;
-                        if (nestedBlock != null) {
-                            env.visit(nestedBlock);
-                        }
+                        env.visit(childBuffer);
                     }
                 }
             } else if (listValue instanceof TemplateSequenceModel) {
@@ -272,17 +260,13 @@ final class IteratorBlock extends TemplateElement {
                             for (index = 0; index < size; index++) {
                                 loopVar = seqModel.get(index);
                                 hasNext = (size > index + 1);
-                                if (nestedBlock != null) {
-                                    env.visit(nestedBlock);
-                                }
+                                env.visit(childBuffer);
                             }
                         } catch (BreakInstruction.Break br) {
                             // Silently exit loop
                         }
                     } else {
-                        if (nestedBlock != null) {
-                            env.visit(nestedBlock);
-                        }
+                        env.visit(childBuffer);
                     }
                 }
             } else if (env.isClassicCompatible()) {
@@ -292,9 +276,7 @@ final class IteratorBlock extends TemplateElement {
                     hasNext = false;
                 }
                 try {
-                    if (nestedBlock != null) {
-                        env.visit(nestedBlock);
-                    }
+                    env.visit(childBuffer);
                 } catch (BreakInstruction.Break br) {
                     // Silently exit "loop"
                 }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/ListElseContainer.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ListElseContainer.java b/src/main/java/freemarker/core/ListElseContainer.java
index d296719..1065e9e 100644
--- a/src/main/java/freemarker/core/ListElseContainer.java
+++ b/src/main/java/freemarker/core/ListElseContainer.java
@@ -28,9 +28,9 @@ class ListElseContainer extends TemplateElement {
     private final ElseOfList elsePart;
 
     public ListElseContainer(IteratorBlock listPart, ElseOfList elsePart) {
-        setRegulatedChildBufferCapacity(2);
-        addRegulatedChild(listPart);
-        addRegulatedChild(elsePart);
+        setChildBufferCapacity(2);
+        addChild(listPart);
+        addChild(elsePart);
         this.listPart = listPart;
         this.elsePart = elsePart;
     }
@@ -52,9 +52,9 @@ class ListElseContainer extends TemplateElement {
     protected String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
-            int ln = getRegulatedChildCount();
+            int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
-                TemplateElement element = getRegulatedChild(i);
+                TemplateElement element = getChild(i);
                 buf.append(element.dump(canonical));
             }
             buf.append("</#list>");

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/Macro.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Macro.java b/src/main/java/freemarker/core/Macro.java
index d639d4f..0cb0159 100644
--- a/src/main/java/freemarker/core/Macro.java
+++ b/src/main/java/freemarker/core/Macro.java
@@ -65,7 +65,7 @@ public final class Macro extends TemplateElement implements TemplateModel {
         this.function = function;
         this.catchAllParamName = catchAllParamName; 
         
-        this.setNestedBlock(nestedBlock);
+        this.setChildrenFromElement(nestedBlock);
     }
 
     public String getCatchAll() {
@@ -137,9 +137,7 @@ public final class Macro extends TemplateElement implements TemplateModel {
         if (function) sb.append(')');
         if (canonical) {
             sb.append('>');
-            if (getNestedBlock() != null) {
-                sb.append(getNestedBlock().getCanonicalForm());
-            }
+            sb.append(getChildrenCanonicalForm());
             sb.append("</").append(getNodeTypeSymbol()).append('>');
         }
         return sb.toString();
@@ -156,17 +154,17 @@ public final class Macro extends TemplateElement implements TemplateModel {
 
     class Context implements LocalContext {
         final Environment.Namespace localVars; 
-        final TemplateElement nestedContent;
+        final TemplateElement[] nestedContentBuffer;
         final Environment.Namespace nestedContentNamespace;
         final List nestedContentParameterNames;
         final LocalContextStack prevLocalContextStack;
         final Context prevMacroContext;
         
         Context(Environment env, 
-                TemplateElement nestedContent,
+                TemplateElement[] nestedContentBuffer,
                 List nestedContentParameterNames) {
             this.localVars = env.new Namespace(); 
-            this.nestedContent = nestedContent;
+            this.nestedContentBuffer = nestedContentBuffer;
             this.nestedContentNamespace = env.getCurrentNamespace();
             this.nestedContentParameterNames = nestedContentParameterNames;
             this.prevLocalContextStack = env.getLocalContextStack();
@@ -180,10 +178,7 @@ public final class Macro extends TemplateElement implements TemplateModel {
 
         void runMacro(Environment env) throws TemplateException, IOException {
             sanityCheck(env);
-            // Set default values for unspecified parameters
-            if (getNestedBlock() != null) {
-                env.visit(getNestedBlock());
-            }
+            env.visit(getChildBuffer());
         }
 
         // Set default parameters, check if all the required parameters are defined.

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/MixedContent.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/MixedContent.java b/src/main/java/freemarker/core/MixedContent.java
index e0ddb89..5b515d8 100644
--- a/src/main/java/freemarker/core/MixedContent.java
+++ b/src/main/java/freemarker/core/MixedContent.java
@@ -31,18 +31,18 @@ final class MixedContent extends TemplateElement {
     MixedContent() { }
 
     void addElement(TemplateElement element) {
-        addRegulatedChild(element);
+        addChild(element);
     }
 
     void addElement(int index, TemplateElement element) {
-        addRegulatedChild(index, element);
+        addChild(index, element);
     }
     
     @Override
     TemplateElement postParseCleanup(boolean stripWhitespace)
         throws ParseException {
         super.postParseCleanup(stripWhitespace);
-        return getRegulatedChildCount() == 1 ? getRegulatedChild(0) : this;
+        return getChildCount() == 1 ? getChild(0) : this;
     }
 
     /**
@@ -52,18 +52,13 @@ final class MixedContent extends TemplateElement {
     @Override
     TemplateElement[] accept(Environment env)
         throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            StringBuilder buf = new StringBuilder();
-            int ln = getRegulatedChildCount();
-            for (int i = 0; i < ln; i++) {
-                buf.append(getRegulatedChild(i).getCanonicalForm());
-            }
-            return buf.toString();
+            return getChildrenCanonicalForm();
         } else {
             if (getParentElement() == null) {
                 return "root";
@@ -74,9 +69,9 @@ final class MixedContent extends TemplateElement {
 
     @Override
     protected boolean isOutputCacheable() {
-        int ln = getRegulatedChildCount();
+        int ln = getChildCount();
         for (int i = 0; i < ln; i++) {
-            if (!getRegulatedChild(i).isOutputCacheable()) {
+            if (!getChild(i).isOutputCacheable()) {
                 return false;
             }
         }
@@ -104,8 +99,8 @@ final class MixedContent extends TemplateElement {
     }
     
     @Override
-    boolean isIgnorable() {
-        return getRegulatedChildCount() == 0;
+    boolean isIgnorable(boolean stripWhitespace) {
+        return getChildCount() == 0;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/NestedContentNotSupportedException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NestedContentNotSupportedException.java b/src/main/java/freemarker/core/NestedContentNotSupportedException.java
index 926fab1..a667af1 100644
--- a/src/main/java/freemarker/core/NestedContentNotSupportedException.java
+++ b/src/main/java/freemarker/core/NestedContentNotSupportedException.java
@@ -37,8 +37,9 @@ class NestedContentNotSupportedException extends TemplateException {
             return;
         }
         if (body instanceof NestedElementTemplateDirectiveBody) {
-            TemplateElement te = ((NestedElementTemplateDirectiveBody) body).getElement();
-            if (te == null || te instanceof ThreadInterruptionCheck) {
+            TemplateElement[] tes = ((NestedElementTemplateDirectiveBody) body).getChildrenBuffer();
+            if (tes == null || tes.length == 0
+                    || tes[0] instanceof ThreadInterruptionCheck && (tes.length == 1 || tes[1] == null)) {
                 return;
             }
         }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/NoAutoEscBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NoAutoEscBlock.java b/src/main/java/freemarker/core/NoAutoEscBlock.java
index acc336a..14980b2 100644
--- a/src/main/java/freemarker/core/NoAutoEscBlock.java
+++ b/src/main/java/freemarker/core/NoAutoEscBlock.java
@@ -29,19 +29,18 @@ import freemarker.template.TemplateException;
 final class NoAutoEscBlock extends TemplateElement {
     
     NoAutoEscBlock(TemplateElement nestedBlock) { 
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            String nested = getNestedBlock() != null ? getNestedBlock().getCanonicalForm() : "";
-            return "<" + getNodeTypeSymbol() + "\">" + nested + "</" + getNodeTypeSymbol() + ">";
+            return "<" + getNodeTypeSymbol() + "\">" + getChildrenCanonicalForm() + "</" + getNodeTypeSymbol() + ">";
         } else {
             return getNodeTypeSymbol();
         }
@@ -68,8 +67,8 @@ final class NoAutoEscBlock extends TemplateElement {
     }
 
     @Override
-    boolean isIgnorable() {
-        return getNestedBlock() == null || getNestedBlock().isIgnorable();
+    boolean isIgnorable(boolean stripWhitespace) {
+        return getChildCount() == 0;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/NoEscapeBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/NoEscapeBlock.java b/src/main/java/freemarker/core/NoEscapeBlock.java
index eb2081a..0722b91 100644
--- a/src/main/java/freemarker/core/NoEscapeBlock.java
+++ b/src/main/java/freemarker/core/NoEscapeBlock.java
@@ -28,18 +28,18 @@ import freemarker.template.TemplateException;
 class NoEscapeBlock extends TemplateElement {
 
     NoEscapeBlock(TemplateElement nestedBlock) {
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
     
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getNodeTypeSymbol() + '>' + getNestedBlock().getCanonicalForm()
+            return "<" + getNodeTypeSymbol() + '>' + getChildrenCanonicalForm()
                     + "</" + getNodeTypeSymbol() + '>';
         } else {
             return getNodeTypeSymbol();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/OutputFormatBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/OutputFormatBlock.java b/src/main/java/freemarker/core/OutputFormatBlock.java
index c0660aa..cc3ef4b 100644
--- a/src/main/java/freemarker/core/OutputFormatBlock.java
+++ b/src/main/java/freemarker/core/OutputFormatBlock.java
@@ -32,20 +32,19 @@ final class OutputFormatBlock extends TemplateElement {
 
     OutputFormatBlock(TemplateElement nestedBlock, Expression paramExp) { 
         this.paramExp = paramExp; 
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
     protected String dump(boolean canonical) {
         if (canonical) {
-            String nested = getNestedBlock() != null ? getNestedBlock().getCanonicalForm() : "";
             return "<" + getNodeTypeSymbol() + " \"" + paramExp.getCanonicalForm() + "\">"
-                    + nested + "</" + getNodeTypeSymbol() + ">";
+                    + getChildrenCanonicalForm() + "</" + getNodeTypeSymbol() + ">";
         } else {
             return getNodeTypeSymbol();
         }
@@ -76,8 +75,8 @@ final class OutputFormatBlock extends TemplateElement {
     }
 
     @Override
-    boolean isIgnorable() {
-        return getNestedBlock() == null || getNestedBlock().isIgnorable();
+    boolean isIgnorable(boolean stripWhitespace) {
+        return getChildCount() == 0;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/RecoveryBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/RecoveryBlock.java b/src/main/java/freemarker/core/RecoveryBlock.java
index 8c483b6..31038e2 100644
--- a/src/main/java/freemarker/core/RecoveryBlock.java
+++ b/src/main/java/freemarker/core/RecoveryBlock.java
@@ -26,12 +26,12 @@ import freemarker.template.TemplateException;
 final class RecoveryBlock extends TemplateElement {
     
     RecoveryBlock(TemplateElement block) {
-        setNestedBlock(block);
+        setChildrenFromElement(block);
     }
 
     @Override
     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
-        return getRegulatedChildren();
+        return getChildBuffer();
     }
 
     @Override
@@ -39,9 +39,7 @@ final class RecoveryBlock extends TemplateElement {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
             buf.append('<').append(getNodeTypeSymbol()).append('>');
-            if (getNestedBlock() != null) {
-                buf.append(getNestedBlock().getCanonicalForm());            
-            }
+            buf.append(getChildrenCanonicalForm());            
             return buf.toString();
         } else {
             return getNodeTypeSymbol();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/ReturnInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ReturnInstruction.java b/src/main/java/freemarker/core/ReturnInstruction.java
index 09852e8..51feab7 100644
--- a/src/main/java/freemarker/core/ReturnInstruction.java
+++ b/src/main/java/freemarker/core/ReturnInstruction.java
@@ -37,15 +37,11 @@ public final class ReturnInstruction extends TemplateElement {
         if (exp != null) {
             env.setLastReturnValue(exp.eval(env));
         }
-        if (nextSibling() != null) {
-            // We need to jump out using an exception.
-            throw Return.INSTANCE;
+        if (nextSibling() == null && getParentElement() instanceof Macro) {
+            // Avoid unnecessary exception throwing 
+            return null;
         }
-        if (!(getParentElement() instanceof Macro || getParentElement().getParentElement() instanceof Macro)) {
-            // Here also, we need to jump out using an exception.
-            throw Return.INSTANCE;
-        }
-        return null;
+        throw Return.INSTANCE;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/Sep.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Sep.java b/src/main/java/freemarker/core/Sep.java
index 7691bfa..d30b3e9 100644
--- a/src/main/java/freemarker/core/Sep.java
+++ b/src/main/java/freemarker/core/Sep.java
@@ -29,7 +29,7 @@ import freemarker.template.TemplateException;
 class Sep extends TemplateElement {
 
     public Sep(TemplateElement nestedBlock) {
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
@@ -42,7 +42,7 @@ class Sep extends TemplateElement {
         }
         
         if (iterCtx.hasNext()) {
-            return getRegulatedChildren();
+            return getChildBuffer();
         }
         return null;
     }
@@ -59,7 +59,7 @@ class Sep extends TemplateElement {
         sb.append(getNodeTypeSymbol());
         if (canonical) {
             sb.append('>');
-            if (getNestedBlock() != null) sb.append(getNestedBlock().getCanonicalForm());
+            sb.append(getChildrenCanonicalForm());
             sb.append("</");
             sb.append(getNodeTypeSymbol());
             sb.append('>');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/SwitchBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/SwitchBlock.java b/src/main/java/freemarker/core/SwitchBlock.java
index 4f3f461..2cef5ce 100644
--- a/src/main/java/freemarker/core/SwitchBlock.java
+++ b/src/main/java/freemarker/core/SwitchBlock.java
@@ -36,7 +36,7 @@ final class SwitchBlock extends TemplateElement {
      */
     SwitchBlock(Expression searched) {
         this.searched = searched;
-        setRegulatedChildBufferCapacity(4);
+        setChildBufferCapacity(4);
     }
 
     /**
@@ -46,17 +46,17 @@ final class SwitchBlock extends TemplateElement {
         if (cas.condition == null) {
             defaultCase = cas;
         }
-        addRegulatedChild(cas);
+        addChild(cas);
     }
 
     @Override
     TemplateElement[] accept(Environment env)
         throws TemplateException, IOException {
         boolean processedCase = false;
-        int ln = getRegulatedChildCount();
+        int ln = getChildCount();
         try {
             for (int i = 0; i < ln; i++) {
-                Case cas = (Case) getRegulatedChild(i);
+                Case cas = (Case) getChild(i);
                 boolean processCase = false;
 
                 // Fall through if a previous case tested true.
@@ -92,9 +92,9 @@ final class SwitchBlock extends TemplateElement {
         buf.append(searched.getCanonicalForm());
         if (canonical) {
             buf.append('>');
-            int ln = getRegulatedChildCount();
+            int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
-                Case cas = (Case) getRegulatedChild(i);
+                Case cas = (Case) getChild(i);
                 buf.append(cas.getCanonicalForm());
             }
             buf.append("</").append(getNodeTypeSymbol()).append('>');

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/TemplateElement.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TemplateElement.java b/src/main/java/freemarker/core/TemplateElement.java
index 7501588..61e3150 100644
--- a/src/main/java/freemarker/core/TemplateElement.java
+++ b/src/main/java/freemarker/core/TemplateElement.java
@@ -45,28 +45,19 @@ abstract public class TemplateElement extends TemplateObject {
     private TemplateElement parent;
 
     /**
-     * Used by elements that has no fixed schema for its child elements. For example, a {@code #case} can enclose any
-     * kind of elements. Only one of {@link #nestedBlock} and {@link #regulatedChildBuffer} can be non-{@code null}
-     * before {@link #postParseCleanup(boolean)}. After {@link #postParseCleanup(boolean)}, the
-     * {@link #regulatedChildBuffer} is always filled and used for execution. This element is typically a
-     * {@link MixedContent}, at least before {@link #postParseCleanup(boolean)} (which optimizes out
-     * {@link MixedContent} with child count less than 2).
+     * Contains 1 or more nested elements with optional trailing {@code null}-s, or is {@code null} exactly if there
+     * are no nested elements.
      */
-    private TemplateElement nestedBlock;
+    private TemplateElement[] childBuffer;
     
     /**
-     * Before {@link #postParseCleanup(boolean)}, it's only used by elements that has a fixed schema for its child
-     * elements. For example, {@code #switch} can only have {@code #case} and {@code #default} child elements. Before
-     * {@link #postParseCleanup(boolean)}, only one of {@link #nestedBlock} and {@link #regulatedChildBuffer} can be
-     * non-{@code null}. After {@link #postParseCleanup(boolean)}, the {@link #regulatedChildBuffer} is always filled
-     * and used for execution.
+     * Contains the number of elements in the {@link #childBuffer}, not counting the trailing {@code null}-s.
+     * If this is 0, then and only then {@link #childBuffer} must be {@code null}.
      */
-    private TemplateElement[] regulatedChildBuffer;
-    private int regulatedChildCount;
+    private int childCount;
 
     /**
-     * The index of the element in the parent's {@link #regulatedChildBuffer} array, or 0 if this is the
-     * {@link #nestedBlock} of the parent.
+     * The index of the element in the parent's {@link #childBuffer} array.
      * 
      * @since 2.3.23
      */
@@ -111,6 +102,18 @@ abstract public class TemplateElement extends TemplateObject {
         return dump(true);
     }
     
+    final String getChildrenCanonicalForm() {
+        int ln = childCount;
+        if (ln == 0) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < ln; i++) {
+            sb.append(childBuffer[i].getCanonicalForm());
+        }
+        return sb.toString();
+    }
+    
     /**
      * Tells if the element should show up in error stack traces. Note that this will be ignored for the top (current)
      * element of a stack trace, as that's always shown.
@@ -120,10 +123,10 @@ abstract public class TemplateElement extends TemplateObject {
     }
     
     /**
-     * Tells if this element possibly executes its {@link #nestedBlock} for many times. This flag is useful when
-     * a template AST is modified for running time limiting (see {@link ThreadInterruptionSupportTemplatePostProcessor}).
-     * Elements that use {@link #regulatedChildBuffer} should not need this, as the insertion of the timeout checks is
-     * impossible there, given their rigid nested element schema.
+     * Tells if this element possibly executes its nested content for many times. This flag is useful when a template
+     * AST is modified for running time limiting (see {@link ThreadInterruptionSupportTemplatePostProcessor}). Elements
+     * that use {@link #childBuffer} should not need this, as the insertion of the timeout checks is impossible
+     * there, given their rigid nested element schema.
      */
     abstract boolean isNestedBlockRepeater();
 
@@ -153,16 +156,12 @@ abstract public class TemplateElement extends TemplateObject {
     
     public TemplateSequenceModel getChildNodes() {
         SimpleSequence result = new SimpleSequence(1);
-        if (nestedBlock != null) {
-            result.add(nestedBlock);
-        } else { 
-            if (regulatedChildBuffer != null) {
-                final SimpleSequence seq = new SimpleSequence(regulatedChildCount);
-                for (int i = 0; i < regulatedChildCount; i++) {
-                    seq.add(regulatedChildBuffer[i]);
-                }
-                return seq;
+        if (childBuffer != null) {
+            final SimpleSequence seq = new SimpleSequence(childCount);
+            for (int i = 0; i < childCount; i++) {
+                seq.add(childBuffer[i]);
             }
+            return seq;
         }
         return result;
     }
@@ -176,7 +175,7 @@ abstract public class TemplateElement extends TemplateObject {
     // Methods so that we can implement the Swing TreeNode API.    
 
     public boolean isLeaf() {
-        return nestedBlock == null && regulatedChildCount == 0;
+        return childCount == 0;
     }
 
     /**
@@ -188,31 +187,16 @@ abstract public class TemplateElement extends TemplateObject {
     }
 
     public int getIndex(TemplateElement node) {
-        if (nestedBlock instanceof MixedContent) {
-            return nestedBlock.getIndex(node);
-        }
-        if (nestedBlock != null) {
-            if (node == nestedBlock) {
-                return 0;
-            }
-        } else {
-            for (int i = 0; i < regulatedChildCount; i++) {
-                if (regulatedChildBuffer[i].equals(node)) { 
-                    return i;
-                }
+        for (int i = 0; i < childCount; i++) {
+            if (childBuffer[i].equals(node)) { 
+                return i;
             }
         }
         return -1;
     }
 
     public int getChildCount() {
-        // Note: regulatedChildren is possibly filled for optimization despite that nestedBlock is filled too, but then
-        // it should only be utilized for execution, as those children might skip a MixedContent parent. 
-        if (nestedBlock != null) {
-            return nestedBlock instanceof MixedContent ? nestedBlock.getChildCount() : 1;
-        } else {
-            return regulatedChildCount;
-        }
+        return childCount;
     }
 
     /**
@@ -220,61 +204,30 @@ abstract public class TemplateElement extends TemplateObject {
      * {@link MixedContent}.
      */
     public Enumeration children() {
-        // Note: regulatedChildren is possibly filled for optimization despite that nestedBlock is filled too, but then
-        // it should only be utilized for execution, as those children might skip a MixedContent parent. 
-        if (nestedBlock != null) {
-            return nestedBlock instanceof MixedContent
-                    ? nestedBlock.children() : Collections.enumeration(Collections.singletonList(nestedBlock));
-        } else if (regulatedChildBuffer != null) {
-            return new _ArrayEnumeration(regulatedChildBuffer, regulatedChildCount);
-        }
-        return Collections.enumeration(Collections.EMPTY_LIST);
+        return childBuffer != null
+                ? new _ArrayEnumeration(childBuffer, childCount)
+                : Collections.enumeration(Collections.EMPTY_LIST);
     }
 
     public TemplateElement getChildAt(int index) {
-        // Note: regulatedChildren is possibly filled for optimization despite that nestedBlock is filled too, but then
-        // it should only be utilized for execution, as those children might skip a MixedContent parent. 
-        if (nestedBlock instanceof MixedContent) {
-            return nestedBlock.getChildAt(index);
-        } else if (nestedBlock != null) {
-            if (index == 0) {
-                return nestedBlock;
-            }
-            throw new ArrayIndexOutOfBoundsException("invalid index");
-        } else if (regulatedChildCount != 0) {
-            try {
-                return regulatedChildBuffer[index];
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // nestedElements was a List earlier, so we emulate the same kind of exception
-                throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + regulatedChildCount);
-            }
+        if (childCount == 0) {
+            throw new IndexOutOfBoundsException("Template element has no children");
+        }
+        try {
+            return childBuffer[index];
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // nestedElements was a List earlier, so we emulate the same kind of exception
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + childCount);
         }
-        throw new ArrayIndexOutOfBoundsException("Template element has no children");
     }
 
     public void setChildAt(int index, TemplateElement element) {
-        if (nestedBlock instanceof MixedContent) {
-            nestedBlock.setChildAt(index, element);
-            if (regulatedChildBuffer != null) {
-                regulatedChildBuffer[index] = element;
-            }            
-        } else if (nestedBlock != null) {
-            if (index == 0) {
-                nestedBlock = element;
-                element.index = 0;
-                element.parent = this;
-                if (regulatedChildBuffer != null) {
-                    regulatedChildBuffer[0] = element;
-                }            
-            } else {
-                throw new IndexOutOfBoundsException("invalid index");
-            }
-        } else if (regulatedChildBuffer != null) {
-            regulatedChildBuffer[index] = element;
+        if (index < childCount && index >= 0) {
+            childBuffer[index] = element;
             element.index = index;
             element.parent = this;
         } else {
-            throw new IndexOutOfBoundsException("element has no children");
+            throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + childCount);
         }
     }
     
@@ -288,29 +241,35 @@ abstract public class TemplateElement extends TemplateObject {
         return parent;
     }
     
-    final void setRegulatedChildBufferCapacity(int capacity) {
-        int ln = regulatedChildCount;
+    final void setChildBufferCapacity(int capacity) {
+        int ln = childCount;
         TemplateElement[] newRegulatedChildBuffer = new TemplateElement[capacity];
         for (int i = 0; i < ln; i++) {
-            newRegulatedChildBuffer[i] = regulatedChildBuffer[i];
+            newRegulatedChildBuffer[i] = childBuffer[i];
         }
-        regulatedChildBuffer = newRegulatedChildBuffer;
+        childBuffer = newRegulatedChildBuffer;
     }
     
-    final void addRegulatedChild(TemplateElement nestedElement) {
-        addRegulatedChild(regulatedChildCount, nestedElement);
+    /**
+     * Inserts a new nested element after the last nested element. 
+     */
+    final void addChild(TemplateElement nestedElement) {
+        addChild(childCount, nestedElement);
     }
 
-    final void addRegulatedChild(int index, TemplateElement nestedElement) {
-        final int lRegulatedChildCount = regulatedChildCount;
+    /**
+     * Inserts a new nested element at the given index, which can also be one higher than the current highest index.
+     */
+    final void addChild(int index, TemplateElement nestedElement) {
+        final int lRegulatedChildCount = childCount;
         
-        TemplateElement[] lRegulatedChildBuffer = regulatedChildBuffer;
+        TemplateElement[] lRegulatedChildBuffer = childBuffer;
         if (lRegulatedChildBuffer == null) {
             lRegulatedChildBuffer = new TemplateElement[INITIAL_REGULATED_CHILD_BUFFER_CAPACITY];
-            regulatedChildBuffer = lRegulatedChildBuffer;
+            childBuffer = lRegulatedChildBuffer;
         } else if (lRegulatedChildCount == lRegulatedChildBuffer.length) {
-            setRegulatedChildBufferCapacity(lRegulatedChildCount != 0 ? lRegulatedChildCount * 2 : 1);
-            lRegulatedChildBuffer = regulatedChildBuffer; 
+            setChildBufferCapacity(lRegulatedChildCount != 0 ? lRegulatedChildCount * 2 : 1);
+            lRegulatedChildBuffer = childBuffer; 
         }
         // At this point: nestedElements == this.nestedElements, and has sufficient capacity.
 
@@ -322,19 +281,37 @@ abstract public class TemplateElement extends TemplateObject {
         nestedElement.index = index;
         nestedElement.parent = this;
         lRegulatedChildBuffer[index] = nestedElement;
-        regulatedChildCount = lRegulatedChildCount + 1;
+        childCount = lRegulatedChildCount + 1;
     }
     
-    final int getRegulatedChildCount() {
-       return regulatedChildCount; 
+    final TemplateElement getChild(int index) {
+        return childBuffer[index];
     }
     
-    final TemplateElement getRegulatedChild(int index) {
-        return regulatedChildBuffer[index];
+    /**
+     * @return Array containing 1 or more nested elements with optional trailing {@code null}-s, or is {@code null}
+     *         exactly if there are no nested elements.
+     */
+    final TemplateElement[] getChildBuffer(){
+        return childBuffer;
     }
-
-    final TemplateElement[] getRegulatedChildren(){
-        return regulatedChildBuffer;
+    
+    // TODO Workaround until the parser doesn't emit TemplateElement[]-s for "blocks" 
+    final void setChildrenFromElement(TemplateElement nestedBlock) {
+        if (nestedBlock == null) {
+            childBuffer = null;
+            childCount = 0;
+        } else if (nestedBlock instanceof MixedContent) {
+            MixedContent mixedContent = (MixedContent) nestedBlock;
+            childBuffer = mixedContent.getChildBuffer();
+            childCount = mixedContent.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                childBuffer[i].parent = this;
+            }
+        } else {
+            childBuffer = new TemplateElement[] { nestedBlock };
+            childCount = 1;
+        }
     }
     
     final int getIndex() {
@@ -348,18 +325,6 @@ abstract public class TemplateElement extends TemplateObject {
         return parent;
     }
     
-    final TemplateElement getNestedBlock() {
-        return nestedBlock;
-    }
-
-    final void setNestedBlock(TemplateElement nestedBlock) {
-        if (nestedBlock != null) {
-            nestedBlock.parent = this;
-            nestedBlock.index = 0;
-        }
-        this.nestedBlock = nestedBlock;
-    }
-    
     /**
      * This is a special case, because a root element is not contained in another element, so we couldn't set the
      * private fields.
@@ -381,62 +346,45 @@ abstract public class TemplateElement extends TemplateObject {
      *         is the duty of the caller, not of this method.
      */
     TemplateElement postParseCleanup(boolean stripWhitespace) throws ParseException {
-        int regulatedChildCount = this.regulatedChildCount;
-        if (nestedBlock != null) {
-            nestedBlock = nestedBlock.postParseCleanup(stripWhitespace);
-            if (nestedBlock.isIgnorable()) {
-                nestedBlock = null;
-            } else {
-                nestedBlock.parent = this;
-                if (nestedBlock instanceof MixedContent) {
-                    // As MixedContent.accept does nothing but return its regulatedChildren, while it will be present as
-                    // a child, the parent element also will have regularedChildren that contains the children of the
-                    // MixedContent directly. 
-                    this.regulatedChildBuffer = nestedBlock.getRegulatedChildren();
-                    this.regulatedChildCount = nestedBlock.getRegulatedChildCount();
-                } else {
-                    // Because execution will use regularChildren only.
-                    this.regulatedChildBuffer = new TemplateElement[] { nestedBlock };
-                    this.regulatedChildCount = 1;
-                }
-            }
-        } else if (regulatedChildCount != 0) {
+        int regulatedChildCount = this.childCount;
+        if (regulatedChildCount != 0) {
             for (int i = 0; i < regulatedChildCount; i++) {
-                TemplateElement te = regulatedChildBuffer[i];
+                TemplateElement te = childBuffer[i];
                 te = te.postParseCleanup(stripWhitespace);
-                regulatedChildBuffer[i] = te;
+                childBuffer[i] = te;
                 te.parent = this;
                 te.index = i;
             }
-            if (stripWhitespace) {
-                for (int i = 0; i < regulatedChildCount; i++) {
-                    TemplateElement te = regulatedChildBuffer[i];
-                    if (te.isIgnorable()) {
-                        regulatedChildCount--;
-                        for (int j = i; j < regulatedChildCount; j++) {
-                            final TemplateElement te2 = regulatedChildBuffer[j  + 1];
-                            regulatedChildBuffer[j] = te2;
-                            te2.index = j;
-                        }
-                        regulatedChildBuffer[regulatedChildCount] = null;
-                        this.regulatedChildCount = regulatedChildCount;
-                        i--;
+            for (int i = 0; i < regulatedChildCount; i++) {
+                TemplateElement te = childBuffer[i];
+                if (te.isIgnorable(stripWhitespace)) {
+                    // TODO Optimize this...
+                    regulatedChildCount--;
+                    for (int j = i; j < regulatedChildCount; j++) {
+                        final TemplateElement te2 = childBuffer[j  + 1];
+                        childBuffer[j] = te2;
+                        te2.index = j;
                     }
+                    childBuffer[regulatedChildCount] = null;
+                    this.childCount = regulatedChildCount;
+                    i--;
                 }
             }
-            if (regulatedChildCount < regulatedChildBuffer.length
-                    && regulatedChildCount <= regulatedChildBuffer.length * 3 / 4) {
+            if (regulatedChildCount == 0) {
+                childBuffer = null;
+            } else if (regulatedChildCount < childBuffer.length
+                && regulatedChildCount <= childBuffer.length * 3 / 4) {
                 TemplateElement[] trimmedregulatedChildBuffer = new TemplateElement[regulatedChildCount];
                 for (int i = 0; i < regulatedChildCount; i++) {
-                    trimmedregulatedChildBuffer[i] = regulatedChildBuffer[i];
+                    trimmedregulatedChildBuffer[i] = childBuffer[i];
                 }
-                regulatedChildBuffer = trimmedregulatedChildBuffer;
+                childBuffer = trimmedregulatedChildBuffer;
             }
         } 
         return this;
     }
 
-    boolean isIgnorable() {
+    boolean isIgnorable(boolean stripWhitespace) {
         return false;
     }
 
@@ -467,35 +415,23 @@ abstract public class TemplateElement extends TemplateObject {
         if (parent == null) {
             return null;
         }
-        return index > 0 ? parent.regulatedChildBuffer[index - 1] : null;
+        return index > 0 ? parent.childBuffer[index - 1] : null;
     }
 
     TemplateElement nextSibling() {
         if (parent == null) {
             return null;
         }
-        return index + 1 < parent.regulatedChildCount ? parent.regulatedChildBuffer[index + 1] : null;
+        return index + 1 < parent.childCount ? parent.childBuffer[index + 1] : null;
     }
 
     private TemplateElement getFirstChild() {
-        if (nestedBlock != null) {
-            return nestedBlock;
-        }
-        if (regulatedChildCount == 0) {
-            return null;
-        }
-        return regulatedChildBuffer[0];
+        return childCount == 0 ? null : childBuffer[0];
     }
 
     private TemplateElement getLastChild() {
-        if (nestedBlock != null) {
-            return nestedBlock;
-        }
-        final int regulatedChildCount = this.regulatedChildCount;
-        if (regulatedChildCount == 0) {
-            return null;
-        }
-        return regulatedChildBuffer[regulatedChildCount - 1];
+        final int regulatedChildCount = this.childCount;
+        return regulatedChildCount == 0 ? null : childBuffer[regulatedChildCount - 1];
     }
 
     private TemplateElement getFirstLeaf() {
@@ -524,6 +460,16 @@ abstract public class TemplateElement extends TemplateObject {
         return false;
     }
     
+    boolean isChildrenOutputCacheable() {
+        int ln = childCount;
+        for (int i = 0; i < ln; i++) {
+            if (!childBuffer[i].isOutputCacheable()) {
+                return false;
+            }
+        }
+        return true;
+    }
+    
     /**
      * determines whether this element's presence on a line 
      * indicates that we should not strip opening whitespace

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/TextBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TextBlock.java b/src/main/java/freemarker/core/TextBlock.java
index 2ddf143..d245d24 100644
--- a/src/main/java/freemarker/core/TextBlock.java
+++ b/src/main/java/freemarker/core/TextBlock.java
@@ -108,7 +108,13 @@ public final class TextBlock extends TemplateElement {
         if (!stripWhitespace || text.length == 0 ) {
             return this;
         }
-        if (getParentElement().getParentElement() == null && previousSibling() == null) return this;
+        TemplateElement parentElement = getParentElement();
+        if (
+                (parentElement == null
+                        || parentElement.getParentElement() == null && parentElement instanceof MixedContent)
+                && previousSibling() == null) {
+            return this;
+        }
         if (!deliberateLeftTrim) {
             trailingCharsToStrip = trailingCharsToStrip();
         }
@@ -306,7 +312,7 @@ public final class TextBlock extends TemplateElement {
 
     @Override
     boolean heedsTrailingWhitespace() {
-        if (isIgnorable()) {
+        if (isIgnorable(true)) {
             return false;
         }
         for (int i = 0; i < text.length; i++) {
@@ -323,7 +329,7 @@ public final class TextBlock extends TemplateElement {
 
     @Override
     boolean heedsOpeningWhitespace() {
-        if (isIgnorable()) {
+        if (isIgnorable(true)) {
             return false;
         }
         for (int i = text.length - 1; i >= 0; i--) {
@@ -339,18 +345,24 @@ public final class TextBlock extends TemplateElement {
     }
 
     @Override
-    boolean isIgnorable() {
+    boolean isIgnorable(boolean stripWhitespace) {
         if (text == null || text.length == 0) {
             return true;
         }
-        if (!StringUtil.isTrimmableToEmpty(text)) {
+        if (stripWhitespace) {
+            if (!StringUtil.isTrimmableToEmpty(text)) {
+                return false;
+            }
+            TemplateElement parentElement = getParentElement();
+            boolean atTopLevel = (parentElement == null
+                    || parentElement.getParentElement() == null && parentElement instanceof MixedContent);
+            TemplateElement prevSibling = previousSibling();
+            TemplateElement nextSibling = nextSibling();
+            return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling))
+                    && ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling));
+        } else {
             return false;
         }
-        boolean atTopLevel = (getParentElement().getParentElement() == null);
-        TemplateElement prevSibling = previousSibling();
-        TemplateElement nextSibling = nextSibling();
-        return ((prevSibling == null && atTopLevel) || nonOutputtingType(prevSibling))
-                && ((nextSibling == null && atTopLevel) || nonOutputtingType(nextSibling));
     }
     
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java b/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
index 7a0e545..26d54aa 100644
--- a/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
+++ b/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
@@ -58,41 +58,14 @@ class ThreadInterruptionSupportTemplatePostProcessor extends TemplatePostProcess
             return;
         }
         
-        final TemplateElement nestedBlock = te.getNestedBlock();
-
-        // Deepest-first recursion:
-        if (nestedBlock != null) {
-            addInterruptionChecks(nestedBlock);
-        }
-        final int regulatedChildrenCount = te.getRegulatedChildCount();
+        final int regulatedChildrenCount = te.getChildCount();
         for (int i = 0; i < regulatedChildrenCount; i++) {
-            addInterruptionChecks(te.getRegulatedChild(i));
+            addInterruptionChecks(te.getChild(i));
         }
         
-        // Because nestedElements (means fixed schema for the children) and nestedBlock (means no fixed schema) are
-        // mutually exclusive, and we only care about the last kind:
         if (te.isNestedBlockRepeater()) {
-            if (nestedBlock == null && regulatedChildrenCount != 0) {
-                // Only elements that use nestedBlock instead of regulatedChildren should be block repeaters.
-                // Note that nestedBlock and nestedElements are (should be) mutually exclusive.
-                throw new BugException(); 
-            }
             try {
-                final ThreadInterruptionCheck interruptedChk = new ThreadInterruptionCheck(te);
-                if (nestedBlock == null) {
-                    te.setNestedBlock(interruptedChk);
-                } else {
-                    final MixedContent nestedMixedC;
-                    if (nestedBlock instanceof MixedContent) {
-                        nestedMixedC = (MixedContent) nestedBlock;
-                    } else {
-                        nestedMixedC = new MixedContent();
-                        nestedMixedC.setLocation(te.getTemplate(), 0, 0, 0, 0);
-                        nestedMixedC.addElement(nestedBlock);
-                        te.setNestedBlock(nestedMixedC);
-                    }
-                    nestedMixedC.addElement(0, interruptedChk);
-                }
+                te.addChild(0, new ThreadInterruptionCheck(te));
             } catch (ParseException e) {
                 throw new TemplatePostProcessorException("Unexpected error; see cause", e);
             }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/TransformBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TransformBlock.java b/src/main/java/freemarker/core/TransformBlock.java
index 8bde8cf..4412664 100644
--- a/src/main/java/freemarker/core/TransformBlock.java
+++ b/src/main/java/freemarker/core/TransformBlock.java
@@ -51,7 +51,7 @@ final class TransformBlock extends TemplateElement {
                    TemplateElement nestedBlock) {
         this.transformExpression = transformExpression;
         this.namedArgs = namedArgs;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
     }
 
     @Override
@@ -72,7 +72,7 @@ final class TransformBlock extends TemplateElement {
             } else {
                 args = EmptyMap.instance;
             }
-            env.visitAndTransform(getNestedBlock(), ttm, args);
+            env.visitAndTransform(getChildBuffer(), ttm, args);
         } else {
             TemplateModel tm = transformExpression.eval(env);
             throw new UnexpectedTypeException(
@@ -100,9 +100,7 @@ final class TransformBlock extends TemplateElement {
         }
         if (canonical) {
             sb.append(">");
-            if (getNestedBlock() != null) {
-                sb.append(getNestedBlock().getCanonicalForm());
-            }
+            sb.append(getChildrenCanonicalForm());
             sb.append("</").append(getNodeTypeSymbol()).append('>');
         }
         return sb.toString();

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/TrimInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/TrimInstruction.java b/src/main/java/freemarker/core/TrimInstruction.java
index a974e0f..7e48367 100644
--- a/src/main/java/freemarker/core/TrimInstruction.java
+++ b/src/main/java/freemarker/core/TrimInstruction.java
@@ -66,7 +66,7 @@ final class TrimInstruction extends TemplateElement {
     }
     
     @Override
-    boolean isIgnorable() {
+    boolean isIgnorable(boolean stripWhitespace) {
         return true;
     }
 

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/808ddadd/src/main/java/freemarker/core/UnifiedCall.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/UnifiedCall.java b/src/main/java/freemarker/core/UnifiedCall.java
index fde6451..831b7bc 100644
--- a/src/main/java/freemarker/core/UnifiedCall.java
+++ b/src/main/java/freemarker/core/UnifiedCall.java
@@ -54,7 +54,7 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
          List bodyParameterNames) {
         this.nameExp = nameExp;
         this.namedArgs = namedArgs;
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         this.bodyParameterNames = bodyParameterNames;
     }
 
@@ -67,7 +67,7 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
         if (nestedBlock == TextBlock.EMPTY_BLOCK) {
             nestedBlock = null;
         }
-        setNestedBlock(nestedBlock);
+        setChildrenFromElement(nestedBlock);
         this.bodyParameterNames = bodyParameterNames;
     }
 
@@ -83,8 +83,7 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
                         + "Functions can only be called from expressions, like in ${f()}, ${x + f()} or ",
                         "<@someDirective someParam=f() />", ".");
             }    
-            env.invoke(macro, namedArgs, positionalArgs, bodyParameterNames,
-                    getNestedBlock());
+            env.invoke(macro, namedArgs, positionalArgs, bodyParameterNames, getChildBuffer());
         } else {
             boolean isDirectiveModel = tm instanceof TemplateDirectiveModel; 
             if (isDirectiveModel || tm instanceof TemplateTransformModel) {
@@ -102,9 +101,9 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
                     args = EmptyMap.instance;
                 }
                 if (isDirectiveModel) {
-                    env.visit(getNestedBlock(), (TemplateDirectiveModel) tm, args, bodyParameterNames);
+                    env.visit(getChildBuffer(), (TemplateDirectiveModel) tm, args, bodyParameterNames);
                 } else { 
-                    env.visitAndTransform(getNestedBlock(), (TemplateTransformModel) tm, args);
+                    env.visitAndTransform(getChildBuffer(), (TemplateTransformModel) tm, args);
                 }
             } else if (tm == null) {
                 throw InvalidReferenceException.getInstance(nameExp, env);
@@ -152,11 +151,11 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
             }
         }
         if (canonical) {
-            if (getNestedBlock() == null) {
+            if (getChildCount() == 0) {
                 sb.append("/>");
             } else {
                 sb.append('>');
-                sb.append(getNestedBlock().getCanonicalForm());
+                sb.append(getChildrenCanonicalForm());
                 sb.append("</@");
                 if (!nameIsInParen
                         && (nameExp instanceof Identifier
@@ -303,8 +302,7 @@ final class UnifiedCall extends TemplateElement implements DirectiveCallPlace {
     }
 
     public boolean isNestedOutputCacheable() {
-        if (getNestedBlock() == null) return true;
-        return getNestedBlock().isOutputCacheable();
+        return isChildrenOutputCacheable();
     }
     
 /*