Skip to content

Commit b1d24f5

Browse files
dcodeIOkripken
authored andcommitted
Sourcemap support for Binaryen C/JS (#1392)
* Initial source map support for C/JS * Also test getDebugInfoFileName
1 parent f908789 commit b1d24f5

File tree

8 files changed

+337
-180
lines changed

8 files changed

+337
-180
lines changed

bin/binaryen.js

Lines changed: 136 additions & 135 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bin/wasm.js

Lines changed: 20 additions & 20 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build-js.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,8 +540,11 @@ export_function "_BinaryenSetDebugInfo"
540540
export_function "_BinaryenModuleRunPasses"
541541
export_function "_BinaryenModuleAutoDrop"
542542
export_function "_BinaryenModuleWrite"
543+
export_function "_BinaryenModuleWriteWithSourceMap"
543544
export_function "_BinaryenModuleRead"
544545
export_function "_BinaryenModuleInterpret"
546+
export_function "_BinaryenModuleAddDebugInfoFileName"
547+
export_function "_BinaryenModuleGetDebugInfoFileName"
545548

546549
# 'FunctionType' operations
547550
export_function "_BinaryenFunctionTypeGetName"
@@ -560,6 +563,7 @@ export_function "_BinaryenFunctionGetVar"
560563
export_function "_BinaryenFunctionGetBody"
561564
export_function "_BinaryenFunctionOptimize"
562565
export_function "_BinaryenFunctionRunPasses"
566+
export_function "_BinaryenFunctionSetDebugLocation"
563567

564568
# 'Import' operations
565569
export_function "_BinaryenImportGetKind"

src/binaryen-c.cpp

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2100,19 +2100,43 @@ void BinaryenModuleAutoDrop(BinaryenModuleRef module) {
21002100
passRunner.run();
21012101
}
21022102

2103-
size_t BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize) {
2104-
if (tracing) {
2105-
std::cout << " // BinaryenModuleWrite\n";
2106-
}
2107-
2103+
static BinaryenBufferSizes writeModule(BinaryenModuleRef module, char* output, size_t outputSize, const char* sourceMapUrl, char* sourceMap, size_t sourceMapSize) {
21082104
Module* wasm = (Module*)module;
21092105
BufferWithRandomAccess buffer(false);
21102106
WasmBinaryWriter writer(wasm, buffer, false);
21112107
writer.setNamesSection(globalPassOptions.debugInfo);
2108+
std::ostringstream os;
2109+
if (sourceMapUrl) {
2110+
writer.setSourceMap(&os, sourceMapUrl);
2111+
}
21122112
writer.write();
21132113
size_t bytes = std::min(buffer.size(), outputSize);
21142114
std::copy_n(buffer.begin(), bytes, output);
2115-
return bytes;
2115+
size_t sourceMapBytes = 0;
2116+
if (sourceMapUrl) {
2117+
auto str = os.str();
2118+
sourceMapBytes = std::min(str.length(), sourceMapSize);
2119+
std::copy_n(str.c_str(), sourceMapBytes, sourceMap);
2120+
}
2121+
return { bytes, sourceMapBytes };
2122+
}
2123+
2124+
size_t BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize) {
2125+
if (tracing) {
2126+
std::cout << " // BinaryenModuleWrite\n";
2127+
}
2128+
2129+
return writeModule((Module*)module, output, outputSize, nullptr, nullptr, 0).outputBytes;
2130+
}
2131+
2132+
BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, const char* url, char* output, size_t outputSize, char* sourceMap, size_t sourceMapSize) {
2133+
if (tracing) {
2134+
std::cout << " // BinaryenModuleWriteWithSourceMap\n";
2135+
}
2136+
2137+
assert(url);
2138+
assert(sourceMap);
2139+
return writeModule((Module*)module, output, outputSize, url, sourceMap, sourceMapSize);
21162140
}
21172141

21182142
BinaryenModuleRef BinaryenModuleRead(char* input, size_t inputSize) {
@@ -2144,6 +2168,26 @@ void BinaryenModuleInterpret(BinaryenModuleRef module) {
21442168
ModuleInstance instance(*wasm, &interface);
21452169
}
21462170

2171+
BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, const char* filename) {
2172+
if (tracing) {
2173+
std::cout << " BinaryenModuleAddDebugInfoFileName(the_module, \"" << filename << "\");\n";
2174+
}
2175+
2176+
Module* wasm = (Module*)module;
2177+
BinaryenIndex index = wasm->debugInfoFileNames.size();
2178+
wasm->debugInfoFileNames.push_back(filename);
2179+
return index;
2180+
}
2181+
2182+
const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, BinaryenIndex index) {
2183+
if (tracing) {
2184+
std::cout << " BinaryenModuleGetDebugInfoFileName(the_module, \"" << index << "\");\n";
2185+
}
2186+
2187+
Module* wasm = (Module*)module;
2188+
return index < wasm->debugInfoFileNames.size() ? wasm->debugInfoFileNames.at(index).c_str() : nullptr;
2189+
}
2190+
21472191
//
21482192
// ======== FunctionType Operations ========
21492193
//
@@ -2275,6 +2319,21 @@ void BinaryenFunctionRunPasses(BinaryenFunctionRef func, BinaryenModuleRef modul
22752319
}
22762320
passRunner.runOnFunction((Function*)func);
22772321
}
2322+
void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, BinaryenExpressionRef expr, BinaryenIndex fileIndex, BinaryenIndex lineNumber, BinaryenIndex columnNumber) {
2323+
if (tracing) {
2324+
std::cout << " BinaryenFunctionSetDebugLocation(functions[" << functions[func] << "], expressions[" << expressions[expr] << "], " << fileIndex << ", " << lineNumber << ", " << columnNumber << ");\n";
2325+
}
2326+
2327+
auto* fn = (Function*)func;
2328+
auto* ex = (Expression*)expr;
2329+
2330+
Function::DebugLocation loc;
2331+
loc.fileIndex = fileIndex;
2332+
loc.lineNumber = lineNumber;
2333+
loc.columnNumber = columnNumber;
2334+
2335+
fn->debugLocations[ex] = loc;
2336+
}
22782337

22792338
//
22802339
// =========== Import operations ===========

src/binaryen-c.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,16 @@ void BinaryenModuleAutoDrop(BinaryenModuleRef module);
697697
// @return how many bytes were written. This will be less than or equal to outputSize
698698
size_t BinaryenModuleWrite(BinaryenModuleRef module, char* output, size_t outputSize);
699699

700+
typedef struct BinaryenBufferSizes {
701+
size_t outputBytes;
702+
size_t sourceMapBytes;
703+
} BinaryenBufferSizes;
704+
705+
// Serialize a module into binary form including its source map. Uses the currently set
706+
// global debugInfo option.
707+
// @returns how many bytes were written. This will be less than or equal to outputSize
708+
BinaryenBufferSizes BinaryenModuleWriteWithSourceMap(BinaryenModuleRef module, const char* url, char* output, size_t outputSize, char* sourceMap, size_t sourceMapSize);
709+
700710
// Deserialize a module from binary form.
701711
BinaryenModuleRef BinaryenModuleRead(char* input, size_t inputSize);
702712

@@ -705,6 +715,13 @@ BinaryenModuleRef BinaryenModuleRead(char* input, size_t inputSize);
705715
// and then destroying the instance.
706716
void BinaryenModuleInterpret(BinaryenModuleRef module);
707717

718+
// Adds a debug info file name to the module and returns its index.
719+
BinaryenIndex BinaryenModuleAddDebugInfoFileName(BinaryenModuleRef module, const char* filename);
720+
721+
// Gets the name of the debug info file at the specified index. Returns `NULL` if it
722+
// does not exist.
723+
const char* BinaryenModuleGetDebugInfoFileName(BinaryenModuleRef module, BinaryenIndex index);
724+
708725
//
709726
// ======== FunctionType Operations ========
710727
//
@@ -747,6 +764,9 @@ void BinaryenFunctionOptimize(BinaryenFunctionRef func, BinaryenModuleRef module
747764
// optimize and shrink level.
748765
void BinaryenFunctionRunPasses(BinaryenFunctionRef func, BinaryenModuleRef module, const char **passes, BinaryenIndex numPasses);
749766

767+
// Sets the debug location of the specified `Expression` within the specified `Function`.
768+
void BinaryenFunctionSetDebugLocation(BinaryenFunctionRef func, BinaryenExpressionRef expr, BinaryenIndex fileIndex, BinaryenIndex lineNumber, BinaryenIndex columnNumber);
769+
750770
//
751771
// ========== Import Operations ==========
752772
//

src/js/binaryen.js-post.js

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ Module['Module'] = function(module) {
286286
// need to make their own Literals, as the C API handles them by value,
287287
// which means we would leak them. Instead, this is the only API that
288288
// accepts Literals, so fuse it with Literal creation
289-
var literal = _malloc(16); // a single literal in memory. the LLVM C ABI
290-
// makes us pass pointers to this.
289+
var temp = _malloc(16); // a single literal in memory. the LLVM C ABI
290+
// makes us pass pointers to this.
291291

292292
this['i32'] = {
293293
'load': function(offset, align, ptr) {
@@ -315,8 +315,8 @@ Module['Module'] = function(module) {
315315
return Module['_BinaryenStore'](module, 2, offset, align, ptr, value, Module['i32']);
316316
},
317317
'const': function(x) {
318-
Module['_BinaryenLiteralInt32'](literal, x);
319-
return Module['_BinaryenConst'](module, literal);
318+
Module['_BinaryenLiteralInt32'](temp, x);
319+
return Module['_BinaryenConst'](module, temp);
320320
},
321321
'clz': function(value) {
322322
return Module['_BinaryenUnary'](module, Module['ClzInt32'], value);
@@ -556,8 +556,8 @@ Module['Module'] = function(module) {
556556
return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['i64']);
557557
},
558558
'const': function(x, y) {
559-
Module['_BinaryenLiteralInt64'](literal, x, y);
560-
return Module['_BinaryenConst'](module, literal);
559+
Module['_BinaryenLiteralInt64'](temp, x, y);
560+
return Module['_BinaryenConst'](module, temp);
561561
},
562562
'clz': function(value) {
563563
return Module['_BinaryenUnary'](module, Module['ClzInt64'], value);
@@ -802,12 +802,12 @@ Module['Module'] = function(module) {
802802
return Module['_BinaryenStore'](module, 4, offset, align, ptr, value, Module['f32']);
803803
},
804804
'const': function(x) {
805-
Module['_BinaryenLiteralFloat32'](literal, x);
806-
return Module['_BinaryenConst'](module, literal);
805+
Module['_BinaryenLiteralFloat32'](temp, x);
806+
return Module['_BinaryenConst'](module, temp);
807807
},
808808
'const_bits': function(x) {
809-
Module['_BinaryenLiteralFloat32Bits'](literal, x);
810-
return Module['_BinaryenConst'](module, literal);
809+
Module['_BinaryenLiteralFloat32Bits'](temp, x);
810+
return Module['_BinaryenConst'](module, temp);
811811
},
812812
'neg': function(value) {
813813
return Module['_BinaryenUnary'](module, Module['NegFloat32'], value);
@@ -901,12 +901,12 @@ Module['Module'] = function(module) {
901901
return Module['_BinaryenStore'](module, 8, offset, align, ptr, value, Module['f64']);
902902
},
903903
'const': function(x) {
904-
Module['_BinaryenLiteralFloat64'](literal, x);
905-
return Module['_BinaryenConst'](module, literal);
904+
Module['_BinaryenLiteralFloat64'](temp, x);
905+
return Module['_BinaryenConst'](module, temp);
906906
},
907907
'const_bits': function(x, y) {
908-
Module['_BinaryenLiteralFloat64Bits'](literal, x, y);
909-
return Module['_BinaryenConst'](module, literal);
908+
Module['_BinaryenLiteralFloat64Bits'](temp, x, y);
909+
return Module['_BinaryenConst'](module, temp);
910910
},
911911
'neg': function(value) {
912912
return Module['_BinaryenUnary'](module, Module['NegFloat64'], value);
@@ -1185,16 +1185,42 @@ Module['Module'] = function(module) {
11851185
Module['_BinaryenModuleDispose'](module);
11861186
};
11871187
var MAX = 1024*1024; // TODO: fix this hard-wired limit
1188-
var writeBuffer = null;
1189-
this['emitBinary'] = function() {
1190-
if (!writeBuffer) writeBuffer = _malloc(MAX);
1191-
var bytes = Module['_BinaryenModuleWrite'](module, writeBuffer, MAX);
1188+
var outputBuffer = null;
1189+
var sourceMapBuffer = null;
1190+
this['emitBinary'] = function(sourceMapUrl) {
1191+
if (!outputBuffer) outputBuffer = _malloc(MAX);
1192+
var bytes = Module['_BinaryenModuleWrite'](module, outputBuffer, MAX);
11921193
assert(bytes < MAX, 'FIXME: hardcoded limit on module size'); // we should not use the whole buffer
1193-
return new Uint8Array(HEAPU8.subarray(writeBuffer, writeBuffer + bytes));
1194+
return new Uint8Array(HEAPU8.subarray(outputBuffer, outputBuffer + bytes));
1195+
};
1196+
this['emitBinaryWithSourceMap'] = function(sourceMapUrl) {
1197+
if (!outputBuffer) outputBuffer = _malloc(MAX);
1198+
if (!sourceMapBuffer) sourceMapBuffer = _malloc(MAX);
1199+
return preserveStack(function() {
1200+
Module['_BinaryenModuleWriteWithSourceMap'](temp, module, strToStack(sourceMapUrl), outputBuffer, MAX, sourceMapBuffer, MAX);
1201+
var outputBytes = HEAPU32[temp >>> 2];
1202+
var sourceMapBytes = HEAPU32[(temp + 1) >>> 2];
1203+
assert(outputBytes < MAX && sourceMapBytes < MAX, 'FIXME: hardcoded limit on module size'); // see above
1204+
return {
1205+
'binary': new Uint8Array(HEAPU8.subarray(outputBuffer, outputBuffer + outputBytes)),
1206+
'sourceMap': Pointer_stringify(sourceMapBuffer)
1207+
};
1208+
});
11941209
};
11951210
this['interpret'] = function() {
11961211
return Module['_BinaryenModuleInterpret'](module);
11971212
};
1213+
this['addDebugInfoFileName'] = function(filename) {
1214+
return preserveStack(function() {
1215+
return Module['_BinaryenModuleAddDebugInfoFileName'](module, strToStack(filename));
1216+
});
1217+
};
1218+
this['getDebugInfoFileName'] = function(index) {
1219+
return Pointer_stringify(Module['_BinaryenModuleGetDebugInfoFileName'](module, index));
1220+
};
1221+
this['setDebugLocation'] = function(func, expr, fileIndex, lineNumber, columnNumber) {
1222+
return Module['_BinaryenFunctionSetDebugLocation'](func, expr, fileIndex, lineNumber, columnNumber);
1223+
};
11981224
};
11991225

12001226
// 'Relooper' interface

test/binaryen.js/sourcemap.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
var module = new Binaryen.Module();
2+
3+
var signature = module.addFunctionType("i", Binaryen.i32, []);
4+
5+
var fileIndex = module.addDebugInfoFileName("module.c");
6+
7+
console.log(module.getDebugInfoFileName(fileIndex));
8+
console.log();
9+
10+
var expr = module.i32.const(1);
11+
var body = module.block("", [
12+
expr
13+
], Binaryen.i32);
14+
15+
var func = module.addFunction("main", signature, [], body);
16+
17+
module.setDebugLocation(func, expr, fileIndex, 1, 2);
18+
module.setDebugLocation(func, body, fileIndex, 0, 3);
19+
20+
var output = module.emitBinaryWithSourceMap("module.wasm.map");
21+
22+
function dumpBinary(buffer) {
23+
var hex = [], o, b, h;
24+
for (var i = 0; i < buffer.length; ++i) {
25+
o = i.toString(16);
26+
while (o.length < 3) o = "0" + o;
27+
if ((i & 15) === 0) hex.push((i ? "\n" : "") + o + ":");
28+
if ((b = buffer[i]) >= 0x21 && b <= 0x7e)
29+
h = String.fromCharCode(b) + ' ';
30+
else if ((h = b.toString(16)).length < 2)
31+
h = "0" + h;
32+
hex.push(h);
33+
}
34+
console.log(hex.join(" "));
35+
}
36+
37+
dumpBinary(output.binary);
38+
console.log();
39+
console.log(output.sourceMap);

test/binaryen.js/sourcemap.js.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module.c
2+
3+
000: 00 a s m 01 00 00 00 01 05 01 ` 00 01 7f 03
4+
010: 02 01 00 0a 06 01 04 00 A 01 0b 00 ! 10 s o
5+
020: u r c e M a p p i n g U R L 0f m
6+
030: o d u l e . w a s m . m a p
7+
8+
{"version":3,"sources":["module.c"],"names":[],"mappings":"gCAAE"}

0 commit comments

Comments
 (0)