11package com .hubspot .jinjava .el .ext ;
22
33import 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 ;
47import com .hubspot .jinjava .interpret .CallStack ;
58import com .hubspot .jinjava .interpret .DeferredValueException ;
69import com .hubspot .jinjava .interpret .JinjavaInterpreter ;
7- import com .hubspot .jinjava .interpret .MacroTagCycleException ;
10+ import com .hubspot .jinjava .interpret .TagCycleException ;
811import com .hubspot .jinjava .interpret .TemplateError ;
912import com .hubspot .jinjava .interpret .errorcategory .BasicTemplateErrorCategory ;
1013import com .hubspot .jinjava .lib .fn .MacroFunction ;
1821
1922public 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