1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #ifndef ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_ 17 #define ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_ 18 19 #include "base/logging.h" 20 #include "base/macros.h" 21 #include "base/value_object.h" 22 #include "globals.h" 23 #include "runtime/primitive.h" 24 25 #include <ostream> 26 27 namespace art { 28 29 namespace mirror { 30 class Object; // forward declaration 31 } // namespace mirror 32 33 namespace lambda { 34 35 struct Closure; // forward declaration 36 37 // TODO: Refactor together with primitive.h 38 39 // The short form of a field type descriptor. Corresponds to ShortyFieldType in dex specification. 40 // Only types usable by a field (and locals) are allowed (i.e. no void type). 41 // Note that arrays and objects are treated both as 'L'. 42 // 43 // This is effectively a 'char' enum-like zero-cost type-safe wrapper with extra helper functions. 44 struct ShortyFieldType : ValueObject { 45 // Use as if this was an enum class, e.g. 'ShortyFieldType::kBoolean'. 46 enum : char { 47 // Primitives (Narrow): 48 kBoolean = 'Z', 49 kByte = 'B', 50 kChar = 'C', 51 kShort = 'S', 52 kInt = 'I', 53 kFloat = 'F', 54 // Primitives (Wide): 55 kLong = 'J', 56 kDouble = 'D', 57 // Managed types: 58 kObject = 'L', // This can also be an array (which is otherwise '[' in a non-shorty). 59 kLambda = '\\', 60 }; // NOTE: This is an anonymous enum so we can get exhaustive switch checking from the compiler. 61 62 // Implicitly construct from the enum above. Value must be one of the enum list members above. 63 // Always safe to use, does not do any DCHECKs. ShortyFieldTypeShortyFieldType64 inline constexpr ShortyFieldType(decltype(kByte) c) : value_(c) { 65 } 66 67 // Default constructor. The initial value is undefined. Initialize before calling methods. 68 // This is very unsafe but exists as a convenience to having undefined values. ShortyFieldTypeShortyFieldType69 explicit ShortyFieldType() : value_(StaticCastValue(0)) { 70 } 71 72 // Explicitly construct from a char. Value must be one of the enum list members above. 73 // Conversion is potentially unsafe, so DCHECKing is performed. ShortyFieldTypeShortyFieldType74 explicit inline ShortyFieldType(char c) : value_(StaticCastValue(c)) { 75 if (kIsDebugBuild) { 76 // Verify at debug-time that our conversion is safe. 77 ShortyFieldType ignored; 78 DCHECK(MaybeCreate(c, &ignored)) << "unknown shorty field type '" << c << "'"; 79 } 80 } 81 82 // Attempts to parse the character in 'shorty_field_type' into its strongly typed version. 83 // Returns false if the character was out of range of the grammar. MaybeCreateShortyFieldType84 static bool MaybeCreate(char shorty_field_type, ShortyFieldType* out) { 85 DCHECK(out != nullptr); 86 switch (shorty_field_type) { 87 case kBoolean: 88 case kByte: 89 case kChar: 90 case kShort: 91 case kInt: 92 case kFloat: 93 case kLong: 94 case kDouble: 95 case kObject: 96 case kLambda: 97 *out = ShortyFieldType(static_cast<decltype(kByte)>(shorty_field_type)); 98 return true; 99 default: 100 break; 101 } 102 103 return false; 104 } 105 106 // Convert the first type in a field type descriptor string into a shorty. 107 // Arrays are converted into objects. 108 // Does not work for 'void' types (as they are illegal in a field type descriptor). CreateFromFieldTypeDescriptorShortyFieldType109 static ShortyFieldType CreateFromFieldTypeDescriptor(const char* field_type_descriptor) { 110 DCHECK(field_type_descriptor != nullptr); 111 char c = *field_type_descriptor; 112 if (UNLIKELY(c == kArray)) { // Arrays are treated as object references. 113 c = kObject; 114 } 115 return ShortyFieldType{c}; // NOLINT [readability/braces] [4] 116 } 117 118 // Parse the first type in the field type descriptor string into a shorty. 119 // See CreateFromFieldTypeDescriptor for more details. 120 // 121 // Returns the pointer offset into the middle of the field_type_descriptor 122 // that would either point to the next shorty type, or to null if there are 123 // no more types. 124 // 125 // DCHECKs that each of the nested types is a valid shorty field type. This 126 // means the type descriptor must be already valid. ParseFromFieldTypeDescriptorShortyFieldType127 static const char* ParseFromFieldTypeDescriptor(const char* field_type_descriptor, 128 ShortyFieldType* out_type) { 129 DCHECK(field_type_descriptor != nullptr); 130 131 if (UNLIKELY(field_type_descriptor[0] == '\0')) { 132 // Handle empty strings by immediately returning null. 133 return nullptr; 134 } 135 136 // All non-empty strings must be a valid list of field type descriptors, otherwise 137 // the DCHECKs will kick in and the program will crash. 138 const char shorter_type = *field_type_descriptor; 139 140 ShortyFieldType safe_type; 141 bool type_set = MaybeCreate(shorter_type, &safe_type); 142 143 // Lambda that keeps skipping characters until it sees ';'. 144 // Stops one character -after- the ';'. 145 auto skip_until_semicolon = [&field_type_descriptor]() { 146 while (*field_type_descriptor != ';' && *field_type_descriptor != '\0') { 147 ++field_type_descriptor; 148 } 149 DCHECK_NE(*field_type_descriptor, '\0') 150 << " type descriptor terminated too early: " << field_type_descriptor; 151 ++field_type_descriptor; // Skip the ';' 152 }; 153 154 ++field_type_descriptor; 155 switch (shorter_type) { 156 case kObject: 157 skip_until_semicolon(); 158 159 DCHECK(type_set); 160 DCHECK(safe_type == kObject); 161 break; 162 case kArray: 163 // Strip out all of the leading [[[[[s, we don't care if it's a multi-dimensional array. 164 while (*field_type_descriptor == '[' && *field_type_descriptor != '\0') { 165 ++field_type_descriptor; 166 } 167 DCHECK_NE(*field_type_descriptor, '\0') 168 << " type descriptor terminated too early: " << field_type_descriptor; 169 // Either a primitive, object, or closure left. No more arrays. 170 { 171 // Now skip all the characters that form the array's interior-most element type 172 // (which itself is guaranteed not to be an array). 173 ShortyFieldType array_interior_type; 174 type_set = MaybeCreate(*field_type_descriptor, &array_interior_type); 175 DCHECK(type_set) << " invalid remaining type descriptor " << field_type_descriptor; 176 177 // Handle array-of-objects case like [[[[[LObject; and array-of-closures like [[[[[\Foo; 178 if (*field_type_descriptor == kObject || *field_type_descriptor == kLambda) { 179 skip_until_semicolon(); 180 } else { 181 // Handle primitives which are exactly one character we can skip. 182 DCHECK(array_interior_type.IsPrimitive()); 183 ++field_type_descriptor; 184 } 185 } 186 187 safe_type = kObject; 188 type_set = true; 189 break; 190 case kLambda: 191 skip_until_semicolon(); 192 193 DCHECK(safe_type == kLambda); 194 DCHECK(type_set); 195 break; 196 default: 197 DCHECK_NE(kVoid, shorter_type) << "cannot make a ShortyFieldType from a void type"; 198 break; 199 } 200 201 DCHECK(type_set) << "invalid shorty type descriptor " << shorter_type; 202 203 *out_type = safe_type; 204 return type_set ? field_type_descriptor : nullptr; 205 } 206 207 // Explicitly convert to a char. 208 inline explicit operator char() const { 209 return value_; 210 } 211 212 // Is this a primitive? IsPrimitiveShortyFieldType213 inline bool IsPrimitive() const { 214 return IsPrimitiveNarrow() || IsPrimitiveWide(); 215 } 216 217 // Is this a narrow primitive (i.e. can fit into 1 virtual register)? IsPrimitiveNarrowShortyFieldType218 inline bool IsPrimitiveNarrow() const { 219 switch (value_) { 220 case kBoolean: 221 case kByte: 222 case kChar: 223 case kShort: 224 case kInt: 225 case kFloat: 226 return true; 227 default: 228 return false; 229 } 230 } 231 232 // Is this a wide primitive (i.e. needs exactly 2 virtual registers)? IsPrimitiveWideShortyFieldType233 inline bool IsPrimitiveWide() const { 234 switch (value_) { 235 case kLong: 236 case kDouble: 237 return true; 238 default: 239 return false; 240 } 241 } 242 243 // Is this an object reference (which can also be an array)? IsObjectShortyFieldType244 inline bool IsObject() const { 245 return value_ == kObject; 246 } 247 248 // Is this a lambda? IsLambdaShortyFieldType249 inline bool IsLambda() const { 250 return value_ == kLambda; 251 } 252 253 // Is the size of this (to store inline as a field) always known at compile-time? IsStaticSizeShortyFieldType254 inline bool IsStaticSize() const { 255 return !IsLambda(); 256 } 257 258 // Get the compile-time size (to be able to store it inline as a field or on stack). 259 // Dynamically-sized values such as lambdas return the guaranteed lower bound. GetStaticSizeShortyFieldType260 inline size_t GetStaticSize() const { 261 switch (value_) { 262 case kBoolean: 263 return sizeof(bool); 264 case kByte: 265 return sizeof(uint8_t); 266 case kChar: 267 return sizeof(int16_t); 268 case kShort: 269 return sizeof(uint16_t); 270 case kInt: 271 return sizeof(int32_t); 272 case kLong: 273 return sizeof(int64_t); 274 case kFloat: 275 return sizeof(float); 276 case kDouble: 277 return sizeof(double); 278 case kObject: 279 return kObjectReferenceSize; 280 case kLambda: 281 return sizeof(void*); // Large enough to store the ArtLambdaMethod 282 default: 283 DCHECK(false) << "unknown shorty field type '" << static_cast<char>(value_) << "'"; 284 UNREACHABLE(); 285 } 286 } 287 288 // Implicitly convert to the anonymous nested inner type. Used for exhaustive switch detection. decltypeShortyFieldType289 inline operator decltype(kByte)() const { 290 return value_; 291 } 292 293 // Returns a read-only static string representing the enum name, useful for printing/debug only. ToStringShortyFieldType294 inline const char* ToString() const { 295 switch (value_) { 296 case kBoolean: 297 return "kBoolean"; 298 case kByte: 299 return "kByte"; 300 case kChar: 301 return "kChar"; 302 case kShort: 303 return "kShort"; 304 case kInt: 305 return "kInt"; 306 case kLong: 307 return "kLong"; 308 case kFloat: 309 return "kFloat"; 310 case kDouble: 311 return "kDouble"; 312 case kObject: 313 return "kObject"; 314 case kLambda: 315 return "kLambda"; 316 default: 317 // Undefined behavior if we get this far. Pray the compiler gods are merciful. 318 return "<undefined>"; 319 } 320 } 321 322 private: 323 static constexpr const char kArray = '['; 324 static constexpr const char kVoid = 'V'; 325 326 // Helper to statically cast anything into our nested anonymous enum type. 327 template <typename T> decltypeShortyFieldType328 inline static decltype(kByte) StaticCastValue(const T& anything) { 329 return static_cast<decltype(value_)>(anything); 330 } 331 332 // The only field in this struct. 333 decltype(kByte) value_; 334 }; 335 336 337 // Print to an output stream. 338 inline std::ostream& operator<<(std::ostream& ostream, ShortyFieldType shorty) { 339 return ostream << shorty.ToString(); 340 } 341 342 static_assert(sizeof(ShortyFieldType) == sizeof(char), 343 "ShortyFieldType must be lightweight just like a char"); 344 345 // Compile-time trait information regarding the ShortyFieldType. 346 // Used by static_asserts to verify that the templates are correctly used at compile-time. 347 // 348 // For example, 349 // ShortyFieldTypeTraits::IsPrimitiveNarrowType<int64_t>() == true 350 // ShortyFieldTypeTraits::IsObjectType<mirror::Object*>() == true 351 struct ShortyFieldTypeTraits { 352 // A type guaranteed to be large enough to holds any of the shorty field types. 353 using MaxType = uint64_t; 354 355 // Type traits: Returns true if 'T' is a valid type that can be represented by a shorty field type. 356 template <typename T> IsTypeShortyFieldTypeTraits357 static inline constexpr bool IsType() { 358 return IsPrimitiveType<T>() || IsObjectType<T>() || IsLambdaType<T>(); 359 } 360 361 // Returns true if 'T' is a primitive type (i.e. a built-in without nested references). 362 template <typename T> IsPrimitiveTypeShortyFieldTypeTraits363 static inline constexpr bool IsPrimitiveType() { 364 return IsPrimitiveNarrowType<T>() || IsPrimitiveWideType<T>(); 365 } 366 367 // Returns true if 'T' is a primitive type that is narrow (i.e. can be stored into 1 vreg). 368 template <typename T> IsPrimitiveNarrowTypeShortyFieldTypeTraits369 static inline constexpr bool IsPrimitiveNarrowType() { 370 return IsPrimitiveNarrowTypeImpl(static_cast<T* const>(nullptr)); 371 } 372 373 // Returns true if 'T' is a primitive type that is wide (i.e. needs 2 vregs for storage). 374 template <typename T> IsPrimitiveWideTypeShortyFieldTypeTraits375 static inline constexpr bool IsPrimitiveWideType() { 376 return IsPrimitiveWideTypeImpl(static_cast<T* const>(nullptr)); 377 } 378 379 // Returns true if 'T' is an object (i.e. it is a managed GC reference). 380 // Note: This is equivalent to std::base_of<mirror::Object*, T>::value 381 template <typename T> IsObjectTypeShortyFieldTypeTraits382 static inline constexpr bool IsObjectType() { 383 return IsObjectTypeImpl(static_cast<T* const>(nullptr)); 384 } 385 386 // Returns true if 'T' is a lambda (i.e. it is a closure with unknown static data); 387 template <typename T> IsLambdaTypeShortyFieldTypeTraits388 static inline constexpr bool IsLambdaType() { 389 return IsLambdaTypeImpl(static_cast<T* const>(nullptr)); 390 } 391 392 private: 393 #define IS_VALID_TYPE_SPECIALIZATION(type, name) \ 394 static inline constexpr bool Is ## name ## TypeImpl(type* const = 0) { \ 395 return true; \ 396 } \ 397 \ 398 static_assert(sizeof(MaxType) >= sizeof(type), "MaxType too small") 399 400 IS_VALID_TYPE_SPECIALIZATION(bool, PrimitiveNarrow); 401 IS_VALID_TYPE_SPECIALIZATION(int8_t, PrimitiveNarrow); 402 IS_VALID_TYPE_SPECIALIZATION(uint8_t, PrimitiveNarrow); // Not strictly true, but close enough. 403 IS_VALID_TYPE_SPECIALIZATION(int16_t, PrimitiveNarrow); 404 IS_VALID_TYPE_SPECIALIZATION(uint16_t, PrimitiveNarrow); // Chars are unsigned. 405 IS_VALID_TYPE_SPECIALIZATION(int32_t, PrimitiveNarrow); 406 IS_VALID_TYPE_SPECIALIZATION(uint32_t, PrimitiveNarrow); // Not strictly true, but close enough. 407 IS_VALID_TYPE_SPECIALIZATION(float, PrimitiveNarrow); 408 IS_VALID_TYPE_SPECIALIZATION(int64_t, PrimitiveWide); 409 IS_VALID_TYPE_SPECIALIZATION(uint64_t, PrimitiveWide); // Not strictly true, but close enough. 410 IS_VALID_TYPE_SPECIALIZATION(double, PrimitiveWide); 411 IS_VALID_TYPE_SPECIALIZATION(mirror::Object*, Object); 412 IS_VALID_TYPE_SPECIALIZATION(Closure*, Lambda); 413 #undef IS_VALID_TYPE_SPECIALIZATION 414 415 #define IS_VALID_TYPE_SPECIALIZATION_IMPL(name) \ 416 template <typename T> \ 417 static inline constexpr bool Is ## name ## TypeImpl(T* const = 0) { \ 418 return false; \ 419 } 420 421 IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveNarrow); 422 IS_VALID_TYPE_SPECIALIZATION_IMPL(PrimitiveWide); 423 IS_VALID_TYPE_SPECIALIZATION_IMPL(Object); 424 IS_VALID_TYPE_SPECIALIZATION_IMPL(Lambda); 425 426 #undef IS_VALID_TYPE_SPECIALIZATION_IMPL 427 }; 428 429 // Maps the ShortyFieldType enum into it's C++ type equivalent, into the "type" typedef. 430 // For example: 431 // ShortyFieldTypeSelectType<ShortyFieldType::kBoolean>::type => bool 432 // ShortyFieldTypeSelectType<ShortyFieldType::kLong>::type => int64_t 433 // 434 // Invalid enums will not have the type defined. 435 template <decltype(ShortyFieldType::kByte) Shorty> 436 struct ShortyFieldTypeSelectType { 437 }; 438 439 // Maps the C++ type into it's ShortyFieldType enum equivalent, into the "value" constexpr. 440 // For example: 441 // ShortyFieldTypeSelectEnum<bool>::value => ShortyFieldType::kBoolean 442 // ShortyFieldTypeSelectEnum<int64_t>::value => ShortyFieldType::kLong 443 // 444 // Signed-ness must match for a valid select, e.g. uint64_t will not map to kLong, but int64_t will. 445 // Invalid types will not have the value defined (see e.g. ShortyFieldTypeTraits::IsType<T>()) 446 template <typename T> 447 struct ShortyFieldTypeSelectEnum { 448 }; 449 450 #define SHORTY_FIELD_TYPE_SELECT_IMPL(cpp_type, enum_element) \ 451 template <> \ 452 struct ShortyFieldTypeSelectType<ShortyFieldType::enum_element> { \ 453 using type = cpp_type; \ 454 }; \ 455 \ 456 template <> \ 457 struct ShortyFieldTypeSelectEnum<cpp_type> { \ 458 static constexpr const auto value = ShortyFieldType::enum_element; \ 459 }; \ 460 461 SHORTY_FIELD_TYPE_SELECT_IMPL(bool, kBoolean); 462 SHORTY_FIELD_TYPE_SELECT_IMPL(int8_t, kByte); 463 SHORTY_FIELD_TYPE_SELECT_IMPL(int16_t, kShort); 464 SHORTY_FIELD_TYPE_SELECT_IMPL(uint16_t, kChar); 465 SHORTY_FIELD_TYPE_SELECT_IMPL(int32_t, kInt); 466 SHORTY_FIELD_TYPE_SELECT_IMPL(float, kFloat); 467 SHORTY_FIELD_TYPE_SELECT_IMPL(int64_t, kLong); 468 SHORTY_FIELD_TYPE_SELECT_IMPL(double, kDouble); 469 SHORTY_FIELD_TYPE_SELECT_IMPL(mirror::Object*, kObject); 470 SHORTY_FIELD_TYPE_SELECT_IMPL(Closure*, kLambda); 471 472 } // namespace lambda 473 } // namespace art 474 475 #endif // ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_ 476