diff --git a/pom.xml b/pom.xml
index ee78e721a..57723ed5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -20,6 +20,7 @@
0.8.3
3.0.1
1.9
+ 1.5
@@ -101,6 +102,11 @@
immutables-exceptions
${dep.hubspot-immutables.version}
+
+ com.hubspot
+ algebra
+ ${dep.algebra.version}
+
@@ -213,6 +219,10 @@
com.hubspot.immutables
immutables-exceptions
+
+ com.hubspot
+ algebra
+
diff --git a/src/main/java/com/hubspot/jinjava/Jinjava.java b/src/main/java/com/hubspot/jinjava/Jinjava.java
index 21179b86d..5c06e3d7a 100644
--- a/src/main/java/com/hubspot/jinjava/Jinjava.java
+++ b/src/main/java/com/hubspot/jinjava/Jinjava.java
@@ -20,6 +20,7 @@
import com.hubspot.jinjava.el.ExtendedSyntaxBuilder;
import com.hubspot.jinjava.el.TruthyTypeConverter;
import com.hubspot.jinjava.el.ext.eager.EagerExtendedSyntaxBuilder;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
import com.hubspot.jinjava.interpret.Context;
import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
import com.hubspot.jinjava.interpret.InterpretException;
@@ -245,52 +246,55 @@ public RenderResult renderForResult(
context = new Context(copyGlobalContext(), bindings, renderConfig.getDisabled());
}
- JinjavaInterpreter interpreter = globalConfig
- .getInterpreterFactory()
- .newInstance(this, context, renderConfig);
- JinjavaInterpreter.pushCurrent(interpreter);
-
- try {
- String result = interpreter.render(template);
- return new RenderResult(
- result,
- interpreter.getContext(),
- interpreter.getErrorsCopy()
- );
- } catch (InterpretException e) {
- if (e instanceof TemplateSyntaxException) {
+ try (
+ AutoCloseableImpl interpreterAutoCloseable = JinjavaInterpreter
+ .closeablePushCurrent(
+ globalConfig.getInterpreterFactory().newInstance(this, context, renderConfig)
+ )
+ .get()
+ ) {
+ JinjavaInterpreter interpreter = interpreterAutoCloseable.value();
+ try {
+ String result = interpreter.render(template);
+ return new RenderResult(
+ result,
+ interpreter.getContext(),
+ interpreter.getErrorsCopy()
+ );
+ } catch (InterpretException e) {
+ if (e instanceof TemplateSyntaxException) {
+ return new RenderResult(
+ TemplateError.fromException((TemplateSyntaxException) e),
+ interpreter.getContext(),
+ interpreter.getErrorsCopy()
+ );
+ }
+ return new RenderResult(
+ TemplateError.fromSyntaxError(e),
+ interpreter.getContext(),
+ interpreter.getErrorsCopy()
+ );
+ } catch (InvalidArgumentException e) {
+ return new RenderResult(
+ TemplateError.fromInvalidArgumentException(e),
+ interpreter.getContext(),
+ interpreter.getErrorsCopy()
+ );
+ } catch (InvalidInputException e) {
return new RenderResult(
- TemplateError.fromException((TemplateSyntaxException) e),
+ TemplateError.fromInvalidInputException(e),
+ interpreter.getContext(),
+ interpreter.getErrorsCopy()
+ );
+ } catch (Exception e) {
+ return new RenderResult(
+ TemplateError.fromException(e),
interpreter.getContext(),
interpreter.getErrorsCopy()
);
}
- return new RenderResult(
- TemplateError.fromSyntaxError(e),
- interpreter.getContext(),
- interpreter.getErrorsCopy()
- );
- } catch (InvalidArgumentException e) {
- return new RenderResult(
- TemplateError.fromInvalidArgumentException(e),
- interpreter.getContext(),
- interpreter.getErrorsCopy()
- );
- } catch (InvalidInputException e) {
- return new RenderResult(
- TemplateError.fromInvalidInputException(e),
- interpreter.getContext(),
- interpreter.getErrorsCopy()
- );
- } catch (Exception e) {
- return new RenderResult(
- TemplateError.fromException(e),
- interpreter.getContext(),
- interpreter.getErrorsCopy()
- );
} finally {
globalContext.reset();
- JinjavaInterpreter.popCurrent();
}
}
diff --git a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java
index 185c60792..6975d5ce6 100644
--- a/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java
+++ b/src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java
@@ -1,10 +1,13 @@
package com.hubspot.jinjava.el.ext;
import com.google.common.collect.ImmutableMap;
+import com.hubspot.algebra.Result;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
import com.hubspot.jinjava.interpret.CallStack;
import com.hubspot.jinjava.interpret.DeferredValueException;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
-import com.hubspot.jinjava.interpret.MacroTagCycleException;
+import com.hubspot.jinjava.interpret.TagCycleException;
import com.hubspot.jinjava.interpret.TemplateError;
import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory;
import com.hubspot.jinjava.lib.fn.MacroFunction;
@@ -18,6 +21,10 @@
public class AstMacroFunction extends AstFunction {
+ public enum MacroCallError {
+ CYCLE_DETECTED,
+ }
+
public AstMacroFunction(String name, int index, AstParameters params, boolean varargs) {
super(name, index, params, varargs);
}
@@ -37,30 +44,16 @@ public Object eval(Bindings bindings, ELContext context) {
interpreter.getPosition()
);
}
- if (!macroFunction.isCaller()) {
- if (checkAndPushMacroStack(interpreter, getName())) {
- return "";
- }
+ if (macroFunction.isCaller()) {
+ return wrapInvoke(bindings, context, macroFunction);
}
-
- try {
- return invoke(
- bindings,
- context,
- macroFunction,
- AbstractCallableMethod.EVAL_METHOD
- );
- } catch (IllegalAccessException e) {
- throw new ELException(LocalMessages.get("error.function.access", getName()), e);
- } catch (InvocationTargetException e) {
- throw new ELException(
- LocalMessages.get("error.function.invocation", getName()),
- e.getCause()
- );
- } finally {
- if (!macroFunction.isCaller()) {
- interpreter.getContext().getMacroStack().pop();
- }
+ try (
+ AutoCloseableImpl> macroStackPush =
+ checkAndPushMacroStackWithWrapper(interpreter, getName()).get()
+ ) {
+ return macroStackPush
+ .value()
+ .match(err -> "", path -> wrapInvoke(bindings, context, macroFunction));
}
}
@@ -69,62 +62,104 @@ public Object eval(Bindings bindings, ELContext context) {
: super.eval(bindings, context);
}
- public static boolean checkAndPushMacroStack(
+ private Object wrapInvoke(
+ Bindings bindings,
+ ELContext context,
+ MacroFunction macroFunction
+ ) {
+ try {
+ return invoke(bindings, context, macroFunction, AbstractCallableMethod.EVAL_METHOD);
+ } catch (IllegalAccessException e) {
+ throw new ELException(LocalMessages.get("error.function.access", getName()), e);
+ } catch (InvocationTargetException e) {
+ throw new ELException(
+ LocalMessages.get("error.function.invocation", getName()),
+ e.getCause()
+ );
+ }
+ }
+
+ public static AutoCloseableSupplier> checkAndPushMacroStackWithWrapper(
JinjavaInterpreter interpreter,
String name
) {
CallStack macroStack = interpreter.getContext().getMacroStack();
- try {
- if (interpreter.getConfig().isEnableRecursiveMacroCalls()) {
- if (interpreter.getConfig().getMaxMacroRecursionDepth() != 0) {
- macroStack.pushWithMaxDepth(
+ if (interpreter.getConfig().isEnableRecursiveMacroCalls()) {
+ if (interpreter.getConfig().getMaxMacroRecursionDepth() != 0) {
+ return macroStack
+ .closeablePushWithMaxDepth(
name,
interpreter.getConfig().getMaxMacroRecursionDepth(),
interpreter.getLineNumber(),
interpreter.getPosition()
+ )
+ .map(result ->
+ result.mapErr(err -> {
+ handleMacroCycleError(interpreter, name, err);
+ return MacroCallError.CYCLE_DETECTED;
+ })
);
- } else {
- macroStack.pushWithoutCycleCheck(
+ } else {
+ return macroStack
+ .closeablePushWithoutCycleCheck(
name,
interpreter.getLineNumber(),
interpreter.getPosition()
- );
- }
- } else {
- macroStack.push(name, -1, -1);
- }
- } catch (MacroTagCycleException e) {
- int maxDepth = interpreter.getConfig().getMaxMacroRecursionDepth();
- if (maxDepth != 0 && interpreter.getConfig().isValidationMode()) {
- // validation mode is only concerned with syntax
- return true;
+ )
+ .map(Result::ok);
}
-
- String message = maxDepth == 0
- ? String.format("Cycle detected for macro '%s'", name)
- : String.format(
- "Max recursion limit of %d reached for macro '%s'",
- maxDepth,
- name
- );
-
- interpreter.addError(
- new TemplateError(
- TemplateError.ErrorType.WARNING,
- TemplateError.ErrorReason.EXCEPTION,
- TemplateError.ErrorItem.TAG,
- message,
- null,
- e.getLineNumber(),
- e.getStartPosition(),
- e,
- BasicTemplateErrorCategory.CYCLE_DETECTED,
- ImmutableMap.of("name", name)
- )
+ }
+ return macroStack
+ .closeablePush(name, -1, -1)
+ .map(result ->
+ result.mapErr(err -> {
+ handleMacroCycleError(interpreter, name, err);
+ return MacroCallError.CYCLE_DETECTED;
+ })
);
+ }
- return true;
+ private static void handleMacroCycleError(
+ JinjavaInterpreter interpreter,
+ String name,
+ TagCycleException e
+ ) {
+ int maxDepth = interpreter.getConfig().getMaxMacroRecursionDepth();
+ if (maxDepth != 0 && interpreter.getConfig().isValidationMode()) {
+ // validation mode is only concerned with syntax
+ return;
}
- return false;
+
+ String message = maxDepth == 0
+ ? String.format("Cycle detected for macro '%s'", name)
+ : String.format("Max recursion limit of %d reached for macro '%s'", maxDepth, name);
+
+ interpreter.addError(
+ new TemplateError(
+ TemplateError.ErrorType.WARNING,
+ TemplateError.ErrorReason.EXCEPTION,
+ TemplateError.ErrorItem.TAG,
+ message,
+ null,
+ e.getLineNumber(),
+ e.getStartPosition(),
+ e,
+ BasicTemplateErrorCategory.CYCLE_DETECTED,
+ ImmutableMap.of("name", name)
+ )
+ );
+ }
+
+ @Deprecated
+ public static boolean checkAndPushMacroStack(
+ JinjavaInterpreter interpreter,
+ String name
+ ) {
+ return checkAndPushMacroStackWithWrapper(interpreter, name)
+ .dangerouslyGetWithoutClosing()
+ .match(
+ err -> true, // cycle detected
+ ok -> false // no cycle
+ );
}
}
diff --git a/src/main/java/com/hubspot/jinjava/interpret/AutoCloseableSupplier.java b/src/main/java/com/hubspot/jinjava/interpret/AutoCloseableSupplier.java
new file mode 100644
index 000000000..12a7b8dfe
--- /dev/null
+++ b/src/main/java/com/hubspot/jinjava/interpret/AutoCloseableSupplier.java
@@ -0,0 +1,68 @@
+package com.hubspot.jinjava.interpret;
+
+import com.google.common.base.Suppliers;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+public class AutoCloseableSupplier implements Supplier> {
+
+ public static AutoCloseableSupplier of(T tSupplier) {
+ return of(() -> tSupplier, ignored -> {});
+ }
+
+ public static AutoCloseableSupplier of(
+ Supplier tSupplier,
+ Consumer closeConsumer
+ ) {
+ return new AutoCloseableSupplier<>(
+ Suppliers.memoize(() -> new AutoCloseableImpl<>(tSupplier.get(), closeConsumer))
+ );
+ }
+
+ private final Supplier> autoCloseableImplWrapper;
+
+ private AutoCloseableSupplier(Supplier> autoCloseableImplWrapper) {
+ this.autoCloseableImplWrapper = autoCloseableImplWrapper;
+ }
+
+ @Override
+ public AutoCloseableImpl get() {
+ return autoCloseableImplWrapper.get();
+ }
+
+ public T dangerouslyGetWithoutClosing() {
+ return autoCloseableImplWrapper.get().value();
+ }
+
+ public AutoCloseableSupplier map(Function mapper) {
+ return new AutoCloseableSupplier<>(() -> {
+ T t = autoCloseableImplWrapper.get().value();
+ return new AutoCloseableImpl<>(
+ mapper.apply(t),
+ r -> autoCloseableImplWrapper.get().closeConsumer.accept(t)
+ );
+ });
+ }
+
+ public static class AutoCloseableImpl implements java.lang.AutoCloseable {
+
+ private final T t;
+ private final Consumer closeConsumer;
+
+ protected AutoCloseableImpl(T t, Consumer closeConsumer) {
+ this.t = t;
+ this.closeConsumer = closeConsumer;
+ }
+
+ public T value() {
+ return t;
+ }
+
+ @Override
+ public void close() {
+ closeConsumer.accept(t);
+ }
+ }
+}
diff --git a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java
index 83bff5d69..f67d96d6b 100644
--- a/src/main/java/com/hubspot/jinjava/interpret/CallStack.java
+++ b/src/main/java/com/hubspot/jinjava/interpret/CallStack.java
@@ -1,5 +1,6 @@
package com.hubspot.jinjava.interpret;
+import com.hubspot.algebra.Result;
import java.util.Optional;
import java.util.Stack;
@@ -111,6 +112,57 @@ public boolean isEmpty() {
return stack.empty() && (parent == null || parent.isEmpty());
}
+ public AutoCloseableSupplier> closeablePush(
+ String path,
+ int lineNumber,
+ int startPosition
+ ) {
+ return AutoCloseableSupplier.of(
+ () -> {
+ try {
+ push(path, lineNumber, startPosition);
+ return Result.ok(path);
+ } catch (TagCycleException e) {
+ return Result.err(e);
+ }
+ },
+ result -> result.ifOk(ok -> pop())
+ );
+ }
+
+ public AutoCloseableSupplier closeablePushWithoutCycleCheck(
+ String path,
+ int lineNumber,
+ int startPosition
+ ) {
+ return AutoCloseableSupplier.of(
+ () -> {
+ pushWithoutCycleCheck(path, lineNumber, startPosition);
+ return path;
+ },
+ ignored -> pop()
+ );
+ }
+
+ public AutoCloseableSupplier> closeablePushWithMaxDepth(
+ String path,
+ int maxDepth,
+ int lineNumber,
+ int startPosition
+ ) {
+ return AutoCloseableSupplier.of(
+ () -> {
+ try {
+ pushWithMaxDepth(path, maxDepth, lineNumber, startPosition);
+ return Result.ok(path);
+ } catch (TagCycleException e) {
+ return Result.err(e);
+ }
+ },
+ result -> result.ifOk(ok -> pop())
+ );
+ }
+
private void pushToStack(String path, int lineNumber, int startPosition) {
if (isEmpty()) {
topLineNumber = lineNumber;
diff --git a/src/main/java/com/hubspot/jinjava/interpret/Context.java b/src/main/java/com/hubspot/jinjava/interpret/Context.java
index 4126371e6..a9892c060 100644
--- a/src/main/java/com/hubspot/jinjava/interpret/Context.java
+++ b/src/main/java/com/hubspot/jinjava/interpret/Context.java
@@ -21,6 +21,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
import com.hubspot.jinjava.interpret.ContextConfigurationIF.ErrorHandlingStrategyIF.TemplateErrorTypeHandlingStrategy;
import com.hubspot.jinjava.lib.Importable;
import com.hubspot.jinjava.lib.expression.ExpressionStrategy;
@@ -677,6 +678,10 @@ public CallStack getImportPathStack() {
return importPathStack;
}
+ public CallStack getFromPathStack() {
+ return fromStack;
+ }
+
public CallStack getIncludePathStack() {
return includePathStack;
}
@@ -693,10 +698,12 @@ public CallStack getCurrentPathStack() {
return currentPathStack;
}
+ @Deprecated
public void pushFromStack(String path, int lineNumber, int startPosition) {
fromStack.push(path, lineNumber, startPosition);
}
+ @Deprecated
public void popFromStack() {
fromStack.pop();
}
@@ -717,10 +724,17 @@ public void setRenderDepth(int renderDepth) {
this.renderDepth = renderDepth;
}
+ public AutoCloseableSupplier closeablePushRenderStack(String template) {
+ renderStack.push(template);
+ return AutoCloseableSupplier.of(() -> template, t -> renderStack.pop());
+ }
+
+ @Deprecated
public void pushRenderStack(String template) {
renderStack.push(template);
}
+ @Deprecated
public String popRenderStack() {
return renderStack.pop();
}
@@ -878,25 +892,16 @@ public TemporaryValueClosable withUnwrapRawOverride() {
return temporaryValueClosable;
}
- public static class TemporaryValueClosable implements AutoCloseable {
-
- private final T previousValue;
- private final Consumer resetValueConsumer;
+ public static class TemporaryValueClosable extends AutoCloseableImpl {
private TemporaryValueClosable(T previousValue, Consumer resetValueConsumer) {
- this.previousValue = previousValue;
- this.resetValueConsumer = resetValueConsumer;
+ super(previousValue, resetValueConsumer);
}
public static TemporaryValueClosable noOp() {
return new NoOpTemporaryValueClosable<>();
}
- @Override
- public void close() {
- resetValueConsumer.accept(previousValue);
- }
-
private static class NoOpTemporaryValueClosable extends TemporaryValueClosable {
private NoOpTemporaryValueClosable() {
diff --git a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java
index 843eb0c49..e60c70fc9 100644
--- a/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java
+++ b/src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java
@@ -24,11 +24,13 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
+import com.hubspot.algebra.Result;
import com.hubspot.jinjava.Jinjava;
import com.hubspot.jinjava.JinjavaConfig;
import com.hubspot.jinjava.el.ExpressionResolver;
import com.hubspot.jinjava.el.ext.DeferredParsingException;
import com.hubspot.jinjava.el.ext.ExtendedParser;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
import com.hubspot.jinjava.interpret.Context.TemporaryValueClosable;
import com.hubspot.jinjava.interpret.ContextConfigurationIF.ErrorHandlingStrategyIF;
import com.hubspot.jinjava.interpret.ContextConfigurationIF.ErrorHandlingStrategyIF.TemplateErrorTypeHandlingStrategy;
@@ -75,6 +77,7 @@
import java.util.Set;
import java.util.Stack;
import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
@@ -380,14 +383,18 @@ private String render(Node root, boolean processExtendRoots, long renderLimit) {
output.addNode(new RenderedOutputNode(renderStr));
} else {
OutputNode out;
- context.pushRenderStack(renderStr);
- try {
- out = node.render(this);
- } catch (DeferredValueException e) {
- context.handleDeferredNode(node);
- out = new RenderedOutputNode(node.getMaster().getImage());
+ try (
+ AutoCloseableImpl closeable = context
+ .closeablePushRenderStack(renderStr)
+ .get()
+ ) {
+ try {
+ out = node.render(this);
+ } catch (DeferredValueException e) {
+ context.handleDeferredNode(node);
+ out = new RenderedOutputNode(node.getMaster().getImage());
+ }
}
- context.popRenderStack();
output.addNode(out);
}
} catch (OutputTooBigException e) {
@@ -434,46 +441,54 @@ private String render(Node root, boolean processExtendRoots, long renderLimit) {
break;
}
extendPaths.add(extendPath.orElse(""));
- context
- .getCurrentPathStack()
- .push(
- extendPath.orElse(""),
- context.getExtendPathStack().getTopLineNumber(),
- context.getExtendPathStack().getTopStartPosition()
- );
- Node parentRoot = extendParentRoots.removeFirst();
- if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
- ignoredOutput.append(
- output
- .getNodes()
- .stream()
- .filter(node -> node instanceof RenderedOutputNode)
- .map(OutputNode::getValue)
- .collect(Collectors.joining())
- );
- }
- numDeferredTokensBefore = context.getDeferredTokens().size();
- output = new OutputList(config.getMaxOutputSize());
- output.addNode(pathSetter);
- boolean hasNestedExtends = false;
- for (Node node : parentRoot.getChildren()) {
- lineNumber = node.getLineNumber() - 1; // The line number is off by one when rendering the extend parent
- position = node.getStartPosition();
- try {
- OutputNode out = node.render(this);
- output.addNode(out);
- if (isExtendsTag(node)) {
- hasNestedExtends = true;
+ try (
+ AutoCloseableImpl> closeableCurrentPath =
+ context
+ .getCurrentPathStack()
+ .closeablePush(
+ extendPath.orElse(""),
+ context.getExtendPathStack().getTopLineNumber(),
+ context.getExtendPathStack().getTopStartPosition()
+ )
+ .get()
+ ) {
+ String currentPath = closeableCurrentPath
+ .value()
+ .unwrapOrElseThrow(Function.identity());
+ Node parentRoot = extendParentRoots.removeFirst();
+ if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
+ ignoredOutput.append(
+ output
+ .getNodes()
+ .stream()
+ .filter(node -> node instanceof RenderedOutputNode)
+ .map(OutputNode::getValue)
+ .collect(Collectors.joining())
+ );
+ }
+ numDeferredTokensBefore = context.getDeferredTokens().size();
+ output = new OutputList(config.getMaxOutputSize());
+ output.addNode(pathSetter);
+ boolean hasNestedExtends = false;
+ for (Node node : parentRoot.getChildren()) {
+ lineNumber = node.getLineNumber() - 1; // The line number is off by one when rendering the extend parent
+ position = node.getStartPosition();
+ try {
+ OutputNode out = node.render(this);
+ output.addNode(out);
+ if (isExtendsTag(node)) {
+ hasNestedExtends = true;
+ }
+ } catch (OutputTooBigException e) {
+ addError(TemplateError.fromOutputTooBigException(e));
+ return output.getValue();
}
- } catch (OutputTooBigException e) {
- addError(TemplateError.fromOutputTooBigException(e));
- return output.getValue();
}
+ Optional currentExtendPath = context.getExtendPathStack().pop();
+ extendPath =
+ hasNestedExtends ? currentExtendPath : context.getExtendPathStack().peek();
+ basePath = Optional.of(currentPath);
}
- Optional currentExtendPath = context.getExtendPathStack().pop();
- extendPath =
- hasNestedExtends ? currentExtendPath : context.getExtendPathStack().peek();
- basePath = context.getCurrentPathStack().pop();
}
}
@@ -547,37 +562,29 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) {
OutputList blockValueBuilder = new OutputList(config.getMaxOutputSize());
DynamicRenderedOutputNode prefix = new DynamicRenderedOutputNode();
blockValueBuilder.addNode(prefix);
- boolean pushedParentPathOntoStack = false;
int numDeferredTokensBefore = context.getDeferredTokens().size();
- if (
- block.getParentPath().isPresent() &&
- !getContext().getCurrentPathStack().contains(block.getParentPath().get())
+
+ try (
+ AutoCloseableImpl parentPathPush = conditionallyPushParentPath(block)
+ .get()
) {
- getContext()
- .getCurrentPathStack()
- .push(
- block.getParentPath().get(),
- block.getParentLineNo(),
- block.getParentPosition()
- );
- pushedParentPathOntoStack = true;
- lineNumber--; // The line number is off by one when rendering the block from the parent template
- }
- for (Node child : block.getNodes()) {
- lineNumber = child.getLineNumber();
- position = child.getStartPosition();
+ if (parentPathPush.value()) {
+ lineNumber--; // The line number is off by one when rendering the block from the parent template
+ }
- blockValueBuilder.addNode(child.render(this));
- }
- if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
- EagerReconstructionUtils.reconstructPathAroundBlock(
- prefix,
- blockValueBuilder,
- this
- );
- }
- if (pushedParentPathOntoStack) {
- getContext().getCurrentPathStack().pop();
+ for (Node child : block.getNodes()) {
+ lineNumber = child.getLineNumber();
+ position = child.getStartPosition();
+
+ blockValueBuilder.addNode(child.render(this));
+ }
+ if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
+ EagerReconstructionUtils.reconstructPathAroundBlock(
+ prefix,
+ blockValueBuilder,
+ this
+ );
+ }
}
blockNames.push(blockPlaceholder.getBlockName());
resolveBlockStubs(blockValueBuilder, blockNames);
@@ -596,6 +603,24 @@ private void resolveBlockStubs(OutputList output, Stack blockNames) {
}
}
+ private AutoCloseableSupplier conditionallyPushParentPath(BlockInfo block) {
+ if (
+ block.getParentPath().isPresent() &&
+ !getContext().getCurrentPathStack().contains(block.getParentPath().get())
+ ) {
+ return getContext()
+ .getCurrentPathStack()
+ .closeablePush(
+ block.getParentPath().get(),
+ block.getParentLineNo(),
+ block.getParentPosition()
+ )
+ .map(path -> true);
+ } else {
+ return AutoCloseableSupplier.of(false);
+ }
+ }
+
/**
* Resolve a variable from the interpreter context, returning null if not found. This method updates the template error accumulators when a variable is not found.
*
@@ -957,10 +982,20 @@ public static Optional getCurrentMaybe() {
return Optional.ofNullable(getCurrent());
}
+ public static AutoCloseableSupplier closeablePushCurrent(
+ JinjavaInterpreter interpreter
+ ) {
+ Stack stack = CURRENT_INTERPRETER.get();
+ stack.push(interpreter);
+ return AutoCloseableSupplier.of(() -> interpreter, i -> stack.pop());
+ }
+
+ @Deprecated
public static void pushCurrent(JinjavaInterpreter interpreter) {
CURRENT_INTERPRETER.get().push(interpreter);
}
+ @Deprecated
public static void popCurrent() {
if (!CURRENT_INTERPRETER.get().isEmpty()) {
CURRENT_INTERPRETER.get().pop();
diff --git a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java
index 14cdaa47c..39f2cfd5c 100644
--- a/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java
+++ b/src/main/java/com/hubspot/jinjava/lib/fn/MacroFunction.java
@@ -1,6 +1,8 @@
package com.hubspot.jinjava.lib.fn;
import com.hubspot.jinjava.el.ext.AbstractCallableMethod;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier;
+import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
import com.hubspot.jinjava.interpret.Context;
import com.hubspot.jinjava.interpret.Context.TemporaryValueClosable;
import com.hubspot.jinjava.interpret.DeferredValue;
@@ -72,31 +74,39 @@ public Object doEvaluate(
List