|
1 | 1 | package com.laytonsmith.core.exceptions; |
2 | 2 |
|
| 3 | +import com.laytonsmith.core.ArgumentValidation; |
| 4 | +import com.laytonsmith.core.constructs.CInt; |
3 | 5 | import com.laytonsmith.core.constructs.Target; |
| 6 | +import com.laytonsmith.core.environments.GlobalEnv; |
| 7 | +import com.laytonsmith.core.exceptions.CRE.CREStackOverflowError; |
| 8 | +import com.laytonsmith.core.natives.interfaces.Mixed; |
4 | 9 | import java.util.ArrayList; |
5 | 10 | import java.util.Collections; |
6 | 11 | import java.util.List; |
|
11 | 16 | */ |
12 | 17 | public class StackTraceManager { |
13 | 18 |
|
| 19 | + /** |
| 20 | + * The runtime setting key for configuring the maximum call depth. |
| 21 | + */ |
| 22 | + public static final String MAX_CALL_DEPTH_SETTING = "system.max_call_depth"; |
| 23 | + |
| 24 | + /** |
| 25 | + * The default maximum call depth. Can be overridden at runtime via the |
| 26 | + * {@code system.max_call_depth} runtime setting. |
| 27 | + */ |
| 28 | + public static final int DEFAULT_MAX_CALL_DEPTH = 1024; |
| 29 | + |
| 30 | + private static final CInt DEFAULT_MAX_DEPTH_MIXED |
| 31 | + = new CInt(DEFAULT_MAX_CALL_DEPTH, Target.UNKNOWN); |
| 32 | + |
14 | 33 | private final Stack<ConfigRuntimeException.StackTraceElement> elements = new Stack<>(); |
| 34 | + private final GlobalEnv gEnv; |
15 | 35 |
|
16 | 36 | /** |
17 | 37 | * Creates a new, empty StackTraceManager object. |
| 38 | + * |
| 39 | + * @param gEnv The global environment, used to read runtime settings for the call depth limit. |
18 | 40 | */ |
19 | | - public StackTraceManager() { |
20 | | - // |
| 41 | + public StackTraceManager(GlobalEnv gEnv) { |
| 42 | + this.gEnv = gEnv; |
21 | 43 | } |
22 | 44 |
|
23 | 45 | /** |
24 | | - * Adds a new stack trace trail |
| 46 | + * Adds a new stack trace element and checks the call depth against the configured maximum. |
| 47 | + * If the depth exceeds the limit, a {@link CREStackOverflowError} is thrown. |
25 | 48 | * |
26 | 49 | * @param element The element to be pushed on |
27 | 50 | */ |
28 | 51 | public void addStackTraceElement(ConfigRuntimeException.StackTraceElement element) { |
29 | 52 | elements.add(element); |
| 53 | + Mixed setting = gEnv.GetRuntimeSetting(MAX_CALL_DEPTH_SETTING, DEFAULT_MAX_DEPTH_MIXED); |
| 54 | + int maxDepth = ArgumentValidation.getInt32(setting, element.getDefinedAt(), null); |
| 55 | + if(elements.size() > maxDepth) { |
| 56 | + throw new CREStackOverflowError("Stack overflow", element.getDefinedAt()); |
| 57 | + } |
30 | 58 | } |
31 | 59 |
|
32 | 60 | /** |
@@ -65,6 +93,15 @@ public boolean isStackSingle() { |
65 | 93 | return elements.size() == 1; |
66 | 94 | } |
67 | 95 |
|
| 96 | + /** |
| 97 | + * Returns the current depth of the stack trace (the number of proc/closure frames currently active). |
| 98 | + * |
| 99 | + * @return The current stack depth |
| 100 | + */ |
| 101 | + public int getDepth() { |
| 102 | + return elements.size(); |
| 103 | + } |
| 104 | + |
68 | 105 | /** |
69 | 106 | * Sets the current element's target. This should be changed at every new element execution. |
70 | 107 | * |
|
0 commit comments