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 2019/05/25 23:02:16 UTC

[freemarker] 02/03: Generalized/simplified the way it's decided if an expression can produce or consume lazily generated values.

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

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

commit 1f25c8b538d9c5ecc5fb4e95f3b65cd55b6a60c7
Author: ddekany <dd...@apache.org>
AuthorDate: Sat May 25 14:59:36 2019 +0200

    Generalized/simplified the way it's decided if an expression can produce or consume lazily generated values.
---
 src/main/java/freemarker/core/BuiltIn.java         | 13 +++---
 .../freemarker/core/BuiltInsForMultipleTypes.java  |  2 +-
 .../java/freemarker/core/BuiltInsForSequences.java | 46 +++++++++-------------
 src/main/java/freemarker/core/DynamicKeyName.java  |  6 +--
 src/main/java/freemarker/core/Expression.java      | 18 +++++++++
 src/main/java/freemarker/core/IteratorBlock.java   | 10 +----
 .../core/LazilyGeneratedSequenceModel.java         |  6 ++-
 .../freemarker/core/ParentheticalExpression.java   |  7 +++-
 .../core/SingleIterationCollectionModel.java       |  2 +-
 9 files changed, 55 insertions(+), 55 deletions(-)

diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java
index 9e3dd3c..57c3313 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -386,18 +386,15 @@ abstract class BuiltIn extends Expression implements Cloneable {
             throw new InternalError();
         }
         bi.key = key;
-        bi.target = target;
-        if (bi.isLazilyGeneratedSequenceModelTargetSupported()) {
-            Expression cleanedTarget = MiscUtil.peelParentheses(target);
-            if (cleanedTarget instanceof BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) {
-                ((BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) cleanedTarget)
-                        .setLazyResultGenerationAllowed(true);
-            }
+        if (bi.isLazilyGeneratedTargetResultSupported()) {
+            target.enableLazilyGeneratedResult();
         }
+        bi.target = target;
         return bi;
     }
 
-    protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+    /** If the built-in supports a lazily generated value as its left operand (the target). */
+    protected boolean isLazilyGeneratedTargetResultSupported() {
         return false;
     }
 
diff --git a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
index b690cf5..c8a0922 100644
--- a/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
+++ b/src/main/java/freemarker/core/BuiltInsForMultipleTypes.java
@@ -485,7 +485,7 @@ class BuiltInsForMultipleTypes {
     static class sizeBI extends BuiltIn {
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
diff --git a/src/main/java/freemarker/core/BuiltInsForSequences.java b/src/main/java/freemarker/core/BuiltInsForSequences.java
index b4acd16..ba40615 100644
--- a/src/main/java/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/freemarker/core/BuiltInsForSequences.java
@@ -144,7 +144,7 @@ class BuiltInsForSequences {
     static class firstBI extends BuiltIn {
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
@@ -185,7 +185,7 @@ class BuiltInsForSequences {
     static class joinBI extends BuiltIn {
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
@@ -302,7 +302,7 @@ class BuiltInsForSequences {
     static class seq_containsBI extends BuiltIn {
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
@@ -374,7 +374,7 @@ class BuiltInsForSequences {
     static class seq_index_ofBI extends BuiltIn {
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
@@ -915,7 +915,7 @@ class BuiltInsForSequences {
         }
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
+        protected boolean isLazilyGeneratedTargetResultSupported() {
             return true;
         }
 
@@ -989,7 +989,7 @@ class BuiltInsForSequences {
 
         private Expression elementTransformerExp;
         private ElementTransformer precreatedElementTransformer;
-        private boolean lazyResultGenerationAllowed;
+        private boolean lazilyGeneratedResultEnabled;
 
         @Override
         void bindToParameters(List<Expression> parameters, Token openParen, Token closeParen) throws ParseException {
@@ -1007,33 +1007,23 @@ class BuiltInsForSequences {
         }
 
         @Override
-        protected boolean isLocalLambdaParameterSupported() {
+        protected final boolean isLocalLambdaParameterSupported() {
             return true;
         }
 
         @Override
-        protected boolean isLazilyGeneratedSequenceModelTargetSupported() {
-            return true;
+        final void enableLazilyGeneratedResult() {
+            this.lazilyGeneratedResultEnabled = true;
         }
 
-        final boolean isLazyResultGenerationAllowed() {
-            return lazyResultGenerationAllowed;
+        /** Tells if {@link #enableLazilyGeneratedResult()} was called. */
+        protected final boolean isLazilyGeneratedResultEnabled() {
+            return lazilyGeneratedResultEnabled;
         }
 
-        /**
-         * Used to allow generating the result collection or sequence elements on an as-needed basis, similarly as
-         * Java 8 Stream intermediate operations do it. This is initially {@code false}. The containing expression or
-         * directive sets it to {@code true} if it can ensure that:
-         * <ul>
-         *   <li>The returned {@link TemplateCollectionModel} is traversed only once, more specifically,
-         *       {@link TemplateCollectionModel#iterator()} is called only once.
-         *   <li>When the methods of the collection or iterator are called, the context provided by
-         *       the {@link Environment} (such as the local context stack) is similar to the context from where the
-         *       built-in was called. This is required as lambda expression are {@link LocalLambdaExpression}-s.
-         * </ul>
-         */
-        void setLazyResultGenerationAllowed(boolean lazyResultGenerationAllowed) {
-            this.lazyResultGenerationAllowed = lazyResultGenerationAllowed;
+        @Override
+        protected final boolean isLazilyGeneratedTargetResultSupported() {
+            return true;
         }
 
         protected List<Expression> getArgumentsAsList() {
@@ -1086,7 +1076,7 @@ class BuiltInsForSequences {
         private TemplateModelIterator getTemplateModelIterator(Environment env, TemplateModel model) throws TemplateModelException,
                 NonSequenceOrCollectionException, InvalidReferenceException {
             if (model instanceof TemplateCollectionModel) {
-                return isLazyResultGenerationAllowed()
+                return isLazilyGeneratedResultEnabled()
                         ? new LazyCollectionTemplateModelIterator((TemplateCollectionModel) model)
                         : ((TemplateCollectionModel) model).iterator();
             } else if (model instanceof TemplateSequenceModel) {
@@ -1176,7 +1166,7 @@ class BuiltInsForSequences {
                 final TemplateModelIterator lhoIterator, final TemplateModel lho,
                 final ElementTransformer elementTransformer,
                 final Environment env) throws TemplateException {
-            if (!isLazyResultGenerationAllowed()) {
+            if (!isLazilyGeneratedResultEnabled()) {
                 List<TemplateModel> resultList = new ArrayList<TemplateModel>();
                 while (lhoIterator.hasNext()) {
                     TemplateModel element = lhoIterator.next();
@@ -1262,7 +1252,7 @@ class BuiltInsForSequences {
         protected TemplateModel calculateResult(
                 final TemplateModelIterator lhoIterator, TemplateModel lho, final ElementTransformer elementTransformer,
                 final Environment env) throws TemplateException {
-            if (!isLazyResultGenerationAllowed()) {
+            if (!isLazilyGeneratedResultEnabled()) {
                 List<TemplateModel> resultList = new ArrayList<TemplateModel>();
                 while (lhoIterator.hasNext()) {
                     resultList.add(fetchAndMapNextElement(lhoIterator, elementTransformer, env));
diff --git a/src/main/java/freemarker/core/DynamicKeyName.java b/src/main/java/freemarker/core/DynamicKeyName.java
index c01d0ef..15ff96b 100644
--- a/src/main/java/freemarker/core/DynamicKeyName.java
+++ b/src/main/java/freemarker/core/DynamicKeyName.java
@@ -51,11 +51,7 @@ final class DynamicKeyName extends Expression {
         this.target = target; 
         this.keyExpression = keyExpression;
 
-        Expression cleanedTarget = MiscUtil.peelParentheses(target);
-        if (cleanedTarget instanceof BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) {
-            ((BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) cleanedTarget)
-                    .setLazyResultGenerationAllowed(true);
-        }
+        target.enableLazilyGeneratedResult();
     }
 
     @Override
diff --git a/src/main/java/freemarker/core/Expression.java b/src/main/java/freemarker/core/Expression.java
index 0c3575c..5a69095 100644
--- a/src/main/java/freemarker/core/Expression.java
+++ b/src/main/java/freemarker/core/Expression.java
@@ -77,6 +77,24 @@ abstract public class Expression extends TemplateObject {
     public final TemplateModel getAsTemplateModel(Environment env) throws TemplateException {
         return eval(env);
     }
+
+    /**
+     * Allows generating the result collection or sequence elements (or maybe others in future) on an as-needed basis,
+     * similarly as Java 8 Stream intermediate operations do it. The default implementation in {@link Expression}
+     * does nothing, as most expressions can't create such result. The containing expression or directive calls
+     * this if it can ensure that:
+     * <ul>
+     *   <li>If the returned value is a {@link TemplateCollectionModel}, then it's traversed at most once, more
+     *       specifically, {@link TemplateCollectionModel#iterator()} is called at most once.
+     *   <li>When the methods of the collection or iterator are called, the context provided by
+     *       the {@link Environment} (such as the local context stack) is similar to the context from where this
+     *       expression was called. This is required as lazily generated results are allowed to be based on
+     *       {@link LocalLambdaExpression}-s.
+     * </ul>
+     */
+    void enableLazilyGeneratedResult() {
+        // Has no effect by default
+    }
     
     final TemplateModel eval(Environment env) throws TemplateException {
         try {
diff --git a/src/main/java/freemarker/core/IteratorBlock.java b/src/main/java/freemarker/core/IteratorBlock.java
index c734cbe..aaaaf64 100644
--- a/src/main/java/freemarker/core/IteratorBlock.java
+++ b/src/main/java/freemarker/core/IteratorBlock.java
@@ -48,7 +48,6 @@ final class IteratorBlock extends TemplateElement {
     private final String loopVar2Name;
     private final boolean hashListing;
     private final boolean forEach;
-    private final boolean fetchElementsOutsideLoopVarContext;
 
     /**
      * @param listedExp
@@ -83,14 +82,7 @@ final class IteratorBlock extends TemplateElement {
         this.hashListing = hashListing;
         this.forEach = forEach;
 
-        Expression cleanedListExp = MiscUtil.peelParentheses(listedExp);
-        if (cleanedListExp instanceof BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) {
-            ((BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn) cleanedListExp)
-                    .setLazyResultGenerationAllowed(true);
-            fetchElementsOutsideLoopVarContext = true;
-        } else {
-            fetchElementsOutsideLoopVarContext = false;
-        }
+        listedExp.enableLazilyGeneratedResult();
     }
     
     boolean isHashListing() {
diff --git a/src/main/java/freemarker/core/LazilyGeneratedSequenceModel.java b/src/main/java/freemarker/core/LazilyGeneratedSequenceModel.java
index a0737f1..26e210a 100644
--- a/src/main/java/freemarker/core/LazilyGeneratedSequenceModel.java
+++ b/src/main/java/freemarker/core/LazilyGeneratedSequenceModel.java
@@ -26,10 +26,12 @@ import freemarker.template.TemplateSequenceModel;
 /**
  * Same as {@link SingleIterationCollectionModel}, but marks the value as something that's in principle a
  * {@link TemplateSequenceModel}, but to allow lazy result generation a {@link CollectionModel} is used internally.
- * This is an optimization that we do where we consider it to be transparent enough for the user.
+ * This is an optimization that we do where we consider it to be transparent enough for the user. An operator or
+ * built-in should only ever receive a {@link LazilyGeneratedSequenceModel} if it has explicitly allowed its
+ * input expression to return such value via calling {@link Expression#enableLazilyGeneratedResult()}.
  */
 class LazilyGeneratedSequenceModel extends SingleIterationCollectionModel {
-    public LazilyGeneratedSequenceModel(TemplateModelIterator iterator) {
+    LazilyGeneratedSequenceModel(TemplateModelIterator iterator) {
         super(iterator);
     }
 }
diff --git a/src/main/java/freemarker/core/ParentheticalExpression.java b/src/main/java/freemarker/core/ParentheticalExpression.java
index f63e0b6..07f5d18 100644
--- a/src/main/java/freemarker/core/ParentheticalExpression.java
+++ b/src/main/java/freemarker/core/ParentheticalExpression.java
@@ -60,6 +60,11 @@ final class ParentheticalExpression extends Expression {
     }
 
     @Override
+    void enableLazilyGeneratedResult() {
+        nested.enableLazilyGeneratedResult();
+    }
+
+    @Override
     protected Expression deepCloneWithIdentifierReplaced_inner(
             String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) {
         return new ParentheticalExpression(
@@ -82,5 +87,5 @@ final class ParentheticalExpression extends Expression {
         if (idx != 0) throw new IndexOutOfBoundsException();
         return ParameterRole.ENCLOSED_OPERAND;
     }
-    
+
 }
diff --git a/src/main/java/freemarker/core/SingleIterationCollectionModel.java b/src/main/java/freemarker/core/SingleIterationCollectionModel.java
index 292bcd3..56efe93 100644
--- a/src/main/java/freemarker/core/SingleIterationCollectionModel.java
+++ b/src/main/java/freemarker/core/SingleIterationCollectionModel.java
@@ -34,7 +34,7 @@ import freemarker.template.utility.NullArgumentException;
 class SingleIterationCollectionModel implements TemplateCollectionModel {
     private TemplateModelIterator iterator;
 
-    public SingleIterationCollectionModel(TemplateModelIterator iterator) {
+    SingleIterationCollectionModel(TemplateModelIterator iterator) {
         NullArgumentException.check(iterator);
         this.iterator = iterator;
     }