-
Notifications
You must be signed in to change notification settings - Fork 830
Sourcemap support for Binaryen C/JS #1392
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -286,8 +286,8 @@ Module['Module'] = function(module) { | |
| // need to make their own Literals, as the C API handles them by value, | ||
| // which means we would leak them. Instead, this is the only API that | ||
| // accepts Literals, so fuse it with Literal creation | ||
| var literal = _malloc(16); // a single literal in memory. the LLVM C ABI | ||
| // makes us pass pointers to this. | ||
| var temp = _malloc(16); // a single literal in memory. the LLVM C ABI | ||
| // makes us pass pointers to this. | ||
|
|
||
| this['i32'] = { | ||
| 'load': function(offset, align, ptr) { | ||
|
|
@@ -315,8 +315,8 @@ Module['Module'] = function(module) { | |
| return Module['_BinaryenStore'](module, 2, offset, align, ptr, value, Module['i32']); | ||
| }, | ||
| 'const': function(x) { | ||
| Module['_BinaryenLiteralInt32'](literal, x); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralInt32'](temp, x); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'clz': function(value) { | ||
| return Module['_BinaryenUnary'](module, Module['ClzInt32'], value); | ||
|
|
@@ -556,8 +556,8 @@ Module['Module'] = function(module) { | |
| return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['i64']); | ||
| }, | ||
| 'const': function(x, y) { | ||
| Module['_BinaryenLiteralInt64'](literal, x, y); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralInt64'](temp, x, y); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'clz': function(value) { | ||
| return Module['_BinaryenUnary'](module, Module['ClzInt64'], value); | ||
|
|
@@ -802,12 +802,12 @@ Module['Module'] = function(module) { | |
| return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['f32']); | ||
| }, | ||
| 'const': function(x) { | ||
| Module['_BinaryenLiteralFloat32'](literal, x); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralFloat32'](temp, x); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'const_bits': function(x) { | ||
| Module['_BinaryenLiteralFloat32Bits'](literal, x); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralFloat32Bits'](temp, x); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'neg': function(value) { | ||
| return Module['_BinaryenUnary'](module, Module['NegFloat32'], value); | ||
|
|
@@ -901,12 +901,12 @@ Module['Module'] = function(module) { | |
| return Module['_BinaryenStore'](module, 8, offset, align, ptr, value, Module['f64']); | ||
| }, | ||
| 'const': function(x) { | ||
| Module['_BinaryenLiteralFloat64'](literal, x); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralFloat64'](temp, x); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'const_bits': function(x, y) { | ||
| Module['_BinaryenLiteralFloat64Bits'](literal, x, y); | ||
| return Module['_BinaryenConst'](module, literal); | ||
| Module['_BinaryenLiteralFloat64Bits'](temp, x, y); | ||
| return Module['_BinaryenConst'](module, temp); | ||
| }, | ||
| 'neg': function(value) { | ||
| return Module['_BinaryenUnary'](module, Module['NegFloat64'], value); | ||
|
|
@@ -1185,16 +1185,42 @@ Module['Module'] = function(module) { | |
| Module['_BinaryenModuleDispose'](module); | ||
| }; | ||
| var MAX = 1024*1024; // TODO: fix this hard-wired limit | ||
| var writeBuffer = null; | ||
| this['emitBinary'] = function() { | ||
| if (!writeBuffer) writeBuffer = _malloc(MAX); | ||
| var bytes = Module['_BinaryenModuleWrite'](module, writeBuffer, MAX); | ||
| var outputBuffer = null; | ||
| var sourceMapBuffer = null; | ||
| this['emitBinary'] = function(sourceMapUrl) { | ||
| if (!outputBuffer) outputBuffer = _malloc(MAX); | ||
| var bytes = Module['_BinaryenModuleWrite'](module, outputBuffer, MAX); | ||
| assert(bytes < MAX, 'FIXME: hardcoded limit on module size'); // we should not use the whole buffer | ||
| return new Uint8Array(HEAPU8.subarray(writeBuffer, writeBuffer + bytes)); | ||
| return new Uint8Array(HEAPU8.subarray(outputBuffer, outputBuffer + bytes)); | ||
| }; | ||
| this['emitBinaryWithSourceMap'] = function(sourceMapUrl) { | ||
| if (!outputBuffer) outputBuffer = _malloc(MAX); | ||
| if (!sourceMapBuffer) sourceMapBuffer = _malloc(MAX); | ||
| return preserveStack(function() { | ||
| Module['_BinaryenModuleWriteWithSourceMap'](temp, module, strToStack(sourceMapUrl), outputBuffer, MAX, sourceMapBuffer, MAX); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (looks like this depends on the ABI issues mentioned above, it assumes the output is pointed to by an extra param at the beginning? I'm actually surprised it's not at the end.)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just reused what's done for
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, I see. Yeah, we already do depend on those ABI details I guess... no big deal to add this, then. |
||
| var outputBytes = HEAPU32[temp >>> 2]; | ||
| var sourceMapBytes = HEAPU32[(temp + 1) >>> 2]; | ||
| assert(outputBytes < MAX && sourceMapBytes < MAX, 'FIXME: hardcoded limit on module size'); // see above | ||
| return { | ||
| 'binary': new Uint8Array(HEAPU8.subarray(outputBuffer, outputBuffer + outputBytes)), | ||
| 'sourceMap': Pointer_stringify(sourceMapBuffer) | ||
| }; | ||
| }); | ||
| }; | ||
| this['interpret'] = function() { | ||
| return Module['_BinaryenModuleInterpret'](module); | ||
| }; | ||
| this['addDebugInfoFileName'] = function(filename) { | ||
| return preserveStack(function() { | ||
| return Module['_BinaryenModuleAddDebugInfoFileName'](module, strToStack(filename)); | ||
| }); | ||
| }; | ||
| this['getDebugInfoFileName'] = function(index) { | ||
| return Pointer_stringify(Module['_BinaryenModuleGetDebugInfoFileName'](module, index)); | ||
| }; | ||
| this['setDebugLocation'] = function(func, expr, fileIndex, lineNumber, columnNumber) { | ||
| return Module['_BinaryenFunctionSetDebugLocation'](func, expr, fileIndex, lineNumber, columnNumber); | ||
| }; | ||
| }; | ||
|
|
||
| // 'Relooper' interface | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| var module = new Binaryen.Module(); | ||
|
|
||
| var signature = module.addFunctionType("i", Binaryen.i32, []); | ||
|
|
||
| var fileIndex = module.addDebugInfoFileName("module.c"); | ||
|
|
||
| console.log(module.getDebugInfoFileName(fileIndex)); | ||
| console.log(); | ||
|
|
||
| var expr = module.i32.const(1); | ||
| var body = module.block("", [ | ||
| expr | ||
| ], Binaryen.i32); | ||
|
|
||
| var func = module.addFunction("main", signature, [], body); | ||
|
|
||
| module.setDebugLocation(func, expr, fileIndex, 1, 2); | ||
| module.setDebugLocation(func, body, fileIndex, 0, 3); | ||
|
|
||
| var output = module.emitBinaryWithSourceMap("module.wasm.map"); | ||
|
|
||
| function dumpBinary(buffer) { | ||
| var hex = [], o, b, h; | ||
| for (var i = 0; i < buffer.length; ++i) { | ||
| o = i.toString(16); | ||
| while (o.length < 3) o = "0" + o; | ||
| if ((i & 15) === 0) hex.push((i ? "\n" : "") + o + ":"); | ||
| if ((b = buffer[i]) >= 0x21 && b <= 0x7e) | ||
| h = String.fromCharCode(b) + ' '; | ||
| else if ((h = b.toString(16)).length < 2) | ||
| h = "0" + h; | ||
| hex.push(h); | ||
| } | ||
| console.log(hex.join(" ")); | ||
| } | ||
|
|
||
| dumpBinary(output.binary); | ||
| console.log(); | ||
| console.log(output.sourceMap); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| module.c | ||
|
|
||
| 000: 00 a s m 01 00 00 00 01 05 01 ` 00 01 7f 03 | ||
| 010: 02 01 00 0a 06 01 04 00 A 01 0b 00 ! 10 s o | ||
| 020: u r c e M a p p i n g U R L 0f m | ||
| 030: o d u l e . w a s m . m a p | ||
|
|
||
| {"version":3,"sources":["module.c"],"names":[],"mappings":"gCAAE"} |
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.
returning a struct has some odd ABI issues in C that I'd rather avoid. instead, how about returning the output size as in
BinaryenModuleWrite, and adding a parameter with a pointer to write the source map bytes into?Uh oh!
There was an error while loading. Please reload this page.
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 actually exactly how
outputworks (seen from the JS-side), just with two such buffers. What's done there is that the user pre-allocates a buffer or two on the JS side and provides their pointers including their maximum length. The function then returns the number of bytes written so that the JS side can trim the buffer, and in this specific case it has to return two (hence the struct).There is a FIXME on the JS side because these buffers are currently capped to 1MB. I guess if we solve this one, the API might change anyway. From JS perspective, it would be ideal not to pre-allocate anything and just receive a properly sized buffer incl. its length from the function. With the ABI stuff, that'd look like
or something, and I don't currently have a better idea (can't use passing by reference from JS for example, except there's also an ABI for that?). Any idea?
Uh oh!
There was an error while loading. Please reload this page.
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.
Maybe, if we could write just the source map without having to also write the binary, we could split that, making it two functions that return just size_t. Might require some changes to the binary writer, though, as source map generation is currently piggybacked on top of writing a binary (what about a specific sourcemap writer?). Wouldn't fix the hard-wired limit, though.
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, an API variant that mallocs the buffer internally and returns it, and requires the caller to free it later, would be a good way to solve the 1MB limit. Alternatively the zlib API has a method that can upper bound the size needed for a buffer. That's less easy for us, but maybe there's a way to that, which would be better (avoid malloc, the user might have another preferred allocation method).