Repository navigation
Discussion: More robust error handling #732
Description
Activity
I saw you mention implementing some kind of try-catch system in Wurst as well, so I'm going to throw my 2 cents in here.
We already have facilities in StdLib for (very rudimentary) catching of errors. The
try()function from theExecutepackage will catch a crashing thread, and the last error (as a string) can be inspected usinglastErrorMessagefromErrorHandling.I use this extensively in my own libraries, especially for FileIO stuff where a lot of things can go wrong (especially user-provided implementations for Persistable), and it works fine for catching errors, and carrying on in case of a failure. In fact, I rely on these errors being thrown to find out whether there was some kind of error in user code. If the thread doesn't crash, then I have no way of telling.
This system can be extended to allow
throw()-ing anExceptionobject instead of just setting alastErrorMessagestring, which could be later extracted by a correspondingtry()function.I think you could use this approach on the language level to implement a
try/catchmechanic. For better error handling, Wurst could include a default, 'hidden'try/catchblock at all "Entry points", such as Trigger Actions, Conditions, Timer Callbacks, etc. with something like a default handler printing outUnhandled exception xxx...Then, the current built-in errors could be modified to throw
Exception-s rather than simple strings, allowing the users to gracefuly handle them.I don't think it's a good idea to choose a "continue after error" approach, because it IMO leads to even more confusion. This was one of the biggest problems with trying to debug JASS code - if a native returned a
nullor something else that you didn't expect, then your code would do all kinds of wonky, unexpected stuff later on.On the other hand, I can totally get behind how this behaviour could be desired in other situations, where you just want to print an error or a warning and carry on anyway, for the sake of players. Some errors aren't that critical, after all. But it depends from case to case.
I'd also like to comment on individual suggestions:
Return the default value from method m in cases 1 and 2.
I'm not sure that's a good idea. If some class method returns an instance of another class, this could easily lead to a cascade of null-pointers, obscuring the original root cause. In complicated hierarchies, this is almost guaranteed to happen, and with or without "continue after error", the code will not work correctly.
Just continue after double frees (case 3)
I think this is possible. Double-free is not a critical error, most of the times, and is sometimes just programmer oversight trying to accidentally delete the same object twice in the same function. If it is a critical error, it will have likely lead to NPEs before the Double-free, so if the thread crashes on NPE but not on Double-free, that's fine, I think.
Destroy a random object or the oldest object in case 4 and use that space for the new object.
This could lead to all kinds of weird, obscure corruption bugs on leaks. Out of memory errors usually indicate a massive leak, and is something that almost certainly must be fixed. On the other hand, it heavily depends on the user code. If someone has reached the 30k-something limit on objects, it might very well be the case that the "older" objects are out of scope anyway.
"Index out of Bounds" (for multi-arrays)
I think this is fine, personally. A lot of JASS natives return default values on incorrect input anyway, so it wouldn't be too different from that. So long as the error is correctly logged, it should be fine.
for case 4
30k-something limit is Frustrating
Especially when I use the 1.27 version of the wc3 ( it is usually in china). i only can use 8191 object .
I think the compiler can use more than one array at compile time to retain the index of these objects. and the number of arrays is configurable
eg:limit = 8191 (or 32767 base on the wc3 version ) let w = index div limit // sign to use which array let i = index mod limit // sign to the array-indexThis does not cause too much runtime burden.
I'm not gonna repeat everything from other packages, but my main reason for this ticket is, that non-destructive errors, like double frees, cause the thread to crash, which often isn't desirable.
It's an easy fix, and good to know error, but if it happens in a full game and breaks the map, it's not worth it.
Errors that will most definitely cause issues should still stop the thread.
Imo this could also be a part of wurst.build/runargs config.- locked and limited conversation to collaborators
on Aug 10, 2021
Problem: Currently we crash the thread if there is an error ingame.
This can spoil the game so people are sad.
One idea is to take a more "Javascript like approach" and try to continue executing code.
Concretely, we have the following runtime exceptions created by the compiler:
Instead of crashing the thread, we could just print the error message and then
min cases 1 and 2.This issue for discussing which of those make sense, or whether we should keep the current behavior.