diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerImportTag.java b/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerImportTag.java index 1683b0ce5..5a7b1cb86 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerImportTag.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerImportTag.java @@ -98,7 +98,7 @@ public String getEagerTagImage(TagToken tagToken, JinjavaInterpreter interpreter return ""; } return EagerReconstructionUtils.wrapInTag( - eagerImportingStrategy.getFinalOutput(newPathSetter, output, childBindings), + eagerImportingStrategy.getFinalOutput(newPathSetter, output, child), DoTag.TAG_NAME, interpreter, true diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerSetTagStrategy.java b/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerSetTagStrategy.java index 9c4cdead0..8e170c4c2 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerSetTagStrategy.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerSetTagStrategy.java @@ -3,6 +3,7 @@ import com.google.common.annotations.Beta; import com.hubspot.jinjava.interpret.JinjavaInterpreter; import com.hubspot.jinjava.lib.tag.SetTag; +import com.hubspot.jinjava.lib.tag.eager.importing.AliasedEagerImportingStrategy; import com.hubspot.jinjava.tree.TagNode; import com.hubspot.jinjava.util.EagerReconstructionUtils; import com.hubspot.jinjava.util.PrefixToPreserveState; @@ -154,30 +155,22 @@ protected String getSuffixToPreserveState( JinjavaInterpreter interpreter ) { StringBuilder suffixToPreserveState = new StringBuilder(); - Optional maybeFullImportAlias = interpreter - .getContext() - .getImportResourceAlias(); - if (maybeFullImportAlias.isPresent()) { - String currentImportAlias = maybeFullImportAlias - .get() - .substring(maybeFullImportAlias.get().lastIndexOf(".") + 1); - String filteredVariables = Arrays - .stream(variables.split(",")) - .filter(var -> !var.equals(currentImportAlias)) - .collect(Collectors.joining(",")); - if (!filteredVariables.isEmpty()) { - String updateString = getUpdateString(filteredVariables); - suffixToPreserveState.append( - interpreter.render( - EagerReconstructionUtils.buildDoUpdateTag( - currentImportAlias, - updateString, - interpreter - ) + Optional maybeTemporaryImportAlias = AliasedEagerImportingStrategy.getTemporaryImportAlias( + interpreter.getContext() + ); + if (maybeTemporaryImportAlias.isPresent()) { + String updateString = getUpdateString(variables); + suffixToPreserveState.append( + interpreter.render( + EagerReconstructionUtils.buildDoUpdateTag( + maybeTemporaryImportAlias.get(), + updateString, + interpreter ) - ); - } + ) + ); } + return suffixToPreserveState.toString(); } diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/AliasedEagerImportingStrategy.java b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/AliasedEagerImportingStrategy.java index 588fc2d69..43559c019 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/AliasedEagerImportingStrategy.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/AliasedEagerImportingStrategy.java @@ -1,6 +1,7 @@ package com.hubspot.jinjava.lib.tag.eager.importing; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableMap; import com.hubspot.jinjava.interpret.Context; import com.hubspot.jinjava.interpret.DeferredValue; import com.hubspot.jinjava.interpret.DeferredValueException; @@ -12,18 +13,33 @@ import com.hubspot.jinjava.objects.serialization.PyishObjectMapper; import com.hubspot.jinjava.util.EagerReconstructionUtils; import com.hubspot.jinjava.util.PrefixToPreserveState; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.StringJoiner; -import java.util.stream.Collectors; import java.util.stream.Stream; public class AliasedEagerImportingStrategy implements EagerImportingStrategy { + private static final String TEMPORARY_IMPORT_ALIAS_FORMAT = "__temp_import_alias_%d__"; + + public static Optional getTemporaryImportAlias(Context context) { + return context + .getImportResourceAlias() + .map(AliasedEagerImportingStrategy::getTemporaryImportAlias); + } + + private static String getTemporaryImportAlias(String fullAlias) { + return String.format( + TEMPORARY_IMPORT_ALIAS_FORMAT, + Math.abs(Objects.hashCode(fullAlias)) + ); + } + private final ImportingData importingData; private final String currentImportAlias; + private final String fullImportAlias; @VisibleForTesting public AliasedEagerImportingStrategy( @@ -32,6 +48,16 @@ public AliasedEagerImportingStrategy( ) { this.importingData = importingData; this.currentImportAlias = currentImportAlias; + Optional maybeParentImportAlias = importingData + .getOriginalInterpreter() + .getContext() + .getImportResourceAlias(); + if (maybeParentImportAlias.isPresent()) { + fullImportAlias = + String.format("%s.%s", maybeParentImportAlias.get(), currentImportAlias); + } else { + fullImportAlias = currentImportAlias; + } } @Override @@ -54,26 +80,17 @@ public String handleDeferredTemplateFile(DeferredValueException e) { @Override public void setup(JinjavaInterpreter child) { - Optional maybeParentImportAlias = importingData + child.getContext().getScope().put(Context.IMPORT_RESOURCE_ALIAS_KEY, fullImportAlias); + child.getContext().put(Context.IMPORT_RESOURCE_ALIAS_KEY, fullImportAlias); + constructFullAliasPathMap(currentImportAlias, child); + Map currentContextAliasMap = getMapForCurrentContextAlias( + currentImportAlias, + child + ); + importingData .getOriginalInterpreter() .getContext() - .getImportResourceAlias(); - if (maybeParentImportAlias.isPresent()) { - child - .getContext() - .getScope() - .put( - Context.IMPORT_RESOURCE_ALIAS_KEY, - String.format("%s.%s", maybeParentImportAlias.get(), currentImportAlias) - ); - } else { - child - .getContext() - .getScope() - .put(Context.IMPORT_RESOURCE_ALIAS_KEY, currentImportAlias); - } - constructFullAliasPathMap(currentImportAlias, child); - getMapForCurrentContextAlias(currentImportAlias, child); + .put(getTemporaryImportAlias(fullImportAlias), DeferredValue.instance()); } @Override @@ -84,46 +101,16 @@ public void integrateChild(JinjavaInterpreter child) { macro.setDeferred(true); } } - if ( - child.getContext().isDeferredExecutionMode() && - child - .getContext() - .getDeferredTokens() - .stream() - .flatMap(deferredToken -> deferredToken.getSetDeferredWords().stream()) - .collect(Collectors.toSet()) - .contains(currentImportAlias) - ) { - // since a child scope will be used, the import alias would not be properly reconstructed - throw new DeferredValueException( - "Same-named variable as import alias: " + currentImportAlias - ); - } Map childBindings = child.getContext().getSessionBindings(); childBindings.putAll(child.getContext().getGlobalMacros()); + String temporaryImportAlias = getTemporaryImportAlias(fullImportAlias); Map mapForCurrentContextAlias = getMapForCurrentContextAlias( currentImportAlias, child ); // Remove layers from self down to original import alias to prevent reference loops - Arrays - .stream( - child - .getContext() - .getImportResourceAlias() - .orElse(currentImportAlias) - .split("\\.") - ) - .filter( - key -> - mapForCurrentContextAlias == - ( - childBindings.get(key) instanceof DeferredValue - ? ((DeferredValue) childBindings.get(key)).getOriginalValue() - : childBindings.get(key) - ) - ) - .forEach(childBindings::remove); + childBindings.remove(temporaryImportAlias); + importingData.getOriginalInterpreter().getContext().remove(temporaryImportAlias); // Remove meta keys childBindings.remove(Context.GLOBAL_MACROS_SCOPE_KEY); childBindings.remove(Context.IMPORT_RESOURCE_ALIAS_KEY); @@ -134,24 +121,29 @@ public void integrateChild(JinjavaInterpreter child) { public String getFinalOutput( String newPathSetter, String output, - Map childBindings + JinjavaInterpreter child ) { + String temporaryImportAlias = getTemporaryImportAlias(fullImportAlias); return ( newPathSetter + - EagerReconstructionUtils.buildBlockOrInlineSetTagAndRegisterDeferredToken( - currentImportAlias, + EagerReconstructionUtils.buildBlockOrInlineSetTag( + temporaryImportAlias, Collections.emptyMap(), importingData.getOriginalInterpreter() ) + wrapInChildScope( - importingData.getOriginalInterpreter(), EagerImportingStrategy.getSetTagForDeferredChildBindings( importingData.getOriginalInterpreter(), currentImportAlias, - childBindings + child.getContext() ) + output, - currentImportAlias + child + ) + + EagerReconstructionUtils.buildSetTag( + ImmutableMap.of(currentImportAlias, temporaryImportAlias), + importingData.getOriginalInterpreter(), + true ) + importingData.getInitialPathSetter() ); @@ -229,30 +221,28 @@ private static Map getMapForCurrentContextAlias( } } - private static String wrapInChildScope( - JinjavaInterpreter interpreter, - String output, - String currentImportAlias - ) { - String combined = output + getDoTagToPreserve(interpreter, currentImportAlias); + private String wrapInChildScope(String output, JinjavaInterpreter child) { + String combined = + output + getDoTagToPreserve(importingData.getOriginalInterpreter(), child); // So that any set variables other than the alias won't exist outside the child's scope - return EagerReconstructionUtils.wrapInChildScope(combined, interpreter); + return EagerReconstructionUtils.wrapInChildScope( + combined, + importingData.getOriginalInterpreter() + ); } - @SuppressWarnings("unchecked") - private static String getDoTagToPreserve( + private String getDoTagToPreserve( JinjavaInterpreter interpreter, - String currentImportAlias + JinjavaInterpreter child ) { StringJoiner keyValueJoiner = new StringJoiner(","); - Object currentAliasMap = interpreter - .getContext() - .getSessionBindings() - .get(currentImportAlias); - for (Map.Entry entry : ( - (Map) ((DeferredValue) currentAliasMap).getOriginalValue() - ).entrySet()) { - if (entry.getKey().equals(currentImportAlias)) { + String temporaryImportAlias = getTemporaryImportAlias(fullImportAlias); + Map currentAliasMap = getMapForCurrentContextAlias( + currentImportAlias, + child + ); + for (Map.Entry entry : currentAliasMap.entrySet()) { + if (entry.getKey().equals(temporaryImportAlias)) { continue; } if (entry.getValue() instanceof DeferredValue) { @@ -269,7 +259,7 @@ private static String getDoTagToPreserve( } if (keyValueJoiner.length() > 0) { return EagerReconstructionUtils.buildDoUpdateTag( - currentImportAlias, + temporaryImportAlias, "{" + keyValueJoiner.toString() + "}", interpreter ); diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/EagerImportingStrategy.java b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/EagerImportingStrategy.java index cede53363..a149e3b2d 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/EagerImportingStrategy.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/EagerImportingStrategy.java @@ -12,11 +12,7 @@ public interface EagerImportingStrategy { void setup(JinjavaInterpreter child); void integrateChild(JinjavaInterpreter child); - String getFinalOutput( - String newPathSetter, - String output, - Map childBindings - ); + String getFinalOutput(String newPathSetter, String output, JinjavaInterpreter child); static String getSetTagForDeferredChildBindings( JinjavaInterpreter interpreter, diff --git a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/FlatEagerImportingStrategy.java b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/FlatEagerImportingStrategy.java index bd7fbfea4..7004d3e11 100644 --- a/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/FlatEagerImportingStrategy.java +++ b/src/main/java/com/hubspot/jinjava/lib/tag/eager/importing/FlatEagerImportingStrategy.java @@ -67,7 +67,7 @@ public void integrateChild(JinjavaInterpreter child) { public String getFinalOutput( String newPathSetter, String output, - Map childBindings + JinjavaInterpreter child ) { if (importingData.getOriginalInterpreter().getContext().isDeferredExecutionMode()) { Set metaContextVariables = importingData @@ -76,7 +76,9 @@ public String getFinalOutput( .getMetaContextVariables(); // defer imported variables EagerReconstructionUtils.buildSetTag( - childBindings + child + .getContext() + .getSessionBindings() .entrySet() .stream() .filter( @@ -94,7 +96,7 @@ public String getFinalOutput( EagerImportingStrategy.getSetTagForDeferredChildBindings( importingData.getOriginalInterpreter(), null, - childBindings + child.getContext().getSessionBindings() ) + output + importingData.getInitialPathSetter()