@@ -33,6 +33,9 @@ void WasmBinaryWriter::prepare() {
3333
3434void WasmBinaryWriter::write () {
3535 writeHeader ();
36+ if (binaryMap) {
37+ writeBinaryMapProlog ();
38+ }
3639
3740 writeTypes ();
3841 writeImports ();
@@ -49,6 +52,9 @@ void WasmBinaryWriter::write() {
4952 if (binaryMap) writeSourceMapUrl ();
5053 if (symbolMap.size () > 0 ) writeSymbolMap ();
5154
55+ if (binaryMap) {
56+ writeBinaryMapEpilog ();
57+ }
5258 finishUp ();
5359}
5460
@@ -238,7 +244,6 @@ void WasmBinaryWriter::writeFunctions() {
238244 size_t start = o.size ();
239245 Function* function = wasm->functions [i].get ();
240246 currFunction = function;
241- lastDebugLocation = {0 , 0 , 0 };
242247 mappedLocals.clear ();
243248 numLocalsByType.clear ();
244249 if (debug) std::cerr << " writing" << function->name << std::endl;
@@ -445,6 +450,50 @@ void WasmBinaryWriter::writeSymbolMap() {
445450 file.close ();
446451}
447452
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+
448497void WasmBinaryWriter::writeInlineString (const char * name) {
449498 int32_t size = strlen (name);
450499 o << U32LEB (size);
@@ -949,6 +998,7 @@ static Name RETURN_BREAK("binaryen|break-to-return");
949998void WasmBinaryBuilder::read () {
950999
9511000 readHeader ();
1001+ readBinaryMapHeader ();
9521002
9531003 // read sections until the end
9541004 while (more ()) {
@@ -1402,43 +1452,135 @@ void WasmBinaryBuilder::readExports() {
14021452 }
14031453}
14041454
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) return ;
1481+
1482+ auto maybeReadChar = [&](char expected) {
1483+ if (binaryMap->peek () != expected) return false ;
1484+ binaryMap->get ();
1485+ return true ;
1486+ };
1487+ auto mustReadChar = [&](char expected) {
1488+ if (binaryMap->get () != expected) {
1489+ throw MapParseException (" Unexpected char" );
1490+ }
1491+ };
1492+ auto findField = [&](const char * name, size_t len) {
1493+ bool matching = false ;
1494+ size_t pos;
1495+ while (1 ) {
1496+ int ch = binaryMap->get ();
1497+ if (ch == EOF) return false ;
1498+ if (ch == ' \" ' ) {
1499+ matching = true ;
1500+ pos = 0 ;
1501+ } else if (matching && name[pos] == ch) {
1502+ ++pos;
1503+ if (pos == len) {
1504+ if (maybeReadChar (' \" ' )) break ; // found field
1505+ }
1506+ } else {
1507+ matching = false ;
14221508 }
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 ;
1509+ }
1510+ mustReadChar (' :' );
1511+ return true ;
1512+ };
1513+ auto readString = [&](std::string& str) {
1514+ std::vector<char > vec;
1515+ mustReadChar (' \" ' );
1516+ if (!maybeReadChar (' \" ' )) {
1517+ while (1 ) {
1518+ int ch = binaryMap->get ();
1519+ if (ch == EOF) {
1520+ throw MapParseException (" unexpected EOF in the middle of string" );
1521+ }
1522+ if (ch == ' \" ' ) break ;
1523+ vec.push_back (ch);
14311524 }
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 ;
14381525 }
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+ }
1532+ mustReadChar (' [' );
1533+ if (!maybeReadChar (' ]' )) {
1534+ do {
1535+ std::string file;
1536+ readString (file);
1537+ Index index = wasm.debugInfoFileNames .size ();
1538+ wasm.debugInfoFileNames .push_back (file);
1539+ debugInfoFileIndices[file] = index;
1540+ } while (maybeReadChar (' ,' ));
1541+ mustReadChar (' ]' );
1542+ }
1543+
1544+ if (!findField (" mappings" , strlen (" mappings" ))) {
1545+ throw MapParseException (" cannot find the mappings field in map" );
1546+ }
1547+ mustReadChar (' \" ' );
1548+ if (maybeReadChar (' \" ' )) { // empty mappings
14391549 nextDebugLocation.first = 0 ;
1550+ return ;
14401551 }
1552+ // read first debug location
1553+ uint32_t position = readBase64VLQ (*binaryMap);
1554+ uint32_t fileIndex = readBase64VLQ (*binaryMap);
1555+ uint32_t lineNumber = readBase64VLQ (*binaryMap) + 1 ; // adjust zero-based line number
1556+ uint32_t columnNumber = readBase64VLQ (*binaryMap);
1557+ nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } };
14411558}
1559+
1560+ void WasmBinaryBuilder::readNextDebugLocation () {
1561+ if (!binaryMap) return ;
1562+
1563+ char ch;
1564+ *binaryMap >> ch;
1565+ if (ch == ' \" ' ) { // end of records
1566+ nextDebugLocation.first = 0 ;
1567+ return ;
1568+ }
1569+ if (ch != ' ,' ) {
1570+ throw MapParseException (" Unexpected delimiter" );
1571+ }
1572+ int32_t positionDelta = readBase64VLQ (*binaryMap);
1573+ uint32_t position = nextDebugLocation.first + positionDelta;
1574+ int32_t fileIndexDelta = readBase64VLQ (*binaryMap);
1575+ uint32_t fileIndex = nextDebugLocation.second .fileIndex + fileIndexDelta;
1576+ int32_t lineNumberDelta = readBase64VLQ (*binaryMap);
1577+ uint32_t lineNumber = nextDebugLocation.second .lineNumber + lineNumberDelta;
1578+ int32_t columnNumberDelta = readBase64VLQ (*binaryMap);
1579+ uint32_t columnNumber = nextDebugLocation.second .columnNumber + columnNumberDelta;
1580+
1581+ nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } };
1582+ }
1583+
14421584Expression* WasmBinaryBuilder::readExpression () {
14431585 assert (depth == 0 );
14441586 processExpressions ();
0 commit comments