-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix stack trace #4428
Fix stack trace #4428
Conversation
…a CommonJS/Node environment, not a browser environment that may happen to have globals named `require` and `exports` (as would be the case if require.js is being used). Fixes jashkenas#4391.
… stack trace for a file that has been deleted since compilation; fixes jashkenas#3890, but not well. A better solution would not try to recompile the file when trying to retrieve its stack trace.
…eeScript project folder
…not throw IO-related exceptions; source maps are either retrieved from memory, or the related source code is retrieved from memory to generate a new source map. Fixes jashkenas#3890 the proper way.
…ecting a CommonJS environment generally, just check for `fs` before trying to use it
|
||
frames = for frame in stack | ||
break if frame.getFunction() is exports.run | ||
" at #{formatSourcePosition frame, getSourceMapping}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While were improving all of this anyway, I think we should take the opportunity to indent the "at" lines just like Node.js does. That is, 4 spaces instead of 2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, really? I hate 4-space indentation . . .
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also prefer 2-space indentation in general, but I thought it might be a good idea to match the standard error formatting as closely as possible?
test.coffee: test = ->
console.log a if true
do test Let's try it out!
Observations:
I think the approach of this PR is very good. 👍 The added code is mostly restored from the old version, with a few tweaks, right? |
@lydell I added new tests to cover #3890 (file deleted between initial compilation and stack trace) and #4418 (wrong line numbers). The latter test seems to be okay to me. Technically I feel like the reported line number should be 1338 (if the first line is 1) or 1337 (if the first line is 0) but 1339 seems close enough. In my experience the line numbers in Coffee stack traces have never been overly precise, more like setting breakpoints on source maps in a browser, but they’ve been close enough to quickly find the relevant code. |
They were correct before:
|
That’s perverse. Here’s the diff between the 1.12.1 |
…y correct line numbers in stack traces
@lydell you were right, the caching wasn’t working properly. I’ve made it much more robust. Stack traces should now be processing source maps correctly both for compiled files (your test) and filename-less strings compiled via |
Nice! All looks good to me now. 👍 Regarding the "prelude":
I have no idea where that comes from, and if it is possible to do anything about it. Anyways, this PR is about restoring what was removed, so that's out of scope. But it'd be nice if we could figure out how to CoffeeScript-ify it in the future! :) |
@lydell It’s probably not a good idea to try to Coffeeify the “prelude.” The error that’s being thrown is a JavaScript runtime error, so the user should probably see the offending JavaScript. The original CoffeeScript might mask what the issue is, especially if the user isn’t a CoffeeScript expert and the Coffee isn’t compiling into what the user is expecting. That said, I would love to include the runtime “prelude” with our output somehow if there was a way to retrieve it. @murrayju do you have a minute to try this branch with your project that originally led to us removing our patched |
@@ -146,13 +163,13 @@ exports.run = (code, options = {}) -> | |||
|
|||
# Set the filename. | |||
mainModule.filename = process.argv[1] = | |||
if options.filename then fs.realpathSync(options.filename) else '.' | |||
if options.filename then fs.realpathSync(options.filename) else '<anonymous>' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lydell I changed this so that we have consistent “filenames” when compiling from run
or eval
or other non-file sources, which is important for looking up cached sources and source maps later. Do you see any issue with this? Was there anything special about a filename of .
before?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can't see any obvious issue, but on the other hand I have no idea if there was anything special about the .
before either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay. I just ran the browser tests and test.html and ran into (and fixed) some issues, which is what led me to change this. We never had a stack trace line number test that ran in the browser before, so I’m not sure our previous stack traces ever worked in the browser (I suspect they didn’t). I hope there aren’t any other edge cases I’m not thinking of.
# of `<anonymous>`, but the browser may request the stack trace with the | ||
# filename of the script file. | ||
else if sourceMaps['<anonymous>']? | ||
sourceMaps['<anonymous>'] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See also.
…work to do to test this branch
👍 Gave it a quick run through my build/tests, and it all worked fine! |
Ok, I’ll try to do that tomorrow. Remind me if I forget. |
@GeoffreyBooth THIS pull request officially makes you MY hero!! This has been broken for a while ;) |
Heroic of you to tackle this — nice work! Conceptually, it feels like the right thing to do would be (while running live via That said, this works too. |
I haven't tested it, but I think that's what this code does. The issue is that source maps are not created by default, so we don't want to always create them regardless of whether they were requested, even if they're not returned. If they're created, whether in the first place or as part of a stack trace, they're cached. |
Fixes #4418, #4391, #3890.
So in #4399 we removed CoffeeScript’s patched
Error.prepareStackTrace
, because it was poorly implemented and threw exceptions when it shouldn’t. However, removing that patch is a major inconvenience (triggering #4418) as errors thrown by the runtime have incorrect line numbers. Just working with failing tests in the CoffeeScript codebase itself is a major pain in the ass, since the line numbers are meaningless for tests that throw errors.So we need this patch back, but we should fix it so that it doesn’t cause the errors described in the other tickets. As I looked into it, it appears that
Error.prepareStackTrace
‘s original implementation went like this:getSourceMap
callingexports._compileFile
callingfs.readFileSync
).The “reopen the file” part is hugely problematic, because the file might not still be available by the time the exception is thrown and this stack trace is requested; that’s what #3890 complains about. It also makes this code completely unusable in a browser, where
fs
doesn’t exist.In #4399 @jashkenas complained that a file is compiled a second time just to generate a source map. Upon closer inspection, though, it seems to me that what’s happening is that a file gets recompiled to generate a source map only when a stack trace is needed, which presumably should be a rare occurrence. I think this is the desired behavior; since the default compilation mode has source map generation off, we shouldn’t generate source maps all the time just so that we have them cached for stack traces. Recompiling only in the event of a stack trace should be faster than generating source maps all the time, whether they were requested or not. (I can see people disagreeing with me on this though; @jashkenas, what do you think?)
What we should cache, then, is the code of the file we compiled. That way when a stack trace needs to recompile it to get the source map, it’s already in memory; it doesn’t need to open a file. This lets us remove the
fs.readFileSync
and its associated exceptions, which fixes #3890.Perhaps unrelated, I patched Jison’s output to not assume that since
require
andexports
are defined,require(‘fs’)
must be as well. This closes #4391, and fixes edge cases in the browser compiler. I also submitted zaach/jison#339 to try to push the fix upstream.