-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Source map support #2082
Source map support #2082
Conversation
This basically fixes #1789 , although there is still more work to be done especially given that this has zero automatic testing. Also, I haven't done any archive testing. |
Codecov Report
@@ Coverage Diff @@
## master #2082 +/- ##
==========================================
- Coverage 72.11% 71.95% -0.17%
==========================================
Files 180 180
Lines 14243 14297 +54
==========================================
+ Hits 10272 10287 +15
- Misses 3346 3380 +34
- Partials 625 630 +5
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
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.
Haven't reviewed the code yet, but you seem to have committed a whole k6 binary in one of the commits, so please clean this up and force-push: https://github.com/k6io/k6/blob/aacadb7e7a69a7b2b828df3ac787d018641c227b/js/compiler/k6
Noticed this when I git pulled
the latest changes and it took a while:
remote: Total 116 (delta 78), reused 110 (delta 75), pack-reused 0
Receiving objects: 100% (116/116), 15.37 MiB | 1.41 MiB/s, done.
😂
3cc4ad2
to
951fcba
Compare
951fcba
to
683d3c1
Compare
This is now ready for review 🎉 Although definitely headed for 0.35.0 not 0.34.0 It seems to finally be working very well for everything that I have tried and doesn't seem to have all that big of performance implications - on a script taking 2m20s and 1.7GB it was taking more or less the same time (it fluctuates even without it) and 100mb more (again it fluctuates this is the biggest I have seen). Notes:
|
of course I have now found that one of my even bigger scripts (24k from the cloud request builder) which takes 1min and 1.7gb(the other script was only 10k but with some posts) now takes 20 seconds more and even more importantly 5.5gb of memory o.O I have no idea why this is happening, but I guess we need to look into it some more :( |
1065bcf
to
218d8f4
Compare
js/bundle.go
Outdated
c.COpts = compiler.CompilerOptions{ | ||
CompatibilityMode: compatMode, | ||
Strict: true, | ||
SourceMapEnabled: true, | ||
SourceMapLoader: generateSourceMapLoader(logger, filesystems), | ||
} |
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.
This can probably be reverted, but still seems kind of nicer to me so I am leaving it for now
efade35
to
e9edc51
Compare
js/bundle.go
Outdated
c.COpts = compiler.Options{ | ||
Strict: true, | ||
CompatibilityMode: compatMode, | ||
SourceMapEnabled: true, | ||
SourceMapLoader: generateSourceMapLoader(logger, arc.Filesystems), | ||
} | ||
pgm, _, err := c.Compile(string(arc.Data), arc.FilenameURL.String(), true, c.COpts) |
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.
This can probably be reverted, but still seems kind of nicer to me so I am leaving it for now
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.
Do you mean removing the "", ""
? If yes then I agree, it's more explicit.
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.
yeah, I mean that currently, this doesn't let you wrap the code in w/e you want. If it's dependency it will wrap it in the function(module, exports ...
wrapper if not it won't. And all of that with 1 boolean instead of 2 strings.
This was never used for anything apart from that outside of tests so 🤷 it seems like a good change to me
e905f38
to
a8e5ec1
Compare
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.
Thanks for working on it 👏, I left a few comments just to "start the discussion". I expect I need to play a bit more with the generated Source Maps.
I expect this will be an important feature, it would be appreciated if we could maintain the commit history atomic and with detailed commit messages I think it could help future readers.
now takes 20 seconds more and even more importantly 5.5gb of memory
I expect all the JSON marshal/unmarshal ops could have an important impact with big source maps.
js/compiler/compiler.go
Outdated
type Options struct { // TODO maybe have the fields an exported and use the functional options pattern | ||
CompatibilityMode lib.CompatibilityMode | ||
SourceMapEnabled bool | ||
// TODO maybe move only this in the compiler itself and leave ht rest as parameters to the Compile |
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.
Is it expected to do it before merge?
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 guess it depends of we want to do it or not. I kind of prefer the options struct as it seems more ... structured ( 😉 😉 ). If we want to keep it I will drop the TODO, otherwise I will need to DO it.
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.
👍 for options struct
Also, it seems Compile
is called only in 3 places, in bundle.js and in initcontext.js: it doesn't look as if anything would need an overwrite of compiler options, etc. So I think it can stay simple. Unless there is an expectation of that changing..
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.
👍 same for me for keeping it and removing the comment
js/bundle.go
Outdated
c.COpts = compiler.Options{ | ||
Strict: true, | ||
CompatibilityMode: compatMode, | ||
SourceMapEnabled: true, | ||
SourceMapLoader: generateSourceMapLoader(logger, arc.Filesystems), | ||
} | ||
pgm, _, err := c.Compile(string(arc.Data), arc.FilenameURL.String(), true, c.COpts) |
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.
Do you mean removing the "", ""
? If yes then I agree, it's more explicit.
@@ -86,10 +87,13 @@ var ( | |||
globalBabel *babel // nolint:gochecknoglobals | |||
) | |||
|
|||
const sourceMapURLFromBabel = "k6://internal-should-not-leak/file.map" |
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.
What does it mean? 🤔 Or better, I think that I know it but a comment could help.
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.
This is literally a constant that babel sets in the file which tells goja (in this case) where the sourcemap is. Goja asks k6 to get this and we return the sourcemap babel generated. It's just a particular very unlikely value for a real sourcemap URL ;)
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.
Partially out of curiosity: what is the worst that can happen if someone does use this internal URL deliberately for external sourcemap?
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.
The code returning the sourcemap to goja matches first on it and returns an internal field that get's set when babel transforms. If babel hasn't ran the field is nil - so nothing, if it has it would've set this url.
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.
If I understood correctly, this PR has some pending design decisions yet so I just added some comments and a question, please see.
ATM, it seems rather straight-forward in terms of implementation changes even with my lacking knowledge of sourcemaps spec 😅
And one more thing: TestSourceMapsExternal
shows one example of externally generated sourcemap but it seems like there's more than one external tool that can do that. Are there any expectations of the range of those tools working: are all of them going to work or only some of them, etc. ?
js/compiler/compiler.go
Outdated
|
||
// ';' is the separator between lines so just adding 1 will make all mappings be for the line after which they were | ||
// originally | ||
m["mappings"] = ";" + m["mappings"].(string) |
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.
Can there be a case when m
doesn't contain "mappings" field here? I assume this field is part of the sourcemap format and should be present but I wonder about "broken input" cases, etc.
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.
It seems extremely unlikely as that is the one thing that sourcemaps are about ;). But it can't hurt checking for it I guess.
js/compiler/compiler.go
Outdated
type Options struct { // TODO maybe have the fields an exported and use the functional options pattern | ||
CompatibilityMode lib.CompatibilityMode | ||
SourceMapEnabled bool | ||
// TODO maybe move only this in the compiler itself and leave ht rest as parameters to the Compile |
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.
👍 for options struct
Also, it seems Compile
is called only in 3 places, in bundle.js and in initcontext.js: it doesn't look as if anything would need an overwrite of compiler options, etc. So I think it can stay simple. Unless there is an expectation of that changing..
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.
Few additional comments
js/compiler/compiler.go
Outdated
// A Compiler compiles JavaScript source code (ES5.1 or ES6) into a goja.Program | ||
type Compiler struct { | ||
logger logrus.FieldLogger | ||
babel *babel | ||
COpts Options // TODO change this, this is just way faster |
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.
Can you give a bit more context for this TODO comment? Like what is your suggestion?
js/compiler/compiler.go
Outdated
type Options struct { // TODO maybe have the fields an exported and use the functional options pattern | ||
CompatibilityMode lib.CompatibilityMode | ||
SourceMapEnabled bool | ||
// TODO maybe move only this in the compiler itself and leave ht rest as parameters to the Compile |
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.
👍 same for me for keeping it and removing the comment
032c028
to
db288cc
Compare
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.
Seems we have done here! 👏
// compilationState is helper struct to keep the state of a compilation | ||
type compilationState struct { | ||
// set when we couldn't load external source map so we can try parsing without loading it | ||
couldntLoadSourceMap bool |
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'm wondering if we can avoid this property. For example, using the wrapped errors and checks like errors.Is
. It seems simpler and more go idiomatic, wdyt?
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.
The problem @olegbespalov is that the error doesn't get propaged through goja and fixing that will take quite a lot refactoring IIRC, so I opted for not trying to do that first.
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 see, thanks for clarifying 👍
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.
LGTM. Things are a bit convoluted, but they should be simplified when we get rid of Babel 🤞
15a6fac
to
5e705b3
Compare
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.
🤞
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.
It seems to be more readable than before 👍 though it might be just multiple re-reads effect 🙂 I have some remarks on comments mostly, please see.
type compilationState struct { | ||
// set when we couldn't load external source map so we can try parsing without loading it | ||
couldntLoadSourceMap bool | ||
// srcMap is the current full sourceMap that has been generated read so far |
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.
Is it 'generated' or 'read' here?
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.
it's an or
- it's either read from disk or generated by babel
@@ -86,10 +87,13 @@ var ( | |||
globalBabel *babel // nolint:gochecknoglobals | |||
) | |||
|
|||
const sourceMapURLFromBabel = "k6://internal-should-not-leak/file.map" |
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.
Partially out of curiosity: what is the worst that can happen if someone does use this internal URL deliberately for external sourcemap?
A problem I found around doing some final testing and especially after #2082 (comment): If the sourcemap is actually broken (in some obvious way) or just can't be parsed as JSON in general this will now abort the test and at least in some cases we will have hard time noticing it as you can have: var o={d:(e,r)=>{for(var t in r)o.o(r,t)&&!o.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},o:(o,e)=>Object.prototype.hasOwnProperty.call(o,e)},e={};o.d(e,{Z:()=>r});const r=()=>{!function(o){throw"cool is cool"}()};var t=e.Z;export{t as default};
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly8vd2VicGFjay9ib290c3RyYXAiLCJ3ZWJwYWNrOi8vL3dlYnBhY2svcnVudGltZS9kZWZpbmUgcHJvcGVydHkgZ2V0dGVycyIsIndlYnBhY2s6Ly8vd2VicGFjay9ydW50aW1lL2hhc093blByb3BlcnR5IHNob3J0aGFuZCIsIndlYnBhY2s6Ly8vLi90ZXN0MS50cyJdLCJuYW1lcyI6WyJfX3dlYnBhY2tfcmVxdWlyZV9fIiwiZXhwb3J0cyIsImRlZmluaXRpb24iLCJrZXkiLCJvIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJlbnVtZXJhYmxlIiwiZ2V0Iiwib2JqIiwicHJvcCIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsInMiLCJjb29sVGhyb3ciXSwibWFwcGluZ3MiOiJBQUNBLElBQUlBLEVBQXNCLENDQTFCLEVBQXdCLENBQUNDLEVBQVNDLEtBQ2pDLElBQUksSUFBSUMsS0FBT0QsRUFDWEYsRUFBb0JJLEVBQUVGLEVBQVlDLEtBQVNILEVBQW9CSSxFQUFFSCxFQUFTRSxJQUM1RUUsT0FBT0MsZUFBZUwsRUFBU0UsRUFBSyxDQUFFSSxZQUFZLEVBQU1DLElBQUtOLEVBQVdDLE1DSjNFLEVBQXdCLENBQUNNLEVBQUtDLElBQVVMLE9BQU9NLFVBQVVDLGVBQWVDLEtBQUtKLEVBQUtDLEksc0JDR2xGLGNBSEEsU0FBbUJJLEdBQ2YsS0FBTSxlQUdOQyxJIiwiZmlsZSI6InRlc3QxLmpzIiwic291cmNlc0NvbnRlbnQiOlsiLy8gVGhlIHJlcXVpcmUgc2NvcGVcbnZhciBfX3dlYnBhY2tfcmVxdWlyZV9fID0ge307XG5cbiIsIi8vIGRlZmluZSBnZXR0ZXIgZnVuY3Rpb25zIGZvciBoYXJtb255IGV4cG9ydHNcbl9fd2VicGFja19yZXF1aXJlX18uZCA9IChleHBvcnRzLCBkZWZpbml0aW9uKSA9PiB7XG5cdGZvcih2YXIga2V5IGluIGRlZmluaXRpb24pIHtcblx0XHRpZihfX3dlYnBhY2tfcmVxdWlyZV9fLm8oZGVmaW5pdGlvbiwga2V5KSAmJiAhX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIGtleSkpIHtcblx0XHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShleHBvcnRzLCBrZXksIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBkZWZpbml0aW9uW2tleV0gfSk7XG5cdFx0fVxuXHR9XG59OyIsIl9fd2VicGFja19yZXF1aXJlX18ubyA9IChvYmosIHByb3ApID0+IChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqLCBwcm9wKSkiLCJmdW5jdGlvbiBjb29sVGhyb3coczogc3RyaW5nKSB7XG4gICAgdGhyb3cgXCJjb29sIFwiKyBzXG59XG5leHBvcnQgZGVmYXVsdCAoKSA9PiB7XG4gICAgY29vbFRocm93KFwiaXMgY29vbFwiKVxufTtcbiJdLCJzb3VyY2VSb290IjoiIn0= A file with embeded sourcemap, if you now corrupt this (delete some symbols in the base64 encoded string) The error we get back is something like(using spew-go):
In this particular case as this code still goes through babel and this is what it returns. if it didn't we will get
Which at least is somewhat matchable with a string 🤷 . I don't think this is a particularly likely occurrence and will become less of a problem as time goes on and hopefully we use less babel. We can also always on error with sourcemap enabled retry with it disabled, but this goes and more in trying to handle random corner cases that I doubt will be hit by anyone. And obviously we can always revert this or add the necessary changes or add a flag to enable/disable source map if the need arises. |
This includes both support for any sourcemap found through the source file and generating sourcemaps when going through babel. There are additional fixes for trying to fix off by 1 line errors in imports, but those may need further work. On not being able to load a sourcemap a warning is emitted but the file is still parsed and compiled just without sourcemaps fixes #1789, #1804
this hopefully has some better performance but more importantly the generated code is more readable without it, while the lines don't match.
51b7df6
to
9ae6aaa
Compare
No description provided.