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
27namespace art {
28
29namespace mirror {
30class Object;  // forward declaration
31}  // namespace mirror
32
33namespace lambda {
34
35struct 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.
44struct 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.
338inline std::ostream& operator<<(std::ostream& ostream, ShortyFieldType shorty) {
339  return ostream << shorty.ToString();
340}
341
342static_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
351struct 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.
435template <decltype(ShortyFieldType::kByte) Shorty>
436struct 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>())
446template <typename T>
447struct ShortyFieldTypeSelectEnum {
448};
449
450#define SHORTY_FIELD_TYPE_SELECT_IMPL(cpp_type, enum_element)      \
451template <> \
452struct ShortyFieldTypeSelectType<ShortyFieldType::enum_element> { \
453  using type = cpp_type; \
454}; \
455\
456template <> \
457struct ShortyFieldTypeSelectEnum<cpp_type> { \
458  static constexpr const auto value = ShortyFieldType::enum_element; \
459}; \
460
461SHORTY_FIELD_TYPE_SELECT_IMPL(bool, kBoolean);
462SHORTY_FIELD_TYPE_SELECT_IMPL(int8_t, kByte);
463SHORTY_FIELD_TYPE_SELECT_IMPL(int16_t, kShort);
464SHORTY_FIELD_TYPE_SELECT_IMPL(uint16_t, kChar);
465SHORTY_FIELD_TYPE_SELECT_IMPL(int32_t, kInt);
466SHORTY_FIELD_TYPE_SELECT_IMPL(float, kFloat);
467SHORTY_FIELD_TYPE_SELECT_IMPL(int64_t, kLong);
468SHORTY_FIELD_TYPE_SELECT_IMPL(double, kDouble);
469SHORTY_FIELD_TYPE_SELECT_IMPL(mirror::Object*, kObject);
470SHORTY_FIELD_TYPE_SELECT_IMPL(Closure*, kLambda);
471
472}  // namespace lambda
473}  // namespace art
474
475#endif  // ART_RUNTIME_LAMBDA_SHORTY_FIELD_TYPE_H_
476