1 /** 2 * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: Jan 30, 2012 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.Type; 8 9 import std.conv; 10 import std..string; 11 import std.range; 12 import std.typecons: Nullable; 13 14 import clang.c.Index; 15 import clang.Cursor; 16 import clang.Type; 17 import clang.Token: Token; 18 19 import dstep.translator.Context; 20 import dstep.translator.IncludeHandler; 21 import dstep.translator.Translator; 22 import dstep.translator.Output; 23 24 SourceNode translateType (Context context, Cursor cursor, bool rewriteIdToObjcObject = true, bool applyConst = true) 25 { 26 return translateType(context, cursor, cursor.type, rewriteIdToObjcObject, applyConst); 27 } 28 29 SourceNode translateType (Context context, Cursor cursor, Type type, bool rewriteIdToObjcObject = true, bool applyConst = true) 30 in 31 { 32 assert(type.isValid); 33 } 34 do 35 { 36 SourceNode result; 37 38 with (CXTypeKind) 39 { 40 if (type.kind == blockPointer || type.isFunctionPointerType) 41 result = translateFunctionPointerType(context, cursor, type.pointee.func); 42 43 else if (type.isFunctionType) 44 result = translateFunctionPointerType(context, cursor, type.canonical.func); 45 46 else if (type.kind == objCObjectPointer && !type.isObjCBuiltinType) 47 result = translateObjCObjectPointerType(context, cursor, type); 48 49 else if (type.isWideCharType) 50 result = makeSourceNode("wchar"); 51 52 else if (type.isObjCIdType) 53 result = makeSourceNode(rewriteIdToObjcObject ? "ObjcObject" : "id"); 54 55 else 56 switch (type.kind) 57 { 58 case pointer: 59 return translatePointer(context, cursor, type, rewriteIdToObjcObject, applyConst); 60 61 case typedef_: 62 result = translateTypedef(context, type).makeSourceNode(); 63 break; 64 65 case record: 66 case enum_: 67 result = makeSourceNode(context.translateTagSpelling(type.declaration)); 68 handleInclude(context, type); 69 break; 70 71 case objCInterface: 72 if (type.spelling.empty) 73 result = makeSourceNode(context.getAnonymousName(type.declaration)); 74 else 75 result = makeSourceNode(type.spelling); 76 77 handleInclude(context, type); 78 break; 79 80 case constantArray: 81 case incompleteArray: 82 result = translateArray( 83 context, 84 cursor, 85 type, 86 rewriteIdToObjcObject, 87 type.array.numDimensions - 1); 88 break; 89 90 case unexposed: 91 result = translateUnexposed( 92 context, 93 type, 94 rewriteIdToObjcObject); 95 break; 96 97 case elaborated: 98 result = translateElaborated( 99 context, 100 cursor, 101 type, 102 rewriteIdToObjcObject); 103 break; 104 105 case complex: 106 result = translateComplex(type).makeSourceNode(); 107 break; 108 109 default: 110 result = translateType( 111 context, 112 type.kind, 113 rewriteIdToObjcObject) 114 .makeSourceNode(); 115 } 116 } 117 118 version (D1) 119 { 120 // ignore const 121 } 122 else 123 { 124 if (applyConst && type.isConst) 125 result = result.prefixWith("const "); 126 } 127 128 return result; 129 } 130 131 SourceNode translateElaborated (Context context, Cursor cursor, Type type, bool rewriteIdToObjcObject = true, bool applyConst = true) 132 { 133 auto named = type.named(); 134 135 if (named.kind == CXTypeKind.record || named.kind == CXTypeKind.enum_) 136 { 137 auto result = context.translateTagSpelling(named.declaration); 138 handleInclude(context, type); 139 return result.makeSourceNode(); 140 } 141 else 142 { 143 return translateType( 144 context, 145 cursor, 146 type.named, 147 rewriteIdToObjcObject); 148 } 149 } 150 151 string translateSelector (string str, bool fullName = false, bool translateIdentifier = true) 152 { 153 import std.array : replace; 154 import std..string : indexOf; 155 156 if (fullName) 157 str = str.replace(":", "_"); 158 159 else 160 { 161 auto i = str.indexOf(":"); 162 163 if (i > -1) 164 str = str[0 .. i]; 165 } 166 167 return translateIdentifier ? .translateIdentifier(str) : str; 168 } 169 170 package string reduceAlias(Type type) 171 { 172 import std.typecons; 173 174 enum aliasMapping = [ 175 tuple("byte", CXTypeKind.uChar): "ubyte", 176 tuple("BOOL", CXTypeKind.bool_): "bool", 177 tuple("BOOL", CXTypeKind.sChar): "bool", 178 179 tuple("int8_t", CXTypeKind.sChar): "byte", 180 tuple("int16_t", CXTypeKind.short_): "short", 181 tuple("int32_t", CXTypeKind.int_): "int", 182 tuple("int64_t", CXTypeKind.longLong): "long", 183 tuple("uint8_t", CXTypeKind.uChar): "ubyte", 184 tuple("uint16_t", CXTypeKind.uShort): "ushort", 185 tuple("uint32_t", CXTypeKind.uInt): "uint", 186 tuple("uint64_t", CXTypeKind.uLongLong): "ulong", 187 188 tuple("__s8", CXTypeKind.sChar): "byte", 189 tuple("__s16", CXTypeKind.short_): "short", 190 tuple("__s32", CXTypeKind.int_): "int", 191 tuple("__s64", CXTypeKind.longLong): "long", 192 tuple("__u8", CXTypeKind.uChar): "ubyte", 193 tuple("__u16", CXTypeKind.uShort): "ushort", 194 tuple("__u32", CXTypeKind.uInt): "uint", 195 tuple("__u64", CXTypeKind.uLongLong): "ulong", 196 197 tuple("s8", CXTypeKind.sChar): "byte", 198 tuple("s16", CXTypeKind.short_): "short", 199 tuple("s32", CXTypeKind.int_): "int", 200 tuple("s64", CXTypeKind.longLong): "long", 201 tuple("u8", CXTypeKind.uChar): "ubyte", 202 tuple("u16", CXTypeKind.uShort): "ushort", 203 tuple("u32", CXTypeKind.uInt): "uint", 204 tuple("u64", CXTypeKind.uLongLong): "ulong" 205 ]; 206 207 auto canonical = type.canonical; 208 auto kind = canonical.kind; 209 210 if (kind == CXTypeKind.long_ && canonical.sizeOf == 8) 211 kind = CXTypeKind.longLong; 212 else if (kind == CXTypeKind.uLong && canonical.sizeOf == 8) 213 kind = CXTypeKind.uLongLong; 214 215 if (auto alias_ = tuple(type.spelling, kind) in aliasMapping) 216 return *alias_; 217 else 218 return null; 219 } 220 221 package bool isAliasReducible(Type type) 222 { 223 return reduceAlias(type) != null; 224 } 225 226 private: 227 228 string translateWCharT(Context context, Type type) 229 { 230 if (context.options.portableWCharT) 231 { 232 context.includeHandler.addImport("core.stdc.stddef"); 233 return "wchar_t"; 234 } 235 else if (type.canonical.kind.isIntegral) 236 { 237 auto sizeOf = type.canonical.sizeOf; 238 239 if (sizeOf == 4) 240 return "dchar"; 241 else if (sizeOf == 2) 242 return "wchar"; 243 } 244 245 return "<unimplemented>"; 246 } 247 248 string translateTypedef(Context context, Type type) 249 { 250 if (context.options.reduceAliases) 251 { 252 if (auto transl = reduceAlias(type)) 253 return transl; 254 } 255 256 auto spelling = type.spelling; 257 258 with (CXTypeKind) 259 switch (spelling) 260 { 261 case "size_t": 262 case "ptrdiff_t": 263 case "sizediff_t": 264 return spelling; 265 266 case "wchar_t": 267 return translateWCharT(context, type); 268 269 default: break; 270 } 271 272 273 handleInclude(context, type); 274 275 if (isDKeyword(type.spelling)) 276 { 277 return type.spelling != translateType(context, type.canonical.kind) 278 ? renameDKeyword(type.spelling) 279 : type.spelling; 280 } 281 else 282 { 283 return type.spelling; 284 } 285 } 286 287 SourceNode translateUnexposed (Context context, Type type, bool rewriteIdToObjcObject) 288 in 289 { 290 assert(type.kind == CXTypeKind.unexposed); 291 } 292 do 293 { 294 auto declaration = type.declaration; 295 296 if (declaration.isValid) 297 return translateType(context, declaration, rewriteIdToObjcObject); 298 else 299 return translateType(context, type.kind, rewriteIdToObjcObject) 300 .makeSourceNode(); 301 } 302 303 string translateComplex (Type type) 304 { 305 switch (type.element.kind) 306 { 307 case CXTypeKind.float_: return "cfloat"; 308 case CXTypeKind.double_: return "cdouble"; 309 case CXTypeKind.longDouble: return "creal"; 310 default: return "<unimplemented>"; 311 } 312 } 313 314 SourceNode translateArrayElement( 315 Context context, 316 Cursor cursor, 317 ArrayType array, 318 bool rewriteIdToObjcObject) 319 { 320 import std.format : format; 321 322 bool isConst = array.elementType.isConst; 323 324 auto type = translateType( 325 context, 326 cursor, 327 array.elementType, 328 rewriteIdToObjcObject, 329 !isConst); 330 331 if (isConst) 332 return type.wrapWith("const(", ")"); 333 else 334 return type; 335 } 336 337 SourceNode translateArray ( 338 Context context, 339 Cursor cursor, 340 Type type, 341 bool rewriteIdToObjcObject, 342 size_t dimension = 0) 343 in 344 { 345 assert(type.kind == CXTypeKind.constantArray 346 || type.kind == CXTypeKind.incompleteArray); 347 } 348 do 349 { 350 import std.format : format; 351 352 auto array = type.array; 353 SourceNode elementType; 354 355 if (array.elementType.kind == CXTypeKind.constantArray) 356 { 357 elementType = translateArray( 358 context, 359 cursor, 360 array.elementType, 361 rewriteIdToObjcObject, 362 dimension == 0 ? 0 : dimension - 1); 363 } 364 else 365 { 366 elementType = translateArrayElement( 367 context, 368 cursor, 369 array, 370 rewriteIdToObjcObject); 371 } 372 373 if (array.size >= 0) 374 { 375 auto children = cursor.filterChildren( 376 CXCursorKind.integerLiteral, 377 CXCursorKind.declRefExpr); 378 379 auto maybeRef(T)(auto ref T value) { 380 return cursor.semanticParent.kind == CXCursorKind.functionDecl && dimension == 0 381 ? elementType.wrapWith("ref ", format("[%s]", value)) 382 : elementType.suffixWith(format("[%s]", value)); 383 } 384 385 if (dimension < children.length) 386 { 387 if (children[dimension].kind == CXCursorKind.integerLiteral) 388 { 389 auto token = tokenInsideSquareBrackets(cursor, dimension); 390 391 if (!token.isNull) 392 { 393 return maybeRef(token.get.spelling); 394 } 395 396 auto expansions = context.macroIndex.queryExpansion(children[dimension]); 397 398 if (expansions.length == 1) 399 return elementType.suffixWith(format("[%s]", expansions[0].spelling)); 400 } 401 else if (children[dimension].kind == CXCursorKind.declRefExpr) 402 { 403 return elementType.suffixWith(format("[%s]", children[dimension].spelling)); 404 } 405 } 406 407 return maybeRef(array.size); 408 } 409 else if (cursor.semanticParent.kind == CXCursorKind.functionDecl) 410 { 411 return elementType.suffixWith("*"); 412 } 413 else 414 { 415 // FIXME: Find a way to translate references to static external arrays with unknown size. 416 417 // extern static arrays (which are normally present in bindings) 418 // have same ABI as extern dynamic arrays, size is only checked 419 // against declaration in header. As it is not possible in D 420 // to define static array with ABI of dynamic one, only way is to 421 // abandon the size information 422 return elementType.suffixWith("[]"); 423 } 424 } 425 426 // find the token for a (possibly multidimensioned) array for a certain dimension, 427 // e.g. int foo[1][2][3] will find the "3" for dimension 0 due to the differences 428 // in array declarations between D and C 429 private Nullable!Token tokenInsideSquareBrackets(Cursor cursor, in size_t dimension) 430 { 431 import std.algorithm: find; 432 import std.range: retro; 433 import clang.Token: TokenKind; 434 435 auto fromNextBracket(R)(R tokens) 436 { 437 return tokens.find!(a => a.kind == TokenKind.punctuation && a.spelling == "]"); 438 } 439 440 auto tokens = cursor.tokens.retro.find!(_ => true); 441 442 // dimension + 1 since dimension is 0-indexed and we need to find at least one 443 foreach(_; 0 .. dimension + 1) 444 { 445 tokens = fromNextBracket(tokens); 446 if (tokens.empty) return typeof(return).init; 447 tokens.popFront; 448 } 449 450 return tokens.empty 451 ? typeof(return).init 452 : typeof(return)(tokens.front); 453 } 454 455 SourceNode translatePointer ( 456 Context context, 457 Cursor cursor, 458 Type type, 459 bool rewriteIdToObjcObject, 460 bool applyConst) 461 in 462 { 463 assert(type.kind == CXTypeKind.pointer); 464 } 465 do 466 { 467 static bool valueTypeIsConst (Type type) 468 { 469 auto pointee = type.pointee; 470 471 while (pointee.kind == CXTypeKind.pointer) 472 pointee = pointee.pointee; 473 474 return pointee.isConst; 475 } 476 477 auto result = translateType(context, cursor, type.pointee, rewriteIdToObjcObject, false); 478 479 version (D1) 480 { 481 result = result ~ '*'; 482 } 483 else 484 { 485 if (applyConst && valueTypeIsConst(type)) 486 { 487 if (type.isConst) 488 result = result.wrapWith("const ", "*"); 489 else 490 result = result.wrapWith("const(", ")*"); 491 } 492 else 493 result = result.suffixWith("*"); 494 } 495 496 return result; 497 } 498 499 Parameter translateParameter (Context context, Cursor parameter) 500 { 501 Parameter result; 502 503 result.type = translateType(context, parameter); 504 result.name = parameter.spelling; 505 result.isConst = false; 506 507 return result; 508 } 509 510 Parameter[] translateParameters (Context context, Cursor cursor, FuncType func) 511 { 512 import std.array : Appender; 513 514 auto result = Appender!(Parameter[])(); 515 auto arguments = func.arguments; 516 517 foreach (child; cursor.all) 518 { 519 if (child.kind == CXCursorKind.parmDecl) 520 result.put(translateParameter(context, child)); 521 } 522 523 return result.data; 524 } 525 526 SourceNode translateFunctionPointerType (Context context, Cursor cursor, FuncType func) 527 { 528 auto params = translateParameters(context, cursor, func); 529 auto result = translateType(context, cursor, func.resultType); 530 auto spacer = context.options.spaceAfterFunctionName ? " " : ""; 531 auto multiline = cursor.extent.isMultiline && 532 !context.options.singleLineFunctionSignatures; 533 534 return translateFunction( 535 result, 536 "function", 537 params, 538 func.isVariadic, 539 "", 540 spacer, 541 multiline); 542 } 543 544 SourceNode translateObjCObjectPointerType (Context context, Cursor cursor, Type type) 545 in 546 { 547 assert(type.kind == CXTypeKind.objCObjectPointer && !type.isObjCBuiltinType); 548 } 549 do 550 { 551 auto pointee = type.pointee; 552 553 if (pointee.spelling == "Protocol") 554 return "Protocol*".makeSourceNode(); 555 556 else 557 return translateType(context, cursor, pointee); 558 } 559 560 string translateType (Context context, CXTypeKind kind, bool rewriteIdToObjcObject = true) 561 { 562 import std.conv; 563 564 with (CXTypeKind) 565 switch (kind) 566 { 567 case invalid: return "<unimplemented>"; 568 case unexposed: return "<unimplemented>"; 569 case void_: return "void"; 570 case bool_: return "bool"; 571 case charU: return "<unimplemented>"; 572 case uChar: return "ubyte"; 573 case char16: return "wchar"; 574 case char32: return "dchar"; 575 case uShort: return "ushort"; 576 case uInt: return "uint"; 577 578 case uLong: 579 context.includeHandler.addCompatible(); 580 return "c_ulong"; 581 582 case uLongLong: return "ulong"; 583 case uInt128: return "<unimplemented>"; 584 case charS: return "char"; 585 case sChar: return "byte"; 586 case wChar: return "wchar"; 587 case short_: return "short"; 588 case int_: return "int"; 589 590 case long_: 591 context.includeHandler.addCompatible(); 592 return "c_long"; 593 594 case longLong: return "long"; 595 case int128: return "<unimplemented>"; 596 case float_: return "float"; 597 case double_: return "double"; 598 case longDouble: return "real"; 599 case nullPtr: return "null"; 600 case overload: return "<unimplemented>"; 601 case dependent: return "<unimplemented>"; 602 case objCId: return rewriteIdToObjcObject ? "ObjcObject" : "id"; 603 case objCClass: return "Class"; 604 case objCSel: return "SEL"; 605 606 case pointer: 607 case blockPointer: 608 case lValueReference: 609 case rValueReference: 610 case record: 611 case enum_: 612 case typedef_: 613 case functionNoProto: 614 case functionProto: 615 case vector: 616 case incompleteArray: 617 case variableArray: 618 case dependentSizedArray: 619 case memberPointer: 620 case elaborated: 621 return "<unimplemented>"; 622 623 default: assert(0, "Unhandled type kind " ~ to!string(kind)); 624 } 625 }