11package com .hubspot .jinjava .el .ext ;
22
33import com .google .common .collect .ImmutableMap ;
4+ import com .hubspot .algebra .Result ;
45import com .hubspot .jinjava .interpret .AutoCloseableSupplier ;
56import com .hubspot .jinjava .interpret .AutoCloseableSupplier .AutoCloseableImpl ;
67import com .hubspot .jinjava .interpret .CallStack ;
78import com .hubspot .jinjava .interpret .DeferredValueException ;
89import com .hubspot .jinjava .interpret .JinjavaInterpreter ;
9- import com .hubspot .jinjava .interpret .MacroTagCycleException ;
10+ import com .hubspot .jinjava .interpret .TagCycleException ;
1011import com .hubspot .jinjava .interpret .TemplateError ;
1112import com .hubspot .jinjava .interpret .errorcategory .BasicTemplateErrorCategory ;
1213import com .hubspot .jinjava .lib .fn .MacroFunction ;
2021
2122public class AstMacroFunction extends AstFunction {
2223
24+ public enum MacroCallError {
25+ CYCLE_DETECTED ,
26+ }
27+
2328 public AstMacroFunction (String name , int index , AstParameters params , boolean varargs ) {
2429 super (name , index , params , varargs );
2530 }
@@ -43,17 +48,12 @@ public Object eval(Bindings bindings, ELContext context) {
4348 return wrapInvoke (bindings , context , macroFunction );
4449 }
4550 try (
46- AutoCloseableImpl <Boolean > macroStackPush = checkAndPushMacroStackWithWrapper (
47- interpreter ,
48- getName ()
49- )
50- .get ()
51+ AutoCloseableImpl <Result <String , MacroCallError >> macroStackPush =
52+ checkAndPushMacroStackWithWrapper (interpreter , getName ()).get ()
5153 ) {
52- if (macroStackPush .value ()) {
53- return "" ;
54- }
55-
56- return wrapInvoke (bindings , context , macroFunction );
54+ return macroStackPush
55+ .value ()
56+ .match (err -> "" , path -> wrapInvoke (bindings , context , macroFunction ));
5757 }
5858 }
5959
@@ -79,65 +79,75 @@ private Object wrapInvoke(
7979 }
8080 }
8181
82- public static AutoCloseableSupplier <Boolean > checkAndPushMacroStackWithWrapper (
82+ public static AutoCloseableSupplier <Result < String , MacroCallError > > checkAndPushMacroStackWithWrapper (
8383 JinjavaInterpreter interpreter ,
8484 String name
8585 ) {
8686 CallStack macroStack = interpreter .getContext ().getMacroStack ();
87- try {
88- if (interpreter .getConfig ().isEnableRecursiveMacroCalls ()) {
89- if (interpreter .getConfig ().getMaxMacroRecursionDepth () != 0 ) {
90- return macroStack
91- .closeablePushWithMaxDepth (
92- name ,
93- interpreter .getConfig ().getMaxMacroRecursionDepth (),
94- interpreter .getLineNumber (),
95- interpreter .getPosition ()
96- )
97- .map (macro -> false );
98- } else {
99- return macroStack
100- .closeablePushWithoutCycleCheck (
101- name ,
102- interpreter .getLineNumber (),
103- interpreter .getPosition ()
104- )
105- .map (macro -> false );
106- }
107- }
108- return macroStack .closeablePush (name , -1 , -1 ).map (macro -> false );
109- } catch (MacroTagCycleException e ) {
110- int maxDepth = interpreter .getConfig ().getMaxMacroRecursionDepth ();
111- if (maxDepth != 0 && interpreter .getConfig ().isValidationMode ()) {
112- // validation mode is only concerned with syntax
113- return AutoCloseableSupplier .of (true );
87+ if (interpreter .getConfig ().isEnableRecursiveMacroCalls ()) {
88+ if (interpreter .getConfig ().getMaxMacroRecursionDepth () != 0 ) {
89+ return macroStack
90+ .closeablePushWithMaxDepth (
91+ name ,
92+ interpreter .getConfig ().getMaxMacroRecursionDepth (),
93+ interpreter .getLineNumber (),
94+ interpreter .getPosition ()
95+ )
96+ .map (result ->
97+ result .mapErr (err -> {
98+ handleMacroCycleError (interpreter , name , err );
99+ return MacroCallError .CYCLE_DETECTED ;
100+ })
101+ );
102+ } else {
103+ return macroStack
104+ .closeablePushWithoutCycleCheck (
105+ name ,
106+ interpreter .getLineNumber (),
107+ interpreter .getPosition ()
108+ )
109+ .map (Result ::ok );
114110 }
115-
116- String message = maxDepth == 0
117- ? String .format ("Cycle detected for macro '%s'" , name )
118- : String .format (
119- "Max recursion limit of %d reached for macro '%s'" ,
120- maxDepth ,
121- name
122- );
123-
124- interpreter .addError (
125- new TemplateError (
126- TemplateError .ErrorType .WARNING ,
127- TemplateError .ErrorReason .EXCEPTION ,
128- TemplateError .ErrorItem .TAG ,
129- message ,
130- null ,
131- e .getLineNumber (),
132- e .getStartPosition (),
133- e ,
134- BasicTemplateErrorCategory .CYCLE_DETECTED ,
135- ImmutableMap .of ("name" , name )
136- )
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+ })
137119 );
120+ }
138121
139- return AutoCloseableSupplier .of (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 ;
140131 }
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+ );
141151 }
142152
143153 @ Deprecated
@@ -146,6 +156,10 @@ public static boolean checkAndPushMacroStack(
146156 String name
147157 ) {
148158 return checkAndPushMacroStackWithWrapper (interpreter , name )
149- .dangerouslyGetWithoutClosing ();
159+ .dangerouslyGetWithoutClosing ()
160+ .match (
161+ err -> true , // cycle detected
162+ ok -> false // no cycle
163+ );
150164 }
151165}
0 commit comments