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 2020/04/11 08:29:57 UTC

[freemarker] branch FREEMARKER-35 updated (888dece -> e3e3185)

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

ddekany pushed a change to branch FREEMARKER-35
in repository https://gitbox.apache.org/repos/asf/freemarker.git.


    from 888dece  Merge pull request #65 from Jaaap/FREEMARKER-35
     add f5fda46  JavaDoc: Some clarifications for variable getters/setters
     add e816f81  Setting Configuration.incompatibleImprovements to the object returned by Configuration.getVersion() will now be logged as an error, but for backward compatibility it will still work. The typical bad pattern is this: new Configuration(Configuration.getVersion()). Doing that defeats the purpose of incompatibleImprovements, and makes upgrading FreeMarker a potentially breaking change. Furthermore, doing this probably won't be allowed starting from 2.4.0, and will throw exception.
     add 6eba023  (Typo in source code comment)
     add 79fbd31  (Manual: Minor fixes in the TemplateConfiguration section)
     add 6ab0150  Updated version to 2.3.30.
     add d2fef8e  Manual: Added release date
     add cfeb8c9  Added missing license header to test data file
     add 2252bcc  (Removed accidental extra space from AST test)
     add 53a810e  Build: dist task didn't fail if JUnit tests were failing. Now it will (though now Ant console output is less useful, most runs tests from IDE anyway).
     add 196d28d  (Trivial fixes/additions in version history)
     add 0bf1ec9  Missing fail()-s in a test
     add d393376  Updated release date in Manual.
     add b907946  Manual: Improved user uploaded template security FAQ entry
     add 6beb29c  (Manual: Typo...)
     new aa7e565  Merge branch '2.3-gae' into FREEMARKER-35
     new e3e3185  Switched to Java 8 as minimum requirement. It's not feasible to support Java 8 time API otherwise, since it's statically referred from TemplateTemporalModel.

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 README.md                                          |  7 +-
 build.properties.sample                            |  1 -
 build.xml                                          | 50 +++--------
 osgi.bnd                                           |  8 +-
 src/main/java/freemarker/core/Environment.java     | 65 ++++++++------
 src/main/java/freemarker/core/_Java8Impl.java      | 42 ---------
 src/main/java/freemarker/core/_JavaVersions.java   | 79 -----------------
 .../ext/beans/BeansWrapperConfiguration.java       |  7 ++
 .../freemarker/ext/beans/ClassIntrospector.java    |  9 +-
 .../java/freemarker/template/Configuration.java    | 17 +++-
 .../DefaultObjectWrapperConfiguration.java         |  3 +
 .../java/freemarker/template/_TemplateAPI.java     | 22 ++++-
 src/main/resources/freemarker/version.properties   |  8 +-
 src/manual/en_US/book.xml                          | 99 ++++++++++++++++------
 .../freemarker/ext/beans/BeansWrapperMiscTest.java |  2 -
 src/test/java/freemarker/manual/ExamplesTest.java  |  3 +-
 .../freemarker/template/ConfigurationTest.java     |  2 +
 src/test/resources/freemarker/core/ast-lambda.ast  | 18 ++++
 .../overloaded-methods-2-bwici-2.3.20.ftl          |  2 +-
 19 files changed, 210 insertions(+), 234 deletions(-)
 delete mode 100644 src/main/java/freemarker/core/_Java8Impl.java
 delete mode 100644 src/main/java/freemarker/core/_JavaVersions.java


[freemarker] 02/02: Switched to Java 8 as minimum requirement. It's not feasible to support Java 8 time API otherwise, since it's statically referred from TemplateTemporalModel.

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e3e3185f2f53205e1c2a91e094de145d626e23ff
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Apr 11 10:27:46 2020 +0200

    Switched to Java 8 as minimum requirement. It's not feasible to support Java 8 time API otherwise, since it's statically referred from TemplateTemporalModel.
---
 README.md                                          |  7 +-
 build.properties.sample                            |  1 -
 build.xml                                          | 48 ++++---------
 osgi.bnd                                           |  8 +--
 src/main/java/freemarker/core/_Java8Impl.java      | 42 ------------
 src/main/java/freemarker/core/_JavaVersions.java   | 79 ----------------------
 .../freemarker/ext/beans/ClassIntrospector.java    |  9 ++-
 .../freemarker/ext/beans/BeansWrapperMiscTest.java |  2 -
 8 files changed, 21 insertions(+), 175 deletions(-)

diff --git a/README.md b/README.md
index d8197cb..b764b84 100644
--- a/README.md
+++ b/README.md
@@ -83,7 +83,7 @@ dependency, as freemarker.ext.dom can't use the XPath support
 included in OpenJDK anymore. It's not needed on Oracle Java 9,
 or if FreeMarker is configured to use Jaxen for XPath.
 
-The minimum required Java version is currently Java SE 7. (The presence
+The minimum required Java version is currently Java SE 8. (The presence
 of a later version may be detected on runtime and utilized by
 FreeMarker.)
 
@@ -201,8 +201,6 @@ Below you find the step-by-step setup for Eclipse (originally done on Mars.1):
    - Press "Finish"
 - Eclipse will indicate many errors at this point; it's expected, read on.
 - Project -> Properties -> Java Compiler
-  - Set "Compiler Compliance Level" to "1.7" (you will have to uncheck
-    "Use compliance from execution environment" for that)
   - In Errors/Warnings, check in "Enable project specific settings", then set
     "Forbidden reference (access rules)" from "Error" to "Warning".
 - You will still have errors on these java files (because different java
@@ -272,9 +270,6 @@ Originally done on IntelliJ IDEA Community 2018.2.4:
     - Test Resource Folders:  
       src/test/resources
       
-  - Still inside the "Sources" tab, change the "Language level" to "7". (Yes, we use Java 8 SDK with
-    language level 7 in the IDE, due to the tricks FreeMarker uses to support different Java versions.)
-    
   - Switch over to the "Dependencies" tab (still inside "Project Structure" / "Modules"), and add
     all the jar-s inside the `ide-dependencies` directory as dependency. (How: Click the "+" icon
     at the right edge, select "JARs or directory", navigate to `ide-dependencies` directory, expand
diff --git a/build.properties.sample b/build.properties.sample
index d5e31cc..49eb98f 100644
--- a/build.properties.sample
+++ b/build.properties.sample
@@ -17,7 +17,6 @@
 
 # Copy this file to "build.properties" before editing!
 # These propeties should point to the rt.jar-s of the respective J2SE versions:
-boot.classpath.j2se1.7=c:/Program Files/Java/jre7/lib/rt.jar
 boot.classpath.j2se1.8=C:/Program Files/Java/jdk1.8.0_66/jre/lib/rt.jar
 mvnCommand=C:/Program Files (x86)/maven3/bin/mvn.bat
 gpgCommand=C:/Program Files (x86)/GNU/GnuPG/pub/gpg.exe
\ No newline at end of file
diff --git a/build.xml b/build.xml
index b1adffc..dfab241 100644
--- a/build.xml
+++ b/build.xml
@@ -43,9 +43,6 @@
   <property name="server.ivy.repo.root" value="${basedir}/build/dummy-server-ivy-repo" />
   
   <property file="build.properties"/>
-  <condition property="has.explicit.boot.classpath.j2se1.7">
-    <isset property="boot.classpath.j2se1.7"/>
-  </condition>
   <condition property="has.explicit.boot.classpath.j2se1.8">
     <isset property="boot.classpath.j2se1.8"/>
   </condition>
@@ -58,14 +55,9 @@
 
   <!-- When boot.classpath.j2se* is missing, these will be the defaults: -->
   <!-- Note: Target "dist" doesn't allow using these. -->
-  <property name="boot.classpath.j2se1.7" value="${sun.boot.class.path}" />
   <property name="boot.classpath.j2se1.8" value="${sun.boot.class.path}" />
   
   <!-- For checking the correctness of the boot.classpath.j2se* -->
-  <available classpath="${boot.classpath.j2se1.7}"
-    classname="java.nio.file.Path" ignoresystemclasses="true" 
-    property="boot.classpath.j2se1.7.correct"
-  />
   <available classpath="${boot.classpath.j2se1.8}"
     classname="java.time.Instant" ignoresystemclasses="true" 
     property="boot.classpath.j2se1.8.correct"
@@ -202,12 +194,6 @@
   </target>
    
   <target name="compile" depends="javacc">
-    <fail unless="boot.classpath.j2se1.7.correct"><!--
-      -->The "boot.classpath.j2se1.7" property value (${boot.classpath.j2se1.7}) <!--
-      -->seems to be an incorrect boot classpath. Please fix it in <!--
-      -->the &lt;projectDir>/build.properties file, or wherever you <!--
-      -->set it.<!--
-    --></fail>
     <fail unless="boot.classpath.j2se1.8.correct"><!--
       -->The "boot.classpath.j2se1.8" property value (${boot.classpath.j2se1.8}) <!--
       -->seems to be an incorrect boot classpath. Please fix it in <!--
@@ -216,7 +202,6 @@
     --></fail>
     <echo level="info"><!--
       -->Using boot classpaths: <!--
-      -->Java 7: ${boot.classpath.j2se1.7};<!--
       -->Java 8: ${boot.classpath.j2se1.8}<!--
     --></echo>
 
@@ -244,10 +229,10 @@
     <!-- Note: the "build.base" conf doesn't include optional FreeMarker dependencies. -->
     <ivy:cachepath conf="build.base" pathid="ivy.dep" />
     <javac destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       excludes="
         freemarker/core/_Java?*Impl.java,
         freemarker/ext/jsp/**,
@@ -264,15 +249,6 @@
       </src>
     </javac>
 
-    <ivy:cachepath conf="build.base" pathid="ivy.dep" />
-    <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
-      includeantruntime="false"
-      classpathref="ivy.dep"
-      bootclasspath="${boot.classpath.j2se1.8}"
-      includes="freemarker/core/_Java8Impl.java"
-    />
-    
     <rmic
       base="build/classes" includes="freemarker/debug/impl/Rmi*Impl.class"
       classpathref="ivy.dep"
@@ -281,10 +257,10 @@
 
     <ivy:cachepath conf="build.jsp2.0" pathid="ivy.dep.jsp2.0" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jsp2.0"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jsp/**,
         freemarker/ext/servlet/**,
@@ -299,10 +275,10 @@
     
     <ivy:cachepath conf="build.jsp2.1" pathid="ivy.dep.jsp2.1" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jsp2.1"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jsp/_FreeMarkerPageContext21.java,
         freemarker/ext/jsp/FreeMarkerJspFactory21.java,
@@ -311,10 +287,10 @@
 
     <ivy:cachepath conf="build.jython2.0" pathid="ivy.dep.jython2.0" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.0"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/ant/**,
         freemarker/template/utility/JythonRuntime.java,
@@ -326,20 +302,20 @@
     
     <ivy:cachepath conf="build.jython2.2" pathid="ivy.dep.jython2.2" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.2"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jython/_Jython22VersionAdapter.java"
     />
     
     <ivy:cachepath conf="build.jython2.5" pathid="ivy.dep.jython2.5" />
     <javac srcdir="build/src-main-java-filtered" destdir="build/classes" deprecation="off" 
-      debug="on" optimize="off" target="1.7" source="1.7" encoding="utf-8"
+      debug="on" optimize="off" target="1.8" source="1.8" encoding="utf-8"
       includeantruntime="false"
       classpathref="ivy.dep.jython2.5"
-      bootclasspath="${boot.classpath.j2se1.7}"
+      bootclasspath="${boot.classpath.j2se1.8}"
       includes="
         freemarker/ext/jython/_Jython25VersionAdapter.java"
     />
diff --git a/osgi.bnd b/osgi.bnd
index 08bac25..906f74d 100644
--- a/osgi.bnd
+++ b/osgi.bnd
@@ -49,10 +49,10 @@ Import-Package: !freemarker.*, *;resolution:="optional"
 # This is needed for "a.class.from.another.Bundle"?new() to work.
 DynamicImport-Package: *
 
-# The required minimum is 1.5, but we utilize versions up to 1.8 if available.
-# See also: http://wiki.eclipse.org/Execution_Environments, "Compiling
-# against more than is required"
-Bundle-RequiredExecutionEnvironment: JavaSE-1.8, JavaSE-1.7, JavaSE-1.6, J2SE-1.5
+# List whole version range between minimum up to where we use new features
+# if available. See also: http://wiki.eclipse.org/Execution_Environments,
+# "Compiling against more than is required"
+Bundle-RequiredExecutionEnvironment: JavaSE-1.8
 
 # Non-OSGi meta:
 Main-Class: freemarker.core.CommandLine
diff --git a/src/main/java/freemarker/core/_Java8Impl.java b/src/main/java/freemarker/core/_Java8Impl.java
deleted file mode 100644
index a25e0f3..0000000
--- a/src/main/java/freemarker/core/_Java8Impl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.lang.reflect.Method;
-
-/**
- * Used internally only, might changes without notice!
- * Used for accessing functionality that's only present in Java 8 or later.
- */
-// Compile this against Java 8
-@SuppressWarnings("Since15") // For IntelliJ inspection
-public class _Java8Impl implements _Java8 {
-    
-    public static final _Java8 INSTANCE = new _Java8Impl();
-
-    private _Java8Impl() {
-        // Not meant to be instantiated
-    }    
-
-    @Override
-    public boolean isDefaultMethod(Method method) {
-        return method.isDefault();
-    }
-
-}
diff --git a/src/main/java/freemarker/core/_JavaVersions.java b/src/main/java/freemarker/core/_JavaVersions.java
deleted file mode 100644
index 3f07673..0000000
--- a/src/main/java/freemarker/core/_JavaVersions.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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 freemarker.log.Logger;
-import freemarker.template.Version;
-import freemarker.template.utility.SecurityUtilities;
-
-/**
- * Used internally only, might changes without notice!
- */
-public final class _JavaVersions {
-    
-    private _JavaVersions() {
-        // Not meant to be instantiated
-    }
-
-    private static final boolean IS_AT_LEAST_8;
-    static {
-        boolean result = false;
-        String vStr = SecurityUtilities.getSystemProperty("java.version", null);
-        if (vStr != null) {
-            try {
-                Version v = new Version(vStr);
-                result = v.getMajor() == 1 && v.getMinor() >= 8 || v.getMajor() > 1;
-            } catch (Exception e) {
-                // Ignore
-            }
-        } else {
-            try {
-                Class.forName("java.time.Instant");
-                result = true;
-            } catch (Exception e) {
-                // Ignore
-            }
-        }
-        IS_AT_LEAST_8 = result;
-    }
-    
-    /**
-     * {@code null} if Java 8 is not available, otherwise the object through with the Java 8 operations are available.
-     */
-    static public final _Java8 JAVA_8;
-    static {
-        _Java8 java8;
-        if (IS_AT_LEAST_8) {
-            try {
-                java8 = (_Java8) Class.forName("freemarker.core._Java8Impl").getField("INSTANCE").get(null);
-            } catch (Exception e) {
-                try {
-                    Logger.getLogger("freemarker.runtime").error("Failed to access Java 8 functionality", e);
-                } catch (Exception e2) {
-                    // Suppressed
-                }
-                java8 = null;
-            }
-        } else {
-            java8 = null;
-        }
-        JAVA_8 = java8;
-    }
-    
-}
diff --git a/src/main/java/freemarker/ext/beans/ClassIntrospector.java b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
index e0c5135..8b5f276 100644
--- a/src/main/java/freemarker/ext/beans/ClassIntrospector.java
+++ b/src/main/java/freemarker/ext/beans/ClassIntrospector.java
@@ -48,7 +48,6 @@ import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import freemarker.core.BugException;
-import freemarker.core._JavaVersions;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecision;
 import freemarker.ext.beans.BeansWrapper.MethodAppearanceDecisionInput;
 import freemarker.ext.util.ModelCache;
@@ -413,7 +412,7 @@ class ClassIntrospector {
         List<PropertyDescriptor> introspectorPDs = introspectorPDsArray != null ? Arrays.asList(introspectorPDsArray)
                 : Collections.<PropertyDescriptor>emptyList();
         
-        if (!treatDefaultMethodsAsBeanMembers || _JavaVersions.JAVA_8 == null) {
+        if (!treatDefaultMethodsAsBeanMembers) {
             // java.beans.Introspector was good enough then.
             return introspectorPDs;
         }
@@ -434,7 +433,7 @@ class ClassIntrospector {
         // (Note that java.beans.Introspector discovers non-accessible public methods, and to emulate that behavior
         // here, we don't utilize the accessibleMethods Map, which we might already have at this point.)
         for (Method method : clazz.getMethods()) {
-            if (_JavaVersions.JAVA_8.isDefaultMethod(method) && method.getReturnType() != void.class
+            if (method.isDefault() && method.getReturnType() != void.class
                     && !method.isBridge()) {
                 Class<?>[] paramTypes = method.getParameterTypes();
                 if (paramTypes.length == 0
@@ -607,14 +606,14 @@ class ClassIntrospector {
         List<MethodDescriptor> introspectionMDs = introspectorMDArray != null && introspectorMDArray.length != 0
                 ? Arrays.asList(introspectorMDArray) : Collections.<MethodDescriptor>emptyList();
 
-        if (!treatDefaultMethodsAsBeanMembers || _JavaVersions.JAVA_8 == null) {
+        if (!treatDefaultMethodsAsBeanMembers) {
             // java.beans.Introspector was good enough then.
             return introspectionMDs;
         }
 
         Map<String, List<Method>> defaultMethodsToAddByName = null;
         for (Method method : clazz.getMethods()) {
-            if (_JavaVersions.JAVA_8.isDefaultMethod(method) && !method.isBridge()) {
+            if (method.isDefault() && !method.isBridge()) {
                 if (defaultMethodsToAddByName == null) {
                     defaultMethodsToAddByName = new HashMap<>();
                 }
diff --git a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
index c3ff772..ac7a7b7 100644
--- a/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
+++ b/src/test/java/freemarker/ext/beans/BeansWrapperMiscTest.java
@@ -29,7 +29,6 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import freemarker.core._JavaVersions;
 import freemarker.template.Configuration;
 import freemarker.template.TemplateBooleanModel;
 import freemarker.template.TemplateHashModel;
@@ -101,7 +100,6 @@ public class BeansWrapperMiscTest {
 
     @Test
     public void java8InaccessibleIndexedAccessibleNonIndexedReadMethodTest() throws TemplateModelException {
-        assertTrue("This test case must be ran on Java 8 or later", _JavaVersions.JAVA_8 != null);
         assertFalse(Modifier.isPublic(BeanWithInaccessibleIndexedProperty.class.getModifiers()));
         
         for (Version ici : new Version[] { Configuration.VERSION_2_3_26, Configuration.VERSION_2_3_27 }) {


[freemarker] 01/02: Merge branch '2.3-gae' into FREEMARKER-35

Posted by dd...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit aa7e565269bf082146ad85123cdbb58b4517f4d8
Merge: 888dece 6beb29c
Author: ddekany <dd...@apache.org>
AuthorDate: Sat Apr 11 09:51:08 2020 +0200

    Merge branch '2.3-gae' into FREEMARKER-35

 build.xml                                          |  2 +-
 src/main/java/freemarker/core/Environment.java     | 65 ++++++++------
 .../ext/beans/BeansWrapperConfiguration.java       |  7 ++
 .../java/freemarker/template/Configuration.java    | 17 +++-
 .../DefaultObjectWrapperConfiguration.java         |  3 +
 .../java/freemarker/template/_TemplateAPI.java     | 22 ++++-
 src/main/resources/freemarker/version.properties   |  8 +-
 src/manual/en_US/book.xml                          | 99 ++++++++++++++++------
 src/test/java/freemarker/manual/ExamplesTest.java  |  3 +-
 .../freemarker/template/ConfigurationTest.java     |  2 +
 src/test/resources/freemarker/core/ast-lambda.ast  | 18 ++++
 .../overloaded-methods-2-bwici-2.3.20.ftl          |  2 +-
 12 files changed, 189 insertions(+), 59 deletions(-)