@@ -33,6 +33,9 @@ void WasmBinaryWriter::prepare() {
33
33
34
34
void WasmBinaryWriter::write () {
35
35
writeHeader ();
36
+ if (binaryMap) {
37
+ writeBinaryMapProlog ();
38
+ }
36
39
37
40
writeTypes ();
38
41
writeImports ();
@@ -49,6 +52,9 @@ void WasmBinaryWriter::write() {
49
52
if (binaryMap) writeSourceMapUrl ();
50
53
if (symbolMap.size () > 0 ) writeSymbolMap ();
51
54
55
+ if (binaryMap) {
56
+ writeBinaryMapEpilog ();
57
+ }
52
58
finishUp ();
53
59
}
54
60
@@ -238,7 +244,6 @@ void WasmBinaryWriter::writeFunctions() {
238
244
size_t start = o.size ();
239
245
Function* function = wasm->functions [i].get ();
240
246
currFunction = function;
241
- lastDebugLocation = {0 , 0 , 0 };
242
247
mappedLocals.clear ();
243
248
numLocalsByType.clear ();
244
249
if (debug) std::cerr << " writing" << function->name << std::endl;
@@ -445,6 +450,50 @@ void WasmBinaryWriter::writeSymbolMap() {
445
450
file.close ();
446
451
}
447
452
453
+ void WasmBinaryWriter::writeBinaryMapProlog () {
454
+ lastDebugLocation = {0 , /* lineNumber = */ 1 , 0 };
455
+ lastBytecodeOffset = 0 ;
456
+ *binaryMap << " {\" version\" :3,\" sources\" :[" ;
457
+ for (size_t i = 0 ; i < wasm->debugInfoFileNames .size (); i++) {
458
+ if (i > 0 ) *binaryMap << " ," ;
459
+ // TODO respect JSON string encoding, e.g. quotes and control chars.
460
+ *binaryMap << " \" " << wasm->debugInfoFileNames [i] << " \" " ;
461
+ }
462
+ *binaryMap << " ],\" names\" :[],\" mappings\" :\" " ;
463
+ }
464
+
465
+ void WasmBinaryWriter::writeBinaryMapEpilog () {
466
+ *binaryMap << " \" }" ;
467
+ }
468
+
469
+ static void writeBase64VLQ (std::ostream& out, int32_t n) {
470
+ uint32_t value = n >= 0 ? n << 1 : ((-n) << 1 ) | 1 ;
471
+ while (1 ) {
472
+ uint32_t digit = value & 0x1F ;
473
+ value >>= 5 ;
474
+ if (!value) {
475
+ // last VLQ digit -- base64 codes 'A'..'Z', 'a'..'f'
476
+ out << char (digit < 26 ? ' A' + digit : ' a' + digit - 26 );
477
+ break ;
478
+ }
479
+ // more VLG digit will follow -- add continuation bit (0x20),
480
+ // base64 codes 'g'..'z', '0'..'9', '+', '/'
481
+ out << char (digit < 20 ? ' g' + digit : digit < 30 ? ' 0' + digit - 20 : digit == 30 ? ' +' : ' /' );
482
+ }
483
+ }
484
+
485
+ void WasmBinaryWriter::writeDebugLocation (size_t offset, const Function::DebugLocation& loc) {
486
+ if (lastBytecodeOffset > 0 ) {
487
+ *binaryMap << " ," ;
488
+ }
489
+ writeBase64VLQ (*binaryMap, int32_t (offset - lastBytecodeOffset));
490
+ writeBase64VLQ (*binaryMap, int32_t (loc.fileIndex - lastDebugLocation.fileIndex ));
491
+ writeBase64VLQ (*binaryMap, int32_t (loc.lineNumber - lastDebugLocation.lineNumber ));
492
+ writeBase64VLQ (*binaryMap, int32_t (loc.columnNumber - lastDebugLocation.columnNumber ));
493
+ lastDebugLocation = loc;
494
+ lastBytecodeOffset = offset;
495
+ }
496
+
448
497
void WasmBinaryWriter::writeInlineString (const char * name) {
449
498
int32_t size = strlen (name);
450
499
o << U32LEB (size);
@@ -949,6 +998,7 @@ static Name RETURN_BREAK("binaryen|break-to-return");
949
998
void WasmBinaryBuilder::read () {
950
999
951
1000
readHeader ();
1001
+ readBinaryMapHeader ();
952
1002
953
1003
// read sections until the end
954
1004
while (more ()) {
@@ -1402,43 +1452,134 @@ void WasmBinaryBuilder::readExports() {
1402
1452
}
1403
1453
}
1404
1454
1405
- void WasmBinaryBuilder::readNextDebugLocation () {
1406
- if (binaryMap) {
1407
- std::string line;
1408
- while (std::getline (*binaryMap, line)) {
1409
- auto pos = line.begin ();
1410
- while (pos < line.end () && pos[0 ] != ' :' ) pos++;
1411
- if (pos == line.end ()) continue ;
1412
- uint32_t position = atoi (std::string (line.begin (), pos).c_str ());
1413
- auto filenameStart = ++pos;
1414
- while (pos < line.end () && pos[0 ] != ' :' ) pos++;
1415
- if (pos == line.end ()) continue ;
1416
- std::string file (filenameStart, pos);
1417
- auto iter = debugInfoFileIndices.find (file);
1418
- if (iter == debugInfoFileIndices.end ()) {
1419
- Index index = wasm.debugInfoFileNames .size ();
1420
- wasm.debugInfoFileNames .push_back (file);
1421
- debugInfoFileIndices[file] = index;
1455
+ static int32_t readBase64VLQ (std::istream& in) {
1456
+ uint32_t value = 0 ;
1457
+ uint32_t shift = 0 ;
1458
+ while (1 ) {
1459
+ char ch = in.get ();
1460
+ if (ch == EOF)
1461
+ throw MapParseException (" unexpected EOF in the middle of VLQ" );
1462
+ if ((ch >= ' A' && ch <= ' Z' ) || (ch >= ' a' && ch < ' g' )) {
1463
+ // last number digit
1464
+ uint32_t digit = ch < ' a' ? ch - ' A' : ch - ' a' + 26 ;
1465
+ value |= digit << shift;
1466
+ break ;
1467
+ }
1468
+ if (!(ch >= ' g' && ch <= ' z' ) && !(ch >= ' 0' && ch <= ' 9' ) &&
1469
+ ch != ' +' && ch != ' /' ) {
1470
+ throw MapParseException (" invalid VLQ digit" );
1471
+ }
1472
+ uint32_t digit = ch > ' 9' ? ch - ' g' : (ch >= ' 0' ? ch - ' 0' + 20 : (ch == ' +' ? 30 : 31 ));
1473
+ value |= digit << shift;
1474
+ shift += 5 ;
1475
+ }
1476
+ return value & 1 ? -int32_t (value >> 1 ) : int32_t (value >> 1 );
1477
+ }
1478
+
1479
+ void WasmBinaryBuilder::readBinaryMapHeader () {
1480
+ if (!binaryMap) {
1481
+ return ;
1482
+ }
1483
+ auto maybeReadChar = [&](char expected) {
1484
+ if (binaryMap->peek () != expected)
1485
+ return false ;
1486
+ binaryMap->get ();
1487
+ return true ;
1488
+ };
1489
+ auto mustReadChar = [&](char expected) {
1490
+ if (binaryMap->get () != expected)
1491
+ throw MapParseException (" Unexpected char" );
1492
+ };
1493
+ auto findField = [&](const char * name, size_t len) {
1494
+ bool matching = false ;
1495
+ size_t pos;
1496
+ while (1 ) {
1497
+ int ch = binaryMap->get ();
1498
+ if (ch == EOF) return false ;
1499
+ if (ch == ' \" ' ) {
1500
+ matching = true ;
1501
+ pos = 0 ;
1502
+ } else if (matching && name[pos] == ch) {
1503
+ ++pos;
1504
+ if (pos == len) {
1505
+ if (maybeReadChar (' \" ' )) break ; // found field
1506
+ }
1507
+ } else {
1508
+ matching = false ;
1422
1509
}
1423
- uint32_t fileIndex = debugInfoFileIndices[file];
1424
- auto lineNumberStart = ++pos;
1425
- while (pos < line.end () && pos[0 ] != ' :' ) pos++;
1426
- if (pos == line.end ()) {
1427
- // old format
1428
- uint32_t lineNumber = atoi (std::string (lineNumberStart, line.end ()).c_str ());
1429
- nextDebugLocation = {position, {fileIndex, lineNumber, 0 }};
1430
- return ;
1510
+ }
1511
+ mustReadChar (' :' );
1512
+ return true ;
1513
+ };
1514
+ auto readString = [&](std::string& str) {
1515
+ std::vector<char > vec;
1516
+ mustReadChar (' \" ' );
1517
+ if (!maybeReadChar (' \" ' )) {
1518
+ while (1 ) {
1519
+ int ch = binaryMap->get ();
1520
+ if (ch == EOF)
1521
+ throw MapParseException (" unexpected EOF in the middle of string" );
1522
+ if (ch == ' \" ' ) break ;
1523
+ vec.push_back (ch);
1431
1524
}
1432
- uint32_t lineNumber = atoi (std::string (lineNumberStart, pos).c_str ());
1433
- auto columnNumberStart = ++pos;
1434
- uint32_t columnNumber = atoi (std::string (columnNumberStart, line.end ()).c_str ());
1435
-
1436
- nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}};
1437
- return ;
1438
1525
}
1526
+ str = std::string (vec.begin (), vec.end ());
1527
+ };
1528
+
1529
+ if (!findField (" sources" , strlen (" sources" )))
1530
+ throw MapParseException (" cannot find the sources field in map" );
1531
+ mustReadChar (' [' );
1532
+ if (!maybeReadChar (' ]' )) {
1533
+ do {
1534
+ std::string file;
1535
+ readString (file);
1536
+ Index index = wasm.debugInfoFileNames .size ();
1537
+ wasm.debugInfoFileNames .push_back (file);
1538
+ debugInfoFileIndices[file] = index;
1539
+ } while (maybeReadChar (' ,' ));
1540
+ mustReadChar (' ]' );
1541
+ }
1542
+
1543
+ if (!findField (" mappings" , strlen (" mappings" )))
1544
+ throw MapParseException (" cannot find the mappings field in map" );
1545
+ mustReadChar (' \" ' );
1546
+ if (maybeReadChar (' \" ' )) { // empty mappings
1547
+ nextDebugLocation.first = 0 ;
1548
+ return ;
1549
+ }
1550
+ // read first debug location
1551
+ uint32_t position = readBase64VLQ (*binaryMap);
1552
+ uint32_t fileIndex = readBase64VLQ (*binaryMap);
1553
+ uint32_t lineNumber = readBase64VLQ (*binaryMap) + 1 ; // adjust zero-based line number
1554
+ uint32_t columnNumber = readBase64VLQ (*binaryMap);
1555
+ nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}};
1556
+ }
1557
+
1558
+ void WasmBinaryBuilder::readNextDebugLocation () {
1559
+ if (!binaryMap) {
1560
+ return ;
1561
+ }
1562
+ char ch;
1563
+ *binaryMap >> ch;
1564
+ if (ch == ' \" ' ) { // end of records
1439
1565
nextDebugLocation.first = 0 ;
1566
+ return ;
1440
1567
}
1568
+ if (ch != ' ,' )
1569
+ throw MapParseException (" Unexpected delimiter" );
1570
+
1571
+ int32_t positionDelta = readBase64VLQ (*binaryMap);
1572
+ uint32_t position = nextDebugLocation.first + positionDelta;
1573
+ int32_t fileIndexDelta = readBase64VLQ (*binaryMap);
1574
+ uint32_t fileIndex = nextDebugLocation.second .fileIndex + fileIndexDelta;
1575
+ int32_t lineNumberDelta = readBase64VLQ (*binaryMap);
1576
+ uint32_t lineNumber = nextDebugLocation.second .lineNumber + lineNumberDelta;
1577
+ int32_t columnNumberDelta = readBase64VLQ (*binaryMap);
1578
+ uint32_t columnNumber = nextDebugLocation.second .columnNumber + columnNumberDelta;
1579
+
1580
+ nextDebugLocation = {position, {fileIndex, lineNumber, columnNumber}};
1441
1581
}
1582
+
1442
1583
Expression* WasmBinaryBuilder::readExpression () {
1443
1584
assert (depth == 0 );
1444
1585
processExpressions ();
0 commit comments