Skip to content

Commit ddd2cdf

Browse files
committed
merged master
2 parents c950892 + d15d7c0 commit ddd2cdf

File tree

15 files changed

+1009
-578
lines changed

15 files changed

+1009
-578
lines changed

pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
<dep.plugin.jacoco.version>0.8.3</dep.plugin.jacoco.version>
2323
<dep.plugin.javadoc.version>3.0.1</dep.plugin.javadoc.version>
2424
<dep.hubspot-immutables.version>1.9</dep.hubspot-immutables.version>
25+
<dep.algebra.version>1.5</dep.algebra.version>
2526

2627

2728
<basepom.test.add.opens>
@@ -103,6 +104,11 @@
103104
<artifactId>immutables-exceptions</artifactId>
104105
<version>${dep.hubspot-immutables.version}</version>
105106
</dependency>
107+
<dependency>
108+
<groupId>com.hubspot</groupId>
109+
<artifactId>algebra</artifactId>
110+
<version>${dep.algebra.version}</version>
111+
</dependency>
106112
</dependencies>
107113
</dependencyManagement>
108114

@@ -215,6 +221,10 @@
215221
<groupId>com.hubspot.immutables</groupId>
216222
<artifactId>immutables-exceptions</artifactId>
217223
</dependency>
224+
<dependency>
225+
<groupId>com.hubspot</groupId>
226+
<artifactId>algebra</artifactId>
227+
</dependency>
218228
</dependencies>
219229

220230
<build>

src/main/java/com/hubspot/jinjava/Jinjava.java

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.hubspot.jinjava.el.ExtendedSyntaxBuilder;
2121
import com.hubspot.jinjava.el.TruthyTypeConverter;
2222
import com.hubspot.jinjava.el.ext.eager.EagerExtendedSyntaxBuilder;
23+
import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
2324
import com.hubspot.jinjava.interpret.Context;
2425
import com.hubspot.jinjava.interpret.FatalTemplateErrorsException;
2526
import com.hubspot.jinjava.interpret.InterpretException;
@@ -245,52 +246,55 @@ public RenderResult renderForResult(
245246
context = new Context(copyGlobalContext(), bindings, renderConfig.getDisabled());
246247
}
247248

248-
JinjavaInterpreter interpreter = globalConfig
249-
.getInterpreterFactory()
250-
.newInstance(this, context, renderConfig);
251-
JinjavaInterpreter.pushCurrent(interpreter);
252-
253-
try {
254-
String result = interpreter.render(template);
255-
return new RenderResult(
256-
result,
257-
interpreter.getContext(),
258-
interpreter.getErrorsCopy()
259-
);
260-
} catch (InterpretException e) {
261-
if (e instanceof TemplateSyntaxException) {
249+
try (
250+
AutoCloseableImpl<JinjavaInterpreter> interpreterAutoCloseable = JinjavaInterpreter
251+
.closeablePushCurrent(
252+
globalConfig.getInterpreterFactory().newInstance(this, context, renderConfig)
253+
)
254+
.get()
255+
) {
256+
JinjavaInterpreter interpreter = interpreterAutoCloseable.value();
257+
try {
258+
String result = interpreter.render(template);
259+
return new RenderResult(
260+
result,
261+
interpreter.getContext(),
262+
interpreter.getErrorsCopy()
263+
);
264+
} catch (InterpretException e) {
265+
if (e instanceof TemplateSyntaxException) {
266+
return new RenderResult(
267+
TemplateError.fromException((TemplateSyntaxException) e),
268+
interpreter.getContext(),
269+
interpreter.getErrorsCopy()
270+
);
271+
}
272+
return new RenderResult(
273+
TemplateError.fromSyntaxError(e),
274+
interpreter.getContext(),
275+
interpreter.getErrorsCopy()
276+
);
277+
} catch (InvalidArgumentException e) {
278+
return new RenderResult(
279+
TemplateError.fromInvalidArgumentException(e),
280+
interpreter.getContext(),
281+
interpreter.getErrorsCopy()
282+
);
283+
} catch (InvalidInputException e) {
262284
return new RenderResult(
263-
TemplateError.fromException((TemplateSyntaxException) e),
285+
TemplateError.fromInvalidInputException(e),
286+
interpreter.getContext(),
287+
interpreter.getErrorsCopy()
288+
);
289+
} catch (Exception e) {
290+
return new RenderResult(
291+
TemplateError.fromException(e),
264292
interpreter.getContext(),
265293
interpreter.getErrorsCopy()
266294
);
267295
}
268-
return new RenderResult(
269-
TemplateError.fromSyntaxError(e),
270-
interpreter.getContext(),
271-
interpreter.getErrorsCopy()
272-
);
273-
} catch (InvalidArgumentException e) {
274-
return new RenderResult(
275-
TemplateError.fromInvalidArgumentException(e),
276-
interpreter.getContext(),
277-
interpreter.getErrorsCopy()
278-
);
279-
} catch (InvalidInputException e) {
280-
return new RenderResult(
281-
TemplateError.fromInvalidInputException(e),
282-
interpreter.getContext(),
283-
interpreter.getErrorsCopy()
284-
);
285-
} catch (Exception e) {
286-
return new RenderResult(
287-
TemplateError.fromException(e),
288-
interpreter.getContext(),
289-
interpreter.getErrorsCopy()
290-
);
291296
} finally {
292297
globalContext.reset();
293-
JinjavaInterpreter.popCurrent();
294298
}
295299
}
296300

Lines changed: 100 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
package com.hubspot.jinjava.el.ext;
22

33
import com.google.common.collect.ImmutableMap;
4+
import com.hubspot.algebra.Result;
5+
import com.hubspot.jinjava.interpret.AutoCloseableSupplier;
6+
import com.hubspot.jinjava.interpret.AutoCloseableSupplier.AutoCloseableImpl;
47
import com.hubspot.jinjava.interpret.CallStack;
58
import com.hubspot.jinjava.interpret.DeferredValueException;
69
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
7-
import com.hubspot.jinjava.interpret.MacroTagCycleException;
10+
import com.hubspot.jinjava.interpret.TagCycleException;
811
import com.hubspot.jinjava.interpret.TemplateError;
912
import com.hubspot.jinjava.interpret.errorcategory.BasicTemplateErrorCategory;
1013
import com.hubspot.jinjava.lib.fn.MacroFunction;
@@ -18,6 +21,10 @@
1821

1922
public class AstMacroFunction extends AstFunction {
2023

24+
public enum MacroCallError {
25+
CYCLE_DETECTED,
26+
}
27+
2128
public AstMacroFunction(String name, int index, AstParameters params, boolean varargs) {
2229
super(name, index, params, varargs);
2330
}
@@ -37,30 +44,16 @@ public Object eval(Bindings bindings, ELContext context) {
3744
interpreter.getPosition()
3845
);
3946
}
40-
if (!macroFunction.isCaller()) {
41-
if (checkAndPushMacroStack(interpreter, getName())) {
42-
return "";
43-
}
47+
if (macroFunction.isCaller()) {
48+
return wrapInvoke(bindings, context, macroFunction);
4449
}
45-
46-
try {
47-
return invoke(
48-
bindings,
49-
context,
50-
macroFunction,
51-
AbstractCallableMethod.EVAL_METHOD
52-
);
53-
} catch (IllegalAccessException e) {
54-
throw new ELException(LocalMessages.get("error.function.access", getName()), e);
55-
} catch (InvocationTargetException e) {
56-
throw new ELException(
57-
LocalMessages.get("error.function.invocation", getName()),
58-
e.getCause()
59-
);
60-
} finally {
61-
if (!macroFunction.isCaller()) {
62-
interpreter.getContext().getMacroStack().pop();
63-
}
50+
try (
51+
AutoCloseableImpl<Result<String, MacroCallError>> macroStackPush =
52+
checkAndPushMacroStackWithWrapper(interpreter, getName()).get()
53+
) {
54+
return macroStackPush
55+
.value()
56+
.match(err -> "", path -> wrapInvoke(bindings, context, macroFunction));
6457
}
6558
}
6659

@@ -69,62 +62,104 @@ public Object eval(Bindings bindings, ELContext context) {
6962
: super.eval(bindings, context);
7063
}
7164

72-
public static boolean checkAndPushMacroStack(
65+
private Object wrapInvoke(
66+
Bindings bindings,
67+
ELContext context,
68+
MacroFunction macroFunction
69+
) {
70+
try {
71+
return invoke(bindings, context, macroFunction, AbstractCallableMethod.EVAL_METHOD);
72+
} catch (IllegalAccessException e) {
73+
throw new ELException(LocalMessages.get("error.function.access", getName()), e);
74+
} catch (InvocationTargetException e) {
75+
throw new ELException(
76+
LocalMessages.get("error.function.invocation", getName()),
77+
e.getCause()
78+
);
79+
}
80+
}
81+
82+
public static AutoCloseableSupplier<Result<String, MacroCallError>> checkAndPushMacroStackWithWrapper(
7383
JinjavaInterpreter interpreter,
7484
String name
7585
) {
7686
CallStack macroStack = interpreter.getContext().getMacroStack();
77-
try {
78-
if (interpreter.getConfig().isEnableRecursiveMacroCalls()) {
79-
if (interpreter.getConfig().getMaxMacroRecursionDepth() != 0) {
80-
macroStack.pushWithMaxDepth(
87+
if (interpreter.getConfig().isEnableRecursiveMacroCalls()) {
88+
if (interpreter.getConfig().getMaxMacroRecursionDepth() != 0) {
89+
return macroStack
90+
.closeablePushWithMaxDepth(
8191
name,
8292
interpreter.getConfig().getMaxMacroRecursionDepth(),
8393
interpreter.getLineNumber(),
8494
interpreter.getPosition()
95+
)
96+
.map(result ->
97+
result.mapErr(err -> {
98+
handleMacroCycleError(interpreter, name, err);
99+
return MacroCallError.CYCLE_DETECTED;
100+
})
85101
);
86-
} else {
87-
macroStack.pushWithoutCycleCheck(
102+
} else {
103+
return macroStack
104+
.closeablePushWithoutCycleCheck(
88105
name,
89106
interpreter.getLineNumber(),
90107
interpreter.getPosition()
91-
);
92-
}
93-
} else {
94-
macroStack.push(name, -1, -1);
95-
}
96-
} catch (MacroTagCycleException e) {
97-
int maxDepth = interpreter.getConfig().getMaxMacroRecursionDepth();
98-
if (maxDepth != 0 && interpreter.getConfig().isValidationMode()) {
99-
// validation mode is only concerned with syntax
100-
return true;
108+
)
109+
.map(Result::ok);
101110
}
102-
103-
String message = maxDepth == 0
104-
? String.format("Cycle detected for macro '%s'", name)
105-
: String.format(
106-
"Max recursion limit of %d reached for macro '%s'",
107-
maxDepth,
108-
name
109-
);
110-
111-
interpreter.addError(
112-
new TemplateError(
113-
TemplateError.ErrorType.WARNING,
114-
TemplateError.ErrorReason.EXCEPTION,
115-
TemplateError.ErrorItem.TAG,
116-
message,
117-
null,
118-
e.getLineNumber(),
119-
e.getStartPosition(),
120-
e,
121-
BasicTemplateErrorCategory.CYCLE_DETECTED,
122-
ImmutableMap.of("name", name)
123-
)
111+
}
112+
return macroStack
113+
.closeablePush(name, -1, -1)
114+
.map(result ->
115+
result.mapErr(err -> {
116+
handleMacroCycleError(interpreter, name, err);
117+
return MacroCallError.CYCLE_DETECTED;
118+
})
124119
);
120+
}
125121

126-
return true;
122+
private static void handleMacroCycleError(
123+
JinjavaInterpreter interpreter,
124+
String name,
125+
TagCycleException e
126+
) {
127+
int maxDepth = interpreter.getConfig().getMaxMacroRecursionDepth();
128+
if (maxDepth != 0 && interpreter.getConfig().isValidationMode()) {
129+
// validation mode is only concerned with syntax
130+
return;
127131
}
128-
return false;
132+
133+
String message = maxDepth == 0
134+
? String.format("Cycle detected for macro '%s'", name)
135+
: String.format("Max recursion limit of %d reached for macro '%s'", maxDepth, name);
136+
137+
interpreter.addError(
138+
new TemplateError(
139+
TemplateError.ErrorType.WARNING,
140+
TemplateError.ErrorReason.EXCEPTION,
141+
TemplateError.ErrorItem.TAG,
142+
message,
143+
null,
144+
e.getLineNumber(),
145+
e.getStartPosition(),
146+
e,
147+
BasicTemplateErrorCategory.CYCLE_DETECTED,
148+
ImmutableMap.of("name", name)
149+
)
150+
);
151+
}
152+
153+
@Deprecated
154+
public static boolean checkAndPushMacroStack(
155+
JinjavaInterpreter interpreter,
156+
String name
157+
) {
158+
return checkAndPushMacroStackWithWrapper(interpreter, name)
159+
.dangerouslyGetWithoutClosing()
160+
.match(
161+
err -> true, // cycle detected
162+
ok -> false // no cycle
163+
);
129164
}
130165
}

0 commit comments

Comments
 (0)