1 module sourcemaps.output; 2 import sourcemaps.vlq; 3 4 import std.array; 5 import std.json; 6 import std.path : buildNormalizedPath; 7 import std.algorithm : find, canFind; 8 import std.file : exists, readText; 9 import std.stdio : writeln, stderr; 10 import std.format; 11 import dwarf.debugline; 12 import dwarf.debuginfo; 13 14 auto getPath(string compDir, const ref LineProgram program, uint fileIndex) { 15 auto filepath = program.fileFromIndex(fileIndex); 16 if (program.dirIndex(fileIndex) == 0) 17 return compDir ~ "/" ~ filepath; 18 return filepath; 19 } 20 21 auto toSourceMap(DebugLine line, DebugInfo info, uint codeSectionOffset, bool embed, string embedBaseUrl, bool includeSources) { 22 uint[string] sourceMap; 23 Appender!(string[]) sources; 24 Appender!(string[]) contents; 25 Appender!(string[]) mappings; 26 27 struct State { 28 long address; 29 int sourceId; 30 int line; 31 int column; 32 State opBinary(string op : "-")(ref State rhs) { 33 return State(address-rhs.address, 34 sourceId-rhs.sourceId, 35 line-rhs.line, 36 column-rhs.column 37 ); 38 } 39 string toVlq() { 40 auto app = appender!string; 41 app.put(encodeVlq(address)); 42 app.put(encodeVlq(sourceId)); 43 app.put(encodeVlq(line)); 44 app.put(encodeVlq(column)); 45 return app.data; 46 } 47 } 48 49 State prevState = State(0,0,1,1), state; 50 foreach (idx, program; line.programs) { 51 string compDir = info.units[idx].getCompDir(); 52 foreach (address; program.addressInfo) { 53 if (address.line == 0) 54 continue; 55 state.line = address.line; 56 state.column = address.column; 57 state.address = address.address + codeSectionOffset; 58 auto filepath = compDir.getPath(program, address.fileIndex).buildNormalizedPath; 59 if (auto p = filepath in sourceMap) { 60 state.sourceId = (*p); 61 } else { 62 state.sourceId = cast(int)sources.data.length; 63 sourceMap[filepath] = state.sourceId; 64 sources.put(filepath); 65 if (includeSources) { 66 if (!exists(filepath)) { 67 if (canFind(filepath, ".d-mixin-")) { 68 stderr.writeln(format("Warning: ignoring file %s. Mixins aren't supported.", filepath)); 69 contents.put(format("Warning: ignoring file %s. Mixins aren't supported.", filepath)); 70 } else { 71 stderr.writeln(format("Error: Cannot find %s", filepath)); 72 contents.put(format("Error: Cannot find %s", filepath)); 73 } 74 } else 75 contents.put(readText(filepath)); 76 } 77 } 78 auto delta = state - prevState; 79 mappings.put(delta.toVlq); 80 prevState = state; 81 } 82 } 83 84 JSONValue[] names; 85 return JSONValue(["version": JSONValue(3), 86 "names": JSONValue(names), 87 "sourcesContent": JSONValue(contents.data), 88 "sources": JSONValue(sources.data), 89 "mappings": JSONValue(mappings.data.join(",")) 90 ]); 91 }