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 dpp.runtime.namespace: Namespace; 27 import clang: Cursor; 28 29 alias SeenCursors = bool[CursorId]; 30 31 /** 32 The lines of output so far. This is needed in order to fix 33 any name collisions between functions or variables with aggregates 34 such as structs, unions and enums. 35 */ 36 private string[] lines; 37 38 /** 39 Structs can be anonymous in C, and it's even common 40 to typedef them to a name. We come up with new names 41 that we track here so as to be able to properly translate 42 those typedefs. 43 */ 44 private string[Cursor.Hash] cursorNickNames; 45 46 // FIXME - there must be a better way 47 /// Used to find the last nickname we coined (e.g. "_Anonymous_1") 48 private string[] nickNames; 49 50 /** 51 Remembers the seen struct pointers so that if any are undeclared in C, 52 we do so in D at the end. 53 */ 54 private bool[string] fieldStructSpellings; 55 56 /** 57 Remembers the field spellings in aggregates in case we need to change any 58 of them. 59 */ 60 private LineNumber[string] fieldDeclarations; 61 62 /** 63 All the aggregates that have been declared 64 */ 65 private bool[string] _aggregateDeclarations; 66 67 /** 68 A linkable is a function or a global variable. We remember all 69 the ones we saw here so that if there's a name clash we can 70 come back and fix the declarations after the fact with 71 pragma(mangle). 72 */ 73 private Linkable[string] linkableDeclarations; 74 75 /** 76 All the function-like macros that have been declared 77 */ 78 private bool[string] functionMacroDeclarations; 79 80 /** 81 Remember all the macros already defined 82 */ 83 private bool[string] macros; 84 85 /** 86 All previously seen cursors 87 */ 88 private SeenCursors seenCursors; 89 90 /** 91 Deals with C++ namespaces 92 */ 93 private Namespace _namespace; 94 95 /// Command-line options 96 Options options; 97 98 /* 99 Remember all declared types so that C-style casts can be recognised 100 */ 101 private string[] _types = [ 102 `void ?\*`, 103 `char`, `unsigned char`, `signed char`, `short`, `unsigned short`, 104 `int`, `unsigned`, `unsigned int`, `long`, `unsigned long`, `long long`, 105 `unsigned long long`, `float`, `double`, `long double`, 106 ]; 107 108 /// to generate unique names 109 private int _anonymousIndex; 110 111 const(Language) language; 112 113 this(Options options, in Language language) @safe pure { 114 this.options = options; 115 this.language = language; 116 } 117 118 ref Context indent() @safe pure return { 119 options = options.indent; 120 return this; 121 } 122 123 string indentation() @safe @nogc pure const { 124 return options.indentation; 125 } 126 127 void setIndentation(in string indentation) @safe pure { 128 options.indentation = indentation; 129 } 130 131 void log(A...)(auto ref A args) const { 132 import std.functional: forward; 133 options.log(forward!args); 134 } 135 136 void indentLog(A...)(auto ref A args) const { 137 import std.functional: forward; 138 options.indent.log(forward!args); 139 } 140 141 bool debugOutput() @safe @nogc pure nothrow const { 142 return options.debugOutput; 143 } 144 145 bool hasSeen(in Cursor cursor) @safe pure nothrow const { 146 if(cursor.kind == Cursor.Kind.Namespace) return false; 147 return cast(bool)(CursorId(cursor) in seenCursors); 148 } 149 150 void rememberCursor(in Cursor cursor) @safe pure nothrow { 151 // EnumDecl can have no spelling but end up defining an enum anyway 152 // See "it.compile.projects.double enum typedef" 153 if(cursor.spelling != "" || cursor.kind == Cursor.Kind.EnumDecl) 154 seenCursors[CursorId(cursor)] = true; 155 } 156 157 string translation() @safe pure nothrow const { 158 import std.array: join; 159 return lines.join("\n"); 160 } 161 162 void writeln(in string line) @safe pure nothrow { 163 lines ~= line.dup; 164 } 165 166 void writeln(in string[] lines) @safe pure nothrow { 167 this.lines ~= lines; 168 } 169 170 // remember a function or variable declaration 171 string rememberLinkable(in Cursor cursor) @safe pure nothrow { 172 import dpp.translation.dlang: maybeRename; 173 const spelling = maybeRename(cursor, this); 174 // since linkables produce one-line translations, the next 175 // will be the linkable 176 linkableDeclarations[spelling] = Linkable(lines.length, cursor.mangling); 177 return spelling; 178 } 179 180 void fixNames() @safe pure { 181 declareUnknownStructs; 182 fixLinkables; 183 fixFields; 184 } 185 186 void fixLinkables() @safe pure { 187 foreach(declarations; [_aggregateDeclarations, functionMacroDeclarations]) { 188 foreach(name, _; declarations) { 189 // if there's a name clash, fix it 190 auto clashingLinkable = name in linkableDeclarations; 191 if(clashingLinkable) { 192 resolveClash(lines[clashingLinkable.lineNumber], name, clashingLinkable.mangling); 193 } 194 } 195 } 196 } 197 198 void fixFields() @safe pure { 199 200 import dpp.translation.dlang: pragmaMangle, rename; 201 import std..string: replace; 202 203 foreach(spelling, lineNumber; fieldDeclarations) { 204 if(spelling in _aggregateDeclarations) { 205 lines[lineNumber] = lines[lineNumber] 206 .replace(spelling ~ `;`, rename(spelling, this) ~ `;`); 207 } 208 } 209 } 210 211 /** 212 Tells the context to remember a struct type encountered in an aggregate field. 213 Typically this will be a pointer to a structure but it could also be the return 214 type or parameter types of a function pointer field. 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 cursorNickNames) { 265 auto nick = newAnonymousTypeName; 266 nickNames ~= nick; 267 cursorNickNames[cursor.hash] = nick; 268 } 269 270 return cursorNickNames[cursor.hash]; 271 } 272 273 private string newAnonymousTypeName() @safe pure { 274 import std.conv: text; 275 return text("_Anonymous_", _anonymousIndex++); 276 } 277 278 string newAnonymousMemberName() @safe pure { 279 import std..string: replace; 280 return newAnonymousTypeName.replace("_A", "_a"); 281 } 282 283 private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const { 284 import dpp.translation.dlang: pragmaMangle; 285 line = ` ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling); 286 } 287 288 private string replaceSpelling(in string line, in string spelling) @safe pure const { 289 import dpp.translation.dlang: rename; 290 import std.array: replace; 291 return line 292 .replace(spelling ~ `;`, rename(spelling, this) ~ `;`) 293 .replace(spelling ~ `(`, rename(spelling, this) ~ `(`) 294 ; 295 } 296 297 void rememberType(in string type) @safe pure nothrow { 298 _types ~= type; 299 } 300 301 /// Matches a C-type cast 302 auto castRegex() @safe const { 303 import std.array: join, array; 304 import std.regex: regex; 305 import std.algorithm: map; 306 import std.range: chain; 307 308 // const and non const versions of each type 309 const typesConstOpt = _types.map!(a => `(?:const )?` ~ a).array; 310 311 const typeSelectionStr = 312 chain(typesConstOpt, 313 // pointers thereof 314 typesConstOpt.map!(a => a ~ ` ?\*`)) 315 .join("|"); 316 317 // parens and a type inside, where "a type" is any we know about 318 const regexStr = `\(( *?(?:` ~ typeSelectionStr ~ `) *?)\)`; 319 320 return regex(regexStr); 321 } 322 323 void rememberMacro(in Cursor cursor) @safe pure { 324 macros[cursor.spelling] = true; 325 if(cursor.isMacroFunction) 326 functionMacroDeclarations[cursor.spelling] = true; 327 } 328 329 bool macroAlreadyDefined(in Cursor cursor) @safe pure const { 330 return cast(bool) (cursor.spelling in macros); 331 } 332 333 string[] pushNamespace(in string name) @safe pure { 334 return _namespace.push(name); 335 } 336 337 string[] popNamespace() @safe pure { 338 return _namespace.pop; 339 } 340 341 void addNamespaceSymbol(in string symbol) @safe pure { 342 _namespace.addSymbol(symbol); 343 } 344 } 345 346 347 // to identify a cursor 348 private struct CursorId { 349 import clang: Cursor, Type; 350 351 string cursorSpelling; 352 Cursor.Kind cursorKind; 353 string typeSpelling; 354 Type.Kind typeKind; 355 356 this(in Cursor cursor) @safe pure nothrow { 357 cursorSpelling = cursor.spelling; 358 cursorKind = cursor.kind; 359 typeSpelling = cursor.type.spelling; 360 typeKind = cursor.type.kind; 361 } 362 }