Home | History | Annotate | Download | only in lambda
      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.
     64   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.
     69   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.
     74   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.
     84   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).
    109   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.
    127   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?
    213   inline bool IsPrimitive() const {
    214     return IsPrimitiveNarrow() || IsPrimitiveWide();
    215   }
    216 
    217   // Is this a narrow primitive (i.e. can fit into 1 virtual register)?
    218   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)?
    233   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)?
    244   inline bool IsObject() const {
    245     return value_ == kObject;
    246   }
    247 
    248   // Is this a lambda?
    249   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?
    254   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.
    260   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.
    289   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.
    294   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>
    328   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>
    357   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>
    363   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>
    369   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>
    375   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>
    382   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>
    388   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