Bug fixed (FREEMARKER-83); this fix is only active when incomplatible_improvements is set to 2.3.28 (or higher). When calling a macro or function (things defined in a template, not directly in Java) and the argument list contains .current_template_name, now it will correctly evaluate to the template that contains the call, rather than to the template that contains the macro or function definition. (Of course, the parameter default value expression is still evaluated in the context of the called macro or function.)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/c4e77749 Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/c4e77749 Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/c4e77749 Branch: refs/heads/2.3 Commit: c4e7774934bc57424b52158cfc0bef07a800028c Parents: 1aabd37 Author: ddekany <ddek...@apache.org> Authored: Tue Mar 6 16:37:30 2018 +0100 Committer: ddekany <ddek...@apache.org> Committed: Tue Mar 6 16:46:21 2018 +0100 ---------------------------------------------------------------------- src/main/java/freemarker/core/Environment.java | 22 +++++++- .../java/freemarker/template/Configuration.java | 13 +++++ .../java/freemarker/template/_TemplateAPI.java | 1 + src/manual/en_US/book.xml | 16 ++++++ .../core/TemplateNameSpecialVariablesTest.java | 56 ++++++++++++++++++++ 5 files changed, 106 insertions(+), 2 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c4e77749/src/main/java/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java index cc62d4c..4e208ee 100644 --- a/src/main/java/freemarker/core/Environment.java +++ b/src/main/java/freemarker/core/Environment.java @@ -110,6 +110,7 @@ public final class Environment extends Configurable { } private final Configuration configuration; + private final boolean incompatibleImprovementsGE2328; private final TemplateHashModel rootDataModel; private TemplateElement[] instructionStack = new TemplateElement[16]; private int instructionStackSize = 0; @@ -199,6 +200,7 @@ public final class Environment extends Configurable { public Environment(Template template, final TemplateHashModel rootDataModel, Writer out) { super(template); configuration = template.getConfiguration(); + incompatibleImprovementsGE2328 = configuration.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_28; this.globalNamespace = new Namespace(null); this.currentNamespace = mainNamespace = new Namespace(template); this.out = out; @@ -735,10 +737,24 @@ public final class Environment extends Configurable { return; } - pushElement(macro); + boolean elementPushed; + if (!incompatibleImprovementsGE2328) { + // Doing this so early is wrong, as now the arguments will be evaluated while the called macro/function is + // in the element stack. Thus .current_template_name will be wrong for example. + pushElement(macro); + elementPushed = true; + } else { + elementPushed = false; + } try { final Macro.Context macroCtx = macro.new Context(this, childBuffer, bodyParameterNames); + // Causes the evaluation of argument expressions: setMacroContextLocalsFromArguments(macroCtx, macro, namedArgs, positionalArgs); + + if (!elementPushed) { // When incompatibleImprovements >= 2.3.28 + pushElement(macro); + elementPushed = true; + } final Macro.Context prevMacroCtx = currentMacroContext; currentMacroContext = macroCtx; @@ -762,7 +778,9 @@ public final class Environment extends Configurable { currentNamespace = prevNamespace; } } finally { - popElement(); + if (elementPushed) { + popElement(); + } } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c4e77749/src/main/java/freemarker/template/Configuration.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/Configuration.java b/src/main/java/freemarker/template/Configuration.java index 8dc77a2..c560d70 100644 --- a/src/main/java/freemarker/template/Configuration.java +++ b/src/main/java/freemarker/template/Configuration.java @@ -417,6 +417,9 @@ public class Configuration extends Configurable implements Cloneable, ParserConf /** FreeMarker version 2.3.27 (an {@link #Configuration(Version) incompatible improvements break-point}) */ public static final Version VERSION_2_3_27 = new Version(2, 3, 27); + + /** FreeMarker version 2.3.28 (an {@link #Configuration(Version) incompatible improvements break-point}) */ + public static final Version VERSION_2_3_28 = new Version(2, 3, 28); /** The default of {@link #getIncompatibleImprovements()}, currently {@link #VERSION_2_3_0}. */ public static final Version DEFAULT_INCOMPATIBLE_IMPROVEMENTS = Configuration.VERSION_2_3_0; @@ -853,6 +856,16 @@ public class Configuration extends Configurable implements Cloneable, ParserConf * that templates utilize the the bug that's not fixed. * </ul> * </li> + * <li><p> + * 2.3.28 (or higher): + * <ul> + * <li><p>When calling a macro or function (things defined in a template, not directly in Java) and the + * argument list contains {@code .current_template_name}, now it will correctly evaluate to the template + * that contains the call, rather than to the template that contains the macro or function definition. + * (Of course, the parameter default value expression is still evaluated in the context of the called + * macro or function.) + * </ul> + * </li> * </ul> * * @throws IllegalArgumentException http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c4e77749/src/main/java/freemarker/template/_TemplateAPI.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/template/_TemplateAPI.java b/src/main/java/freemarker/template/_TemplateAPI.java index cf5f39c..9a6ee58 100644 --- a/src/main/java/freemarker/template/_TemplateAPI.java +++ b/src/main/java/freemarker/template/_TemplateAPI.java @@ -49,6 +49,7 @@ public class _TemplateAPI { public static final int VERSION_INT_2_3_25 = Configuration.VERSION_2_3_25.intValue(); public static final int VERSION_INT_2_3_26 = Configuration.VERSION_2_3_26.intValue(); public static final int VERSION_INT_2_3_27 = Configuration.VERSION_2_3_27.intValue(); + public static final int VERSION_INT_2_3_28 = Configuration.VERSION_2_3_28.intValue(); public static final int VERSION_INT_2_4_0 = Version.intValueFor(2, 4, 0); public static void checkVersionNotNullAndSupported(Version incompatibleImprovements) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c4e77749/src/manual/en_US/book.xml ---------------------------------------------------------------------- diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml index 5e01cd7..dab2c23 100644 --- a/src/manual/en_US/book.xml +++ b/src/manual/en_US/book.xml @@ -27267,6 +27267,22 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting> linkend="ref_specvar_get_optional_template">See the details here...</link></para> </listitem> + + <listitem> + <para>Bug fixed (<link + xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-83">FREEMARKER-83</link>); + this fix is only active when <link + linkend="pgui_config_incompatible_improvements_how_to_set"><literal>incomplatible_improvements</literal></link> + is set to 2.3.28 (or higher). When calling a macro or function + (things defined in a template, not directly in Java) and the + argument list contains + <literal>.current_template_name</literal>, now it will correctly + evaluate to the template that contains the call, rather than to + the template that contains the macro or function definition. (Of + course, the parameter default value expression is still + evaluated in the context of the called macro or + function.)</para> + </listitem> </itemizedlist> </section> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/c4e77749/src/test/java/freemarker/core/TemplateNameSpecialVariablesTest.java ---------------------------------------------------------------------- diff --git a/src/test/java/freemarker/core/TemplateNameSpecialVariablesTest.java b/src/test/java/freemarker/core/TemplateNameSpecialVariablesTest.java index 32f3702..5aa735d 100644 --- a/src/test/java/freemarker/core/TemplateNameSpecialVariablesTest.java +++ b/src/test/java/freemarker/core/TemplateNameSpecialVariablesTest.java @@ -208,5 +208,61 @@ public class TemplateNameSpecialVariablesTest extends TemplateTest { "t=foo.ftl, ct=foo.ftl, mt=foo.ftl; " + "t=foo.ftl->bar, ct=foo.ftl->bar, mt=foo.ftl"); } + + + @Test + public void testArgumentBugWithMacro() throws TemplateException, IOException { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("main.ftl", "" + + "<#include 'inc.ftl'>" + + "Before: ${.currentTemplateName}\n" + + "<@m p1=.currentTemplateName; x>" + + "Loop var: ${x}\n" + + "In nested: ${.currentTemplateName}\n" + + "</@>" + + "After: ${.currentTemplateName}"); + tl.putTemplate("inc.ftl", "" + + "<#macro m p1 p2=.currentTemplateName>" + + "p1: ${p1}\n" + + "p2: ${p2}\n" + + "Inside: ${.currentTemplateName}\n" + + "<#nested .currentTemplateName>" + + "</#macro>"); + Configuration cfg = getConfiguration(); + cfg.setTemplateLoader(tl); + + for (boolean fixed : new boolean[] { false, true }) { + cfg.setIncompatibleImprovements(fixed ? Configuration.VERSION_2_3_28 : Configuration.VERSION_2_3_27); + assertOutputForNamed("main.ftl", "" + + "Before: main.ftl\n" + + "p1: " + (fixed ? "main.ftl" : "inc.ftl") + "\n" + + "p2: inc.ftl\n" + + "Inside: inc.ftl\n" + + "Loop var: inc.ftl\n" + + "In nested: main.ftl\n" + + "After: main.ftl"); + } + } + + @Test + public void testArgumentBugWithFunction() throws TemplateException, IOException { + StringTemplateLoader tl = new StringTemplateLoader(); + tl.putTemplate("main.ftl", "" + + "<#include 'inc.ftl'>" + + "${f(.currentTemplateName)}"); + tl.putTemplate("inc.ftl", "" + + "<#function f(p1, p2=.currentTemplateName)>" + + "<#return 'p1=${p1}, p2=${p2}, inside=${.currentTemplateName}'>" + + "</#function>"); + Configuration cfg = getConfiguration(); + cfg.setTemplateLoader(tl); + + for (boolean fixed : new boolean[] { false, true }) { + cfg.setIncompatibleImprovements(fixed ? Configuration.VERSION_2_3_28 : Configuration.VERSION_2_3_27); + assertOutputForNamed("main.ftl", "" + + "p1=" + (fixed ? "main.ftl" : "inc.ftl") + + ", p2=inc.ftl, inside=inc.ftl"); + } + } }