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 }