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 2018/06/16 11:42:32 UTC

[3/3] freemarker git commit: Some ground work to support dialects later. Added classes like Dialect and StaticallyLinkedNamespaceEntry, but these are non-public for now, and just drafts, and aren't actually used by the templates. The final ones will need

Some ground work to support dialects later. Added classes like Dialect and StaticallyLinkedNamespaceEntry, but these are non-public for now, and just drafts, and aren't actually used by the templates. The final ones will need more features to be able to replace the trickier built-in directives. Note that some early version (not a public API) of dialects will be needed for FREEMARKER-99, which is why I started this.

Along the way, some internal renaming in the ASTNode related API-s. Most notably visit(...) to execute(...), as we don't use the visitor pattern.


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

Branch: refs/heads/3
Commit: e8e58ffa52937ae4e67d03edb81dd76c84ac9118
Parents: 056635c
Author: ddekany <dd...@apache.org>
Authored: Wed Apr 18 19:19:08 2018 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Jun 16 13:42:15 2018 +0200

----------------------------------------------------------------------
 README.md                                       |   3 +
 .../org/apache/freemarker/core/ASTPrinter.java  |  10 +-
 .../core/GetOptionalTemplateTest.java           |   2 +-
 .../core/InterpolationSyntaxTest.java           |   2 +
 .../core/MistakenlyPublicImportAPIsTest.java    |   9 +-
 .../core/TemplateCallableModelTest.java         |   2 +-
 .../freemarker/core/TemplateLanguageTest.java   |   5 +-
 .../org/apache/freemarker/core/ASTComment.java  |   8 +-
 .../apache/freemarker/core/ASTDebugBreak.java   |  15 +-
 .../freemarker/core/ASTDirAdhocTest1.java       | 211 +++++++++++++++++++
 .../freemarker/core/ASTDirAdhocTest2.java       | 165 +++++++++++++++
 .../freemarker/core/ASTDirAssignment.java       |   8 +-
 .../core/ASTDirAssignmentsContainer.java        |  10 +-
 .../core/ASTDirAttemptRecoverContainer.java     |  12 +-
 .../apache/freemarker/core/ASTDirAutoEsc.java   |  10 +-
 .../org/apache/freemarker/core/ASTDirBreak.java |   8 +-
 .../core/ASTDirCapturingAssignment.java         |  19 +-
 .../org/apache/freemarker/core/ASTDirCase.java  |   8 +-
 .../apache/freemarker/core/ASTDirCompress.java  |  15 +-
 .../apache/freemarker/core/ASTDirContinue.java  |   8 +-
 .../freemarker/core/ASTDirElseOfList.java       |  10 +-
 .../apache/freemarker/core/ASTDirEscape.java    |  10 +-
 .../apache/freemarker/core/ASTDirFallback.java  |   8 +-
 .../org/apache/freemarker/core/ASTDirFlush.java |   8 +-
 .../core/ASTDirIfElseIfElseContainer.java       |  14 +-
 .../freemarker/core/ASTDirIfOrElseOrElseIf.java |   8 +-
 .../apache/freemarker/core/ASTDirImport.java    |   8 +-
 .../apache/freemarker/core/ASTDirInclude.java   |   8 +-
 .../org/apache/freemarker/core/ASTDirItems.java |  12 +-
 .../org/apache/freemarker/core/ASTDirList.java  |  18 +-
 .../core/ASTDirListElseContainer.java           |  12 +-
 .../freemarker/core/ASTDirMacroOrFunction.java  |  10 +-
 .../apache/freemarker/core/ASTDirNested.java    |   8 +-
 .../apache/freemarker/core/ASTDirNoAutoEsc.java |  10 +-
 .../apache/freemarker/core/ASTDirNoEscape.java  |  12 +-
 .../freemarker/core/ASTDirOutputFormat.java     |  12 +-
 .../apache/freemarker/core/ASTDirRecover.java   |  10 +-
 .../apache/freemarker/core/ASTDirRecurse.java   |   8 +-
 .../apache/freemarker/core/ASTDirReturn.java    |   8 +-
 .../org/apache/freemarker/core/ASTDirSep.java   |  12 +-
 .../apache/freemarker/core/ASTDirSetting.java   |   8 +-
 .../org/apache/freemarker/core/ASTDirStop.java  |   8 +-
 .../apache/freemarker/core/ASTDirSwitch.java    |  22 +-
 .../freemarker/core/ASTDirTOrRtOrLtOrNt.java    |   8 +-
 .../org/apache/freemarker/core/ASTDirVisit.java |   8 +-
 .../apache/freemarker/core/ASTDirective.java    |  63 ++++++
 .../freemarker/core/ASTDynamicTopLevelCall.java |  26 +--
 .../org/apache/freemarker/core/ASTElement.java  | 138 ++++++------
 .../freemarker/core/ASTExpAddOrConcat.java      |   4 +-
 .../org/apache/freemarker/core/ASTExpAnd.java   |   4 +-
 .../freemarker/core/ASTExpArithmetic.java       |   4 +-
 .../freemarker/core/ASTExpBooleanLiteral.java   |   9 +-
 .../apache/freemarker/core/ASTExpBuiltIn.java   |   4 +-
 .../freemarker/core/ASTExpBuiltInVariable.java  |   9 +-
 .../freemarker/core/ASTExpComparison.java       |   4 +-
 .../apache/freemarker/core/ASTExpDefault.java   |   4 +-
 .../org/apache/freemarker/core/ASTExpDot.java   |   6 +-
 .../freemarker/core/ASTExpDynamicKeyName.java   |   4 +-
 .../apache/freemarker/core/ASTExpExists.java    |   6 +-
 .../freemarker/core/ASTExpFunctionCall.java     |   6 +-
 .../freemarker/core/ASTExpHashLiteral.java      |   4 +-
 .../freemarker/core/ASTExpListLiteral.java      |   4 +-
 .../freemarker/core/ASTExpNegateOrPlus.java     |   4 +-
 .../org/apache/freemarker/core/ASTExpNot.java   |   4 +-
 .../freemarker/core/ASTExpNumberLiteral.java    |   4 +-
 .../org/apache/freemarker/core/ASTExpOr.java    |   4 +-
 .../freemarker/core/ASTExpParenthesis.java      |   4 +-
 .../org/apache/freemarker/core/ASTExpRange.java |   6 +-
 .../freemarker/core/ASTExpStringLiteral.java    |   4 +-
 .../apache/freemarker/core/ASTExpVariable.java  |   4 +-
 .../apache/freemarker/core/ASTExpression.java   |  15 +-
 .../freemarker/core/ASTImplicitParent.java      |  15 +-
 .../freemarker/core/ASTInterpolation.java       |  45 ++--
 .../org/apache/freemarker/core/ASTNode.java     |  95 ++++-----
 .../apache/freemarker/core/ASTStaticText.java   |  15 +-
 .../core/BuiltInWithParseTimeParameters.java    |   6 +-
 .../org/apache/freemarker/core/CallPlace.java   |   2 +
 .../apache/freemarker/core/Configuration.java   |  53 ++++-
 .../apache/freemarker/core/DefaultDialect.java  |  82 +++++++
 .../core/DefaultTemplateLanguage.java           |  25 ++-
 .../org/apache/freemarker/core/Dialect.java     | 127 +++++++++++
 .../org/apache/freemarker/core/Environment.java | 161 ++++++++++----
 .../apache/freemarker/core/MessageUtils.java    |  19 ++
 .../freemarker/core/ParsingConfiguration.java   |   5 +-
 .../core/StaticLinkingCheckException.java       |  74 +++++++
 .../core/StaticallyLinkedNamespaceEntry.java    | 117 ++++++++++
 .../org/apache/freemarker/core/Template.java    |   2 +-
 .../freemarker/core/TemplateConfiguration.java  |   9 +-
 .../core/TemplateElementArrayBuilder.java       |  13 --
 .../core/TemplateElementsToVisit.java           |   4 +-
 .../freemarker/core/TemplateLanguage.java       |  35 +--
 ...nterruptionSupportTemplatePostProcessor.java |  10 +-
 .../core/UnparsedTemplateLanguage.java          |   3 +-
 .../core/UnsupportedFM2TemplateLanguage.java    |   2 +-
 .../apache/freemarker/core/_CallableUtils.java  | 147 +++++++------
 .../java/org/apache/freemarker/core/_Debug.java |  25 ++-
 .../core/_ErrorDescriptionBuilder.java          |   6 +-
 .../org/apache/freemarker/core/_EvalUtils.java  |  30 +++
 .../freemarker/core/util/_ArrayAdapterList.java |   2 +-
 .../freemarker/core/util/_ArrayEnumeration.java |  51 -----
 .../freemarker/core/util/_ArrayIterator.java    |   8 +-
 .../core/util/_NullArgumentException.java       |   2 +
 freemarker-core/src/main/javacc/FTL.jj          |   8 +-
 103 files changed, 1691 insertions(+), 645 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 15a5f33..7d34e6d 100644
--- a/README.md
+++ b/README.md
@@ -201,6 +201,9 @@ Last tested Eclipse Oxygen (4.7.0)
   - Fro each project: Project -> Properties -> FindBugs -> [x] Run Automatically
   - There should 0 errors. But sometimes the plugin fails to take the
     @SuppressFBWarnings annotations into account; then use Project -> Clean. 
+- Setting type filters (not required, but convenient): "Window" -> "Preferences" ->
+  "Java" -> "Appearance" -> "Type filter", "Add..." these:
+  `javax.swing.*`, `freemarker.*`, `com.sun.*`
 
 ### IntelliJ IDEA
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
index 8157c19..297e975 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/ASTPrinter.java
@@ -35,7 +35,6 @@ import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 import java.nio.charset.CodingErrorAction;
 import java.nio.charset.StandardCharsets;
-import java.util.Enumeration;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
 
@@ -261,7 +260,7 @@ public class ASTPrinter {
     private static void validateAST(ASTElement te) {
         int childCount = te.getChildCount();
         for (int i = 0; i < childCount; i++) {
-            ASTElement child = te.getChild(i);
+            ASTElement child = te.fastGetChild(i);
             ASTElement parentElement = child.getParent();
             // As ASTImplicitParent.accept does nothing but returns its children, it's optimized out in the final
             // AST tree. While it will be present as a child, the parent element also will have children
@@ -321,7 +320,7 @@ public class ASTPrinter {
             ASTNode tObj = (ASTNode) node;
 
             printNodeLineStart(paramRole, ind, out);
-            out.write(tObj.getASTNodeDescriptor());
+            out.write(tObj.getLabelWithoutParameters());
             printNodeLineEnd(node, out, opts);
             
             if (opts.getShowConstantValue() && node instanceof ASTExpression) {
@@ -345,9 +344,8 @@ public class ASTPrinter {
                 printNode(value, ind + INDENTATION, role, opts, out);
             }
             if (tObj instanceof ASTElement) {
-                Enumeration enu = ((ASTElement) tObj).children();
-                while (enu.hasMoreElements()) {
-                    printNode(enu.nextElement(), INDENTATION + ind, null, opts, out);
+                for (ASTElement child : ((ASTElement) tObj).getChildren()) {
+                    printNode(child, INDENTATION + ind, null, opts, out);
                 }
             }
         } else {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/GetOptionalTemplateTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/GetOptionalTemplateTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/GetOptionalTemplateTest.java
index 22d6aff..f6c973e 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/GetOptionalTemplateTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/GetOptionalTemplateTest.java
@@ -107,7 +107,7 @@ public class GetOptionalTemplateTest extends TemplateTest {
     @Test
     public void testWrongArguments() throws Exception {
         assertErrorContains("<#assign t = .getOptionalTemplate()>", "argument");
-        assertErrorContains("<#assign t = .getOptionalTemplate('1', '2', '3')>", "arguments", "3");
+        assertErrorContains("<#assign t = .getOptionalTemplate('1', '2', '3')>", "1", "arguments", "more");
         assertErrorContains("<#assign t = .getOptionalTemplate(1)>", "1st argument", "string", "number");
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
index 2bf4200..9865c18 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/InterpolationSyntaxTest.java
@@ -33,11 +33,13 @@ public class InterpolationSyntaxTest extends TemplateTest {
 
     /** Non-standard template language */
     private final TemplateLanguage F3ASU = new DefaultTemplateLanguage("dummy",
+            DefaultDialect.INSTANCE,
             UndefinedOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.ANGLE_BRACKET, InterpolationSyntax.SQUARE_BRACKET); 
 
     /** Non-standard template language */
     private final TemplateLanguage F3SDU = new DefaultTemplateLanguage("dummy",
+            DefaultDialect.INSTANCE,
             UndefinedOutputFormat.INSTANCE, AutoEscapingPolicy.ENABLE_IF_DEFAULT,
             TagSyntax.SQUARE_BRACKET, InterpolationSyntax.DOLLAR); 
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java
index 5fcee97..be85eeb 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/MistakenlyPublicImportAPIsTest.java
@@ -35,9 +35,12 @@ import org.junit.Test;
 
 /**
  * These are things that users shouldn't do, but we shouldn't break backward compatibility without knowing about it.
+ * 
+ * TODO [FM3] Now we should make this illegal, but I'm not sure how to catch when the user does this.
  */
 public class MistakenlyPublicImportAPIsTest {
 
+    
     @Test
     public void testImportCopying() throws IOException, TemplateException {
         StringTemplateLoader tl = new StringTemplateLoader();
@@ -60,7 +63,7 @@ public class MistakenlyPublicImportAPIsTest {
                 t2.process(null, _NullWriter.INSTANCE);
                 fail();
             } catch (InvalidReferenceException e) {
-                // Apparenly, it has never worked like this...
+                // Apparently, it has never worked like this...
                 assertEquals("i1", e.getBlamedExpressionString());
             }
         }
@@ -72,6 +75,7 @@ public class MistakenlyPublicImportAPIsTest {
         assertThat(i1, instanceOf(Namespace.class));
         TemplateModel i2 = env.getVariable("i2");
         assertThat(i2, instanceOf(Namespace.class));
+        Environment originalEnv = env;
 
         {
             Template t2 = new Template(null, "<@i1.m/>", cfg);
@@ -80,6 +84,7 @@ public class MistakenlyPublicImportAPIsTest {
             env = t2.createProcessingEnvironment(null, sw);
             env.setVariable("i1", i1);
             
+            originalEnv.setOut(sw); // The imported macros are still bound to and will use this.
             env.process();
             assertEquals("1", sw.toString());
         }
@@ -88,10 +93,12 @@ public class MistakenlyPublicImportAPIsTest {
             Template t2 = new Template(null, "<@i2.m/>", cfg);
             
             StringWriter sw = new StringWriter();
+            env.setOut(sw); // In the old Environment instance, to which the imported macros are bound.
             env = t2.createProcessingEnvironment(null, sw);
             env.setVariable("i2", i2);
             
             try {
+                originalEnv.setOut(sw); // The imported macros are still bound to and will use this.
                 env.process();
                 assertEquals("2", sw.toString());
             } catch (NullPointerException e) {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
index a1e684d..8675d13 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateCallableModelTest.java
@@ -219,7 +219,7 @@ public class TemplateCallableModelTest extends TemplateTest {
     @Test
     @SuppressWarnings("ThrowableNotThrown")
     public void testRuntimeErrors() throws IOException, TemplateException {
-        assertErrorContains("<@p 9, 9, 9 />", "can only have 2", "3", "by position");
+        assertErrorContains("<@p 9, 9, 9 />", "can only have 2", "more", "by position");
         assertErrorContains("<@n 9 />", "can't have arguments passed by position");
         assertErrorContains("<@n n3=9 />", "has no", "\"n3\"", "supported", "\"n1\", \"n2\"");
         assertErrorContains("<@p n1=9 />", "directive", "can't have arguments that are passed by name", "\"n1\"",

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLanguageTest.java
----------------------------------------------------------------------
diff --git a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLanguageTest.java b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLanguageTest.java
index 069394b..3bcc382 100644
--- a/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLanguageTest.java
+++ b/freemarker-core-test/src/test/java/org/apache/freemarker/core/TemplateLanguageTest.java
@@ -67,12 +67,13 @@ public class TemplateLanguageTest {
 
         public DummyTemplateLanguage(String fileExtension, OutputFormat outputFormat,
                 AutoEscapingPolicy autoEscapingPolicy) {
-            super(fileExtension, outputFormat, autoEscapingPolicy);
+            super(fileExtension, DefaultDialect.INSTANCE, outputFormat, autoEscapingPolicy);
         }
 
         DummyTemplateLanguage(String fileExtension, boolean allowExtensionStartingWithF, OutputFormat outputFormat,
                 AutoEscapingPolicy autoEscapingPolicy) {
-            super(fileExtension, allowExtensionStartingWithF, outputFormat, autoEscapingPolicy);
+            super(fileExtension, allowExtensionStartingWithF, DefaultDialect.INSTANCE,
+                    outputFormat, autoEscapingPolicy);
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTComment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTComment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTComment.java
index 8a16965..2224fa1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTComment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTComment.java
@@ -24,6 +24,7 @@ import org.apache.freemarker.core.util._StringUtils;
 /**
  * AST comment node
  */
+//TODO [FM3] will be public
 final class ASTComment extends ASTElement {
 
     private final String text;
@@ -33,13 +34,13 @@ final class ASTComment extends ASTElement {
     }
 
     @Override
-    ASTElement[] accept(Environment env) {
+    ASTElement[] execute(Environment env) {
         // do nothing, skip the body
         return null;
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             return "<#--" + text + "-->";
         } else {
@@ -48,10 +49,9 @@ final class ASTComment extends ASTElement {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#--...--";
     }
-    
 
     @Override
     int getParameterCount() {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java
index 562a2cb..0634ffc 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDebugBreak.java
@@ -27,23 +27,24 @@ import org.apache.freemarker.core.debug._DebuggerService;
  * AST node: A debug breakpoint
  */
 class ASTDebugBreak extends ASTElement {
-    public ASTDebugBreak(ASTElement nestedBlock) {
+    
+    ASTDebugBreak(ASTElement nestedBlock) {
         addChild(nestedBlock);
         copyLocationFrom(nestedBlock);
     }
     
     @Override
-    protected ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    protected ASTElement[] execute(Environment env) throws TemplateException, IOException {
         if (!_DebuggerService.suspendEnvironment(
-                env, getTemplate().getSourceName(), getChild(0).getBeginLine())) {
-            return getChild(0).accept(env);
+                env, getTemplate().getSourceName(), fastGetChild(0).getBeginLine())) {
+            return fastGetChild(0).execute(env);
         } else {
             throw new StopException(env, "Stopped by debugger");
         }
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             StringBuilder sb = new StringBuilder();
             sb.append("<#-- ");
@@ -52,7 +53,7 @@ class ASTDebugBreak extends ASTElement {
                 sb.append(" /-->");
             } else {
                 sb.append(" -->");
-                sb.append(getChild(0).getCanonicalForm());                
+                sb.append(fastGetChild(0).getCanonicalForm());                
                 sb.append("<#--/ debug break -->");
             }
             return sb.toString();
@@ -62,7 +63,7 @@ class ASTDebugBreak extends ASTElement {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#debugBreak";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest1.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest1.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest1.java
new file mode 100644
index 0000000..69d40ef
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest1.java
@@ -0,0 +1,211 @@
+package org.apache.freemarker.core;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.impl.SimpleNumber;
+import org.apache.freemarker.core.outputformat.MarkupOutputFormat;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.apache.freemarker.core.util.CommonSupplier;
+import org.apache.freemarker.core.util.StringToIndexMap;
+
+/**
+ * Example of an AST directive that's bound to some Configuration setting (the htmlOutputFormat in this case).
+ */
+//TODO [FM3][FREEMARKER-99] Delete this class when we are finished.
+class ASTDirAdhocTest1 extends ASTDirective {
+    private final MarkupOutputFormat<?> htmlOutputFormat;
+    private ASTExpression p1;
+    private ASTExpression p2;
+    private ASTExpression n1;
+    private ASTExpression n2;
+    private StringToIndexMap nestedContentParamNames;
+
+    private ASTDirAdhocTest1(MarkupOutputFormat<?> htmlOutputFormat) {
+        this.htmlOutputFormat = htmlOutputFormat;
+    }
+
+    static class Factory implements CommonSupplier<ASTDirective> {
+        private final MarkupOutputFormat<?> htmlOutputFormat;
+
+        Factory(MarkupOutputFormat<?> htmlOutputFormat) {
+            this.htmlOutputFormat = htmlOutputFormat;
+        }
+
+        @Override
+        public ASTDirective get() {
+            return new ASTDirAdhocTest1(htmlOutputFormat);
+        }
+    }
+    
+    static TemplateDirectiveModel VALUE = new TemplateDirectiveModel() {
+
+        @Override
+        public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+                throws TemplateException, IOException {
+            throw new UnsupportedOperationException("execute not implemented");
+        }
+
+        @Override
+        public boolean isNestedContentSupported() {
+            return true;
+        }
+
+        @Override
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+            return ARGS_LAYOUT;
+        }
+
+    };
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create(2, false,
+            StringToIndexMap.of("n1", 2, "n2", 3), false);
+    
+    @Override
+    public void setPositionalArgument(int position, ASTExpression valueExp) throws StaticLinkingCheckException {
+        if (position == 0) {
+            p1 = valueExp;
+        } else if (position == 1) {
+            p2 = valueExp;
+        } else {
+            // TODO: Standardize error message; see CallableUtils 
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                  "Too many positional arguments; can only have ",
+                  ARGS_LAYOUT.getPredefinedPositionalArgumentCount(), "."));
+        }
+    }
+
+    @Override
+    public void setNamedArgument(String name, ASTExpression valueExp) throws StaticLinkingCheckException {
+        if (name.equals("n1")) {
+            n1 = valueExp;
+        } else if (name.equals("n2")) {
+            n2 = valueExp;
+        } else {
+            // TODO: Standardize error message; see CallableUtils 
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                    "Unsupported parameter name ", new _DelayedJQuote(name), ". The supported parameters are: ",
+                    new _DelayedJQuotedListing(ARGS_LAYOUT.getPredefinedNamedArgumentsMap().getKeys())));
+        }
+    }
+
+    @Override
+    public void checkArgumentsAndSetNestedContentParameters(StringToIndexMap nestedContentParamNames)
+            throws StaticLinkingCheckException {
+        // TODO: Standardize error message; see CallableUtils 
+        if (p1 == null) {
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                    "Missing mandatory ", new _DelayedOrdinal(1), " positional argument"));
+        }
+        // TODO: Standardize error message; see CallableUtils 
+        if (n1 == null) {
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                    "Missing mandatory named argument, ", new _DelayedJQuote("n1")));
+        }
+        
+        // TODO: Make this check easier? 
+        if (nestedContentParamNames == null || nestedContentParamNames.size() != 1) {
+            throw new StaticLinkingCheckException(
+                    MessageUtils.newBadNumberOfNestedContentParameterPassedMessage(nestedContentParamNames, 1));
+        }
+        this.nestedContentParamNames = nestedContentParamNames;
+    }
+
+    @Override
+    public StringToIndexMap getNestedContentParamNames() {
+        return nestedContentParamNames;
+    }
+    
+    @Override
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
+        env.pushElement(this);
+        try {
+            Writer out = env.getOut();
+            out.write("#foo(");
+            // TODO Bind to cfg.getOutputFormat("HTML")
+            
+            int p1Int = CallableUtils.castArgumentValueToInt(p1.eval(env), 0, false, 0, VALUE, false);
+            
+            env.interpolate(p1, htmlOutputFormat, AutoEscapingPolicy.ENABLE_IF_DEFAULT, env);
+            if (p2 != null) {
+                out.write(", ");
+                env.interpolate(p2, htmlOutputFormat, AutoEscapingPolicy.ENABLE_IF_DEFAULT, env);
+            }
+            out.write(", n1=");
+            env.interpolate(n1, htmlOutputFormat, AutoEscapingPolicy.ENABLE_IF_DEFAULT, env);
+            if (n2 != null) {
+                out.write(", n2=");
+                env.interpolate(n2, htmlOutputFormat, AutoEscapingPolicy.ENABLE_IF_DEFAULT, env);
+            }
+            out.write(") {\n");
+            
+            for (int i = 0; i < p1Int; i++) {
+                env.executeNestedContent(this, nestedContentParamNames, new TemplateModel[] { new SimpleNumber(i) });
+            }
+            
+            out.write("}-foo\n");
+            return null;
+        } finally {
+            env.popElement();
+        }
+    }
+
+    @Override
+    boolean isNestedBlockRepeater() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    String dump(boolean canonical) {
+        return "<#adhocTest1 ...>";
+    }
+
+    @Override
+    public String getLabelWithoutParameters() {
+        return "#adhocTest1";
+    }
+
+    @Override
+    int getParameterCount() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+    
+    
+
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest2.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest2.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest2.java
new file mode 100644
index 0000000..1b03af8
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAdhocTest2.java
@@ -0,0 +1,165 @@
+package org.apache.freemarker.core;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.freemarker.core.model.ArgumentArrayLayout;
+import org.apache.freemarker.core.model.TemplateDirectiveModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.CallableUtils;
+import org.apache.freemarker.core.util.CommonSupplier;
+import org.apache.freemarker.core.util.StringToIndexMap;
+
+/**
+ * Example of an AST directive that's not bound to some Configuration setting.
+ */
+//TODO [FM3][FREEMARKER-99] Delete this class when we are finished.
+class ASTDirAdhocTest2 extends ASTDirective {
+    private ASTExpression countExp;
+
+    private ASTDirAdhocTest2() {
+        //
+    }
+
+    final static CommonSupplier<ASTDirective> FACTORY = new CommonSupplier<ASTDirective>() {
+        @Override
+        public ASTDirective get() throws Exception {
+            return new ASTDirAdhocTest2();
+        }
+    };
+    
+    static TemplateDirectiveModel VALUE = new TemplateDirectiveModel() {
+
+        @Override
+        public void execute(TemplateModel[] args, CallPlace callPlace, Writer out, Environment env)
+                throws TemplateException, IOException {
+            throw new UnsupportedOperationException("execute not implemented");
+        }
+
+        @Override
+        public boolean isNestedContentSupported() {
+            return true;
+        }
+
+        @Override
+        public ArgumentArrayLayout getDirectiveArgumentArrayLayout() {
+            return ARGS_LAYOUT;
+        }
+
+    };
+
+    private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER;
+    
+    @Override
+    public void setPositionalArgument(int position, ASTExpression valueExp) throws StaticLinkingCheckException {
+        if (position == 0) {
+            countExp = valueExp;
+        } else {
+            // TODO: Standardize error message; see CallableUtils 
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                  "Too many positional arguments; can only have ",
+                  ARGS_LAYOUT.getPredefinedPositionalArgumentCount(), "."));
+        }
+    }
+
+    @Override
+    public void setNamedArgument(String name, ASTExpression valueExp) throws StaticLinkingCheckException {
+        // TODO: Standardize error message; see CallableUtils 
+        throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                "Unsupported parameter name ", new _DelayedJQuote(name), ". This directive has no named parameters."));
+    }
+
+    @Override
+    public void checkArgumentsAndSetNestedContentParameters(StringToIndexMap nestedContentParamNames)
+            throws StaticLinkingCheckException {
+        // TODO: Standardize error message; see CallableUtils 
+        if (countExp == null) {
+            throw new StaticLinkingCheckException(new _ErrorDescriptionBuilder(
+                    "Missing mandatory ", new _DelayedOrdinal(1), " positional argument"));
+        }
+
+        // TODO: Standardize error message; see CallableUtils
+        if (nestedContentParamNames != null) {
+            throw new StaticLinkingCheckException("This directive doesn't support nested content parameters.");
+        }
+    }
+
+    @Override
+    public StringToIndexMap getNestedContentParamNames() {
+        return null;
+    }
+    
+    @Override
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
+        env.pushElement(this);
+        try {
+            Writer out = env.getOut();
+            int count = CallableUtils.castArgumentValueToInt(countExp.eval(env), 0, false, 0, VALUE, false);
+            for (int i = 0; i < count; i++) {
+                out.write(".");
+            }
+            return null;
+        } finally {
+            env.popElement();
+        }
+    }
+
+    @Override
+    boolean isNestedBlockRepeater() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    String dump(boolean canonical) {
+        return "<#adhocTest2 ...>";
+    }
+
+    @Override
+    public String getLabelWithoutParameters() {
+        return "#adhocTest2";
+    }
+
+    @Override
+    int getParameterCount() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    Object getParameterValue(int idx) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    ParameterRole getParameterRole(int idx) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public boolean isNestedContentSupported() {
+        return false;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
index 26a6d69..aba716d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignment.java
@@ -102,7 +102,7 @@ final class ASTDirAssignment extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException {
+    ASTElement[] execute(Environment env) throws TemplateException {
         final Environment.Namespace namespace;
         if (namespaceExp == null) {
             switch (scope) {
@@ -187,9 +187,9 @@ final class ASTDirAssignment extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
-        String dn = getParent() instanceof ASTDirAssignmentsContainer ? null : getASTNodeDescriptor();
+        String dn = getParent() instanceof ASTDirAssignmentsContainer ? null : getLabelWithoutParameters();
         if (dn != null) {
             if (canonical) buf.append("<");
             buf.append(dn);
@@ -217,7 +217,7 @@ final class ASTDirAssignment extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return getDirectiveName(scope);
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignmentsContainer.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignmentsContainer.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignmentsContainer.java
index 3cd1e91..b572795 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignmentsContainer.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAssignmentsContainer.java
@@ -44,17 +44,17 @@ final class ASTDirAssignmentsContainer extends ASTDirective {
         this.namespaceExp = namespaceExp;
         int ln = getChildCount();
         for (int i = 0; i < ln; i++) {
-            ((ASTDirAssignment) getChild(i)).setNamespaceExp(namespaceExp);
+            ((ASTDirAssignment) fastGetChild(i)).setNamespaceExp(namespaceExp);
         }
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
         if (canonical) buf.append('<');
         buf.append(ASTDirAssignment.getDirectiveName(scope));
@@ -65,7 +65,7 @@ final class ASTDirAssignmentsContainer extends ASTDirective {
                 if (i != 0) {
                     buf.append(", ");
                 }
-                ASTDirAssignment assignment = (ASTDirAssignment) getChild(i);
+                ASTDirAssignment assignment = (ASTDirAssignment) fastGetChild(i);
                 buf.append(assignment.getCanonicalForm());
             }
         } else {
@@ -103,7 +103,7 @@ final class ASTDirAssignmentsContainer extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return ASTDirAssignment.getDirectiveName(scope);
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAttemptRecoverContainer.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAttemptRecoverContainer.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAttemptRecoverContainer.java
index 19aff47..d996420 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAttemptRecoverContainer.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAttemptRecoverContainer.java
@@ -40,20 +40,20 @@ final class ASTDirAttemptRecoverContainer extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         env.visitAttemptRecover(this, attemptedSection, recoverySection);
         return null;
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (!canonical) {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         } else {
             StringBuilder buf = new StringBuilder();
-            buf.append("<").append(getASTNodeDescriptor()).append(">");
+            buf.append("<").append(getLabelWithoutParameters()).append(">");
             buf.append(getChildrenCanonicalForm());            
-            buf.append("</").append(getASTNodeDescriptor()).append(">");
+            buf.append("</").append(getLabelWithoutParameters()).append(">");
             return buf.toString();
         }
     }
@@ -76,7 +76,7 @@ final class ASTDirAttemptRecoverContainer extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#attempt";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAutoEsc.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAutoEsc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAutoEsc.java
index b982e56..9e52f30 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAutoEsc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirAutoEsc.java
@@ -31,21 +31,21 @@ final class ASTDirAutoEsc extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getASTNodeDescriptor() + "\">" + getChildrenCanonicalForm() + "</" + getASTNodeDescriptor() + ">";
+            return "<" + getLabelWithoutParameters() + "\">" + getChildrenCanonicalForm() + "</" + getLabelWithoutParameters() + ">";
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#autoEsc";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirBreak.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirBreak.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirBreak.java
index 128e684..428f4be 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirBreak.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirBreak.java
@@ -25,17 +25,17 @@ package org.apache.freemarker.core;
 final class ASTDirBreak extends ASTDirective {
 
     @Override
-    ASTElement[] accept(Environment env) {
+    ASTElement[] execute(Environment env) {
         throw BreakOrContinueException.BREAK_INSTANCE;
     }
 
     @Override
-    protected String dump(boolean canonical) {
-        return canonical ? "<" + getASTNodeDescriptor() + "/>" : getASTNodeDescriptor();
+    String dump(boolean canonical) {
+        return canonical ? "<" + getLabelWithoutParameters() + "/>" : getLabelWithoutParameters();
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#break";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
index 6fc4c5f..f151ba5 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCapturingAssignment.java
@@ -21,6 +21,7 @@ package org.apache.freemarker.core;
 
 import java.io.IOException;
 import java.io.StringWriter;
+import java.io.Writer;
 
 import org.apache.freemarker.core.model.TemplateModel;
 import org.apache.freemarker.core.model.impl.SimpleString;
@@ -46,13 +47,19 @@ final class ASTDirCapturingAssignment extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         ASTElement[] children = getChildBuffer();
 
         TemplateModel capturedValue;
         if (children != null) {
             StringWriter out = new StringWriter();
-            env.visit(children, out);
+            Writer prevOut = env.getOut();
+            try {
+                env.setOut(out);
+                env.executeElements(children);
+            } finally {
+                env.setOut(prevOut);
+            }
             capturedValue = capturedStringToModel(out.toString());
         } else {
             capturedValue = capturedStringToModel("");
@@ -79,10 +86,10 @@ final class ASTDirCapturingAssignment extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append("<");
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         sb.append(' ');
         sb.append(varName);
         if (namespaceExp != null) {
@@ -93,7 +100,7 @@ final class ASTDirCapturingAssignment extends ASTDirective {
             sb.append('>');
             sb.append(getChildrenCanonicalForm());
             sb.append("</");
-            sb.append(getASTNodeDescriptor());
+            sb.append(getLabelWithoutParameters());
             sb.append('>');
         } else {
             sb.append(" = .nestedOutput");
@@ -102,7 +109,7 @@ final class ASTDirCapturingAssignment extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return ASTDirAssignment.getDirectiveName(scope);
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCase.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCase.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCase.java
index eca7ae4..6a4cfab 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCase.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCase.java
@@ -35,15 +35,15 @@ final class ASTDirCase extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) {
+    ASTElement[] execute(Environment env) {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         if (condition != null) {
             sb.append(' ');
             sb.append(condition.getCanonicalForm());
@@ -56,7 +56,7 @@ final class ASTDirCase extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return condition != null ? "#case" : "#default";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCompress.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCompress.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCompress.java
index 6bdda37..b03c16d 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCompress.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirCompress.java
@@ -34,30 +34,33 @@ final class ASTDirCompress extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         ASTElement[] childBuffer = getChildBuffer();
         if (childBuffer != null) {
             CompressWriter out = new CompressWriter(env.getOut(), 2048, false);
+            Writer prevOut = env.getOut();
             try {
-                env.visit(childBuffer, out);
+                env.setOut(out);
+                env.executeElements(childBuffer);
             } finally {
                 out.close();
+                env.setOut(prevOut);
             }
         }
         return null;
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getASTNodeDescriptor() + ">" + getChildrenCanonicalForm() + "</" + getASTNodeDescriptor() + ">";
+            return "<" + getLabelWithoutParameters() + ">" + getChildrenCanonicalForm() + "</" + getLabelWithoutParameters() + ">";
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#compress";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirContinue.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirContinue.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirContinue.java
index 4d18785..8ca7afa 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirContinue.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirContinue.java
@@ -25,17 +25,17 @@ package org.apache.freemarker.core;
 final class ASTDirContinue extends ASTDirective {
 
     @Override
-    ASTElement[] accept(Environment env) {
+    ASTElement[] execute(Environment env) {
         throw BreakOrContinueException.CONTINUE_INSTANCE;
     }
 
     @Override
-    protected String dump(boolean canonical) {
-        return canonical ? "<" + getASTNodeDescriptor() + "/>" : getASTNodeDescriptor();
+    String dump(boolean canonical) {
+        return canonical ? "<" + getLabelWithoutParameters() + "/>" : getLabelWithoutParameters();
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#continue";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirElseOfList.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirElseOfList.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirElseOfList.java
index 31ebea7..6126904 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirElseOfList.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirElseOfList.java
@@ -31,24 +31,24 @@ final class ASTDirElseOfList extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
-            buf.append('<').append(getASTNodeDescriptor()).append('>');
+            buf.append('<').append(getLabelWithoutParameters()).append('>');
             buf.append(getChildrenCanonicalForm());            
             return buf.toString();
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#else";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirEscape.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirEscape.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirEscape.java
index 7a324cd..57678d3 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirEscape.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirEscape.java
@@ -47,7 +47,7 @@ class ASTDirEscape extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
@@ -56,22 +56,22 @@ class ASTDirEscape extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor())
+        sb.append(getLabelWithoutParameters())
                 .append(' ').append(_StringUtils.toFTLTopLevelIdentifierReference(variable))
                 .append(" as ").append(expr.getCanonicalForm());
         if (canonical) {
             sb.append('>');
             sb.append(getChildrenCanonicalForm());
-            sb.append("</").append(getASTNodeDescriptor()).append('>');
+            sb.append("</").append(getLabelWithoutParameters()).append('>');
         }
         return sb.toString();
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#escape";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFallback.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFallback.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFallback.java
index 255de11..0ddb5d6 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFallback.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFallback.java
@@ -27,18 +27,18 @@ import java.io.IOException;
 final class ASTDirFallback extends ASTDirective {
 
     @Override
-    ASTElement[] accept(Environment env) throws IOException, TemplateException {
+    ASTElement[] execute(Environment env) throws IOException, TemplateException {
         env.fallback();
         return null;
     }
 
     @Override
-    protected String dump(boolean canonical) {
-        return canonical ? "<" + getASTNodeDescriptor() + "/>" : getASTNodeDescriptor();
+    String dump(boolean canonical) {
+        return canonical ? "<" + getLabelWithoutParameters() + "/>" : getLabelWithoutParameters();
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#fallback";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFlush.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFlush.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFlush.java
index 469eff8..0b2faed 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFlush.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirFlush.java
@@ -27,18 +27,18 @@ import java.io.IOException;
 final class ASTDirFlush extends ASTDirective {
 
     @Override
-    ASTElement[] accept(Environment env) throws IOException {
+    ASTElement[] execute(Environment env) throws IOException {
         env.getOut().flush();
         return null;
     }
 
     @Override
-    protected String dump(boolean canonical) {
-        return canonical ? "<" + getASTNodeDescriptor() + "/>" : getASTNodeDescriptor();
+    String dump(boolean canonical) {
+        return canonical ? "<" + getLabelWithoutParameters() + "/>" : getLabelWithoutParameters();
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#flush";
     }
  

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfElseIfElseContainer.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfElseIfElseContainer.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfElseIfElseContainer.java
index 8a250bf..1c8fad7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfElseIfElseContainer.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfElseIfElseContainer.java
@@ -38,10 +38,10 @@ final class ASTDirIfElseIfElseContainer extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         int ln  = getChildCount();
         for (int i = 0; i < ln; i++) {
-            ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) getChild(i);
+            ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) fastGetChild(i);
             ASTExpression condition = cblock.condition;
             env.replaceElementStackTop(cblock);
             if (condition == null || condition.evalToBoolean(env)) {
@@ -55,7 +55,7 @@ final class ASTDirIfElseIfElseContainer extends ASTDirective {
     ASTElement postParseCleanup(boolean stripWhitespace)
         throws ParseException {
         if (getChildCount() == 1) {
-            ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) getChild(0);
+            ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) fastGetChild(0);
             cblock.setLocation(getTemplate(), cblock, this);
             return cblock.postParseCleanup(stripWhitespace);
         } else {
@@ -64,23 +64,23 @@ final class ASTDirIfElseIfElseContainer extends ASTDirective {
     }
     
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
             int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
-                ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) getChild(i);
+                ASTDirIfOrElseOrElseIf cblock = (ASTDirIfOrElseOrElseIf) fastGetChild(i);
                 buf.append(cblock.dump(canonical));
             }
             buf.append("</#if>");
             return buf.toString();
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#if-#elseIf-#else-container";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfOrElseOrElseIf.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfOrElseOrElseIf.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfOrElseOrElseIf.java
index 28178d9..cb6a8cf 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfOrElseOrElseIf.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirIfOrElseOrElseIf.java
@@ -44,7 +44,7 @@ final class ASTDirIfOrElseOrElseIf extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         if (condition == null || condition.evalToBoolean(env)) {
             return getChildBuffer();
         }
@@ -52,10 +52,10 @@ final class ASTDirIfOrElseOrElseIf extends ASTDirective {
     }
     
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
         if (canonical) buf.append('<');
-        buf.append(getASTNodeDescriptor());
+        buf.append(getLabelWithoutParameters());
         if (condition != null) {
             buf.append(' ');
             buf.append(condition.getCanonicalForm());
@@ -71,7 +71,7 @@ final class ASTDirIfOrElseOrElseIf extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         if (type == TYPE_ELSE) {
             return "#else";
         } else if (type == TYPE_IF) {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirImport.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirImport.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirImport.java
index 88cfeb5..9d7b522 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirImport.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirImport.java
@@ -45,7 +45,7 @@ final class ASTDirImport extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         final String importedTemplateName = importedTemplateNameExp.evalAndCoerceToPlainText(env);
         final String fullImportedTemplateName;
         try {
@@ -68,10 +68,10 @@ final class ASTDirImport extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
         if (canonical) buf.append('<');
-        buf.append(getASTNodeDescriptor());
+        buf.append(getLabelWithoutParameters());
         buf.append(' ');
         buf.append(importedTemplateNameExp.getCanonicalForm());
         buf.append(" as ");
@@ -81,7 +81,7 @@ final class ASTDirImport extends ASTDirective {
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#import";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
index 76ce1f6..5afc30a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirInclude.java
@@ -56,7 +56,7 @@ final class ASTDirInclude extends ASTDirective {
     }
     
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         final String includedTemplateName = includedTemplateNameExp.evalAndCoerceToPlainText(env);
         final String fullIncludedTemplateName;
         try {
@@ -93,10 +93,10 @@ final class ASTDirInclude extends ASTDirective {
     }
     
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
         if (canonical) buf.append('<');
-        buf.append(getASTNodeDescriptor());
+        buf.append(getLabelWithoutParameters());
         buf.append(' ');
         buf.append(includedTemplateNameExp.getCanonicalForm());
         if (ignoreMissingExp != null) {
@@ -107,7 +107,7 @@ final class ASTDirInclude extends ASTDirective {
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#include";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
index cf85b77..04aaf94 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirItems.java
@@ -43,12 +43,12 @@ class ASTDirItems extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         final IterationContext iterCtx = ASTDirList.findEnclosingIterationContext(env, null);
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new TemplateException(env,
-                    getASTNodeDescriptor(), " without iteration in context");
+                    getLabelWithoutParameters(), " without iteration in context");
         }
         
         iterCtx.loopForItemsElement(env, getChildBuffer(), nestedContentParamName, nestedContentParam2Name);
@@ -61,10 +61,10 @@ class ASTDirItems extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         sb.append(" as ");
         sb.append(_StringUtils.toFTLTopLevelIdentifierReference(nestedContentParamName));
         if (nestedContentParam2Name != null) {
@@ -75,14 +75,14 @@ class ASTDirItems extends ASTDirective {
             sb.append('>');
             sb.append(getChildrenCanonicalForm());
             sb.append("</");
-            sb.append(getASTNodeDescriptor());
+            sb.append(getLabelWithoutParameters());
             sb.append('>');
         }
         return sb.toString();
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#items";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
index 8034018..201c96c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirList.java
@@ -78,7 +78,7 @@ final class ASTDirList extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         acceptWithResult(env);
         return null;
     }
@@ -117,10 +117,10 @@ final class ASTDirList extends ASTDirective {
     }
     
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
         if (canonical) buf.append('<');
-        buf.append(getASTNodeDescriptor());
+        buf.append(getLabelWithoutParameters());
         buf.append(' ');
         buf.append(listedExp.getCanonicalForm());
         if (nestedContentParamName != null) {
@@ -136,7 +136,7 @@ final class ASTDirList extends ASTDirective {
             buf.append(getChildrenCanonicalForm());
             if (!(getParent() instanceof ASTDirListElseContainer)) {
                 buf.append("</");
-                buf.append(getASTNodeDescriptor());
+                buf.append(getLabelWithoutParameters());
                 buf.append('>');
             }
         }
@@ -179,7 +179,7 @@ final class ASTDirList extends ASTDirective {
     }    
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#list";
     }
 
@@ -266,7 +266,7 @@ final class ASTDirList extends ASTDirective {
                                 nestedContentParam = iterModel.next();
                                 hasNext = iterModel.hasNext();
                                 try {
-                                    env.visit(childBuffer);
+                                    env.executeElements(childBuffer);
                                 } catch (BreakOrContinueException br) {
                                     if (br == BreakOrContinueException.BREAK_INSTANCE) {
                                         break listLoop;
@@ -279,7 +279,7 @@ final class ASTDirList extends ASTDirective {
                         // We must reuse this later, because TemplateIterableModel-s that wrap an Iterator only
                         // allow one iterator() call.
                         openedIterator = iterModel;
-                        env.visit(childBuffer);
+                        env.executeElements(childBuffer);
                     }
                 }
             } else if (listedValue instanceof TemplateHashModelEx) {
@@ -316,7 +316,7 @@ final class ASTDirList extends ASTDirective {
                                 nestedContentParam2 = kvp.getValue();
                                 hasNext = kvpIter.hasNext();
                                 try {
-                                    env.visit(childBuffer);
+                                    env.executeElements(childBuffer);
                                 } catch (BreakOrContinueException br) {
                                     if (br == BreakOrContinueException.BREAK_INSTANCE) {
                                         break listLoop;
@@ -328,7 +328,7 @@ final class ASTDirList extends ASTDirective {
                     } else {
                         // We will reuse this at the #iterms
                         openedIterator = kvpIter;
-                        env.visit(childBuffer);
+                        env.executeElements(childBuffer);
                     }
                 }
             } else if (listedValue instanceof TemplateIterableModel) {

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirListElseContainer.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirListElseContainer.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirListElseContainer.java
index 686b03c..47cc9f7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirListElseContainer.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirListElseContainer.java
@@ -37,9 +37,9 @@ class ASTDirListElseContainer extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         if (!listPart.acceptWithResult(env)) {
-            return elsePart.accept(env);
+            return elsePart.execute(env);
         }
         return null;
     }
@@ -50,23 +50,23 @@ class ASTDirListElseContainer extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
             int ln = getChildCount();
             for (int i = 0; i < ln; i++) {
-                ASTElement element = getChild(i);
+                ASTElement element = fastGetChild(i);
                 buf.append(element.dump(canonical));
             }
             buf.append("</#list>");
             return buf.toString();
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#list-#else-container";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
index 786bfcf..3c64eaa 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirMacroOrFunction.java
@@ -112,13 +112,13 @@ final class ASTDirMacroOrFunction extends ASTDirective implements TemplateModel
     }
 
     @Override
-    ASTElement[] accept(Environment env) {
+    ASTElement[] execute(Environment env) {
         env.visitMacroOrFunctionDefinition(this);
         return null;
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return function ? "#function" : "#macro";
     }
 
@@ -138,10 +138,10 @@ final class ASTDirMacroOrFunction extends ASTDirective implements TemplateModel
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         sb.append(' ');
         sb.append(_StringUtils.toFTLTopLevelTragetIdentifier(name));
 
@@ -236,7 +236,7 @@ final class ASTDirMacroOrFunction extends ASTDirective implements TemplateModel
         if (canonical) {
             sb.append('>');
             sb.append(getChildrenCanonicalForm());
-            sb.append("</").append(getASTNodeDescriptor()).append('>');
+            sb.append("</").append(getLabelWithoutParameters()).append('>');
         }
         return sb.toString();
     }

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
index 25344e6..df0074e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNested.java
@@ -42,7 +42,7 @@ final class ASTDirNested extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws IOException, TemplateException {
+    ASTElement[] execute(Environment env) throws IOException, TemplateException {
         CallPlace macroCallPlace = env.getCurrentMacroContext().callPlace;
 
         // When nestedContParamCnt < nestedContentParameters.getCollectionSize(), then we just skip calculating the
@@ -68,10 +68,10 @@ final class ASTDirNested extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         if (nestedContentParameters != null) {
             for (int i = 0; i < nestedContentParameters.size(); i++) {
                 sb.append(' ');
@@ -83,7 +83,7 @@ final class ASTDirNested extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#nested";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoAutoEsc.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoAutoEsc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoAutoEsc.java
index a3ef03d..5bbc6c7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoAutoEsc.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoAutoEsc.java
@@ -31,21 +31,21 @@ final class ASTDirNoAutoEsc extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getASTNodeDescriptor() + "\">" + getChildrenCanonicalForm() + "</" + getASTNodeDescriptor() + ">";
+            return "<" + getLabelWithoutParameters() + "\">" + getChildrenCanonicalForm() + "</" + getLabelWithoutParameters() + ">";
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#noAutoEsc";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoEscape.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoEscape.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoEscape.java
index 61a32b8..b3af156 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoEscape.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirNoEscape.java
@@ -31,17 +31,17 @@ class ASTDirNoEscape extends ASTDirective {
     }
     
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getASTNodeDescriptor() + '>' + getChildrenCanonicalForm()
-                    + "</" + getASTNodeDescriptor() + '>';
+            return "<" + getLabelWithoutParameters() + '>' + getChildrenCanonicalForm()
+                    + "</" + getLabelWithoutParameters() + '>';
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
 
@@ -61,7 +61,7 @@ class ASTDirNoEscape extends ASTDirective {
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#noEscape";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirOutputFormat.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirOutputFormat.java
index c2aa7b8..33912ab 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirOutputFormat.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirOutputFormat.java
@@ -34,22 +34,22 @@ final class ASTDirOutputFormat extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
-            return "<" + getASTNodeDescriptor() + " \"" + paramExp.getCanonicalForm() + "\">"
-                    + getChildrenCanonicalForm() + "</" + getASTNodeDescriptor() + ">";
+            return "<" + getLabelWithoutParameters() + " \"" + paramExp.getCanonicalForm() + "\">"
+                    + getChildrenCanonicalForm() + "</" + getLabelWithoutParameters() + ">";
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
     
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#outputFormat";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecover.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecover.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecover.java
index 7879f18..b441eeb 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecover.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecover.java
@@ -31,24 +31,24 @@ final class ASTDirRecover extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException, IOException {
+    ASTElement[] execute(Environment env) throws TemplateException, IOException {
         return getChildBuffer();
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         if (canonical) {
             StringBuilder buf = new StringBuilder();
-            buf.append('<').append(getASTNodeDescriptor()).append('>');
+            buf.append('<').append(getLabelWithoutParameters()).append('>');
             buf.append(getChildrenCanonicalForm());            
             return buf.toString();
         } else {
-            return getASTNodeDescriptor();
+            return getLabelWithoutParameters();
         }
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#recover";
     }
     

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
index 1904edc..1a491f5 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirRecurse.java
@@ -41,7 +41,7 @@ final class ASTDirRecurse extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws IOException, TemplateException {
+    ASTElement[] execute(Environment env) throws IOException, TemplateException {
         TemplateModel node = targetNode == null ? null : targetNode.eval(env);
         if (node != null && !(node instanceof TemplateNodeModel)) {
             throw MessageUtils.newUnexpectedOperandTypeException(
@@ -75,10 +75,10 @@ final class ASTDirRecurse extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         if (targetNode != null) {
             sb.append(' ');
             sb.append(targetNode.getCanonicalForm());
@@ -92,7 +92,7 @@ final class ASTDirRecurse extends ASTDirective {
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#recurse";
     }
 

http://git-wip-us.apache.org/repos/asf/freemarker/blob/e8e58ffa/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
index c5d23b3..5a24eef 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirReturn.java
@@ -31,7 +31,7 @@ final class ASTDirReturn extends ASTDirective {
     }
 
     @Override
-    ASTElement[] accept(Environment env) throws TemplateException {
+    ASTElement[] execute(Environment env) throws TemplateException {
         if (exp != null) {
             env.setLastReturnValue(exp.eval(env));
         }
@@ -43,10 +43,10 @@ final class ASTDirReturn extends ASTDirective {
     }
 
     @Override
-    protected String dump(boolean canonical) {
+    String dump(boolean canonical) {
         StringBuilder sb = new StringBuilder();
         if (canonical) sb.append('<');
-        sb.append(getASTNodeDescriptor());
+        sb.append(getLabelWithoutParameters());
         if (exp != null) {
             sb.append(' ');
             sb.append(exp.getCanonicalForm());
@@ -56,7 +56,7 @@ final class ASTDirReturn extends ASTDirective {
     }
 
     @Override
-    String getASTNodeDescriptor() {
+    public String getLabelWithoutParameters() {
         return "#return";
     }