@@ -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,134 @@ 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) {
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 ;
14221509 }
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);
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+ 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
14391565 nextDebugLocation.first = 0 ;
1566+ return ;
14401567 }
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}};
14411581}
1582+
14421583Expression* WasmBinaryBuilder::readExpression () {
14431584 assert (depth == 0 );
14441585 processExpressions ();
0 commit comments