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 import std.array: Appender; 28 29 alias SeenCursors = bool[CursorId]; 30 31 private auto lines(this This)() { 32 return _lines.data; 33 } 34 35 /** 36 The lines of output so far. This is needed in order to fix 37 any name collisions between functions or variables with aggregates 38 such as structs, unions and enums. 39 */ 40 private Appender!(string[]) _lines; 41 42 /** 43 Structs can be anonymous in C, and it's even common 44 to typedef them to a name. We come up with new names 45 that we track here so as to be able to properly translate 46 those typedefs. 47 */ 48 private string[Cursor.Hash] _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 Mapping between the original aggregate spelling and the renamed one, 69 if renaming was necessary. 70 */ 71 private string[string] _aggregateSpelling; 72 73 /** 74 Mapping between a child aggregate's name and its parent aggregate's 75 name. 76 */ 77 private string[string] _aggregateParents; 78 79 /** 80 Mapping between a line number and an array of strings representing all 81 the aggregate types' names contained at that index. 82 */ 83 private string[][LineNumber] _aggregateTypeLines; 84 85 /** 86 A linkable is a function or a global variable. We remember all 87 the ones we saw here so that if there's a name clash we can 88 come back and fix the declarations after the fact with 89 pragma(mangle). 90 */ 91 private Linkable[string] _linkableDeclarations; 92 93 /** 94 All the function-like macros that have been declared 95 */ 96 private bool[string] _functionMacroDeclarations; 97 98 /** 99 Remember all the macros already defined 100 */ 101 private bool[string] _macros; 102 103 /** 104 All previously seen cursors 105 */ 106 private SeenCursors _seenCursors; 107 108 AccessSpecifier accessSpecifier = AccessSpecifier.Public; 109 110 /// Command-line options 111 Options options; 112 113 /* 114 Remember all declared types so that C-style casts can be recognised 115 */ 116 private string[] _types; 117 118 /// to generate unique names 119 private int _anonymousIndex; 120 121 private string[] _namespaces; 122 123 Language language; 124 125 this(Options options, in Language language) @safe pure { 126 this.options = options; 127 this.language = language; 128 } 129 130 ref Context indent() @safe pure return { 131 options.indent; 132 return this; 133 } 134 135 auto indentation() @safe @nogc pure const { 136 return options.indentation; 137 } 138 139 void setIndentation(in int indentation) @safe pure { 140 options.indentation = indentation; 141 } 142 143 void log(A...)(auto ref A args) const { 144 import std.functional: forward; 145 options.log(forward!args); 146 } 147 148 bool debugOutput() @safe @nogc pure nothrow const { 149 return options.debugOutput; 150 } 151 152 bool hasSeen(in Cursor cursor) @safe pure nothrow const { 153 return cast(bool)(CursorId(cursor) in _seenCursors); 154 } 155 156 void rememberCursor(in Cursor cursor) @safe pure nothrow { 157 // EnumDecl can have no spelling but end up defining an enum anyway 158 // See "it.compile.projects.double enum typedef" 159 if(cursor.spelling != "" || cursor.kind == Cursor.Kind.EnumDecl) 160 _seenCursors[CursorId(cursor)] = true; 161 } 162 163 string translation() @safe pure nothrow const { 164 import std.array: join; 165 return lines.join("\n"); 166 } 167 168 /** 169 Writes a line of translation. 170 */ 171 void writeln(in string line) @safe pure nothrow { 172 _lines ~= line; 173 } 174 175 /** 176 Writes lines of translation. 177 */ 178 void writeln(in string[] lines) @safe pure nothrow { 179 _lines ~= lines; 180 } 181 182 // remember a function or variable declaration 183 string rememberLinkable(in Cursor cursor) @safe pure nothrow { 184 import dpp.translation.dlang: maybeRename; 185 186 const spelling = maybeRename(cursor, this); 187 // since linkables produce one-line translations, the next 188 // will be the linkable 189 _linkableDeclarations[spelling] = Linkable(lines.length, cursor.mangling); 190 191 return spelling; 192 } 193 194 void fixNames() @safe { 195 declareUnknownStructs; 196 fixLinkables; 197 if (language == Language.C) 198 fixAggregateTypes; 199 fixFields; 200 } 201 202 void fixLinkables() @safe pure { 203 foreach(declarations; [_aggregateDeclarations, _functionMacroDeclarations]) { 204 foreach(name, _; declarations) { 205 // if there's a name clash, fix it 206 auto clashingLinkable = name in _linkableDeclarations; 207 if(clashingLinkable) { 208 resolveClash(lines[clashingLinkable.lineNumber], name, clashingLinkable.mangling); 209 } 210 } 211 } 212 } 213 214 void fixFields() @safe pure { 215 216 import dpp.translation.dlang: pragmaMangle, rename; 217 import std.string: replace; 218 219 foreach(spelling, lineNumbers; _fieldDeclarations) { 220 if(spelling in _aggregateDeclarations || spelling in _aggregateSpelling) { 221 const actual = spelling in _aggregateSpelling 222 ? _aggregateSpelling[spelling] 223 : spelling; 224 const renamed = rename(actual, this); 225 foreach (lineNumber; lineNumbers) { 226 lines[lineNumber] = lines[lineNumber] 227 // Member declaration 228 .replace(" " ~ actual ~ `;`, " " ~ renamed ~ `;`) 229 // Pointer declaration 230 .replace(" *" ~ actual ~ `;`, " *" ~ renamed ~ `;`) 231 // Accessing member in getter (C11 anon records) 232 .replace("." ~ actual ~ ";", "." ~ renamed ~ ";") 233 // Accessing member in setter (C11 anon records) 234 .replace("." ~ actual ~ " =", "." ~ renamed ~ " =") 235 // Getter function name (C11 anon records) 236 .replace("auto " ~ actual ~ "()", "auto " ~ renamed ~ "()") 237 // Setter function name (C11 anon records) 238 .replace("void " ~ actual ~ "(_T_)", "void " ~ renamed ~ "(_T_)"); 239 } 240 } 241 } 242 } 243 244 void fixAggregateTypes() @safe pure { 245 import dpp.translation.type : removeDppDecorators; 246 import std.array : join; 247 import std.algorithm : reverse; 248 import std.string : replace; 249 250 string aggregateTypeName(in string spelling) @safe pure { 251 if (spelling !in _aggregateParents) 252 return spelling; 253 254 string[] elems; 255 elems ~= spelling; 256 string curr = _aggregateParents[spelling]; 257 258 while (curr in _aggregateParents) { 259 elems ~= curr ~ "."; 260 curr = _aggregateParents[curr]; 261 } 262 263 elems ~= curr ~ "."; 264 265 return elems.reverse.join; 266 } 267 268 foreach (elem; _aggregateTypeLines.byKeyValue) { 269 LineNumber lineNumber = elem.key; 270 string[] aggregateTypeNames = elem.value; 271 272 foreach (name; aggregateTypeNames) { 273 const actualName = aggregateTypeName(name); 274 lines[lineNumber] = lines[lineNumber] 275 .replace("__dpp_aggregate__ " ~ name, actualName); 276 } 277 } 278 } 279 280 /** 281 Tells the context to remember a struct type encountered in an aggregate field. 282 Typically this will be a pointer to a structure but it could also be the return 283 type or parameter types of a function pointer field. This is (surprisingly!) 284 perfectly valid C code, even though `Foo` is never declared anywhere: 285 ---------------------- 286 struct Foo* fun(void); 287 ---------------------- 288 See issues #22 and #24 289 */ 290 void rememberFieldStruct(in string typeSpelling) @safe pure { 291 _fieldStructSpellings[typeSpelling] = true; 292 } 293 294 /** 295 In C it's possible for a struct field name to have the same name as a struct 296 because of elaborated names. We remember them here in case we need to fix them. 297 */ 298 void rememberField(scope const string spelling) @safe pure { 299 _fieldDeclarations[spelling] ~= lines.length; 300 } 301 302 /** 303 Remember this aggregate cursor 304 */ 305 void rememberAggregate(in Cursor cursor) @safe pure { 306 const spelling = resolveSpelling(cursor); 307 rememberType(spelling); 308 } 309 310 bool aggregateIsRemembered(in Cursor cursor) @safe pure { 311 import std.algorithm: canFind; 312 const spelling = resolveSpelling(cursor); 313 return _types.canFind(spelling); 314 } 315 316 void rememberAggregateParent(in Cursor child, in Cursor parent) @safe pure { 317 const parentSpelling = spelling(parent.spelling); 318 const childSpelling = resolveSpelling(child); 319 _aggregateParents[childSpelling] = parentSpelling; 320 } 321 322 void rememberAggregateTypeLine(in string typeName) @safe pure { 323 _aggregateTypeLines[lines.length] ~= typeName; 324 } 325 326 private string resolveSpelling(in Cursor cursor) @safe pure { 327 const spelling = spellingOrNickname(cursor); 328 _aggregateDeclarations[spelling] = true; 329 rememberSpelling(cursor.spelling, spelling); 330 return spelling; 331 } 332 333 void rememberSpelling(scope const string original, in string spelling) @safe pure { 334 if (original != "" && original != spelling) 335 _aggregateSpelling[original] = spelling; 336 } 337 338 bool isUnknownStruct(in string name) @safe pure const { 339 import std.algorithm: canFind; 340 import dpp.clang: hasAnonymousSpelling; 341 return name !in _aggregateDeclarations 342 && (name !in _aggregateSpelling 343 || _aggregateSpelling[name] !in _aggregateDeclarations) 344 && !hasAnonymousSpelling(name); 345 } 346 347 /** 348 If unknown structs show up in functions or fields (as a pointer), 349 define them now so the D file can compile 350 See `it.c.compile.delayed`. 351 */ 352 void declareUnknownStructs() @safe { 353 import dpp.translation.type : removeDppDecorators; 354 355 foreach(name, _; _fieldStructSpellings) { 356 name = name.removeDppDecorators; 357 if(isUnknownStruct(name)) { 358 log("Could not find '", name, "' in aggregate declarations, defining it"); 359 const spelling = name in _aggregateSpelling ? _aggregateSpelling[name] 360 : name; 361 writeln("struct " ~ spelling ~ ";"); 362 _aggregateDeclarations[spelling] = true; 363 } 364 } 365 } 366 367 const(typeof(_aggregateDeclarations)) aggregateDeclarations() @safe pure nothrow const { 368 return _aggregateDeclarations; 369 } 370 371 /// return the spelling if it exists, or our made-up nickname for it if not 372 string spellingOrNickname(in Cursor cursor) @safe pure { 373 if (cursor.spelling == "" || cursor.isAnonymous) 374 return nickName(cursor); 375 376 return spelling(cursor.spelling); 377 } 378 379 string spelling(const string cursorSpelling) @safe pure { 380 import dpp.translation.dlang: rename, isKeyword; 381 382 if (cursorSpelling in _aggregateSpelling) 383 return _aggregateSpelling[cursorSpelling]; 384 385 return cursorSpelling.isKeyword ? rename(cursorSpelling, this) 386 : cursorSpelling; 387 } 388 389 private string nickName(in Cursor cursor) @safe pure { 390 if(cursor.hash !in _nickNames) { 391 auto nick = newAnonymousTypeName; 392 _nickNames[cursor.hash] = nick; 393 } 394 395 return _nickNames[cursor.hash]; 396 } 397 398 private string newAnonymousTypeName() @safe pure { 399 import std.conv: text; 400 return text("_Anonymous_", _anonymousIndex++); 401 } 402 403 string newAnonymousMemberName() @safe pure { 404 import std.string: replace; 405 return newAnonymousTypeName.replace("_A", "_a"); 406 } 407 408 private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const { 409 import dpp.translation.dlang: pragmaMangle; 410 line = ` ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling); 411 } 412 413 private string replaceSpelling(in string line, in string spelling) @safe pure const { 414 import dpp.translation.dlang: rename; 415 import std.array: replace; 416 return line 417 .replace(spelling ~ `;`, rename(spelling, this) ~ `;`) 418 .replace(spelling ~ `(`, rename(spelling, this) ~ `(`) 419 ; 420 } 421 422 void rememberType(in string type) @safe pure nothrow { 423 _types ~= type; 424 } 425 426 bool isUserDefinedType(in string spelling) @safe pure const { 427 import std.algorithm: canFind; 428 return _types.canFind(spelling); 429 } 430 431 void rememberMacro(in Cursor cursor) @safe pure { 432 _macros[cursor.spelling.idup] = true; 433 if(cursor.isMacroFunction) 434 _functionMacroDeclarations[cursor.spelling.idup] = true; 435 } 436 437 bool macroAlreadyDefined(in Cursor cursor) @safe pure const { 438 return cast(bool) (cursor.spelling in _macros); 439 } 440 441 void pushNamespace(in string ns) @safe pure nothrow { 442 _namespaces ~= ns; 443 } 444 445 void popNamespace(in string ns) @safe pure nothrow { 446 _namespaces = _namespaces[0 .. $-1]; 447 } 448 449 // returns the current namespace so it can be deleted 450 // from translated names 451 string namespace() @safe pure nothrow const { 452 import std.array: join; 453 return _namespaces.join("::"); 454 } 455 456 /// If this cursor is from one of the ignored namespaces 457 bool isFromIgnoredNs(in Type type) @safe const { 458 import std.algorithm: canFind, any; 459 return options.ignoredNamespaces.any!(a => type.spelling.canFind(a ~ "::")); 460 } 461 462 /// Is the file from an ignored path? Note it uses file globbing 463 bool isFromIgnoredPath(in Cursor cursor) @safe const { 464 import std.path: globMatch; 465 import std.algorithm: any; 466 string sourcePath = cursor.sourceRange.path; 467 return options.ignoredPaths.any!(a => sourcePath.globMatch(a)); 468 } 469 } 470 471 472 // to identify a cursor 473 private struct CursorId { 474 import clang: Cursor, Type; 475 476 string cursorSpelling; 477 Cursor.Kind cursorKind; 478 string typeSpelling; 479 Type.Kind typeKind; 480 481 this(in Cursor cursor) @safe pure nothrow { 482 cursorSpelling = cursor.spelling.idup; 483 cursorKind = cursor.kind; 484 typeSpelling = cursor.type.spelling.idup; 485 typeKind = cursor.type.kind; 486 } 487 }