How can we actually tell that the function that threw was callme? The problem here is that JavaScript engine will tell you that what actually happened is that the first frame was a new error. Because well, technically, this is what produced the error. But this is not useful. Otherwise, all we would see is most, you know, most of cases just new error, new error, new error. What we want to know is what's the function that actually caused it.
So in this case, it would be callme. However, how can we know that it's actually this? So we have few tricks in our sleeves. The first one is so called backward scanning, where you take the minified source, you tokenize it, and you go one token at the time backwards. So what you do is you have error, you go to new, you go back to throw, and you do it as long as you see the minified name of the function. Or rather you see the token function preceded with the minified name. In our case here, it was XY, right? Once we have this function and XY tokens, we can ask what is the position of the token XY, in this case, and then ask the source map, hey, okay, now I know new location. This is not new error. I know the location of XY. Do you have a corresponding name for this? And if yes, we are lucky, we can now tell that it was actually call me and not new error that caused the error itself. However, it's not always possible. If you have a anonymous function or ES6 arrow function, it just won't work.
The second trick that we can use is so-called caller naming, which makes an assumption that the function that called us didn't modify the original name. For example, it didn't reassign it anywhere or didn't modify in some strange way or didn't call it dynamically, here is the call stack, the original one, which is function maybe, which calls function call me, which produces the error, and the minified one, VM calling XY, calling new error. What we can do is instead of asking what is the location of new error, we can go one step up, which is XY in this case, and ask the XY what is the corresponding column and line of this frame. What it requires is that you need to know the whole stack in advance. For example, if you just get the first frame, let's go back to the example here, if you just know at X asset bundle min JS, the first one, you're out of luck, you're not able to do this because what you need to do is you need to consult the frame below this, which like a column 2430, right? This usually gives some ideas, like we can fall back to this, but it's also not great. It's not always working, it's still just a guess.
The best idea that we came up with and we use with Success right now, and this is what plenty of DevTools are doing right now, which is doing AST reconstruction. What it does is, it takes minified source, and it's doing the same work that bundlers and transpilers already did, but, again, it's very memory inefficient, it's heavy, it basically requires a lot of work. However, the perk with this is that we get all the information that we may ever, ever need. So for example, we can tell that here a new error was thrown inside static method on class foo method bar, right? Or in this case, we can exactly tell that it was just object literal, assigned to variable capital A, and it was method, actually the property, sorry, the other method, or other custom getter called foo. And here we can tell that it was constructor of class B. So we can know exactly where we are at the time where the error happened, because we could reconstruct the whole thing. However, the problem is that again, you need to process the whole IST, which can be very, very expensive for very large files. And bundlers and transpiles already had this data.
Comments