Skip to content

Commit 6070e7c

Browse files
authored
Better runtime error filtering (#8195)
* make filtering consumer-specific. * better remove() * Requested Changes * avoid null
1 parent 8f657a8 commit 6070e7c

File tree

6 files changed

+164
-78
lines changed

6 files changed

+164
-78
lines changed

src/main/java/ch/njol/skript/sections/SecCatchErrors.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public boolean isSatisfiedBy(ExperimentSet experimentSet) {
6868
TriggerItem.walk(first, event);
6969
ExprCaughtErrors.lastErrors = catcher.getCachedErrors().stream().map(RuntimeError::error).toArray(String[]::new);
7070
catcher.clearCachedErrors()
71-
.clearCachedFrames()
7271
.stop();
7372
return walk(event, false);
7473
}
Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package org.skriptlang.skript.log.runtime;
22

3+
import ch.njol.skript.Skript;
34
import ch.njol.skript.log.SkriptLogger;
5+
import org.jetbrains.annotations.Nullable;
46
import org.jetbrains.annotations.UnmodifiableView;
57
import org.skriptlang.skript.log.runtime.Frame.FrameOutput;
68

79
import java.util.ArrayList;
810
import java.util.Collections;
911
import java.util.List;
10-
import java.util.Map.Entry;
1112
import java.util.logging.Level;
1213

1314
/**
@@ -20,15 +21,21 @@ public class RuntimeErrorCatcher implements RuntimeErrorConsumer {
2021

2122
private final List<RuntimeError> cachedErrors = new ArrayList<>();
2223

23-
private final List<Entry<FrameOutput, Level>> cachedFrames = new ArrayList<>();
24+
// hard limit on stored errors to prevent a runaway loop from filling up memory, for example.
25+
private static final int ERROR_LIMIT = 1000;
2426

2527
public RuntimeErrorCatcher() {}
2628

2729
/**
2830
* Gets the {@link RuntimeErrorManager}.
2931
*/
3032
private RuntimeErrorManager getManager() {
31-
return RuntimeErrorManager.getInstance();
33+
return Skript.getRuntimeErrorManager();
34+
}
35+
36+
@Override
37+
public @Nullable RuntimeErrorFilter getFilter() {
38+
return RuntimeErrorFilter.NO_FILTER; // no filter means everything gets printed.
3239
}
3340

3441
/**
@@ -47,7 +54,7 @@ public RuntimeErrorCatcher start() {
4754
/**
4855
* Stops this {@link RuntimeErrorCatcher}, removing from {@link RuntimeErrorManager} and restoring the previous
4956
* {@link RuntimeErrorConsumer}s from {@link #storedConsumers}.
50-
* Prints all cached {@link RuntimeError}s, {@link #cachedErrors}, and cached {@link FrameOutput}s, {@link #cachedFrames}.
57+
* Prints all cached {@link RuntimeError}s, {@link #cachedErrors}.
5158
*/
5259
public void stop() {
5360
if (!getManager().removeConsumer(this)) {
@@ -57,8 +64,6 @@ public void stop() {
5764
getManager().addConsumers(storedConsumers.toArray(RuntimeErrorConsumer[]::new));
5865
for (RuntimeError runtimeError : cachedErrors)
5966
storedConsumers.forEach(consumer -> consumer.printError(runtimeError));
60-
for (Entry<FrameOutput, Level> entry : cachedFrames)
61-
storedConsumers.forEach(consumer -> consumer.printFrameOutput(entry.getKey(), entry.getValue()));
6267
}
6368

6469
/**
@@ -68,13 +73,6 @@ public void stop() {
6873
return Collections.unmodifiableList(cachedErrors);
6974
}
7075

71-
/**
72-
* Gets all cached {@link FrameOutput}s stored with its corresponding {@link Level} in an {@link Entry}
73-
*/
74-
public @UnmodifiableView List<Entry<FrameOutput, Level>> getCachedFrames() {
75-
return Collections.unmodifiableList(cachedFrames);
76-
}
77-
7876
/**
7977
* Clear all cached {@link RuntimeError}s.
8078
*/
@@ -83,37 +81,15 @@ public RuntimeErrorCatcher clearCachedErrors() {
8381
return this;
8482
}
8583

86-
/**
87-
* Clears all cached {@link FrameOutput}s.
88-
*/
89-
public RuntimeErrorCatcher clearCachedFrames() {
90-
cachedFrames.clear();
91-
return this;
92-
}
93-
9484
@Override
9585
public void printError(RuntimeError error) {
96-
cachedErrors.add(error);
86+
if (cachedErrors.size() < ERROR_LIMIT)
87+
cachedErrors.add(error);
9788
}
9889

9990
@Override
10091
public void printFrameOutput(FrameOutput output, Level level) {
101-
cachedFrames.add(new Entry<FrameOutput, Level>() {
102-
@Override
103-
public FrameOutput getKey() {
104-
return output;
105-
}
106-
107-
@Override
108-
public Level getValue() {
109-
return level;
110-
}
111-
112-
@Override
113-
public Level setValue(Level value) {
114-
return null;
115-
}
116-
});
92+
// do nothing, this won't be called since we have no filter.
11793
}
11894

11995
}

src/main/java/org/skriptlang/skript/log/runtime/RuntimeErrorConsumer.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.skriptlang.skript.log.runtime;
22

3+
import org.jetbrains.annotations.Nullable;
4+
35
import java.util.logging.Level;
46

57
/**
@@ -15,6 +17,15 @@ public interface RuntimeErrorConsumer {
1517
*/
1618
void printError(RuntimeError error);
1719

20+
/**
21+
* @return The filter to use when checking if this consumer should print an error. Defaults to the standard
22+
* config-driven filter. If no filter should be used, return {@link RuntimeErrorFilter#NO_FILTER}. This value
23+
* MUST be effectively final.
24+
*/
25+
default @Nullable RuntimeErrorFilter getFilter() {
26+
return RuntimeErrorManager.standardFilter;
27+
}
28+
1829
/**
1930
* Prints the output of a frame, including skipped errors, timeouts, and whatever other information required.
2031
* @param output An output object containing unmodifiable views of the frame data.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package org.skriptlang.skript.log.runtime;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import java.util.logging.Level;
6+
7+
/**
8+
* Handles filtering of runtime errors based on their level and predefined limits.
9+
* Uses the concept of 'frames' to track the number of errors and warnings that are allowed in a given period.
10+
* Use {@link #test(RuntimeError)} to determine if a runtime error should be printed or not.
11+
* Printing the frame outputs can be done via {@link #getErrorFrame()} and {@link #getWarningFrame()}.
12+
*/
13+
public class RuntimeErrorFilter {
14+
15+
/**
16+
* A filter that does not filter anything, allowing all errors and warnings to be printed.
17+
* Modifications to its limits will do nothing, and the returned frames will hold no relevant data.
18+
*/
19+
public static final RuntimeErrorFilter NO_FILTER = new RuntimeErrorFilter(
20+
new Frame.FrameLimit(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE),
21+
new Frame.FrameLimit(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE)
22+
) {
23+
@Override
24+
public boolean test(@NotNull RuntimeError error) {
25+
return true;
26+
}
27+
};
28+
29+
private Frame errorFrame, warningFrame;
30+
31+
public RuntimeErrorFilter(Frame.FrameLimit errorFrameLimits, Frame.FrameLimit warningFrameLimits) {
32+
this.errorFrame = new Frame(errorFrameLimits);
33+
this.warningFrame = new Frame(warningFrameLimits);
34+
}
35+
36+
/**
37+
* Tests whether a runtime error should be printed or not.
38+
* @param error True if it should be printed, false if not.
39+
*/
40+
public boolean test(@NotNull RuntimeError error) {
41+
// print if < limit
42+
return (error.level() == Level.SEVERE && errorFrame.add(error))
43+
|| (error.level() == Level.WARNING && warningFrame.add(error));
44+
}
45+
46+
public void setErrorFrameLimits(Frame.FrameLimit limits) {
47+
this.errorFrame = new Frame(limits);
48+
}
49+
50+
public void setWarningFrameLimits(Frame.FrameLimit limits) {
51+
this.warningFrame = new Frame(limits);
52+
}
53+
54+
/**
55+
* @return The frame containing emitted errors.
56+
*/
57+
public Frame getErrorFrame() {
58+
return errorFrame;
59+
}
60+
61+
/**
62+
* @return The frame containing emitted warnings.
63+
*/
64+
public Frame getWarningFrame() {
65+
return warningFrame;
66+
}
67+
68+
}

0 commit comments

Comments
 (0)