This is an automated email from the ASF dual-hosted git repository.

ddekany pushed a commit to branch 3
in repository https://gitbox.apache.org/repos/asf/freemarker.git


The following commit(s) were added to refs/heads/3 by this push:
     new 7dba60f  Forward ported from 2.3-gae: Restricting narrowing scope from 
which #list loop variables are visible (although it was only necessary because 
of lambdas in 2.3, and we don't have lambdas in 3.0 yet). Some code cleanup 
regarding #list.
7dba60f is described below

commit 7dba60fb2d3f9a5c77e0b38d88f9d7fe1368d815
Author: ddekany <[email protected]>
AuthorDate: Sat Feb 23 17:35:24 2019 +0100

    Forward ported from 2.3-gae: Restricting narrowing scope from which #list 
loop variables are visible (although it was only necessary because of lambdas 
in 2.3, and we don't have lambdas in 3.0 yet). Some code cleanup regarding 
#list.
---
 .../org/apache/freemarker/core/ASTDirItems.java    |   2 +-
 .../org/apache/freemarker/core/ASTDirList.java     | 117 ++++++++++-----------
 .../java/org/apache/freemarker/core/ASTDirSep.java |   2 +-
 .../core/BuiltInForNestedContentParameter.java     |   2 +-
 .../org/apache/freemarker/core/Environment.java    |  33 ++++++
 5 files changed, 92 insertions(+), 64 deletions(-)

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 04aaf94..afe04e9 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
@@ -44,7 +44,7 @@ class ASTDirItems extends ASTDirective {
 
     @Override
     ASTElement[] execute(Environment env) throws TemplateException, 
IOException {
-        final IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, null);
+        final IterationContext iterCtx = 
env.findClosestEnclosingIterationContext();
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new TemplateException(env,
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 201c96c..362b117 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
@@ -39,14 +39,14 @@ import org.apache.freemarker.core.util._StringUtils;
 final class ASTDirList extends ASTDirective {
 
     private final ASTExpression listedExp;
-    private final String nestedContentParamName;
+    private final String nestedContentParam1Name;
     private final String nestedContentParam2Name;
     private final boolean hashListing;
 
     /**
      * @param listedExp
      *            a variable referring to an iterable or extended hash that we 
want to list
-     * @param nestedContentParamName
+     * @param nestedContentParam1Name
      *            The name of the variable that will hold the value of the 
current item when looping through listed value,
      *            or {@code null} if we have a nested {@code #items}. If this 
is a hash listing then this variable will holds the value
      *            of the hash key.
@@ -62,12 +62,12 @@ final class ASTDirList extends ASTDirective {
      *            a nested {@code #items}.
      */
     ASTDirList(ASTExpression listedExp,
-                  String nestedContentParamName,
+                  String nestedContentParam1Name,
                   String nestedContentParam2Name,
                   TemplateElements childrenBeforeElse,
                   boolean hashListing) {
         this.listedExp = listedExp;
-        this.nestedContentParamName = nestedContentParamName;
+        this.nestedContentParam1Name = nestedContentParam1Name;
         this.nestedContentParam2Name = nestedContentParam2Name;
         setChildren(childrenBeforeElse);
         this.hashListing = hashListing;
@@ -89,33 +89,9 @@ final class ASTDirList extends ASTDirective {
             listedExp.assertNonNull(null, env);
         }
 
-        return env.visitIteratorBlock(new IterationContext(listedValue, 
nestedContentParamName, nestedContentParam2Name));
+        return env.visitIteratorBlock(new IterationContext(listedValue, 
nestedContentParam1Name, nestedContentParam2Name));
     }
 
-    /**
-     * @param nestedContentParamName
-     *            Then name of the nested content parameter whose context we 
are looking for, or {@code null} if we
-     *            simply look for the innermost context.
-     * @return The matching context or {@code null} if no such context exists.
-     */
-    static IterationContext findEnclosingIterationContext(Environment env, 
String nestedContentParamName)
-            throws TemplateException {
-        LocalContextStack ctxStack = env.getLocalContextStack();
-        if (ctxStack != null) {
-            for (int i = ctxStack.size() - 1; i >= 0; i--) {
-                Object ctx = ctxStack.get(i);
-                if (ctx instanceof IterationContext
-                        && (nestedContentParamName == null
-                            || 
nestedContentParamName.equals(((IterationContext) 
ctx).getNestedContentParameter1Name())
-                            || 
nestedContentParamName.equals(((IterationContext) 
ctx).getNestedContentParameter2Name())
-                            )) {
-                    return (IterationContext) ctx;
-                }
-            }
-        }
-        return null;
-    }
-    
     @Override
     String dump(boolean canonical) {
         StringBuilder buf = new StringBuilder();
@@ -123,9 +99,9 @@ final class ASTDirList extends ASTDirective {
         buf.append(getLabelWithoutParameters());
         buf.append(' ');
         buf.append(listedExp.getCanonicalForm());
-        if (nestedContentParamName != null) {
+        if (nestedContentParam1Name != null) {
             buf.append(" as ");
-            
buf.append(_StringUtils.toFTLTopLevelIdentifierReference(nestedContentParamName));
+            
buf.append(_StringUtils.toFTLTopLevelIdentifierReference(nestedContentParam1Name));
             if (nestedContentParam2Name != null) {
                 buf.append(", ");
                 
buf.append(_StringUtils.toFTLTopLevelIdentifierReference(nestedContentParam2Name));
@@ -145,7 +121,7 @@ final class ASTDirList extends ASTDirective {
     
     @Override
     int getParameterCount() {
-        return 1 + (nestedContentParamName != null ? 1 : 0) + 
(nestedContentParam2Name != null ? 1 : 0);
+        return 1 + (nestedContentParam1Name != null ? 1 : 0) + 
(nestedContentParam2Name != null ? 1 : 0);
     }
 
     @Override
@@ -154,8 +130,8 @@ final class ASTDirList extends ASTDirective {
         case 0:
             return listedExp;
         case 1:
-            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
-            return nestedContentParamName;
+            if (nestedContentParam1Name == null) throw new 
IndexOutOfBoundsException();
+            return nestedContentParam1Name;
         case 2:
             if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
             return nestedContentParam2Name;
@@ -169,7 +145,7 @@ final class ASTDirList extends ASTDirective {
         case 0:
             return ParameterRole.LIST_SOURCE;
         case 1:
-            if (nestedContentParamName == null) throw new 
IndexOutOfBoundsException();
+            if (nestedContentParam1Name == null) throw new 
IndexOutOfBoundsException();
             return ParameterRole.NESTED_CONTENT_PARAMETER;
         case 2:
             if (nestedContentParam2Name == null) throw new 
IndexOutOfBoundsException();
@@ -185,7 +161,7 @@ final class ASTDirList extends ASTDirective {
 
     @Override
     boolean isNestedBlockRepeater() {
-        return nestedContentParamName != null;
+        return nestedContentParam1Name != null;
     }
 
     /**
@@ -208,6 +184,13 @@ final class ASTDirList extends ASTDirective {
         private String nestedContentParam1Name;
         /** Used if we list key-value pairs */
         private String nestedContentParam2Name;
+        /**
+         * Whether the nested content parameters are visible from the template 
at the moment.
+         * It would be more intuitive if the {@link LocalContext} is not in 
the local stack when they aren't visible,
+         * but the {@link LocalContext} is also used for {@code #items} to 
find its parent, for which we need the tricky
+         * scoping of the local context stack {@link 
Environment#getLocalContextStack()}.
+         */
+        private boolean nestedContentParamsVisible;
         
         private final TemplateModel listedValue;
         
@@ -266,19 +249,23 @@ final class ASTDirList extends ASTDirective {
                                 nestedContentParam = iterModel.next();
                                 hasNext = iterModel.hasNext();
                                 try {
+                                    nestedContentParamsVisible = true;
                                     env.executeElements(childBuffer);
                                 } catch (BreakOrContinueException br) {
                                     if (br == 
BreakOrContinueException.BREAK_INSTANCE) {
                                         break listLoop;
                                     }
+                                } finally {
+                                    nestedContentParamsVisible = false;
                                 }
                                 index++;
                             } while (hasNext);
                         openedIterator = null;
                     } else {
-                        // We must reuse this later, because 
TemplateIterableModel-s that wrap an Iterator only
-                        // allow one iterator() call.
+                        // We must reuse this later, because some 
TemplateIterableModel can only allow one iterator()
+                        // call (such as those wrapping an Iterator).
                         openedIterator = iterModel;
+                        // Note: Nested content parameters will only become 
visible inside #items
                         env.executeElements(childBuffer);
                     }
                 }
@@ -311,23 +298,27 @@ final class ASTDirList extends ASTDirective {
                 if (hashNotEmpty) {
                     if (nestedContentParam1Name != null) {
                         listLoop: do {
-                                TemplateHashModelEx.KeyValuePair kvp = 
kvpIter.next();
-                                nestedContentParam = kvp.getKey();
-                                nestedContentParam2 = kvp.getValue();
-                                hasNext = kvpIter.hasNext();
-                                try {
-                                    env.executeElements(childBuffer);
-                                } catch (BreakOrContinueException br) {
-                                    if (br == 
BreakOrContinueException.BREAK_INSTANCE) {
-                                        break listLoop;
-                                    }
+                            TemplateHashModelEx.KeyValuePair kvp = 
kvpIter.next();
+                            nestedContentParam = kvp.getKey();
+                            nestedContentParam2 = kvp.getValue();
+                            hasNext = kvpIter.hasNext();
+                            try {
+                                nestedContentParamsVisible = true;
+                                env.executeElements(childBuffer);
+                            } catch (BreakOrContinueException br) {
+                                if (br == 
BreakOrContinueException.BREAK_INSTANCE) {
+                                    break listLoop;
                                 }
-                                index++;
-                            } while (hasNext);
+                            } finally {
+                                nestedContentParamsVisible = false;
+                            }
+                            index++;
+                        } while (hasNext);
                         openedIterator = null;
                     } else {
                         // We will reuse this at the #iterms
                         openedIterator = kvpIter;
+                        // Note: Nested content parameters will only become 
visible inside #items
                         env.executeElements(childBuffer);
                     }
                 }
@@ -355,8 +346,12 @@ final class ASTDirList extends ASTDirective {
         
         @Override
         public TemplateModel getLocalVariable(String name) {
+            if (!nestedContentParamsVisible) {
+                return null;
+            }
+
             String nestedContentParamName = this.nestedContentParam1Name;
-            if (nestedContentParamName != null && 
name.startsWith(nestedContentParamName)) {
+            if (name.startsWith(nestedContentParamName)) {
                 switch(name.length() - nestedContentParamName.length()) {
                     case 0: 
                         return nestedContentParam;
@@ -382,18 +377,18 @@ final class ASTDirList extends ASTDirective {
         
         @Override
         public Collection<String> getLocalVariableNames() {
-            String nestedContentParamName = this.nestedContentParam1Name;
-            if (nestedContentParamName != null) {
-                if (localVarNames == null) {
-                    localVarNames = new ArrayList(3);
-                    localVarNames.add(nestedContentParamName);
-                    localVarNames.add(nestedContentParamName + 
LOOP_STATE_INDEX);
-                    localVarNames.add(nestedContentParamName + 
LOOP_STATE_HAS_NEXT);
-                }
-                return localVarNames;
-            } else {
+            if (!nestedContentParamsVisible) {
                 return Collections.EMPTY_LIST;
             }
+
+            String nestedContentParamName = this.nestedContentParam1Name;
+            if (localVarNames == null) {
+                localVarNames = new ArrayList(3);
+                localVarNames.add(nestedContentParamName);
+                localVarNames.add(nestedContentParamName + LOOP_STATE_INDEX);
+                localVarNames.add(nestedContentParamName + 
LOOP_STATE_HAS_NEXT);
+            }
+            return localVarNames;
         }
 
         boolean hasNext() {
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java
index 1624732..3293cc7 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ASTDirSep.java
@@ -33,7 +33,7 @@ class ASTDirSep extends ASTDirective {
 
     @Override
     ASTElement[] execute(Environment env) throws TemplateException, 
IOException {
-        final IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, null);
+        final IterationContext iterCtx = 
env.findClosestEnclosingIterationContext();
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new TemplateException(env,
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
index d35c2f5..61bc1dc 100644
--- 
a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
+++ 
b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInForNestedContentParameter.java
@@ -32,7 +32,7 @@ abstract class BuiltInForNestedContentParameter extends 
SpecialBuiltIn {
     
     @Override
     TemplateModel _eval(Environment env) throws TemplateException {
-        IterationContext iterCtx = 
ASTDirList.findEnclosingIterationContext(env, nestedContentParamName);
+        IterationContext iterCtx = 
env.findEnclosingIterationContextWithVisibleVariable(nestedContentParamName);
         if (iterCtx == null) {
             // The parser should prevent this situation
             throw new TemplateException(
diff --git 
a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java 
b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
index 5b2c8f8..f0138c8 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java
@@ -576,6 +576,39 @@ public final class Environment extends 
MutableProcessingConfiguration<Environmen
     }
 
     /**
+     * @param nestedContentParamName
+     *            Then name of the loop variable that's also visible in FTL at 
the moment, whose context we are looking
+     *            for.
+     * @return The matching context or {@code null} if no such context exists.
+     */
+    ASTDirList.IterationContext 
findEnclosingIterationContextWithVisibleVariable(String nestedContentParamName) 
{
+        return findEnclosingIterationContext(nestedContentParamName);
+    }
+
+    /**
+     * @return The matching context or {@code null} if no such context exists.
+     */
+    ASTDirList.IterationContext findClosestEnclosingIterationContext() {
+        return findEnclosingIterationContext(null);
+    }
+    private ASTDirList.IterationContext findEnclosingIterationContext(String 
nestedContentParamName) {
+        LocalContextStack ctxStack = getLocalContextStack();
+        if (ctxStack != null) {
+            for (int i = ctxStack.size() - 1; i >= 0; i--) {
+                Object ctx = ctxStack.get(i);
+                if (ctx instanceof ASTDirList.IterationContext
+                        && (nestedContentParamName == null
+                        || 
nestedContentParamName.equals(((ASTDirList.IterationContext) 
ctx).getNestedContentParameter1Name())
+                        || 
nestedContentParamName.equals(((ASTDirList.IterationContext) 
ctx).getNestedContentParameter2Name())
+                )) {
+                    return (ASTDirList.IterationContext) ctx;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
      * Used for {@code #visit} and {@code #recurse}.
      */
     void invokeNodeHandlerFor(TemplateNodeModel node, TemplateSequenceModel 
namespaces)

Reply via email to