-
Notifications
You must be signed in to change notification settings - Fork 2k
Fix stack trace #4428
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
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
src/coffee-script.coffee
Outdated
|
|
||
| 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?
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.
Using a tab (\t) instead of spaces would allow users to control the visual spacing in their editor/terminal preferences.
|
test.coffee: test = ->
console.log a if true
do testLet'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 |
| # 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. |
* Revert aee27fb * Patch Jison’s output so that it requires `fs` only if we’re truly in 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. * Temporary fix for exceptions getting thrown when trying to generate a 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. * Save the test REPL history in the system temp folder, not in the CoffeeScript project folder * Rewrite `getSourceMap` to never read a file from disk, and therefore 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. * Add test to verify that stack traces reference the correct line number. Closes jashkenas#4418. * Get the parser working in the browser compiler again; rather than detecting a CommonJS environment generally, just check for `fs` before trying to use it * Follow Node’s standard of 4-space indentation of stack trace data * Better .gitignore * Fix caching of compiled code and source maps; add more tests to verify correct line numbers in stack traces * Better fallback value for the parser source * Fix the stack traces and tests when running in a browser * Update the browser compiler so that @murrayju doesn’t have any extra work to do to test this branch
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:getSourceMapcallingexports._compileFilecallingfs.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
fsdoesn’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.readFileSyncand its associated exceptions, which fixes #3890.Perhaps unrelated, I patched Jison’s output to not assume that since
requireandexportsare 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.