Skip to content

Make stack operations use AutoCloseable for safer usage with try-with-resources #1250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
<dep.plugin.jacoco.version>0.8.3</dep.plugin.jacoco.version>
<dep.plugin.javadoc.version>3.0.1</dep.plugin.javadoc.version>
<dep.hubspot-immutables.version>1.9</dep.hubspot-immutables.version>
<dep.algebra.version>1.5</dep.algebra.version>


<basepom.test.add.opens>
Expand Down Expand Up @@ -101,6 +102,11 @@
<artifactId>immutables-exceptions</artifactId>
<version>${dep.hubspot-immutables.version}</version>
</dependency>
<dependency>
<groupId>com.hubspot</groupId>
<artifactId>algebra</artifactId>
<version>${dep.algebra.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down Expand Up @@ -213,6 +219,10 @@
<groupId>com.hubspot.immutables</groupId>
<artifactId>immutables-exceptions</artifactId>
</dependency>
<dependency>
<groupId>com.hubspot</groupId>
<artifactId>algebra</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
82 changes: 43 additions & 39 deletions src/main/java/com/hubspot/jinjava/Jinjava.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<JinjavaInterpreter> 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();
}
}

Expand Down
165 changes: 100 additions & 65 deletions src/main/java/com/hubspot/jinjava/el/ext/AstMacroFunction.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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);
}
Expand All @@ -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<Result<String, MacroCallError>> macroStackPush =
checkAndPushMacroStackWithWrapper(interpreter, getName()).get()
) {
return macroStackPush
.value()
.match(err -> "", path -> wrapInvoke(bindings, context, macroFunction));
}
}

Expand All @@ -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<Result<String, MacroCallError>> 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
);
}
}
Loading