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 body 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 auto spelling = type.spelling; 173 174 switch (type.spelling) 175 { 176 case "BOOL": return "bool"; 177 case "int8_t": return "byte"; 178 case "int16_t": return "short"; 179 case "int32_t": return "int"; 180 case "int64_t": return "long"; 181 case "uint8_t": return "ubyte"; 182 case "uint16_t": return "ushort"; 183 case "uint32_t": return "uint"; 184 case "uint64_t": return "ulong"; 185 186 case "__s8": return "byte"; 187 case "__s16": return "short"; 188 case "__s32": return "int"; 189 case "__s64": return "long"; 190 case "__u8": return "ubyte"; 191 case "__u16": return "ushort"; 192 case "__u32": return "uint"; 193 case "__u64": return "ulong"; 194 195 case "s8": return "byte"; 196 case "s16": return "short"; 197 case "s32": return "int"; 198 case "s64": return "long"; 199 case "u8": return "ubyte"; 200 case "u16": return "ushort"; 201 case "u32": return "uint"; 202 case "u64": return "ulong"; 203 204 default: return null; 205 } 206 } 207 208 package bool isAliasReducible(Type type) 209 { 210 return reduceAlias(type) != null; 211 } 212 213 private: 214 215 string translateWCharT(Context context, Type type) 216 { 217 if (context.options.portableWCharT) 218 { 219 context.includeHandler.addImport("core.stdc.stddef"); 220 return "wchar_t"; 221 } 222 else if (type.canonical.kind.isIntegral) 223 { 224 auto sizeOf = type.canonical.sizeOf; 225 226 if (sizeOf == 4) 227 return "dchar"; 228 else if (sizeOf == 2) 229 return "wchar"; 230 } 231 232 return "<unimplemented>"; 233 } 234 235 string translateTypedef(Context context, Type type) 236 { 237 if (context.options.reduceAliases) 238 { 239 if (auto transl = reduceAlias(type)) 240 return transl; 241 } 242 243 auto spelling = type.spelling; 244 245 with (CXTypeKind) 246 switch (spelling) 247 { 248 case "size_t": 249 case "ptrdiff_t": 250 case "sizediff_t": 251 return spelling; 252 253 case "wchar_t": 254 return translateWCharT(context, type); 255 256 default: break; 257 } 258 259 260 handleInclude(context, type); 261 return type.spelling; 262 } 263 264 SourceNode translateUnexposed (Context context, Type type, bool rewriteIdToObjcObject) 265 in 266 { 267 assert(type.kind == CXTypeKind.unexposed); 268 } 269 body 270 { 271 auto declaration = type.declaration; 272 273 if (declaration.isValid) 274 return translateType(context, declaration, rewriteIdToObjcObject); 275 else 276 return translateType(context, type.kind, rewriteIdToObjcObject) 277 .makeSourceNode(); 278 } 279 280 string translateComplex (Type type) 281 { 282 switch (type.element.kind) 283 { 284 case CXTypeKind.float_: return "cfloat"; 285 case CXTypeKind.double_: return "cdouble"; 286 case CXTypeKind.longDouble: return "creal"; 287 default: return "<unimplemented>"; 288 } 289 } 290 291 SourceNode translateArrayElement( 292 Context context, 293 Cursor cursor, 294 ArrayType array, 295 bool rewriteIdToObjcObject) 296 { 297 import std.format : format; 298 299 bool isConst = array.elementType.isConst; 300 301 auto type = translateType( 302 context, 303 cursor, 304 array.elementType, 305 rewriteIdToObjcObject, 306 !isConst); 307 308 if (isConst) 309 return type.wrapWith("const(", ")"); 310 else 311 return type; 312 } 313 314 SourceNode translateArray ( 315 Context context, 316 Cursor cursor, 317 Type type, 318 bool rewriteIdToObjcObject, 319 size_t dimension = 0) 320 in 321 { 322 assert(type.kind == CXTypeKind.constantArray 323 || type.kind == CXTypeKind.incompleteArray); 324 } 325 body 326 { 327 import std.format : format; 328 329 auto array = type.array; 330 SourceNode elementType; 331 332 if (array.elementType.kind == CXTypeKind.constantArray) 333 { 334 elementType = translateArray( 335 context, 336 cursor, 337 array.elementType, 338 rewriteIdToObjcObject, 339 dimension == 0 ? 0 : dimension - 1); 340 } 341 else 342 { 343 elementType = translateArrayElement( 344 context, 345 cursor, 346 array, 347 rewriteIdToObjcObject); 348 } 349 350 if (array.size >= 0) 351 { 352 auto children = cursor.filterChildren( 353 CXCursorKind.integerLiteral, 354 CXCursorKind.declRefExpr); 355 356 auto maybeRef(T)(auto ref T value) { 357 return cursor.semanticParent.kind == CXCursorKind.functionDecl && dimension == 0 358 ? elementType.wrapWith("ref ", format("[%s]", value)) 359 : elementType.suffixWith(format("[%s]", value)); 360 } 361 362 if (dimension < children.length) 363 { 364 if (children[dimension].kind == CXCursorKind.integerLiteral) 365 { 366 auto token = tokenInsideSquareBrackets(cursor, dimension); 367 368 if (!token.isNull) 369 { 370 return maybeRef(token.spelling); 371 } 372 373 auto expansions = context.macroIndex.queryExpansion(children[dimension]); 374 375 if (expansions.length == 1) 376 return elementType.suffixWith(format("[%s]", expansions[0].spelling)); 377 } 378 else if (children[dimension].kind == CXCursorKind.declRefExpr) 379 { 380 return elementType.suffixWith(format("[%s]", children[dimension].spelling)); 381 } 382 } 383 384 return maybeRef(array.size); 385 } 386 else if (cursor.semanticParent.kind == CXCursorKind.functionDecl) 387 { 388 return elementType.suffixWith("*"); 389 } 390 else 391 { 392 // FIXME: Find a way to translate references to static external arrays with unknown size. 393 394 // extern static arrays (which are normally present in bindings) 395 // have same ABI as extern dynamic arrays, size is only checked 396 // against declaration in header. As it is not possible in D 397 // to define static array with ABI of dynamic one, only way is to 398 // abandon the size information 399 return elementType.suffixWith("[]"); 400 } 401 } 402 403 // find the token for a (possibly multidimensioned) array for a certain dimension, 404 // e.g. int foo[1][2][3] will find the "3" for dimension 0 due to the differences 405 // in array declarations between D and C 406 private Nullable!Token tokenInsideSquareBrackets(Cursor cursor, in size_t dimension) 407 { 408 import std.algorithm: find; 409 import std.range: retro; 410 import clang.Token: TokenKind; 411 412 auto fromNextBracket(R)(R tokens) 413 { 414 return tokens.find!(a => a.kind == TokenKind.punctuation && a.spelling == "]"); 415 } 416 417 auto tokens = cursor.tokens.retro.find!(_ => true); 418 419 // dimension + 1 since dimension is 0-indexed and we need to find at least one 420 foreach(_; 0 .. dimension + 1) 421 { 422 tokens = fromNextBracket(tokens); 423 if (tokens.empty) return typeof(return).init; 424 tokens.popFront; 425 } 426 427 return tokens.empty 428 ? typeof(return).init 429 : typeof(return)(tokens.front); 430 } 431 432 SourceNode translatePointer ( 433 Context context, 434 Cursor cursor, 435 Type type, 436 bool rewriteIdToObjcObject, 437 bool applyConst) 438 in 439 { 440 assert(type.kind == CXTypeKind.pointer); 441 } 442 body 443 { 444 static bool valueTypeIsConst (Type type) 445 { 446 auto pointee = type.pointee; 447 448 while (pointee.kind == CXTypeKind.pointer) 449 pointee = pointee.pointee; 450 451 return pointee.isConst; 452 } 453 454 auto result = translateType(context, cursor, type.pointee, rewriteIdToObjcObject, false); 455 456 version (D1) 457 { 458 result = result ~ '*'; 459 } 460 else 461 { 462 if (applyConst && valueTypeIsConst(type)) 463 { 464 if (type.isConst) 465 result = result.wrapWith("const ", "*"); 466 else 467 result = result.wrapWith("const(", ")*"); 468 } 469 else 470 result = result.suffixWith("*"); 471 } 472 473 return result; 474 } 475 476 Parameter translateParameter (Context context, Cursor parameter) 477 { 478 Parameter result; 479 480 result.type = translateType(context, parameter); 481 result.name = parameter.spelling; 482 result.isConst = false; 483 484 return result; 485 } 486 487 Parameter[] translateParameters (Context context, Cursor cursor, FuncType func) 488 { 489 import std.array : Appender; 490 491 auto result = Appender!(Parameter[])(); 492 auto arguments = func.arguments; 493 494 foreach (child; cursor.all) 495 { 496 if (child.kind == CXCursorKind.parmDecl) 497 result.put(translateParameter(context, child)); 498 } 499 500 return result.data; 501 } 502 503 SourceNode translateFunctionPointerType (Context context, Cursor cursor, FuncType func) 504 { 505 auto params = translateParameters(context, cursor, func); 506 auto result = translateType(context, cursor, func.resultType); 507 auto spacer = context.options.spaceAfterFunctionName ? " " : ""; 508 auto multiline = cursor.extent.isMultiline && 509 !context.options.singleLineFunctionSignatures; 510 511 return translateFunction( 512 result, 513 "function", 514 params, 515 func.isVariadic, 516 "", 517 spacer, 518 multiline); 519 } 520 521 SourceNode translateObjCObjectPointerType (Context context, Cursor cursor, Type type) 522 in 523 { 524 assert(type.kind == CXTypeKind.objCObjectPointer && !type.isObjCBuiltinType); 525 } 526 body 527 { 528 auto pointee = type.pointee; 529 530 if (pointee.spelling == "Protocol") 531 return "Protocol*".makeSourceNode(); 532 533 else 534 return translateType(context, cursor, pointee); 535 } 536 537 string translateType (Context context, CXTypeKind kind, bool rewriteIdToObjcObject = true) 538 { 539 import std.conv; 540 541 with (CXTypeKind) 542 switch (kind) 543 { 544 case invalid: return "<unimplemented>"; 545 case unexposed: return "<unimplemented>"; 546 case void_: return "void"; 547 case bool_: return "bool"; 548 case charU: return "<unimplemented>"; 549 case uChar: return "ubyte"; 550 case char16: return "wchar"; 551 case char32: return "dchar"; 552 case uShort: return "ushort"; 553 case uInt: return "uint"; 554 555 case uLong: 556 context.includeHandler.addCompatible(); 557 return "c_ulong"; 558 559 case uLongLong: return "ulong"; 560 case uInt128: return "<unimplemented>"; 561 case charS: return "char"; 562 case sChar: return "byte"; 563 case wChar: return "wchar"; 564 case short_: return "short"; 565 case int_: return "int"; 566 567 case long_: 568 context.includeHandler.addCompatible(); 569 return "c_long"; 570 571 case longLong: return "long"; 572 case int128: return "<unimplemented>"; 573 case float_: return "float"; 574 case double_: return "double"; 575 case longDouble: return "real"; 576 case nullPtr: return "null"; 577 case overload: return "<unimplemented>"; 578 case dependent: return "<unimplemented>"; 579 case objCId: return rewriteIdToObjcObject ? "ObjcObject" : "id"; 580 case objCClass: return "Class"; 581 case objCSel: return "SEL"; 582 583 case pointer: 584 case blockPointer: 585 case lValueReference: 586 case rValueReference: 587 case record: 588 case enum_: 589 case typedef_: 590 case functionNoProto: 591 case functionProto: 592 case vector: 593 case incompleteArray: 594 case variableArray: 595 case dependentSizedArray: 596 case memberPointer: 597 case elaborated: 598 return "<unimplemented>"; 599 600 default: assert(0, "Unhandled type kind " ~ to!string(kind)); 601 } 602 }