freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [23/23] incubator-freemarker git commit: Merge remote-tracking branch 'origin/2.3-gae'
Date Tue, 15 Dec 2015 23:26:28 GMT
Merge remote-tracking branch 'origin/2.3-gae'

Conflicts:
	src/main/java/freemarker/core/DebugBreak.java
	src/main/java/freemarker/core/Environment.java
	src/main/java/freemarker/core/Expression.java
	src/main/java/freemarker/core/IfBlock.java
	src/main/java/freemarker/core/Macro.java
	src/main/java/freemarker/core/TemplateObject.java
	src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
	src/main/java/freemarker/core/UnifiedCall.java
	src/main/javacc/FTL.jj


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

Branch: refs/heads/master
Commit: 88d5c7f3de77c6b023a26cb68c841a8ea905be3c
Parents: 407b6b8 204723f
Author: ddekany <ddekany@apache.org>
Authored: Wed Dec 16 00:25:36 2015 +0100
Committer: ddekany <ddekany@apache.org>
Committed: Wed Dec 16 00:25:36 2015 +0100

----------------------------------------------------------------------
 .../cache/TemplateLookupStrategy.java           |   2 +-
 src/main/java/freemarker/core/Assignment.java   |   3 +-
 .../freemarker/core/AssignmentInstruction.java  |  26 +-
 src/main/java/freemarker/core/AttemptBlock.java |  38 +-
 src/main/java/freemarker/core/AutoEscBlock.java |  17 +-
 .../java/freemarker/core/BlockAssignment.java   |  14 +-
 .../java/freemarker/core/BodyInstruction.java   |   8 +-
 .../java/freemarker/core/BoundCallable.java     |   4 +-
 .../java/freemarker/core/BreakInstruction.java  |   2 +-
 .../core/CallableInvocationContext.java         |  18 +-
 src/main/java/freemarker/core/Case.java         |  17 +-
 src/main/java/freemarker/core/Comment.java      |   3 +-
 .../java/freemarker/core/CompressedBlock.java   |  19 +-
 .../java/freemarker/core/ConditionalBlock.java  |  18 +-
 src/main/java/freemarker/core/DebugBreak.java   |  14 +-
 .../java/freemarker/core/DollarVariable.java    |   3 +-
 src/main/java/freemarker/core/ElseOfList.java   |  14 +-
 src/main/java/freemarker/core/Environment.java  | 192 +++++----
 src/main/java/freemarker/core/EscapeBlock.java  |  19 +-
 src/main/java/freemarker/core/Expression.java   |   3 +-
 .../freemarker/core/FallbackInstruction.java    |   8 +-
 .../java/freemarker/core/FlushInstruction.java  |   3 +-
 src/main/java/freemarker/core/IfBlock.java      |  30 +-
 src/main/java/freemarker/core/Include.java      |   9 +-
 src/main/java/freemarker/core/Items.java        |  11 +-
 .../java/freemarker/core/IteratorBlock.java     |  51 +--
 src/main/java/freemarker/core/LibraryLoad.java  |   9 +-
 .../java/freemarker/core/ListElseContainer.java |  15 +-
 .../java/freemarker/core/LocalContextStack.java |  39 ++
 src/main/java/freemarker/core/MixedContent.java |  43 +-
 .../NestedContentNotSupportedException.java     |   5 +-
 .../java/freemarker/core/NoAutoEscBlock.java    |  17 +-
 .../java/freemarker/core/NoEscapeBlock.java     |  12 +-
 .../java/freemarker/core/NumericalOutput.java   |   3 +-
 .../java/freemarker/core/OutputFormatBlock.java |  17 +-
 .../java/freemarker/core/PropertySetting.java   |   3 +-
 .../java/freemarker/core/RecoveryBlock.java     |  14 +-
 src/main/java/freemarker/core/RecurseNode.java  |   8 +-
 .../java/freemarker/core/ReturnInstruction.java |  13 +-
 src/main/java/freemarker/core/Sep.java          |  11 +-
 .../freemarker/core/StackTraceVisibility.java   |   8 +
 .../java/freemarker/core/StopInstruction.java   |   2 +-
 src/main/java/freemarker/core/SwitchBlock.java  |  19 +-
 .../java/freemarker/core/TemplateElement.java   | 430 +++++++++----------
 .../core/TemplateElementArrayBuilder.java       |  80 ++++
 .../core/TemplateElementsToVisit.java           |  30 ++
 .../java/freemarker/core/TemplateObject.java    |  29 +-
 src/main/java/freemarker/core/TextBlock.java    |  44 +-
 ...nterruptionSupportTemplatePostProcessor.java |  36 +-
 .../java/freemarker/core/TransformBlock.java    |  18 +-
 .../java/freemarker/core/TrimInstruction.java   |   5 +-
 .../java/freemarker/core/UnboundCallable.java   |  13 +-
 src/main/java/freemarker/core/UnifiedCall.java  |  35 +-
 src/main/java/freemarker/core/VisitNode.java    |   8 +-
 src/main/java/freemarker/core/_CoreAPI.java     |   8 +
 .../debug/impl/RmiDebuggerService.java          |  13 +-
 .../freemarker/template/SimpleSequence.java     |   1 -
 src/main/java/freemarker/template/Template.java |  37 +-
 src/main/javacc/FTL.jj                          | 220 +++++-----
 src/manual/book.xml                             |  24 ++
 src/test/java/freemarker/core/ASTPrinter.java   |  51 ++-
 src/test/java/freemarker/core/ASTTest.java      |   4 +
 .../core/ast-multipleignoredchildren.ast        |  12 +
 .../core/ast-multipleignoredchildren.ftl        |  15 +
 64 files changed, 1070 insertions(+), 827 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/BodyInstruction.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/BodyInstruction.java
index a0795c1,0c82841..db7527b
--- a/src/main/java/freemarker/core/BodyInstruction.java
+++ b/src/main/java/freemarker/core/BodyInstruction.java
@@@ -113,8 -114,13 +114,13 @@@ final class BodyInstruction extends Tem
      }
      */
      
+     @Override
+     boolean isShownInStackTrace() {
+         return true;
+     }
+ 
      class Context implements LocalContext {
 -        Macro.Context invokingMacroContext;
 +        CallableInvocationContext invokingMacroContext;
          Environment.Namespace bodyVars;
          
          Context(Environment env) throws TemplateException {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/BoundCallable.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/BoundCallable.java
index 65105c5,0000000..1855c08
mode 100644,000000..100644
--- a/src/main/java/freemarker/core/BoundCallable.java
+++ b/src/main/java/freemarker/core/BoundCallable.java
@@@ -1,155 -1,0 +1,155 @@@
 +/*
 + * 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.
 + */
 +
 +package freemarker.core;
 +
 +import java.io.IOException;
 +
 +import freemarker.core.Environment.Namespace;
 +import freemarker.template.Template;
 +import freemarker.template.TemplateException;
 +
 +/**
 + * A macro or function (or other future callable entity) associated to a namespace and a template.
 + * 
 + * <p>
 + * With an analogy, a {@link UnboundCallable} is like a non-static {@link java.lang.reflect.Method} in Java; it
 + * describes everything about the method, but it isn't bound to any object on which the method could be called.
 + * Continuing this analogy, a {@link BoundCallable} is like a {@link java.lang.reflect.Method} paired with the object
 + * whose method it is (the {@code this} object), and is thus callable in itself. In the case of FTL macros and FTL
 + * functions, instead of a single {@code this} object, we have two such objects: a namespace and a template. (One may
 + * wonder why the namespace is not enough, given that a namespace already specifies a template (
 + * {@link Namespace#getTemplate()} ). It's because a namespace can contain macros from included templates, and so the
 + * template that the callable belongs to isn't always the same as {@link Namespace#getTemplate()}, which just gives the
 + * "root" template of the namespace. Furthermore, several namespaces my include exactly the same template, so we can't
 + * get away with a template instead of a namespace either. Also note that knowing which template we are in is needed for
 + * example to resolve relative references to other templates.)
 + * 
 + * <p>
 + * Historical note: Prior to 2.4, the two concepts ({@link UnboundCallable} and {@link BoundCallable}) were these same,
 + * represented by {@link Macro}, which still exists due to backward compatibility constraints. This class extends
 + * {@link Macro} only for the sake of legacy applications which expect macro and function FTL variables to be
 + * {@link Macro}-s. Especially, this class should not extend {@link TemplateElement} (which it does, because
 + * {@link Macro} is a subclass of that), but it had to, for backward compatibility. It just delegates the {@link Macro}
 + * methods to the embedded {@link UnboundCallable}.
 + * 
 + * @see UnboundCallable
 + * 
 + * @since 2.4.0
 + */
 +final class BoundCallable extends Macro {
 +    
 +    private final UnboundCallable unboundCallable;
 +    private final Template template;
 +    private final Namespace namespace;
 +    
 +    BoundCallable(UnboundCallable callableDefinition, Template template, Namespace namespace) {
 +        this.unboundCallable = callableDefinition;
 +        this.template = template;
 +        this.namespace = namespace;
 +    }
 +
 +    UnboundCallable getUnboundCallable() {
 +        return unboundCallable;
 +    }
 +    
 +    Template getTemplate() {
 +        return template;
 +    }
 +    
 +    Namespace getNamespace() {
 +        return namespace;
 +    }
 +
 +    @Override
 +    public String toString() {
 +        return "BoundCallable("
 +                + "name=" + getName()
 +                + ", isFunction=" + isFunction()
 +                + ", template" + (template != null ? ".name=" + template.getName() : "=null")
 +                + ", namespace=" + (namespace != null ? namespace.getTemplate().getName() : "null")
 +                + ")";
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    public String getCatchAll() {
 +        return unboundCallable.getCatchAll();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    public String[] getArgumentNames() {
 +        return unboundCallable.getArgumentNames();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    public String getName() {
 +        return unboundCallable.getName();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    public boolean isFunction() {
 +        return unboundCallable.isFunction();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
-     void accept(Environment env) throws TemplateException, IOException {
-         unboundCallable.accept(env);
++    TemplateElement[] accept(Environment env) throws TemplateException, IOException {
++        return unboundCallable.accept(env);
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    protected String dump(boolean canonical) {
 +        return unboundCallable.dump(canonical);
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    String getNodeTypeSymbol() {
 +        return unboundCallable.getNodeTypeSymbol();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    int getParameterCount() {
 +        return unboundCallable.getParameterCount();
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    Object getParameterValue(int idx) {
 +        return unboundCallable.getParameterValue(idx);
 +    }
 +
 +    /** For backward compatibility only; delegates to the {@link UnboundCallable}'s identical method. */
 +    @Override
 +    ParameterRole getParameterRole(int idx) {
 +        return unboundCallable.getParameterRole(idx);
 +    }
 +
 +    @Override
 +    boolean isNestedBlockRepeater() {
 +        return unboundCallable.isNestedBlockRepeater();
 +    }
 +    
 +}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/CallableInvocationContext.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/CallableInvocationContext.java
index 1d0f731,0000000..d2e204b
mode 100644,000000..100644
--- a/src/main/java/freemarker/core/CallableInvocationContext.java
+++ b/src/main/java/freemarker/core/CallableInvocationContext.java
@@@ -1,164 -1,0 +1,154 @@@
 +/*
 + * 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.
 + */
 +
 +package freemarker.core;
 +
- import java.io.IOException;
- import java.util.ArrayList;
 +import java.util.Collection;
 +import java.util.HashSet;
 +import java.util.List;
 +
 +import freemarker.template.Template;
 +import freemarker.template.TemplateException;
 +import freemarker.template.TemplateModel;
 +import freemarker.template.TemplateModelException;
 +import freemarker.template.TemplateModelIterator;
 +
 +/**
 + * The local variables and such of an FTL macro or FTL function (or other future FTL callable) call.
 + */
 +class CallableInvocationContext implements LocalContext {
 +    final UnboundCallable callableDefinition;
 +    final Environment.Namespace localVars; 
-     final TemplateElement nestedContent;
++    final TemplateElement[] nestedContentBuffer;
 +    final Environment.Namespace nestedContentNamespace;
 +    final Template nestedContentTemplate;
 +    final List nestedContentParameterNames;
-     final ArrayList prevLocalContextStack;
++    final LocalContextStack prevLocalContextStack;
 +    final CallableInvocationContext prevMacroContext;
 +    
 +    CallableInvocationContext(UnboundCallable callableDefinition,
 +            Environment env, 
-             TemplateElement nestedContent,
++            TemplateElement[] nestedContentBuffer,
 +            List nestedContentParameterNames) {
 +        this.callableDefinition = callableDefinition;
 +        this.localVars = env.new Namespace();
-         this.nestedContent = nestedContent;
++        this.nestedContentBuffer = nestedContentBuffer;
 +        this.nestedContentNamespace = env.getCurrentNamespace();
 +        this.nestedContentTemplate = env.getCurrentTemplate();
 +        this.nestedContentParameterNames = nestedContentParameterNames;
 +        this.prevLocalContextStack = env.getLocalContextStack();
 +        this.prevMacroContext = env.getCurrentMacroContext();
 +    }
 +    
 +    Macro getCallableDefinition() {
 +        return callableDefinition;
 +    }
 +
-     void invoce(Environment env) throws TemplateException, IOException {
-         sanityCheck(env);
-         // Set default values for unspecified parameters
-         if (callableDefinition.getNestedBlock() != null) {
-             env.visit(callableDefinition.getNestedBlock());
-         }
-     }
- 
 +    // Set default parameters, check if all the required parameters are defined.
 +    void sanityCheck(Environment env) throws TemplateException {
 +        boolean resolvedAnArg, hasUnresolvedArg;
 +        Expression firstUnresolvedExpression;
 +        InvalidReferenceException firstReferenceException;
 +        do {
 +            firstUnresolvedExpression = null;
 +            firstReferenceException = null;
 +            resolvedAnArg = hasUnresolvedArg = false;
 +            for (int i = 0; i < callableDefinition.getParamNames().length; ++i) {
 +                String argName = callableDefinition.getParamNames()[i];
 +                if (localVars.get(argName) == null) {
 +                    Expression valueExp = (Expression) callableDefinition.getParamDefaults().get(argName);
 +                    if (valueExp != null) {
 +                        try {
 +                            TemplateModel tm = valueExp.eval(env);
 +                            if (tm == null) {
 +                                if (!hasUnresolvedArg) {
 +                                    firstUnresolvedExpression = valueExp;
 +                                    hasUnresolvedArg = true;
 +                                }
 +                            } else {
 +                                localVars.put(argName, tm);
 +                                resolvedAnArg = true;
 +                            }
 +                        } catch (InvalidReferenceException e) {
 +                            if (!hasUnresolvedArg) {
 +                                hasUnresolvedArg = true;
 +                                firstReferenceException = e;
 +                            }
 +                        }
 +                    } else if (!env.isClassicCompatible()) {
 +                        boolean argWasSpecified = localVars.containsKey(argName);
 +                        throw new _MiscTemplateException(env,
 +                                new _ErrorDescriptionBuilder(new Object[] {
 +                                        "When calling macro ", new _DelayedJQuote(callableDefinition.getName()), 
 +                                        ", required parameter ", new _DelayedJQuote(argName),
 +                                        " (parameter #", Integer.valueOf(i + 1), ") was ", 
 +                                        (argWasSpecified
 +                                                ? "specified, but had null/missing value."
 +                                                : "not specified.") 
 +                                }).tip(argWasSpecified
 +                                        ? new Object[] {
 +                                                "If the parameter value expression on the caller side is known to "
 +                                                + "be legally null/missing, you may want to specify a default "
 +                                                + "value for it with the \"!\" operator, like "
 +                                                + "paramValue!defaultValue." }
 +                                        : new Object[] { 
 +                                                "If the omission was deliberate, you may consider making the "
 +                                                + "parameter optional in the macro by specifying a default value "
 +                                                + "for it, like ", "<#macro macroName paramName=defaultExpr>", ")" }
 +                                        ));
 +                    }
 +                }
 +            }
 +        } while (resolvedAnArg && hasUnresolvedArg);
 +        if (hasUnresolvedArg) {
 +            if (firstReferenceException != null) {
 +                throw firstReferenceException;
 +            } else if (!env.isClassicCompatible()) {
 +                throw InvalidReferenceException.getInstance(firstUnresolvedExpression, env);
 +            }
 +        }
 +    }
 +
 +    /**
 +     * @return the local variable of the given name
 +     * or null if it doesn't exist.
 +     */ 
 +    public TemplateModel getLocalVariable(String name) throws TemplateModelException {
 +         return localVars.get(name);
 +    }
 +
 +    Environment.Namespace getLocals() {
 +        return localVars;
 +    }
 +    
 +    /**
 +     * Set a local variable in this macro 
 +     */
 +    void setLocalVar(String name, TemplateModel var) {
 +        localVars.put(name, var);
 +    }
 +
 +    public Collection getLocalVariableNames() throws TemplateModelException {
 +        HashSet result = new HashSet();
 +        for (TemplateModelIterator it = localVars.keys().iterator(); it.hasNext(); ) {
 +            result.add(it.next().toString());
 +        }
 +        return result;
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/DebugBreak.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/DebugBreak.java
index 53ad26c,2fa1cf3..32efcac
--- a/src/main/java/freemarker/core/DebugBreak.java
+++ b/src/main/java/freemarker/core/DebugBreak.java
@@@ -38,12 -38,12 +38,12 @@@ public class DebugBreak extends Templat
      }
      
      @Override
-     protected void accept(Environment env) throws TemplateException, IOException {
+     protected TemplateElement[] accept(Environment env) throws TemplateException, IOException {
          if (!DebuggerService.suspendEnvironment(
-                 env, this.getUnboundTemplate().getSourceName(), getNestedBlock().getBeginLine())) {
-             getNestedBlock().accept(env);
 -                env, this.getTemplate().getSourceName(), getChild(0).getBeginLine())) {
++                env, this.getUnboundTemplate().getSourceName(), getChild(0).getBeginLine())) {
+             return getChild(0).accept(env);
          } else {
-             throw new StopException(env, "Stopped by debugger");        
+             throw new StopException(env, "Stopped by debugger");
          }
      }
  

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/Environment.java
index 6242cea,ef7a301..55a0ad9
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@@ -155,14 -157,11 +157,14 @@@ public final class Environment extends 
  
      private Collator cachedCollator;
  
 +    private Template currentTemplate;
 +    private Namespace currentNamespace;
 +    private CallableInvocationContext currentMacroContext;
 +    
      private Writer out;
-     private ArrayList localContextStack;
 -    private Macro.Context currentMacroContext;
+     private LocalContextStack localContextStack;
      private final Namespace mainNamespace;
 -    private Namespace currentNamespace, globalNamespace;
 +    private Namespace globalNamespace;
      private HashMap loadedLibs;
      private Configurable legacyParent;
  
@@@ -251,8 -250,10 +253,9 @@@
       * 
       * @since 2.3.23
       */
+     @SuppressFBWarnings(value = "RANGE_ARRAY_INDEX", justification = "False alarm")
      public Template getCurrentTemplate() {
 -        int ln = instructionStackSize;
 -        return ln == 0 ? getMainTemplate() : instructionStack[ln - 1].getTemplate();
 +        return currentTemplate;
      }
  
      /**
@@@ -508,18 -547,13 +549,18 @@@
       * Used for {@code #nested}.
       */
      void invokeNestedContent(BodyInstruction.Context bodyCtx) throws TemplateException, IOException {
 -        Macro.Context invokingMacroContext = getCurrentMacroContext();
 +        CallableInvocationContext invokingMacroContext = getCurrentMacroContext();
-         ArrayList prevLocalContextStack = localContextStack;
-         TemplateElement nestedContent = invokingMacroContext.nestedContent;
-         if (nestedContent != null) {
+         LocalContextStack prevLocalContextStack = localContextStack;
+         TemplateElement[] nestedContentBuffer = invokingMacroContext.nestedContentBuffer;
+         if (nestedContentBuffer != null) {
              this.currentMacroContext = invokingMacroContext.prevMacroContext;
 +            
 +            final Namespace prevCurrentNamespace = currentNamespace;  
              currentNamespace = invokingMacroContext.nestedContentNamespace;
  
 +            final Template prevCurrentTemplate = currentTemplate;
 +            currentTemplate = invokingMacroContext.nestedContentTemplate;
 +            
              final Configurable prevParent;
              final boolean parentReplacementOn = isBeforeIcI2322();
              prevParent = getParent();
@@@ -534,14 -568,13 +575,14 @@@
                  pushLocalContext(bodyCtx);
              }
              try {
-                 visit(nestedContent);
+                 visit(nestedContentBuffer);
              } finally {
                  if (invokingMacroContext.nestedContentParameterNames != null) {
-                     popLocalContext();
+                     localContextStack.pop();
                  }
                  this.currentMacroContext = invokingMacroContext;
 -                currentNamespace = getMacroNamespace(invokingMacroContext.getMacro());
 +                currentNamespace = prevCurrentNamespace;
 +                currentTemplate = prevCurrentTemplate;
                  if (parentReplacementOn) {
                      setParent(prevParent);
                  } else {
@@@ -655,33 -688,30 +696,34 @@@
      /**
       * Calls the macro or function with the given arguments and nested block.
       */
 -    void invoke(Macro macro,
 +    void invoke(BoundCallable boundCallable, 
              Map namedArgs, List positionalArgs,
-             List bodyParameterNames, TemplateElement nestedBlock) throws TemplateException, IOException {
+             List bodyParameterNames, TemplateElement[] childBuffer) throws TemplateException, IOException {
 -        if (macro == Macro.DO_NOTHING_MACRO) {
 +        UnboundCallable unboundCallable = boundCallable.getUnboundCallable();
 +        if (unboundCallable == UnboundCallable.NO_OP_MACRO) {
              return;
          }
  
 -        pushElement(macro);
 +        pushElement(unboundCallable);
          try {
-             final CallableInvocationContext macroCtx = new CallableInvocationContext(unboundCallable, this, nestedBlock, bodyParameterNames);
 -            final Macro.Context macroCtx = macro.new Context(this, childBuffer, bodyParameterNames);
 -            setMacroContextLocalsFromArguments(macroCtx, macro, namedArgs, positionalArgs);
++            final CallableInvocationContext macroCtx = new CallableInvocationContext(unboundCallable, this, childBuffer, bodyParameterNames);
 +            setMacroContextLocalsFromArguments(macroCtx, unboundCallable, namedArgs, positionalArgs);
  
 -            final Macro.Context prevMacroCtx = currentMacroContext;
 +            final CallableInvocationContext prevMacroCtx = currentMacroContext;
              currentMacroContext = macroCtx;
  
-             final ArrayList prevLocalContextStack = localContextStack;
+             final LocalContextStack prevLocalContextStack = localContextStack;
              localContextStack = null;
  
 -            final Namespace prevNamespace = currentNamespace;
 -            currentNamespace = (Namespace) macroToNamespaceLookup.get(macro);
 +            final Namespace prevCurrentNamespace = currentNamespace;
 +            currentNamespace = boundCallable.getNamespace();
 +            
 +            final Template prevCurrentTemplate = currentTemplate;
 +            currentTemplate = boundCallable.getTemplate();
  
              try {
-                 macroCtx.invoce(this);
+                 macroCtx.sanityCheck(this);
 -                visit(macro.getChildBuffer());
++                visit(unboundCallable.getChildBuffer());
              } catch (ReturnInstruction.Return re) {
                  // Not an error, just a <#return>
              } catch (TemplateException te) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/Expression.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/Expression.java
index 10ff7df,1b07c7d..fec7090
--- a/src/main/java/freemarker/core/Expression.java
+++ b/src/main/java/freemarker/core/Expression.java
@@@ -57,9 -58,8 +57,8 @@@ abstract public class Expression extend
      // Hook in here to set the constant value if possible.
      
      @Override
-     void setLocation(UnboundTemplate unboundTemplate, int beginColumn, int beginLine, int endColumn, int endLine)
-     throws ParseException {
 -    void setLocation(Template template, int beginColumn, int beginLine, int endColumn, int endLine) {
 -        super.setLocation(template, beginColumn, beginLine, endColumn, endLine);
++    void setLocation(UnboundTemplate unboundTemplate, int beginColumn, int beginLine, int endColumn, int endLine) {
 +        super.setLocation(unboundTemplate, beginColumn, beginLine, endColumn, endLine);
          if (isLiteral()) {
              try {
                  constantValue = _eval(null);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/IfBlock.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/IfBlock.java
index 0bb476f,42526f3..69b3484
--- a/src/main/java/freemarker/core/IfBlock.java
+++ b/src/main/java/freemarker/core/IfBlock.java
@@@ -58,10 -56,9 +56,9 @@@ final class IfBlock extends TemplateEle
      @Override
      TemplateElement postParseCleanup(boolean stripWhitespace)
          throws ParseException {
-         if (getRegulatedChildCount() == 1) {
-             ConditionalBlock cblock = (ConditionalBlock) getRegulatedChild(0);
-             cblock.isLonelyIf = true;
+         if (getChildCount() == 1) {
+             ConditionalBlock cblock = (ConditionalBlock) getChild(0);
 -            cblock.setLocation(getTemplate(), cblock, this);
 +            cblock.setLocation(getUnboundTemplate(), cblock, this);
              return cblock.postParseCleanup(stripWhitespace);
          } else {
              return super.postParseCleanup(stripWhitespace);

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/Include.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/LibraryLoad.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/PropertySetting.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/TemplateElementArrayBuilder.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/TemplateElementArrayBuilder.java
index 0000000,c18e48b..70a990b
mode 000000,100644..100644
--- a/src/main/java/freemarker/core/TemplateElementArrayBuilder.java
+++ b/src/main/java/freemarker/core/TemplateElementArrayBuilder.java
@@@ -1,0 -1,80 +1,80 @@@
+ package freemarker.core;
+ 
+ import freemarker.template.utility.CollectionUtils;
+ 
+ /**
+  * Holds an buffer (array) of {@link TemplateElement}-s with the count of the utilized items in it. The un-utilized tail
+  * of the array must only contain {@code null}-s.
+  * 
+  * @since 2.3.24
+  */
+ class TemplateElements {
+     
+     static final TemplateElements EMPTY = new TemplateElements(null, 0);
+ 
+     private final TemplateElement[] buffer;
+     private final int count;
+ 
+     /**
+      * @param buffer
+      *            The buffer; {@code null} exactly if {@code count} is 0.
+      * @param count
+      *            The number of utilized buffer elements; if 0, then {@code null} must be {@code null}.
+      */
+     TemplateElements(TemplateElement[] buffer, int count) {
+         /*
+         // Assertion:
+         if (count == 0 && buffer != null) {
+             throw new IllegalArgumentException(); 
+         }
+         */
+         
+         this.buffer = buffer;
+         this.count = count;
+     }
+ 
+     TemplateElement[] getBuffer() {
+         return buffer;
+     }
+ 
+     int getCount() {
+         return count;
+     }
+     
+     TemplateElement getLast() {
+         return buffer != null ? buffer[count - 1] : null;
+     }
+     
+     /**
+      * Used for some backward compatibility hacks.
+      */
+     TemplateElement asSingleElement() {
+         if (count == 0) {
+             return new TextBlock(CollectionUtils.EMPTY_CHAR_ARRAY, false); 
+         } else {
+             TemplateElement first = buffer[0];
+             if (count == 1) {
+                 return first;
+             } else {
+                 MixedContent mixedContent = new MixedContent();
+                 mixedContent.setChildren(this);
 -                mixedContent.setLocation(first.getTemplate(), first, getLast());
++                mixedContent.setLocation(first.getUnboundTemplate(), first, getLast());
+                 return mixedContent;
+             }
+         }
+     }
+     
+     /**
+      * Used for some backward compatibility hacks.
+      */
+     MixedContent asMixedContent() {
+         MixedContent mixedContent = new MixedContent();
+         if (count != 0) {
+             TemplateElement first = buffer[0];
+             mixedContent.setChildren(this);
 -            mixedContent.setLocation(first.getTemplate(), first, getLast());
++            mixedContent.setLocation(first.getUnboundTemplate(), first, getLast());
+         }
+         return mixedContent;
+     }
+ 
+ }

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/TemplateObject.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/TemplateObject.java
index 2333645,f3f9e05..805af1c
--- a/src/main/java/freemarker/core/TemplateObject.java
+++ b/src/main/java/freemarker/core/TemplateObject.java
@@@ -41,29 -42,35 +42,35 @@@ public abstract class TemplateObject 
       *  by a negative line numbers, starting from this constant as line 1. */
      static final int RUNTIME_EVAL_LINE_DISPLACEMENT = -1000000000;  
  
-     final void setLocation(UnboundTemplate unboundTemplate, Token begin, Token end)
-     throws ParseException {
 -    final void setLocation(Template template, Token begin, Token end) {
 -        setLocation(template, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
++    final void setLocation(UnboundTemplate unboundTemplate, Token begin, Token end) {
 +        setLocation(unboundTemplate, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
      }
  
-     final void setLocation(UnboundTemplate unboundTemplate, Token begin, TemplateObject end)
-     throws ParseException {
 -    final void setLocation(Template template, Token tagBegin, Token tagEnd, TemplateElements children) {
++    final void setLocation(UnboundTemplate unboundTemplate, Token tagBegin, Token tagEnd, TemplateElements children) {
+         TemplateElement lastChild = children.getLast();
+         if (lastChild != null) {
+             // [<#if exp>children]<#else>
 -            setLocation(template, tagBegin, lastChild);
++            setLocation(unboundTemplate, tagBegin, lastChild);
+         } else {
+             // [<#if exp>]<#else>
 -            setLocation(template, tagBegin, tagEnd);
++            setLocation(unboundTemplate, tagBegin, tagEnd);
+         }
+     }
+     
 -    final void setLocation(Template template, Token begin, TemplateObject end) {
 -        setLocation(template, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
++    final void setLocation(UnboundTemplate unboundTemplate, Token begin, TemplateObject end) {
 +        setLocation(unboundTemplate, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
      }
- 
-     final void setLocation(UnboundTemplate unboundTemplate, TemplateObject begin, Token end)
-     throws ParseException {
+     
 -    final void setLocation(Template template, TemplateObject begin, Token end) {
 -        setLocation(template, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
++    final void setLocation(UnboundTemplate unboundTemplate, TemplateObject begin, Token end) {
 +        setLocation(unboundTemplate, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
      }
  
-     final void setLocation(UnboundTemplate unboundTemplate, TemplateObject begin, TemplateObject end)
-     throws ParseException {
 -    final void setLocation(Template template, TemplateObject begin, TemplateObject end) {
 -        setLocation(template, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
++    final void setLocation(UnboundTemplate unboundTemplate, TemplateObject begin, TemplateObject end) {
 +        setLocation(unboundTemplate, begin.beginColumn, begin.beginLine, end.endColumn, end.endLine);
      }
  
-     void setLocation(UnboundTemplate unboundTemplate, int beginColumn, int beginLine, int endColumn, int endLine)
-     throws ParseException {
 -    void setLocation(Template template, int beginColumn, int beginLine, int endColumn, int endLine) {
 -        this.template = template;
++    void setLocation(UnboundTemplate unboundTemplate, int beginColumn, int beginLine, int endColumn, int endLine) {
 +        this.unboundTemplate = unboundTemplate;
          this.beginColumn = beginColumn;
          this.beginLine = beginLine;
          this.endColumn = endColumn;

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/UnboundCallable.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/UnboundCallable.java
index 61e3da9,0000000..4066818
mode 100644,000000..100644
--- a/src/main/java/freemarker/core/UnboundCallable.java
+++ b/src/main/java/freemarker/core/UnboundCallable.java
@@@ -1,241 -1,0 +1,240 @@@
 +/*
 + * 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.
 + */
 +
 +package freemarker.core;
 +
 +import java.util.Collections;
 +import java.util.List;
 +import java.util.Map;
 +
 +import freemarker.template.TemplateModel;
 +
 +/**
 + * Represents the definition of a macro or function (or other future callable entity) in the AST. For understanding
 + * related concepts more, see {@link BoundCallable}.
 + * 
 + * <p>
 + * Historical note: Prior to 2.4, the two concepts ({@link UnboundCallable} and {@link BoundCallable}) were these same,
 + * represented by {@link Macro}, which still exists due to backward compatibility constraints, but now is abstract and
 + * is implemented by this class. This class should not implement {@link TemplateModel} (which it does, because
 + * {@link Macro} implements it), but it had to, for backward compatibility.
 + * 
 + * @see BoundCallable
 + * 
 + * @since 2.4.0
 + */
 +class UnboundCallable extends Macro {
 +
 +    static final UnboundCallable NO_OP_MACRO = new UnboundCallable(".pass", 
 +            Collections.EMPTY_LIST, 
 +            Collections.EMPTY_MAP,
 +            null, false,
-             TextBlock.EMPTY_BLOCK);
++            TemplateElements.EMPTY);
 +    
 +    final static int TYPE_MACRO = 0;
 +    final static int TYPE_FUNCTION = 1;
 +    
 +    private final String name;
 +    private final String[] paramNames;
 +    private final Map paramDefaults;
 +    private final String catchAllParamName;
 +    private final boolean function;
 +
 +    UnboundCallable(String name, List argumentNames, Map args, 
 +            String catchAllParamName, boolean function,
-             TemplateElement nestedBlock) {
++            TemplateElements children) {
 +        this.name = name;
 +        this.paramNames = (String[]) argumentNames.toArray(
 +                new String[argumentNames.size()]);
 +        this.paramDefaults = args;
 +        
 +        this.function = function;
 +        this.catchAllParamName = catchAllParamName; 
 +        
-         this.setNestedBlock(nestedBlock);
++        this.setChildren(children);
 +    }
 +    
 +    String[] getParamNames() {
 +        return paramNames;
 +    }
 +    
 +    Map getParamDefaults() {
 +        return paramDefaults;
 +    }
 +
 +    @Override
 +    public String getCatchAll() {
 +        return catchAllParamName;
 +    }
 +    
 +    @Override
 +    public String[] getArgumentNames() {
 +        return paramNames.clone();
 +    }
 +
 +    String[] getArgumentNamesInternal() {
 +        return paramNames;
 +    }
 +
 +    boolean hasArgNamed(String name) {
 +        return paramDefaults.containsKey(name);
 +    }
 +    
 +    @Override
 +    public String getName() {
 +        return name;
 +    }
 +
 +    @Override
-     void accept(Environment env) {
++    TemplateElement[] accept(Environment env) {
 +        env.visitCallableDefinition(this);
++        return null;
 +    }
 +
 +    @Override
 +    protected String dump(boolean canonical) {
 +        StringBuilder sb = new StringBuilder();
 +        if (canonical) sb.append('<');
 +        sb.append(getNodeTypeSymbol());
 +        sb.append(' ');
 +        sb.append(_CoreStringUtils.toFTLTopLevelTragetIdentifier(name));
 +        if (function) sb.append('(');
 +        int argCnt = paramNames.length;
 +        for (int i = 0; i < argCnt; i++) {
 +            if (function) {
 +                if (i != 0) {
 +                    sb.append(", ");
 +                }
 +            } else {
 +                sb.append(' ');
 +            }
 +            String argName = paramNames[i];
 +            sb.append(_CoreStringUtils.toFTLTopLevelIdentifierReference(argName));
 +            if (paramDefaults != null && paramDefaults.get(argName) != null) {
 +                sb.append('=');
 +                Expression defaultExpr = (Expression) paramDefaults.get(argName);
 +                if (function) {
 +                    sb.append(defaultExpr.getCanonicalForm());
 +                } else {
 +                    MessageUtil.appendExpressionAsUntearable(sb, defaultExpr);
 +                }
 +            }
 +        }
 +        if (catchAllParamName != null) {
 +            if (function) {
 +                if (argCnt != 0) {
 +                    sb.append(", ");
 +                }
 +            } else {
 +                sb.append(' ');
 +            }
 +            sb.append(catchAllParamName);
 +            sb.append("...");
 +        }
 +        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();
 +    }
 +    
 +    @Override
 +    String getNodeTypeSymbol() {
 +        return function ? "#function" : "#macro";
 +    }
 +    
 +    @Override
 +    boolean isShownInStackTrace() {
 +        return false;
 +    }
 +    
 +    @Override
 +    boolean isNestedBlockRepeater() {
 +        // Because of recursive calls
 +        return true;
 +    }
 +    @Override
 +    public boolean isFunction() {
 +        return function;
 +    }
 +
 +    @Override
 +    int getParameterCount() {
 +        return 1/*name*/ + paramNames.length * 2/*name=default*/ + 1/*catchAll*/ + 1/*type*/;
 +    }
 +
 +    @Override
 +    Object getParameterValue(int idx) {
 +        if (idx == 0) {
 +            return name;
 +        } else {
 +            final int argDescsEnd = paramNames.length * 2 + 1;
 +            if (idx < argDescsEnd) {
 +                String paramName = paramNames[(idx - 1) / 2];
 +                if (idx % 2 != 0) {
 +                    return paramName;
 +                } else {
 +                    return paramDefaults.get(paramName);
 +                }
 +            } else if (idx == argDescsEnd) {
 +                return catchAllParamName;
 +            } else if (idx == argDescsEnd + 1) {
 +                return Integer.valueOf(function ? TYPE_FUNCTION : TYPE_MACRO);
 +            } else {
 +                throw new IndexOutOfBoundsException();
 +            }
 +        }
 +    }
 +
 +    @Override
 +    ParameterRole getParameterRole(int idx) {
 +        if (idx == 0) {
 +            return ParameterRole.ASSIGNMENT_TARGET;
 +        } else {
 +            final int argDescsEnd = paramNames.length * 2 + 1;
 +            if (idx < argDescsEnd) {
 +                if (idx % 2 != 0) {
 +                    return ParameterRole.PARAMETER_NAME;
 +                } else {
 +                    return ParameterRole.PARAMETER_DEFAULT;
 +                }
 +            } else if (idx == argDescsEnd) {
 +                return ParameterRole.CATCH_ALL_PARAMETER_NAME;
 +            } else if (idx == argDescsEnd + 1) {
 +                return ParameterRole.AST_NODE_SUBTYPE;
 +            } else {
 +                throw new IndexOutOfBoundsException();
 +            }
 +        }
 +    }
 +    
 +    @Override
 +    public String toString() {
 +        final UnboundTemplate unboundTemplate = getUnboundTemplate();
 +        return "UnboundCallable("
 +                + "name=" + getName()
 +                + ", isFunction=" + isFunction()
 +                + ", unboundTemplate"
 +                + (unboundTemplate != null ? ".sourceName=" + unboundTemplate.getSourceName() : "=null")
 +                + ")";
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/UnifiedCall.java
----------------------------------------------------------------------
diff --cc src/main/java/freemarker/core/UnifiedCall.java
index 3c0ce94,6608b0a..04a6d3a
--- a/src/main/java/freemarker/core/UnifiedCall.java
+++ b/src/main/java/freemarker/core/UnifiedCall.java
@@@ -72,20 -69,18 +69,19 @@@ final class UnifiedCall extends Templat
      }
  
      @Override
-     void accept(Environment env) throws TemplateException, IOException {
+     TemplateElement[] accept(Environment env) throws TemplateException, IOException {
 -        TemplateModel tm = nameExp.eval(env);
 -        if (tm == Macro.DO_NOTHING_MACRO) return null; // shortcut here.
 -        if (tm instanceof Macro) {
 -            Macro macro = (Macro) tm;
 -            if (macro.isFunction() && !legacySyntax) {
 +        final TemplateModel tm = nameExp.eval(env);
-         if (tm == UnboundCallable.NO_OP_MACRO) return; // shortcut here.
++        if (tm == UnboundCallable.NO_OP_MACRO) return null; // shortcut here.
 +        if (tm instanceof BoundCallable) {
 +            final BoundCallable boundMacro = (BoundCallable) tm;
 +            final Macro unboundMacro = boundMacro.getUnboundCallable();
 +            if (unboundMacro.isFunction() && !legacySyntax) {
                  throw new _MiscTemplateException(env,
 -                        "Routine ", new _DelayedJQuote(macro.getName()), " is a function, not a directive. "
 +                        "Routine ", new _DelayedJQuote(unboundMacro.getName()), " is a function, not a directive. "
                          + "Functions can only be called from expressions, like in ${f()}, ${x + f()} or ",
                          "<@someDirective someParam=f() />", ".");
              }    
-             env.invoke(boundMacro, namedArgs, positionalArgs, bodyParameterNames,
-                     getNestedBlock());
 -            env.invoke(macro, namedArgs, positionalArgs, bodyParameterNames, getChildBuffer());
++            env.invoke(boundMacro, namedArgs, positionalArgs, bodyParameterNames, getChildBuffer());
          } else {
              boolean isDirectiveModel = tm instanceof TemplateDirectiveModel; 
              if (isDirectiveModel || tm instanceof TemplateTransformModel) {

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/core/_CoreAPI.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/java/freemarker/template/Template.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/88d5c7f3/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --cc src/main/javacc/FTL.jj
index 608b43d,254c27a..2cffd4d
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@@ -3165,9 -3252,9 +3165,9 @@@ Macro Macro() 
          }
          
          inMacro = inFunction = false;
-         UnboundCallable result = new UnboundCallable(name, argNames, args, catchAll, isFunction, block);
 -        Macro result = new Macro(name, argNames, args, catchAll, isFunction, children);
++        UnboundCallable result = new UnboundCallable(name, argNames, args, catchAll, isFunction, children);
          result.setLocation(template, start, end);
 -        template.addMacro(result);
 +        template.addUnboundCallable(result);
          return result;
      }
  }


Mime
View raw message