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 }