Upgraded my traceback pcall module
There are only two ways I’m aware of to have both error checking and traceback:
-
xpcall
, which does not allow yielding
- A combination of
BindableEvent
s, ScriptContext.Error
, and require
-ing uniquely-named modules
Of course I want to be able to yield! tpcall
uses the second way in order to give tracebacks with error checking.
ScriptContext.Error
will give the error message, traceback, and script that caused the error. Using BindableEvent
s, we can cause an error in the output that ScriptContext.Error
can catch, which gives us a traceback.
But how do we differentiate between our errors, and other errors? Easy first answer: check the traceback! The traceback should have the name of the tpcall
module in it, so only those errors are ours.
…But how do we differentiate between multiple calls to tpcall
?
We have to somehow include a unique string in the traceback. The only way I’m aware of to do this is to require a module with a unique name.
Attempt 1: Every call to tpcall
clones a base module, gives it a unique name, then requires it. tpcall
will look for this unique name in the traceback.
Problem: require
is actually very slow! tpcall
started causing momentary freezing!
Attempt 2: Use only one module with a unique name. Keep track of when tpcall
is called, and create a “stack” of events to return to. If I call tpcall
inside of a tpcall
-ed function then it will return errors and results to the inner tpcall
first, remove that event from the stack, then return to the first tpcall
on the next error or return.
Problem: coroutines exist. Whoops. If a script in a different coroutine calls tpcall
then this “stack” approach completely breaks down. Script 2 can call tpcall
in the middle of Script 1’s tpcall
, and those two should not be “stacked” since they run “in parallel”. Script 1’s tpcall
can end before Script 2’s, causing a return to the wrong caller.
Attempt 3: Multiple uniquely-named ModuleScripts. Reuse them after they’re done. Use the “stack” approach for nested tpcall
s.
Problem: None! It’s awesome! Things will only slow down if there are a lot of non-nested tpcall
s yielding at one time, since it runs out of cached uniquely-named modules. Otherwise, it re-uses a previously-generated module or the one for its “parent” coroutine, keeping it super speedy.