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 enum Language {
15     C,
16     Cpp,
17 }
18 
19 
20 /**
21    Context for the current translation, to avoid global variables
22  */
23 struct Context {
24 
25     import dpp.runtime.options: Options;
26     import clang: Cursor, Type, AccessSpecifier;
27 
28     alias SeenCursors = bool[CursorId];
29 
30     /**
31        The lines of output so far. This is needed in order to fix
32        any name collisions between functions or variables with aggregates
33        such as structs, unions and enums.
34      */
35     private string[] lines;
36 
37     /**
38        Structs can be anonymous in C, and it's even common
39        to typedef them to a name. We come up with new names
40        that we track here so as to be able to properly translate
41        those typedefs.
42     */
43     private string[Cursor.Hash] _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.  We remember all
64        the ones we saw here so that if there's a name clash we can
65        come back and fix the declarations after the fact with
66        pragma(mangle).
67      */
68     private Linkable[string] _linkableDeclarations;
69 
70     /**
71        All the function-like macros that have been declared
72      */
73     private bool[string] _functionMacroDeclarations;
74 
75     /**
76        Remember all the macros already defined
77      */
78     private bool[string] _macros;
79 
80     /**
81        All previously seen cursors
82      */
83     private SeenCursors _seenCursors;
84 
85     AccessSpecifier accessSpecifier = AccessSpecifier.Public;
86 
87     /// Command-line options
88     Options options;
89 
90     /*
91       Remember all declared types so that C-style casts can be recognised
92      */
93     private string[] _types = [
94         `void ?\*`,
95         `char`, `unsigned char`, `signed char`, `short`, `unsigned short`,
96         `int`, `unsigned`, `unsigned int`, `long`, `unsigned long`, `long long`,
97         `unsigned long long`, `float`, `double`, `long double`,
98     ];
99 
100     /// to generate unique names
101     private int _anonymousIndex;
102 
103     private string[] _namespaces;
104 
105     Language language;
106 
107     this(Options options, in Language language) @safe pure {
108         this.options = options;
109         this.language = language;
110     }
111 
112     ref Context indent() @safe pure return {
113         options = options.indent;
114         return this;
115     }
116 
117     string indentation() @safe @nogc pure const {
118         return options.indentation;
119     }
120 
121     void setIndentation(in string indentation) @safe pure {
122         options.indentation = indentation;
123     }
124 
125     void log(A...)(auto ref A args) const {
126         import std.functional: forward;
127         options.log(forward!args);
128     }
129 
130     void indentLog(A...)(auto ref A args) const {
131         import std.functional: forward;
132         options.indent.log(forward!args);
133     }
134 
135     bool debugOutput() @safe @nogc pure nothrow const {
136         return options.debugOutput;
137     }
138 
139     bool hasSeen(in Cursor cursor) @safe pure nothrow const {
140         return cast(bool)(CursorId(cursor) in _seenCursors);
141     }
142 
143     void rememberCursor(in Cursor cursor) @safe pure nothrow {
144         // EnumDecl can have no spelling but end up defining an enum anyway
145         // See "it.compile.projects.double enum typedef"
146         if(cursor.spelling != "" || cursor.kind == Cursor.Kind.EnumDecl)
147             _seenCursors[CursorId(cursor)] = true;
148     }
149 
150     string translation() @safe pure nothrow const {
151         import std.array: join;
152         return lines.join("\n");
153     }
154 
155     void writeln(in string line) @safe pure nothrow {
156         lines ~= line.dup;
157     }
158 
159     void writeln(in string[] lines) @safe pure nothrow {
160         this.lines ~= lines;
161     }
162 
163     // remember a function or variable declaration
164     string rememberLinkable(in Cursor cursor) @safe pure nothrow {
165         import dpp.translation.dlang: maybeRename;
166 
167         const spelling = maybeRename(cursor, this);
168         // since linkables produce one-line translations, the next
169         // will be the linkable
170         _linkableDeclarations[spelling] = Linkable(lines.length, cursor.mangling);
171 
172         return spelling;
173     }
174 
175     void fixNames() @safe pure {
176         declareUnknownStructs;
177         fixLinkables;
178         fixFields;
179     }
180 
181     void fixLinkables() @safe pure {
182         foreach(declarations; [_aggregateDeclarations, _functionMacroDeclarations]) {
183             foreach(name, _; declarations) {
184                 // if there's a name clash, fix it
185                 auto clashingLinkable = name in _linkableDeclarations;
186                 if(clashingLinkable) {
187                     resolveClash(lines[clashingLinkable.lineNumber], name, clashingLinkable.mangling);
188                 }
189             }
190         }
191     }
192 
193     void fixFields() @safe pure {
194 
195         import dpp.translation.dlang: pragmaMangle, rename;
196         import std..string: replace;
197 
198         foreach(spelling, lineNumber; _fieldDeclarations) {
199             if(spelling in _aggregateDeclarations) {
200                 lines[lineNumber] = lines[lineNumber]
201                     .replace(spelling ~ `;`, rename(spelling, this) ~ `;`);
202             }
203         }
204     }
205 
206     /**
207        Tells the context to remember a struct type encountered in an aggregate field.
208        Typically this will be a pointer to a structure but it could also be the return
209        type or parameter types of a function pointer field. This is (surprisingly!)
210        perfectly valid C code, even though `Foo` is never declared anywhere:
211        ----------------------
212        struct Foo* fun(void);
213        ----------------------
214        See issues #22 and #24
215      */
216     void rememberFieldStruct(in string typeSpelling) @safe pure {
217         _fieldStructSpellings[typeSpelling] = true;
218     }
219 
220     /**
221        In C it's possible for a struct field name to have the same name as a struct
222        because of elaborated names. We remember them here in case we need to fix them.
223      */
224     void rememberField(in string spelling) @safe pure {
225         _fieldDeclarations[spelling] = lines.length;
226     }
227 
228     /**
229        Remember this aggregate cursor
230      */
231     void rememberAggregate(in Cursor cursor) @safe pure {
232         const spelling = spellingOrNickname(cursor);
233         _aggregateDeclarations[spelling] = true;
234         rememberType(spelling);
235     }
236 
237     /**
238        If unknown structs show up in functions or fields (as a pointer),
239         define them now so the D file can compile
240         See `it.c.compile.delayed`.
241     */
242     void declareUnknownStructs() @safe pure {
243         foreach(name, _; _fieldStructSpellings) {
244             if(name !in _aggregateDeclarations) {
245                 log("Could not find '", name, "' in aggregate declarations, defining it");
246                 writeln("struct " ~ name ~ ";");
247                 _aggregateDeclarations[name] = true;
248             }
249         }
250     }
251 
252     const(typeof(_aggregateDeclarations)) aggregateDeclarations() @safe pure nothrow const {
253         return _aggregateDeclarations;
254     }
255 
256     /// return the spelling if it exists, or our made-up nickname for it if not
257     string spellingOrNickname(in Cursor cursor) @safe pure {
258         import dpp.translation.dlang: rename, isKeyword;
259         if(cursor.spelling == "") return nickName(cursor);
260         return cursor.spelling.isKeyword ? rename(cursor.spelling, this) : cursor.spelling;
261     }
262 
263     private string nickName(in Cursor cursor) @safe pure {
264         if(cursor.hash !in _nickNames) {
265             auto nick = newAnonymousTypeName;
266             _nickNames[cursor.hash] = nick;
267         }
268 
269         return _nickNames[cursor.hash];
270     }
271 
272     private string newAnonymousTypeName() @safe pure {
273         import std.conv: text;
274         return text("_Anonymous_", _anonymousIndex++);
275     }
276 
277     string newAnonymousMemberName() @safe pure {
278         import std..string: replace;
279         return newAnonymousTypeName.replace("_A", "_a");
280     }
281 
282     private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const {
283         import dpp.translation.dlang: pragmaMangle;
284         line = `    ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling);
285     }
286 
287     private string replaceSpelling(in string line, in string spelling) @safe pure const {
288         import dpp.translation.dlang: rename;
289         import std.array: replace;
290         return line
291             .replace(spelling ~ `;`, rename(spelling, this) ~ `;`)
292             .replace(spelling ~ `(`, rename(spelling, this) ~ `(`)
293             ;
294     }
295 
296     void rememberType(in string type) @safe pure nothrow {
297         _types ~= type;
298     }
299 
300     /// Matches a C-type cast
301     auto castRegex() @safe const {
302         import std.array: join, array;
303         import std.regex: regex;
304         import std.algorithm: map;
305         import std.range: chain;
306 
307         // const and non const versions of each type
308         const typesConstOpt = _types.map!(a => `(?:const )?` ~ a).array;
309 
310         const typeSelectionStr =
311             chain(typesConstOpt,
312                   // pointers thereof
313                   typesConstOpt.map!(a => a ~ ` ?\*`))
314             .join("|");
315 
316         // parens and a type inside, where "a type" is any we know about
317         const regexStr = `\(( *?(?:` ~ typeSelectionStr ~ `) *?)\)`;
318 
319         return regex(regexStr);
320     }
321 
322     void rememberMacro(in Cursor cursor) @safe pure {
323         _macros[cursor.spelling] = true;
324         if(cursor.isMacroFunction)
325             _functionMacroDeclarations[cursor.spelling] = true;
326     }
327 
328     bool macroAlreadyDefined(in Cursor cursor) @safe pure const {
329         return cast(bool) (cursor.spelling in _macros);
330     }
331 
332     void pushNamespace(in string ns) @safe pure nothrow {
333         _namespaces ~= ns;
334     }
335 
336     void popNamespace(in string ns) @safe pure nothrow {
337         _namespaces = _namespaces[0 .. $-1];
338     }
339 
340     // returns the current namespace so it can be deleted
341     // from translated names
342     string namespace() @safe pure nothrow const {
343         import std.array: join;
344         return _namespaces.join("::");
345     }
346 
347     /// If this cursor is from one of the ignored namespaces
348     bool isFromIgnoredNs(in Type type) @safe const {
349         import std.algorithm: canFind, any;
350         return options.ignoredNamespaces.any!(a => type.spelling.canFind(a ~ "::"));
351     }
352 }
353 
354 
355 // to identify a cursor
356 private struct CursorId {
357     import clang: Cursor, Type;
358 
359     string cursorSpelling;
360     Cursor.Kind cursorKind;
361     string typeSpelling;
362     Type.Kind typeKind;
363 
364     this(in Cursor cursor) @safe pure nothrow {
365         cursorSpelling = cursor.spelling;
366         cursorKind = cursor.kind;
367         typeSpelling = cursor.type.spelling;
368         typeKind = cursor.type.kind;
369     }
370 }