Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/wasm.h
Original file line number Diff line number Diff line change
Expand Up @@ -2406,9 +2406,15 @@ class Module {
// Optional user section IR representation.
std::unique_ptr<DylinkSection> dylinkSection;

// Source maps debug info.
// Source maps debug info. All of these fields are read directly in from the
// source map and are encoded as in the original JSON (UTF-8 encoded with
// with escaped quotes and slashes). The string values are uninterpreted in
// Binaryen, and they are written directly back out without re-encoding.
std::vector<std::string> debugInfoFileNames;
std::vector<std::string> debugInfoSymbolNames;
std::string debugInfoSourceRoot;
std::string debugInfoFile;
std::vector<std::string> debugInfoSourcesContent;

// `features` are the features allowed to be used in this module and should be
// respected regardless of the value of`hasFeaturesSection`.
Expand Down
26 changes: 26 additions & 0 deletions src/wasm/source-map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ void SourceMapReader::parse(Module& wasm) {
wasm.debugInfoFileNames.push_back(v->getCString());
}

if (json.has("sourcesContent")) {
json::Ref sc = json["sourcesContent"];
if (!sc->isArray()) {
throw MapParseException("Source map sourcesContent is not an array");
}
for (size_t i = 0; i < sc->size(); i++) {
wasm.debugInfoSourcesContent.push_back(sc[i]->getCString());
}
}

if (json.has("names")) {
json::Ref n = json["names"];
if (!n->isArray()) {
Expand All @@ -73,6 +83,22 @@ void SourceMapReader::parse(Module& wasm) {
}
}

if (json.has("sourceRoot")) {
json::Ref sr = json["sourceRoot"];
if (!sr->isString()) {
throw MapParseException("Source map sourceRoot is not a string");
}
wasm.debugInfoSourceRoot = sr->getCString();
}

if (json.has("file")) {
json::Ref f = json["file"];
if (!f->isString()) {
throw MapParseException("Source map file is not a string");
}
wasm.debugInfoFile = f->getCString();
}

if (!json.has("mappings")) {
throw MapParseException("Source map mappings missing");
}
Expand Down
43 changes: 29 additions & 14 deletions src/wasm/wasm-binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1226,25 +1226,40 @@ void WasmBinaryWriter::writeSourceMapProlog() {
}
}

*sourceMap << "\"sources\":[";
for (size_t i = 0; i < wasm->debugInfoFileNames.size(); i++) {
if (i > 0) {
*sourceMap << ",";
auto writeOptionalString = [&](const char* name, const std::string& str) {
if (!str.empty()) {
*sourceMap << "\"" << name << "\":\"" << str << "\",";
}
// TODO respect JSON string encoding, e.g. quotes and control chars.
*sourceMap << "\"" << wasm->debugInfoFileNames[i] << "\"";
}
*sourceMap << "],\"names\":[";
};

for (size_t i = 0; i < wasm->debugInfoSymbolNames.size(); i++) {
if (i > 0) {
*sourceMap << ",";
writeOptionalString("file", wasm->debugInfoFile);
writeOptionalString("sourceRoot", wasm->debugInfoSourceRoot);

auto writeStringVector = [&](const char* name,
const std::vector<std::string>& vec) {
*sourceMap << "\"" << name << "\":[";
for (size_t i = 0; i < vec.size(); i++) {
if (i > 0) {
*sourceMap << ",";
}
*sourceMap << "\"" << vec[i] << "\"";
}
// TODO respect JSON string encoding, e.g. quotes and control chars.
*sourceMap << "\"" << wasm->debugInfoSymbolNames[i] << "\"";
*sourceMap << "],";
};

writeStringVector("sources", wasm->debugInfoFileNames);

if (!wasm->debugInfoSourcesContent.empty()) {
writeStringVector("sourcesContent", wasm->debugInfoSourcesContent);
}

*sourceMap << "],\"mappings\":\"";
// TODO: This field is optional; maybe we should omit if it's empty.
// TODO: Binaryen actually does not correctly preserve symbol names when it
// rewrites the mappings. We should maybe just drop them, or else handle
// them correctly.
writeStringVector("names", wasm->debugInfoSymbolNames);

*sourceMap << "\"mappings\":\"";
}

static void writeBase64VLQ(std::ostream& out, int32_t n) {
Expand Down
35 changes: 35 additions & 0 deletions test/gtest/source-map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,38 @@ TEST_F(SourceMapTest, Fibonacci) {
// program?
ExpectDbgLocEq(9999, 0, 8, 0, std::nullopt);
}

TEST_F(SourceMapTest, SourceMapSourceRootFile) {
std::string sourceMap = R"(
{
"version":3,
"file": "foo.wasm",
"sources":[],
"names":[],
"mappings": "",
"sourceRoot": "/foo/bar"
}
)";
parseMap(sourceMap);
EXPECT_EQ(wasm.debugInfoSourceRoot, "/foo/bar");
EXPECT_EQ(wasm.debugInfoFile, "foo.wasm");
}

TEST_F(SourceMapTest, SourcesContent) {
// The backslash escapes appear in the JSON encoding, and are preserved in
// the internal representation. The string values are uninterpreted in
// Binaryen, and they are written directly back out without re-encoding.
std::string sourceMap = R"(
{
"version": 3,
"sources": ["foo.c"],
"sourcesContent": ["#include <stdio.h> int main()\n{ printf(\"Gr\u00fc\u00df Gott, Welt!\"); return 0;}"],
"mappings" : ""
}
)";
parseMap(sourceMap);
ASSERT_EQ(wasm.debugInfoSourcesContent.size(), 1);
EXPECT_EQ(wasm.debugInfoSourcesContent[0],
"#include <stdio.h> int main()\\n{ printf(\\\"Gr\\u00fc\\u00df "
"Gott, Welt!\\\"); return 0;}");
}
14 changes: 14 additions & 0 deletions test/lit/sourcemap-sourceroot-file.wat
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
;; RUN: wasm-opt %s.wasm -ism %s.map -osm %t -o %t2
;; Running multiple times is needed here because the output is all on one line.
;; RUN: cat %t | filecheck %s --check-prefix=FILE
;; RUN: cat %t | filecheck %s --check-prefix=SOURCEROOT
;; RUN: cat %t | filecheck %s --check-prefix=CONTENT


;; This wat file is not actually part of the test (the binary file is used),
;; but no comments are allowed in JSON so the RUN and CHECK lines are here.

;; FILE: "file":"foo.wasm",
;; SOURCEROOT: "sourceRoot":"/foo/bar",
;; CONTENT: "sourcesContent":["#include <stdio.h> int main()\n{ printf(\"Gr\u00fc\u00df Gott, Welt!\"); return 0;}"]
(module)
9 changes: 9 additions & 0 deletions test/lit/sourcemap-sourceroot-file.wat.map
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"version":3,
"file": "foo.wasm",
"sources":[],
"names":[],
"mappings": "",
"sourceRoot": "/foo/bar",
"sourcesContent": ["#include <stdio.h> int main()\n{ printf(\"Gr\u00fc\u00df Gott, Welt!\"); return 0;}"]
}
Binary file added test/lit/sourcemap-sourceroot-file.wat.wasm
Binary file not shown.