Skip to content
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
13 changes: 4 additions & 9 deletions src/main/java/com/hubspot/jinjava/Jinjava.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
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 @@ -246,14 +245,10 @@ public RenderResult renderForResult(
context = new Context(copyGlobalContext(), bindings, renderConfig.getDisabled());
}

try (
AutoCloseableImpl<JinjavaInterpreter> interpreterAutoCloseable = JinjavaInterpreter
.closeablePushCurrent(
globalConfig.getInterpreterFactory().newInstance(this, context, renderConfig)
)
.get()
) {
JinjavaInterpreter interpreter = interpreterAutoCloseable.value();
try {
JinjavaInterpreter interpreter = globalConfig
.getInterpreterFactory()
.newInstance(this, context, renderConfig);
try {
String result = interpreter.render(template);
return new RenderResult(
Expand Down
306 changes: 160 additions & 146 deletions src/main/java/com/hubspot/jinjava/interpret/JinjavaInterpreter.java
Original file line number Diff line number Diff line change
Expand Up @@ -355,170 +355,184 @@ public String render(Node root, boolean processExtendRoots) {
* @return rendered result
*/
private String render(Node root, boolean processExtendRoots, long renderLimit) {
OutputList output = new OutputList(
RenderLimitUtils.clampProvidedRenderLimitToConfig(renderLimit, config)
);
for (Node node : root.getChildren()) {
lineNumber = node.getLineNumber();
position = node.getStartPosition();
String renderStr = node.getMaster().getImage();
try {
if (node instanceof ExpressionNode && context.doesRenderStackContain(renderStr)) {
// This is a circular rendering. Stop rendering it here.
addError(
new TemplateError(
ErrorType.WARNING,
ErrorReason.EXCEPTION,
ErrorItem.TAG,
"Rendering cycle detected: '" + renderStr + "'",
null,
getLineNumber(),
node.getStartPosition(),
null,
BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED,
ImmutableMap.of("string", renderStr)
)
);
output.addNode(new RenderedOutputNode(renderStr));
} else {
OutputNode out;
try (
AutoCloseableImpl<String> closeable = context
.closeablePushRenderStack(renderStr)
.get()
boolean pushed = false;
//noinspection ErrorProne
if (JinjavaInterpreter.getCurrent() != this) {
JinjavaInterpreter.pushCurrent(this);
pushed = true;
}
try {
OutputList output = new OutputList(
RenderLimitUtils.clampProvidedRenderLimitToConfig(renderLimit, config)
);
for (Node node : root.getChildren()) {
lineNumber = node.getLineNumber();
position = node.getStartPosition();
String renderStr = node.getMaster().getImage();
try {
if (
node instanceof ExpressionNode && context.doesRenderStackContain(renderStr)
) {
try {
out = node.render(this);
} catch (DeferredValueException e) {
context.handleDeferredNode(node);
out = new RenderedOutputNode(node.getMaster().getImage());
// This is a circular rendering. Stop rendering it here.
addError(
new TemplateError(
ErrorType.WARNING,
ErrorReason.EXCEPTION,
ErrorItem.TAG,
"Rendering cycle detected: '" + renderStr + "'",
null,
getLineNumber(),
node.getStartPosition(),
null,
BasicTemplateErrorCategory.IMPORT_CYCLE_DETECTED,
ImmutableMap.of("string", renderStr)
)
);
output.addNode(new RenderedOutputNode(renderStr));
} else {
OutputNode out;
try (
AutoCloseableImpl<String> closeable = context
.closeablePushRenderStack(renderStr)
.get()
) {
try {
out = node.render(this);
} catch (DeferredValueException e) {
context.handleDeferredNode(node);
out = new RenderedOutputNode(node.getMaster().getImage());
}
}
output.addNode(out);
}
output.addNode(out);
}
} catch (OutputTooBigException e) {
addError(TemplateError.fromOutputTooBigException(e));
return output.getValue();
} catch (CollectionTooBigException e) {
addError(
new TemplateError(
ErrorType.FATAL,
ErrorReason.COLLECTION_TOO_BIG,
ErrorItem.OTHER,
ExceptionUtils.getMessage(e),
null,
-1,
-1,
e,
BasicTemplateErrorCategory.UNKNOWN,
ImmutableMap.of()
)
);
return output.getValue();
}
}
DynamicRenderedOutputNode pathSetter = new DynamicRenderedOutputNode();
output.addNode(pathSetter);
Optional<String> basePath = context.getCurrentPathStack().peek();
StringBuilder ignoredOutput = new StringBuilder();
// render all extend parents, keeping the last as the root output
if (processExtendRoots) {
Set<String> extendPaths = new HashSet<>();
Optional<String> extendPath = context.getExtendPathStack().peek();
int numDeferredTokensBefore = 0;
while (!extendParentRoots.isEmpty()) {
if (extendPaths.contains(extendPath.orElse(""))) {
} catch (OutputTooBigException e) {
addError(TemplateError.fromOutputTooBigException(e));
return output.getValue();
} catch (CollectionTooBigException e) {
addError(
TemplateError.fromException(
new ExtendsTagCycleException(
extendPath.orElse(""),
context.getExtendPathStack().getTopLineNumber(),
context.getExtendPathStack().getTopStartPosition()
)
new TemplateError(
ErrorType.FATAL,
ErrorReason.COLLECTION_TOO_BIG,
ErrorItem.OTHER,
ExceptionUtils.getMessage(e),
null,
-1,
-1,
e,
BasicTemplateErrorCategory.UNKNOWN,
ImmutableMap.of()
)
);
break;
return output.getValue();
}
extendPaths.add(extendPath.orElse(""));
try (
AutoCloseableImpl<Result<String, TagCycleException>> closeableCurrentPath =
context
.getCurrentPathStack()
.closeablePush(
extendPath.orElse(""),
context.getExtendPathStack().getTopLineNumber(),
context.getExtendPathStack().getTopStartPosition()
}
DynamicRenderedOutputNode pathSetter = new DynamicRenderedOutputNode();
output.addNode(pathSetter);
Optional<String> basePath = context.getCurrentPathStack().peek();
StringBuilder ignoredOutput = new StringBuilder();
// render all extend parents, keeping the last as the root output
if (processExtendRoots) {
Set<String> extendPaths = new HashSet<>();
Optional<String> extendPath = context.getExtendPathStack().peek();
int numDeferredTokensBefore = 0;
while (!extendParentRoots.isEmpty()) {
if (extendPaths.contains(extendPath.orElse(""))) {
addError(
TemplateError.fromException(
new ExtendsTagCycleException(
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())
);
break;
}
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;
extendPaths.add(extendPath.orElse(""));
try (
AutoCloseableImpl<Result<String, TagCycleException>> 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<String> currentExtendPath = context.getExtendPathStack().pop();
extendPath =
hasNestedExtends ? currentExtendPath : context.getExtendPathStack().peek();
basePath = Optional.of(currentPath);
}
Optional<String> currentExtendPath = context.getExtendPathStack().pop();
extendPath =
hasNestedExtends ? currentExtendPath : context.getExtendPathStack().peek();
basePath = Optional.of(currentPath);
}
}
}

int numDeferredTokensBefore = context.getDeferredTokens().size();
resolveBlockStubs(output);
if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
pathSetter.setValue(
EagerReconstructionUtils.buildBlockOrInlineSetTag(
RelativePathResolver.CURRENT_PATH_CONTEXT_KEY,
basePath,
this
)
);
}
int numDeferredTokensBefore = context.getDeferredTokens().size();
resolveBlockStubs(output);
if (context.getDeferredTokens().size() > numDeferredTokensBefore) {
pathSetter.setValue(
EagerReconstructionUtils.buildBlockOrInlineSetTag(
RelativePathResolver.CURRENT_PATH_CONTEXT_KEY,
basePath,
this
)
);
}

if (ignoredOutput.length() > 0) {
return (
EagerReconstructionUtils.labelWithNotes(
EagerReconstructionUtils.wrapInTag(
ignoredOutput.toString(),
DoTag.TAG_NAME,
this,
false
),
IGNORED_OUTPUT_FROM_EXTENDS_NOTE,
this
) +
output.getValue()
);
if (ignoredOutput.length() > 0) {
return (
EagerReconstructionUtils.labelWithNotes(
EagerReconstructionUtils.wrapInTag(
ignoredOutput.toString(),
DoTag.TAG_NAME,
this,
false
),
IGNORED_OUTPUT_FROM_EXTENDS_NOTE,
this
) +
output.getValue()
);
}
return output.getValue();
} finally {
if (pushed) {
JinjavaInterpreter.popCurrent();
}
}
return output.getValue();
}

private void resolveBlockStubs(OutputList output) {
Expand Down
8 changes: 1 addition & 7 deletions src/main/java/com/hubspot/jinjava/lib/tag/FromTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
.getInterpreterFactory()
.newInstance(interpreter);
child.getContext().put(Context.IMPORT_RESOURCE_PATH_KEY, templateFile);
try (
AutoCloseableImpl<JinjavaInterpreter> a = JinjavaInterpreter
.closeablePushCurrent(child)
.get()
) {
child.render(node);
}
child.render(node);

interpreter.addAllChildErrors(templateFile, child.getErrorsCopy());

Expand Down
9 changes: 1 addition & 8 deletions src/main/java/com/hubspot/jinjava/lib/tag/ImportTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,14 +123,7 @@ public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) {
.getInterpreterFactory()
.newInstance(interpreter);
child.getContext().put(Context.IMPORT_RESOURCE_PATH_KEY, templateFile);

try (
AutoCloseableImpl<JinjavaInterpreter> a = JinjavaInterpreter
.closeablePushCurrent(child)
.get()
) {
child.render(node.value());
}
child.render(node.value());

interpreter.addAllChildErrors(templateFile, child.getErrorsCopy());

Expand Down
Loading