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 void rememberAggregateParent(in Cursor child, in Cursor parent) @safe pure { 311 const parentSpelling = spelling(parent.spelling); 312 const childSpelling = resolveSpelling(child); 313 _aggregateParents[childSpelling] = parentSpelling; 314 } 315 316 void rememberAggregateTypeLine(in string typeName) @safe pure { 317 _aggregateTypeLines[lines.length] ~= typeName; 318 } 319 320 private string resolveSpelling(in Cursor cursor) @safe pure { 321 const spelling = spellingOrNickname(cursor); 322 _aggregateDeclarations[spelling] = true; 323 rememberSpelling(cursor.spelling, spelling); 324 return spelling; 325 } 326 327 void rememberSpelling(scope const string original, in string spelling) @safe pure { 328 if (original != "" && original != spelling) 329 _aggregateSpelling[original] = spelling; 330 } 331 332 bool isUnknownStruct(in string name) @safe pure const { 333 return name !in _aggregateDeclarations 334 && (name !in _aggregateSpelling 335 || _aggregateSpelling[name] !in _aggregateDeclarations); 336 } 337 338 /** 339 If unknown structs show up in functions or fields (as a pointer), 340 define them now so the D file can compile 341 See `it.c.compile.delayed`. 342 */ 343 void declareUnknownStructs() @safe { 344 import dpp.translation.type : removeDppDecorators; 345 346 foreach(name, _; _fieldStructSpellings) { 347 name = name.removeDppDecorators; 348 if(isUnknownStruct(name)) { 349 log("Could not find '", name, "' in aggregate declarations, defining it"); 350 const spelling = name in _aggregateSpelling ? _aggregateSpelling[name] 351 : name; 352 writeln("struct " ~ spelling ~ ";"); 353 _aggregateDeclarations[spelling] = true; 354 } 355 } 356 } 357 358 const(typeof(_aggregateDeclarations)) aggregateDeclarations() @safe pure nothrow const { 359 return _aggregateDeclarations; 360 } 361 362 /// return the spelling if it exists, or our made-up nickname for it if not 363 string spellingOrNickname(in Cursor cursor) @safe pure { 364 if (cursor.spelling == "") 365 return nickName(cursor); 366 367 return spelling(cursor.spelling); 368 } 369 370 string spelling(scope const string cursorSpelling) @safe pure { 371 import dpp.translation.dlang: rename, isKeyword; 372 373 if (cursorSpelling in _aggregateSpelling) 374 return _aggregateSpelling[cursorSpelling]; 375 376 return cursorSpelling.isKeyword ? rename(cursorSpelling, this) 377 : cursorSpelling.idup; 378 } 379 380 private string nickName(in Cursor cursor) @safe pure { 381 if(cursor.hash !in _nickNames) { 382 auto nick = newAnonymousTypeName; 383 _nickNames[cursor.hash] = nick; 384 } 385 386 return _nickNames[cursor.hash]; 387 } 388 389 private string newAnonymousTypeName() @safe pure { 390 import std.conv: text; 391 return text("_Anonymous_", _anonymousIndex++); 392 } 393 394 string newAnonymousMemberName() @safe pure { 395 import std..string: replace; 396 return newAnonymousTypeName.replace("_A", "_a"); 397 } 398 399 private void resolveClash(ref string line, in string spelling, in string mangling) @safe pure const { 400 import dpp.translation.dlang: pragmaMangle; 401 line = ` ` ~ pragmaMangle(mangling) ~ replaceSpelling(line, spelling); 402 } 403 404 private string replaceSpelling(in string line, in string spelling) @safe pure const { 405 import dpp.translation.dlang: rename; 406 import std.array: replace; 407 return line 408 .replace(spelling ~ `;`, rename(spelling, this) ~ `;`) 409 .replace(spelling ~ `(`, rename(spelling, this) ~ `(`) 410 ; 411 } 412 413 void rememberType(in string type) @safe pure nothrow { 414 _types ~= type; 415 } 416 417 bool isUserDefinedType(in string spelling) @safe pure const { 418 import std.algorithm: canFind; 419 return _types.canFind(spelling); 420 } 421 422 void rememberMacro(in Cursor cursor) @safe pure { 423 _macros[cursor.spelling.idup] = true; 424 if(cursor.isMacroFunction) 425 _functionMacroDeclarations[cursor.spelling.idup] = true; 426 } 427 428 bool macroAlreadyDefined(in Cursor cursor) @safe pure const { 429 return cast(bool) (cursor.spelling in _macros); 430 } 431 432 void pushNamespace(in string ns) @safe pure nothrow { 433 _namespaces ~= ns; 434 } 435 436 void popNamespace(in string ns) @safe pure nothrow { 437 _namespaces = _namespaces[0 .. $-1]; 438 } 439 440 // returns the current namespace so it can be deleted 441 // from translated names 442 string namespace() @safe pure nothrow const { 443 import std.array: join; 444 return _namespaces.join("::"); 445 } 446 447 /// If this cursor is from one of the ignored namespaces 448 bool isFromIgnoredNs(in Type type) @safe const { 449 import std.algorithm: canFind, any; 450 return options.ignoredNamespaces.any!(a => type.spelling.canFind(a ~ "::")); 451 } 452 453 /// Is the file from an ignored path? Note it uses file globbing 454 bool isFromIgnoredPath(in Cursor cursor) @safe const { 455 import std.path: globMatch; 456 import std.algorithm: any; 457 string sourcePath = cursor.sourceRange.path; 458 return options.ignoredPaths.any!(a => sourcePath.globMatch(a)); 459 } 460 } 461 462 463 // to identify a cursor 464 private struct CursorId { 465 import clang: Cursor, Type; 466 467 string cursorSpelling; 468 Cursor.Kind cursorKind; 469 string typeSpelling; 470 Type.Kind typeKind; 471 472 this(in Cursor cursor) @safe pure nothrow { 473 cursorSpelling = cursor.spelling.idup; 474 cursorKind = cursor.kind; 475 typeSpelling = cursor.type.spelling.idup; 476 typeKind = cursor.type.kind; 477 } 478 }