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 }