1 /** 2 * Provides runtime traits, which provide much of the functionality of tango.core.Traits and 3 * is-expressions, as well as some functionality that is only available at runtime, using 4 * runtime type information. 5 * 6 * Authors: Chris Wright (dhasenan) $(EMAIL dhasenan@gmail.com) 7 * License: Tango License, Apache 2.0 8 * Copyright: Copyright (c) 2009, CHRISTOPHER WRIGHT 9 */ 10 module tango.core.RuntimeTraits; 11 12 import tango.core.Compiler; 13 14 /// If the given type represents a typedef, return the actual type. 15 const(TypeInfo) realType (const(TypeInfo) type) 16 { 17 // TypeInfo_Typedef.next() doesn't return the actual type. 18 // I think it returns TypeInfo_Typedef.base.next(). 19 // So, a slightly different method. 20 auto def = cast(TypeInfo_Typedef) type; 21 if (def !is null) 22 { 23 return def.base; 24 } 25 else if ((type.classinfo.name.length is 14 && type.classinfo.name[9..$] == "Const") || 26 (type.classinfo.name.length is 18 && type.classinfo.name[9..$] == "Invariant") || 27 (type.classinfo.name.length is 15 && type.classinfo.name[9..$] == "Shared") || 28 (type.classinfo.name.length is 14 && type.classinfo.name[9..$] == "Inout")) 29 { 30 return (cast(TypeInfo_Const)type).next; 31 } 32 return type; 33 } 34 35 /// If the given type represents a class, return its ClassInfo; else return null; 36 const(ClassInfo) asClass (const(TypeInfo) type) 37 { 38 if (isInterface (type)) 39 { 40 auto klass = cast(TypeInfo_Interface) type; 41 return klass.info; 42 } 43 if (isClass (type)) 44 { 45 auto klass = cast(TypeInfo_Class) type; 46 return klass.info; 47 } 48 return null; 49 } 50 51 /** Returns true iff one type is an ancestor of the other, or if the types are the same. 52 * If either is null, returns false. */ 53 bool isDerived (const(ClassInfo) derived, const(ClassInfo) base) 54 { 55 auto derived_nc = cast(ClassInfo)derived; 56 auto base_nc = cast(ClassInfo)base; 57 if (derived is null || base is null) 58 return false; 59 do 60 if (derived_nc is base_nc) 61 return true; 62 while ((derived_nc = cast(ClassInfo)derived_nc.base) !is null); 63 return false; 64 } 65 66 /** Returns true iff implementor implements the interface described 67 * by iface. This is an expensive operation (linear in the number of 68 * interfaces and base classes). 69 */ 70 bool implements (const(ClassInfo) implementor, const(ClassInfo) iface) 71 { 72 foreach (info; applyInterfaces (implementor)) 73 { 74 if (iface is info) 75 return true; 76 } 77 return false; 78 } 79 80 /** Returns true iff an instance of class test is implicitly castable to target. 81 * This is an expensive operation (isDerived + implements). */ 82 bool isImplicitly (const(ClassInfo) test, const(ClassInfo) target) 83 { 84 // Keep isDerived first. 85 // isDerived will be much faster than implements. 86 return (isDerived (test, target) || implements (test, target)); 87 } 88 89 /** Returns true iff an instance of type test is implicitly castable to target. 90 * If the types describe classes or interfaces, this is an expensive operation. */ 91 bool isImplicitly (const(TypeInfo) test, const(TypeInfo) target) 92 { 93 // A lot of special cases. This is ugly. 94 if (test is target) 95 return true; 96 if (isStaticArray (test) && isDynamicArray (target) && valueType (test) is valueType (target)) 97 { 98 // you can implicitly cast static to dynamic (currently) if they 99 // have the same value type. Other casts should be forbidden. 100 return true; 101 } 102 auto klass1 = asClass (test); 103 auto klass2 = asClass (target); 104 if (isClass (test) && isClass (target)) 105 { 106 return isDerived (klass1, klass2); 107 } 108 if (isInterface (test) && isInterface (target)) 109 { 110 return isDerived (klass1, klass2); 111 } 112 if (klass1 && klass2) 113 { 114 return isImplicitly (klass1, klass2); 115 } 116 if (klass1 || klass2) 117 { 118 // no casts from class to non-class 119 return false; 120 } 121 if ((isSignedInteger (test) && isSignedInteger (target)) || (isUnsignedInteger (test) && isUnsignedInteger (target)) || (isFloat ( 122 test) && isFloat (target)) || (isCharacter (test) && isCharacter (target))) 123 { 124 return test.tsize () <= target.tsize (); 125 } 126 if (isSignedInteger (test) && isUnsignedInteger (target)) 127 { 128 // potential loss of data 129 return false; 130 } 131 if (isUnsignedInteger (test) && isSignedInteger (target)) 132 { 133 // if the sizes are the same, you could be losing data 134 // the upper half of the range wraps around to negatives 135 // if the target type is larger, you can safely hold it 136 return test.tsize () < target.tsize (); 137 } 138 // delegates and functions: no can do 139 // pointers: no 140 // structs: no 141 return false; 142 } 143 144 /// 145 const(ClassInfo)[] baseClasses (const(ClassInfo) type) 146 { 147 if (type is null) 148 return null; 149 auto type_nc = cast()type; 150 const(ClassInfo)[] types; 151 while ((type_nc = type_nc.base) !is null) 152 types ~= type_nc; 153 return types; 154 } 155 156 /** Returns a list of all interfaces that this type implements, directly 157 * or indirectly. This includes base interfaces of types the class implements, 158 * and interfaces that base classes implement, and base interfaces of interfaces 159 * that base classes implement. This is an expensive operation. */ 160 const(ClassInfo)[] baseInterfaces (const(ClassInfo) type) 161 { 162 if (type is null) 163 return null; 164 auto type_nc = cast()type; 165 const(ClassInfo)[] types = directInterfaces (type); 166 while ((type_nc = type_nc.base) !is null) 167 { 168 types ~= interfaceGraph (type_nc); 169 } 170 return types; 171 } 172 173 /** Returns all the interfaces that this type directly implements, including 174 * inherited interfaces. This is an expensive operation. 175 * 176 * Examples: 177 * --- 178 * interface I1 {} 179 * interface I2 : I1 {} 180 * class A : I2 {} 181 * 182 * auto interfaces = interfaceGraph (A.classinfo); 183 * // interfaces = [I1.classinfo, I2.classinfo] 184 * --- 185 * 186 * --- 187 * interface I1 {} 188 * interface I2 {} 189 * class A : I1 {} 190 * class B : A, I2 {} 191 * 192 * auto interfaces = interfaceGraph (B.classinfo); 193 * // interfaces = [I2.classinfo] 194 * --- 195 */ 196 const(ClassInfo)[] interfaceGraph (const(ClassInfo) type) 197 { 198 const(ClassInfo)[] info; 199 foreach (iface; type.interfaces) 200 { 201 info ~= iface.classinfo; 202 info ~= interfaceGraph (iface.classinfo); 203 } 204 return info; 205 } 206 207 /** Iterate through all interfaces that type implements, directly or indirectly, including base interfaces. */ 208 struct applyInterfaces 209 { 210 /// 211 this(const(ClassInfo) type) 212 { 213 this.type = cast()type; 214 } 215 216 /// 217 int opApply (scope int delegate (ref ClassInfo) dg) 218 { 219 int result = 0; 220 for (; type; type = type.base) 221 { 222 foreach (iface; type.interfaces) 223 { 224 result = dg (iface.classinfo); 225 if (result) 226 return result; 227 result = applyInterfaces (iface.classinfo).opApply (dg); 228 if (result) 229 return result; 230 } 231 } 232 return result; 233 } 234 235 ClassInfo type; 236 } 237 238 /// 239 const(ClassInfo)[] baseTypes (const(ClassInfo) type) 240 { 241 if (type is null) 242 return null; 243 return baseClasses (type) ~ baseInterfaces (type); 244 } 245 246 /// 247 ModuleInfo* moduleOf (const(ClassInfo) type) 248 { 249 foreach (modula; ModuleInfo) 250 foreach (klass; modula.localClasses) 251 if (klass is type) 252 return modula; 253 return null; 254 } 255 256 /// Returns a list of interfaces that this class directly implements. 257 const(ClassInfo)[] directInterfaces (const(ClassInfo) type) 258 { 259 const(ClassInfo)[] types; 260 foreach (iface; type.interfaces) 261 types ~= iface.classinfo; 262 return types; 263 } 264 265 /** Returns a list of all types that are derived from the given type. This does not 266 * count interfaces; that is, if type is an interface, you will only get derived 267 * interfaces back. It is an expensive operations. */ 268 const(ClassInfo)[] derivedTypes (const(ClassInfo) type) 269 { 270 const(ClassInfo)[] types; 271 foreach (modula; ModuleInfo) 272 foreach (klass; modula.localClasses) 273 if (isDerived (klass, type) && (klass !is type)) 274 types ~= klass; 275 return types; 276 } 277 278 /// 279 bool isDynamicArray (const(TypeInfo) type) 280 { 281 // This implementation is evil. 282 // Array typeinfos are named TypeInfo_A?, and defined individually for each 283 // possible type aside from structs. For example, typeinfo for int[] is 284 // TypeInfo_Ai; for uint[], TypeInfo_Ak. 285 // So any TypeInfo with length 11 and starting with TypeInfo_A is an array 286 // type. 287 // Also, TypeInfo_Array is an array type. 288 auto type2 = realType (type); 289 return ((type2.classinfo.name[9] == 'A') && (type2.classinfo.name.length == 11)) || ((type2.classinfo.name.length == 12) && (type2.classinfo.name[9..12] == "Aya")) || 290 ((cast(TypeInfo_Array) type2) !is null); 291 } 292 293 /// 294 bool isStaticArray (const(TypeInfo) type) 295 { 296 auto type2 = realType (type); 297 return (cast(TypeInfo_StaticArray) type2) !is null; 298 } 299 300 /** Returns true iff the given type is a dynamic or static array (false for associative 301 * arrays and non-arrays). */ 302 bool isArray (const(TypeInfo) type) 303 { 304 auto type2 = realType (type); 305 return isDynamicArray (type2) || isStaticArray (type2); 306 } 307 308 /// 309 bool isAssociativeArray (const(TypeInfo) type) 310 { 311 auto type2 = realType (type); 312 return (cast(TypeInfo_AssociativeArray) type2) !is null; 313 } 314 315 /// 316 bool isCharacter (const(TypeInfo) type) 317 { 318 auto type2 = realType (type); 319 return (type2 is typeid(char) || type2 is typeid(wchar) || type2 is typeid(dchar)); 320 } 321 322 /// 323 bool isString (const(TypeInfo) type) 324 { 325 auto type2 = realType (type); 326 return isArray (type2) && isCharacter (valueType (type2)); 327 } 328 329 /// 330 bool isUnsignedInteger (const(TypeInfo) type) 331 { 332 auto type2 = realType (type); 333 return (type2 is typeid(uint) || type2 is typeid(ulong) || type2 is typeid(ushort) || type2 is typeid(ubyte)); 334 } 335 336 /// 337 bool isSignedInteger (const(TypeInfo) type) 338 { 339 auto type2 = realType (type); 340 return (type2 is typeid(int) || type2 is typeid(long) || type2 is typeid(short) || type2 is typeid(byte)); 341 } 342 343 /// 344 bool isInteger (const(TypeInfo) type) 345 { 346 auto type2 = realType (type); 347 return isSignedInteger (type2) || isUnsignedInteger (type2); 348 } 349 350 /// 351 bool isBool (const(TypeInfo) type) 352 { 353 auto type2 = realType (type); 354 return (type2 is typeid(bool)); 355 } 356 357 /// 358 bool isFloat (const(TypeInfo) type) 359 { 360 auto type2 = realType (type); 361 return (type2 is typeid(float) || type2 is typeid(double) || type2 is typeid(real)); 362 } 363 364 /// 365 bool isPrimitive (const(TypeInfo) type) 366 { 367 auto type2 = realType (type); 368 return (isArray (type2) || isAssociativeArray (type2) || isCharacter (type2) || isFloat (type2) || isInteger (type2)); 369 } 370 371 /// Returns true iff the given type represents an interface. 372 bool isInterface (const(TypeInfo) type) 373 { 374 return (cast(TypeInfo_Interface) type) !is null; 375 } 376 377 /// 378 bool isPointer (const(TypeInfo) type) 379 { 380 auto type2 = realType (type); 381 return (cast(TypeInfo_Pointer) type2) !is null; 382 } 383 384 /// Returns true iff the type represents a class (false for interfaces). 385 bool isClass (const(TypeInfo) type) 386 { 387 auto type2 = realType (type); 388 return (cast(TypeInfo_Class) type2) !is null; 389 } 390 391 /// 392 bool isStruct (const(TypeInfo) type) 393 { 394 auto type2 = realType (type); 395 return (cast(TypeInfo_Struct) type2) !is null; 396 } 397 398 /// 399 bool isFunction (const(TypeInfo) type) 400 { 401 auto type2 = realType (type); 402 return ((cast(TypeInfo_Function) type2) !is null) || ((cast(TypeInfo_Delegate) type2) !is null); 403 } 404 405 /** Returns true iff the given type is a reference type. */ 406 bool isReferenceType (const(TypeInfo) type) 407 { 408 return isClass (type) || isPointer (type) || isDynamicArray (type); 409 } 410 411 /** Returns true iff the given type represents a user-defined type. 412 * This does not include functions, delegates, aliases, or typedefs. */ 413 bool isUserDefined (const(TypeInfo) type) 414 { 415 return isClass (type) || isStruct (type); 416 } 417 418 /** Returns true for all value types, false for all reference types. 419 * For functions and delegates, returns false (is this the way it should be?). */ 420 bool isValueType (const(TypeInfo) type) 421 { 422 return !(isDynamicArray (type) || isAssociativeArray (type) || isPointer (type) || isClass (type) || isFunction ( 423 type)); 424 } 425 426 /** The key type of the given type. For an array, size_t; for an associative 427 * array T[U], U. */ 428 const(TypeInfo) keyType (const(TypeInfo) type) 429 { 430 auto type2 = realType (type); 431 auto assocArray = cast(TypeInfo_AssociativeArray) type2; 432 if (assocArray) 433 return assocArray.key; 434 if (isArray (type2)) 435 return typeid(size_t); 436 return null; 437 } 438 439 /** The value type of the given type -- given T[] or T[n], T; given T[U], 440 * T; given T*, T; anything else, null. */ 441 const(TypeInfo) valueType (const(TypeInfo) type) 442 { 443 auto type2 = realType (type); 444 if (isArray (type2)) 445 return type2.next; 446 auto assocArray = cast(TypeInfo_AssociativeArray) type2; 447 if (assocArray) 448 return assocArray.value; 449 auto pointer = cast(TypeInfo_Pointer) type2; 450 if (pointer) 451 return pointer.m_next; 452 return null; 453 } 454 455 /** If the given type represents a delegate or function, the return type 456 * of that function. Otherwise, null. */ 457 const(TypeInfo) returnType (const(TypeInfo) type) 458 { 459 auto type2 = realType (type); 460 auto delegat = cast(TypeInfo_Delegate) type2; 461 if (delegat) 462 return delegat.next; 463 auto func = cast(TypeInfo_Function) type2; 464 if (func) 465 return func.next; 466 return null; 467 } 468 469 debug (UnitTest) 470 { 471 472 interface I1 473 { 474 } 475 476 interface I2 477 { 478 } 479 480 interface I3 481 { 482 } 483 484 interface I4 485 { 486 } 487 488 class A 489 { 490 } 491 492 class B : A, I1 493 { 494 } 495 496 class C : B, I2, I3 497 { 498 } 499 500 class D : A, I1 501 { 502 int foo (int i) 503 { 504 return i; 505 } 506 } 507 508 struct S1 509 { 510 } 511 512 unittest { 513 // Struct-related stuff. 514 auto type = typeid(S1); 515 assert (isStruct (type)); 516 assert (isValueType (type)); 517 assert (isUserDefined (type)); 518 assert (!isClass (type)); 519 assert (!isPointer (type)); 520 assert (null is returnType (type)); 521 assert (!isPrimitive (type)); 522 assert (valueType (type) is null); 523 } 524 525 unittest { 526 auto type = A.classinfo; 527 assert (baseTypes (type) == [Object.classinfo]); 528 assert (baseClasses (type) == [Object.classinfo]); 529 assert (baseInterfaces (type).length == 0); 530 type = C.classinfo; 531 assert (baseClasses (type) == [B.classinfo, A.classinfo, Object.classinfo]); 532 assert (baseInterfaces (type) == [I2.classinfo, I3.classinfo, I1.classinfo]); 533 assert (baseTypes (type) == [B.classinfo, A.classinfo, Object.classinfo, I2.classinfo, I3.classinfo, 534 I1.classinfo]); 535 } 536 537 unittest { 538 assert (isPointer (typeid(S1*))); 539 assert (isArray (typeid(S1[]))); 540 assert (valueType (typeid(S1*)) is typeid(S1)); 541 auto d = new D; 542 assert (returnType (typeid(typeof(&d.foo))) is typeid(int)); 543 assert (isFloat (typeid(real))); 544 assert (isFloat (typeid(double))); 545 assert (isFloat (typeid(float))); 546 assert (!isFloat (typeid(creal))); 547 assert (!isFloat (typeid(cdouble))); 548 assert (!isInteger (typeid(float))); 549 assert (!isInteger (typeid(creal))); 550 assert (isInteger (typeid(ulong))); 551 assert (isInteger (typeid(ubyte))); 552 assert (isCharacter (typeid(char))); 553 assert (isCharacter (typeid(wchar))); 554 assert (isCharacter (typeid(dchar))); 555 assert (!isCharacter (typeid(ubyte))); 556 assert (isArray (typeid(typeof("hello")))); 557 assert (isCharacter (typeid(typeof("hello"[0])))); 558 assert (valueType (typeid(typeof("hello"))) is typeid(typeof(cast(immutable(char))'h'))); 559 assert (isString (typeid(typeof("hello"))), typeof("hello").stringof); 560 immutable(dchar)[5] staticString_s = "hello"d; 561 auto staticString = typeid(typeof(staticString_s)); 562 auto dynamicString = typeid(typeof("hello"d[0 .. $])); 563 assert (isString (staticString)); 564 assert (isStaticArray (staticString)); 565 assert (isDynamicArray (dynamicString), dynamicString.toString () ~ dynamicString.classinfo.name); 566 assert (isString (dynamicString)); 567 568 auto type = typeid(int[immutable(char)[]]); 569 assert (valueType (type) is typeid(int), (cast()valueType (type)).toString ()); 570 assert (keyType (type) is typeid(immutable(char)[]), (cast()keyType (type)).toString ()); 571 void delegate (int) dg = (int i) 572 { 573 }; 574 assert (returnType (typeid(typeof(dg))) is typeid(void)); 575 assert (returnType (typeid(int delegate (int))) is typeid(int)); 576 577 assert (!isDynamicArray (typeid(int[4]))); 578 assert (isStaticArray (typeid(int[4]))); 579 } 580 581 }