summaryrefslogtreecommitdiff
path: root/src/wasm/wasm-binary.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm/wasm-binary.cpp')
-rw-r--r--src/wasm/wasm-binary.cpp206
1 files changed, 206 insertions, 0 deletions
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index 65c6f2a21..c4bc66f76 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -33,6 +33,9 @@ void WasmBinaryWriter::prepare() {
void WasmBinaryWriter::write() {
writeHeader();
+ if (sourceMap) {
+ writeSourceMapProlog();
+ }
writeTypes();
writeImports();
@@ -46,8 +49,12 @@ void WasmBinaryWriter::write() {
writeFunctions();
writeDataSegments();
if (debugInfo) writeNames();
+ if (sourceMap) writeSourceMapUrl();
if (symbolMap.size() > 0) writeSymbolMap();
+ if (sourceMap) {
+ writeSourceMapEpilog();
+ }
finishUp();
}
@@ -236,6 +243,7 @@ void WasmBinaryWriter::writeFunctions() {
size_t sizePos = writeU32LEBPlaceholder();
size_t start = o.size();
Function* function = wasm->functions[i].get();
+ currFunction = function;
mappedLocals.clear();
numLocalsByType.clear();
if (debug) std::cerr << "writing" << function->name << std::endl;
@@ -258,6 +266,7 @@ void WasmBinaryWriter::writeFunctions() {
if (debug) std::cerr << "body size: " << size << ", writing at " << sizePos << ", next starts at " << o.size() << std::endl;
o.writeAt(sizePos, U32LEB(size));
}
+ currFunction = nullptr;
finishSection(start);
}
@@ -420,6 +429,14 @@ void WasmBinaryWriter::writeNames() {
finishSection(start);
}
+void WasmBinaryWriter::writeSourceMapUrl() {
+ if (debug) std::cerr << "== writeSourceMapUrl" << std::endl;
+ auto start = startSection(BinaryConsts::Section::User);
+ writeInlineString(BinaryConsts::UserSections::SourceMapUrl);
+ writeInlineString(sourceMapUrl.c_str());
+ finishSection(start);
+}
+
void WasmBinaryWriter::writeSymbolMap() {
std::ofstream file(symbolMap);
for (auto& import : wasm->imports) {
@@ -433,6 +450,50 @@ void WasmBinaryWriter::writeSymbolMap() {
file.close();
}
+void WasmBinaryWriter::writeSourceMapProlog() {
+ lastDebugLocation = { 0, /* lineNumber = */ 1, 0 };
+ lastBytecodeOffset = 0;
+ *sourceMap << "{\"version\":3,\"sources\":[";
+ for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) {
+ if (i > 0) *sourceMap << ",";
+ // TODO respect JSON string encoding, e.g. quotes and control chars.
+ *sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\"";
+ }
+ *sourceMap << "],\"names\":[],\"mappings\":\"";
+}
+
+void WasmBinaryWriter::writeSourceMapEpilog() {
+ *sourceMap << "\"}";
+}
+
+static void writeBase64VLQ(std::ostream& out, int32_t n) {
+ uint32_t value = n >= 0 ? n << 1 : ((-n) << 1) | 1;
+ while (1) {
+ uint32_t digit = value & 0x1F;
+ value >>= 5;
+ if (!value) {
+ // last VLQ digit -- base64 codes 'A'..'Z', 'a'..'f'
+ out << char(digit < 26 ? 'A' + digit : 'a' + digit - 26);
+ break;
+ }
+ // more VLG digit will follow -- add continuation bit (0x20),
+ // base64 codes 'g'..'z', '0'..'9', '+', '/'
+ out << char(digit < 20 ? 'g' + digit : digit < 30 ? '0' + digit - 20 : digit == 30 ? '+' : '/');
+ }
+}
+
+void WasmBinaryWriter::writeDebugLocation(size_t offset, const Function::DebugLocation& loc) {
+ if (lastBytecodeOffset > 0) {
+ *sourceMap << ",";
+ }
+ writeBase64VLQ(*sourceMap, int32_t(offset - lastBytecodeOffset));
+ writeBase64VLQ(*sourceMap, int32_t(loc.fileIndex - lastDebugLocation.fileIndex));
+ writeBase64VLQ(*sourceMap, int32_t(loc.lineNumber - lastDebugLocation.lineNumber));
+ writeBase64VLQ(*sourceMap, int32_t(loc.columnNumber - lastDebugLocation.columnNumber));
+ lastDebugLocation = loc;
+ lastBytecodeOffset = offset;
+}
+
void WasmBinaryWriter::writeInlineString(const char* name) {
int32_t size = strlen(name);
o << U32LEB(size);
@@ -937,6 +998,7 @@ static Name RETURN_BREAK("binaryen|break-to-return");
void WasmBinaryBuilder::read() {
readHeader();
+ readSourceMapHeader();
// read sections until the end
while (more()) {
@@ -1342,6 +1404,7 @@ void WasmBinaryBuilder::readFunctions() {
// process the function body
if (debug) std::cerr << "processing function: " << i << std::endl;
nextLabel = 0;
+ useDebugLocation = false;
breaksToReturn = false;
// process body
assert(breakStack.empty());
@@ -1389,6 +1452,136 @@ void WasmBinaryBuilder::readExports() {
}
}
+static int32_t readBase64VLQ(std::istream& in) {
+ uint32_t value = 0;
+ uint32_t shift = 0;
+ while (1) {
+ char ch = in.get();
+ if (ch == EOF)
+ throw MapParseException("unexpected EOF in the middle of VLQ");
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch < 'g')) {
+ // last number digit
+ uint32_t digit = ch < 'a' ? ch - 'A' : ch - 'a' + 26;
+ value |= digit << shift;
+ break;
+ }
+ if (!(ch >= 'g' && ch <= 'z') && !(ch >= '0' && ch <= '9') &&
+ ch != '+' && ch != '/') {
+ throw MapParseException("invalid VLQ digit");
+ }
+ uint32_t digit = ch > '9' ? ch - 'g' : (ch >= '0' ? ch - '0' + 20 : (ch == '+' ? 30 : 31));
+ value |= digit << shift;
+ shift += 5;
+ }
+ return value & 1 ? -int32_t(value >> 1) : int32_t(value >> 1);
+}
+
+void WasmBinaryBuilder::readSourceMapHeader() {
+ if (!sourceMap) return;
+
+ auto maybeReadChar = [&](char expected) {
+ if (sourceMap->peek() != expected) return false;
+ sourceMap->get();
+ return true;
+ };
+ auto mustReadChar = [&](char expected) {
+ if (sourceMap->get() != expected) {
+ throw MapParseException("Unexpected char");
+ }
+ };
+ auto findField = [&](const char* name, size_t len) {
+ bool matching = false;
+ size_t pos;
+ while (1) {
+ int ch = sourceMap->get();
+ if (ch == EOF) return false;
+ if (ch == '\"') {
+ matching = true;
+ pos = 0;
+ } else if (matching && name[pos] == ch) {
+ ++pos;
+ if (pos == len) {
+ if (maybeReadChar('\"')) break; // found field
+ }
+ } else {
+ matching = false;
+ }
+ }
+ mustReadChar(':');
+ return true;
+ };
+ auto readString = [&](std::string& str) {
+ std::vector<char> vec;
+ mustReadChar('\"');
+ if (!maybeReadChar('\"')) {
+ while (1) {
+ int ch = sourceMap->get();
+ if (ch == EOF) {
+ throw MapParseException("unexpected EOF in the middle of string");
+ }
+ if (ch == '\"') break;
+ vec.push_back(ch);
+ }
+ }
+ str = std::string(vec.begin(), vec.end());
+ };
+
+ if (!findField("sources", strlen("sources"))) {
+ throw MapParseException("cannot find the sources field in map");
+ }
+ mustReadChar('[');
+ if (!maybeReadChar(']')) {
+ do {
+ std::string file;
+ readString(file);
+ Index index = wasm.debugInfoFileNames.size();
+ wasm.debugInfoFileNames.push_back(file);
+ debugInfoFileIndices[file] = index;
+ } while (maybeReadChar(','));
+ mustReadChar(']');
+ }
+
+ if (!findField("mappings", strlen("mappings"))) {
+ throw MapParseException("cannot find the mappings field in map");
+ }
+ mustReadChar('\"');
+ if (maybeReadChar('\"')) { // empty mappings
+ nextDebugLocation.first = 0;
+ return;
+ }
+ // read first debug location
+ uint32_t position = readBase64VLQ(*sourceMap);
+ uint32_t fileIndex = readBase64VLQ(*sourceMap);
+ uint32_t lineNumber = readBase64VLQ(*sourceMap) + 1; // adjust zero-based line number
+ uint32_t columnNumber = readBase64VLQ(*sourceMap);
+ nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } };
+}
+
+void WasmBinaryBuilder::readNextDebugLocation() {
+ if (!sourceMap) return;
+
+ char ch;
+ *sourceMap >> ch;
+ if (ch == '\"') { // end of records
+ nextDebugLocation.first = 0;
+ return;
+ }
+ if (ch != ',') {
+ throw MapParseException("Unexpected delimiter");
+ }
+
+ int32_t positionDelta = readBase64VLQ(*sourceMap);
+ uint32_t position = nextDebugLocation.first + positionDelta;
+ int32_t fileIndexDelta = readBase64VLQ(*sourceMap);
+ uint32_t fileIndex = nextDebugLocation.second.fileIndex + fileIndexDelta;
+ int32_t lineNumberDelta = readBase64VLQ(*sourceMap);
+ uint32_t lineNumber = nextDebugLocation.second.lineNumber + lineNumberDelta;
+ int32_t columnNumberDelta = readBase64VLQ(*sourceMap);
+ uint32_t columnNumber = nextDebugLocation.second.columnNumber + columnNumberDelta;
+
+ nextDebugLocation = { position, { fileIndex, lineNumber, columnNumber } };
+}
+
Expression* WasmBinaryBuilder::readExpression() {
assert(depth == 0);
processExpressions();
@@ -1627,6 +1820,16 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
throw ParseException("Reached function end without seeing End opcode");
}
if (debug) std::cerr << "zz recurse into " << ++depth << " at " << pos << std::endl;
+ if (nextDebugLocation.first) {
+ while (nextDebugLocation.first && nextDebugLocation.first <= pos) {
+ if (nextDebugLocation.first < pos) {
+ std::cerr << "skipping debug location info for " << nextDebugLocation.first << std::endl;
+ }
+ debugLocation = nextDebugLocation.second;
+ useDebugLocation = currFunction; // using only for function expressions
+ readNextDebugLocation();
+ }
+ }
uint8_t code = getInt8();
if (debug) std::cerr << "readExpression seeing " << (int)code << std::endl;
switch (code) {
@@ -1661,6 +1864,9 @@ BinaryConsts::ASTNodes WasmBinaryBuilder::readExpression(Expression*& curr) {
throw ParseException("bad node code " + std::to_string(code));
}
}
+ if (useDebugLocation && curr) {
+ currFunction->debugLocations[curr] = debugLocation;
+ }
if (debug) std::cerr << "zz recurse from " << depth-- << " at " << pos << std::endl;
return BinaryConsts::ASTNodes(code);
}