1 /**
2    The context the translation happens in, to avoid global variables
3  */
4 module dpp.runtime.context;
5 
6 alias LineNumber = size_t;
7 
8 // A function or global variable
9 struct Linkable {
10     LineNumber lineNumber;
11     string mangling;
12 }
13 
14 
15 /**
16    Context for the current translation, to avoid global variables
17  */
18 struct Context {
19 
20     import dpp.runtime.options: Options;
21     import clang: Cursor;
22 
23     alias CursorHash = uint;
24     alias SeenCursors = bool[CursorId];
25 
26     /**
27        The lines of output so far. This is needed in order to fix
28        any name collisions between functions or variables with aggregates
29        such as structs, unions and enums.
30      */
31     private string[] lines;
32 
33     /**
34        Structs can be anonymous in C, and it's even common
35        to typedef them to a name. We come up with new names
36        that we track here so as to be able to properly transate
37        those typedefs.
38     */
39     private string[CursorHash] cursorNickNames;
40 
41     // FIXME - there must be a better way
42     /// Used to find the last nickname we coined (e.g. "_Anonymous_1")
43     private string[] nickNames;
44 
45     /**
46        Remembers the seen struct pointers so that if any are undeclared in C,
47        we do so in D at the end.
48      */
49     private bool[string] fieldStructSpellings;
50 
51     /**
52        Remembers the field spellings in aggregates in case we need to change any
53        of them.
54      */
55     private LineNumber[string] fieldDeclarations;
56 
57     /**
58        All the aggregates that have been declared
59      */
60     private bool[string] _aggregateDeclarations;
61 
62     /**
63        A linkable is a function or a global variable.
64        We remember all the ones we saw here so that if there's a name clash
65        with an aggregate we can come back and fix the declarations after
66        the fact with  pragma(mangle).
67      */
68     private Linkable[string] linkableDeclarations;
69 
70     /**
71        All previously seen cursors
72      */
73     private SeenCursors seenCursors;
74 
75     /// Command-line options
76     Options options;
77 
78     /// to generate unique names
79     private int anonymousIndex;
80 
81     this(Options options) @safe pure {
82         this.options = options;
83     }
84 
85     ref Context indent() @safe pure return {
86         options = options.indent;
87         return this;
88     }
89 
90     string indentation() @safe @nogc pure const {
91         return options.indentation;
92     }
93 
94     void setIndentation(in string indentation) @safe pure {
95         options.indentation = indentation;
96     }
97 
98     void log(A...)(auto ref A args) const {
99         import std.functional: forward;
100         options.log(forward!args);
101     }
102 
103     void indentLog(A...)(auto ref A args) const {
104         import std.functional: forward;
105         options.indent.log(forward!args);
106     }
107 
108     bool debugOutput() @safe @nogc pure nothrow const {
109         return options.debugOutput;
110     }
111 
112     bool hasSeen(in Cursor cursor) @safe pure nothrow const {
113         return cast(bool)(CursorId(cursor) in seenCursors);
114     }
115 
116     void rememberCursor(in Cursor cursor) @safe pure nothrow {
117         // EnumDecl can have no spelling but end up defining an enum anyway
118         // See "it.compile.projects.double enum typedef"
119         if(cursor.spelling != "" || cursor.kind == Cursor.Kind.EnumDecl)
120             seenCursors[CursorId(cursor)] = true;
121     }
122 
123     string translation() @safe pure nothrow const {
124         import std.array: join;
125         return lines.join("\n");
126     }
127 
128     void writeln(in string line) @safe pure nothrow {
129         lines ~= line.dup;
130     }
131 
132     void writeln(in string[] lines) @safe pure nothrow {
133         this.lines ~= lines;
134     }
135 
136     // remember a function or variable declaration
137     string rememberLinkable(in Cursor cursor) @safe pure nothrow {
138         import dpp.cursor.dlang: maybeRename;
139         const spelling = maybeRename(cursor, this);
140         // since linkables produce one-line translations, the next
141         // will be the linkable
142         linkableDeclarations[spelling] = Linkable(lines.length, cursor.mangling);
143         return spelling;
144     }
145 
146     void fixNames() @safe pure {
147         declareUnknownStructs;
148         fixLinkables;
149         fixFields;
150     }
151 
152     void fixLinkables() @safe pure {
153         foreach(aggregate, _; _aggregateDeclarations) {
154             // if there's a name clash, fix it
155             auto clashingLinkable = aggregate in linkableDeclarations;
156             if(clashingLinkable) {
157                 resolveClash(lines[clashingLinkable.lineNumber], aggregate, clashingLinkable.mangling);
158             }
159         }
160     }
161 
162     void fixFields() @safe pure {
163 
164         import dpp.cursor.dlang: pragmaMangle, rename;
165         import std..string: replace;
166 
167         foreach(spelling, lineNumber; fieldDeclarations) {
168             if(spelling in _aggregateDeclarations) {
169                 lines[lineNumber] = lines[lineNumber]
170                     .replace(spelling ~ `;`, rename(spelling, this) ~ `;`);
171             }
172         }
173     }
174 
175     /**
176        Tells the context to remember a struct type encountered in an aggregate field.
177        Typically this will be a pointer to a structure but it could also be the return
178        type or parameter types of a function pointer field.
179      */
180     void rememberFieldStruct(in string typeSpelling) @safe pure {
181         fieldStructSpellings[typeSpelling] = true;
182     }
183 
184     /**
185        In C it's possible for a struct field name to have the same name as a struct
186        because of elaborated names. We remember them here in case we need to fix them.
187      */
188     void rememberField(in string spelling) @safe pure {
189         fieldDeclarations[spelling] = lines.length;
190     }
191 
192     /**
193        Remember this aggregate cursor
194      */
195     void rememberAggregate(in Cursor cursor) @safe pure {
196         _aggregateDeclarations[spellingOrNickname(cursor)] = true;
197     }
198 
199     // find the last one we named, pop it off, and return it
200     string popLastNickName() @safe pure {
201 
202         if(nickNames.length == 0) throw new Exception("No nickname to pop");
203 
204         auto ret = nickNames[$-1];
205         nickNames = nickNames[0 .. $-1];
206         return ret;
207     }
208 
209     /**
210        If unknown structs show up in functions or fields (as a pointer),
211         define them now so the D file can compile
212         See `it.c.compile.delayed`.
213     */
214     void declareUnknownStructs() @safe pure {
215         foreach(name, _; fieldStructSpellings) {
216             if(name !in _aggregateDeclarations) {
217                 log("Could not find '", name, "' in aggregate declarations, defining it");
218                 writeln("struct " ~ name ~ ";");
219                 _aggregateDeclarations[name] = true;
220             }
221         }
222     }
223 
224     const(typeof(_aggregateDeclarations)) aggregateDeclarations() @safe pure nothrow const {
225         return _aggregateDeclarations;
226     }
227 
228     /// return the spelling if it exists, or our made-up nickname for it if not
229     string spellingOrNickname(in Cursor cursor) @safe pure {
230         return cursor.spelling == ""
231             ? nickName(cursor)
232             : cursor.spelling;
233     }
234 
235     private string nickName(in Cursor cursor) @safe pure {
236         if(cursor.hash !in cursorNickNames) {
237             auto nick = newAnonymousName;
238             nickNames ~= nick;
239             cursorNickNames[cursor.hash] = nick;
240         }
241 
242         return cursorNickNames[cursor.hash];
243     }
244 
245     private string newAnonymousName() @safe pure {
246         import std.conv: text;
247         import core.atomic: atomicOp;
248         return text("_Anonymous_", anonymousIndex++);
249     }
250 
251     private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const {
252         import dpp.cursor.dlang: pragmaMangle;
253         line = `    ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling);
254     }
255 
256     private string replaceSpelling(in string line, in string spelling) @safe pure const {
257         import dpp.cursor.dlang: rename;
258         import std.array: replace;
259         return line
260             .replace(spelling ~ `;`, rename(spelling, this) ~ `;`)
261             .replace(spelling ~ `(`, rename(spelling, this) ~ `(`)
262             ;
263     }
264 
265 }
266 
267 
268 // to identify a cursor
269 private struct CursorId {
270     import clang: Cursor, Type;
271 
272     string cursorSpelling;
273     Cursor.Kind cursorKind;
274     string typeSpelling;
275     Type.Kind typeKind;
276 
277     this(in Cursor cursor) @safe pure nothrow {
278         cursorSpelling = cursor.spelling;
279         cursorKind = cursor.kind;
280         typeSpelling = cursor.type.spelling;
281         typeKind = cursor.type.kind;
282     }
283 }