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
17 #include "lambda/shorty_field_type.h"
18 #include "mirror/object_reference.h"
19
20 #include "utils.h"
21 #include <numeric>
22 #include <stdint.h>
23 #include "gtest/gtest.h"
24
25 #define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
26 reinterpret_cast<void*>(nullptr));
27
28 namespace art {
29 namespace lambda {
30
31 class ShortyFieldTypeTest : public ::testing::Test {
32 public:
33 ShortyFieldTypeTest() = default;
34 ~ShortyFieldTypeTest() = default;
35
36 protected:
SetUpTestCase()37 static void SetUpTestCase() {
38 }
39
SetUp()40 virtual void SetUp() {
41 }
42
IsResultSuccessful(bool result)43 static ::testing::AssertionResult IsResultSuccessful(bool result) {
44 if (result) {
45 return ::testing::AssertionSuccess();
46 } else {
47 return ::testing::AssertionFailure();
48 }
49 }
50
51 template <typename T>
ListToString(const T & list)52 static std::string ListToString(const T& list) {
53 std::stringstream stream;
54
55 stream << "[";
56 for (auto&& val : list) {
57 stream << val << ", ";
58 }
59 stream << "]";
60
61 return stream.str();
62 }
63
64 // Compare two vector-like types for equality.
65 template <typename T>
AreListsEqual(const T & expected,const T & actual)66 static ::testing::AssertionResult AreListsEqual(const T& expected, const T& actual) {
67 bool success = true;
68 std::stringstream stream;
69
70 if (expected.size() != actual.size()) {
71 success = false;
72 stream << "Expected list size: " << expected.size()
73 << ", but got list size: " << actual.size();
74 stream << std::endl;
75 }
76
77 for (size_t j = 0; j < std::min(expected.size(), actual.size()); ++j) {
78 if (expected[j] != actual[j]) {
79 success = false;
80 stream << "Expected element '" << j << "' to be '" << expected[j] << "', but got actual: '"
81 << actual[j] << "'.";
82 stream << std::endl;
83 }
84 }
85
86 if (success) {
87 return ::testing::AssertionSuccess();
88 }
89
90 stream << "Expected list was: " << ListToString(expected)
91 << ", actual list was: " << ListToString(actual);
92
93 return ::testing::AssertionFailure() << stream.str();
94 }
95
ParseLongTypeDescriptorsToList(const char * type_descriptor)96 static std::vector<ShortyFieldType> ParseLongTypeDescriptorsToList(const char* type_descriptor) {
97 std::vector<ShortyFieldType> lst;
98
99 ShortyFieldType shorty;
100
101 const char* parsed = type_descriptor;
102 while ((parsed = ShortyFieldType::ParseFromFieldTypeDescriptor(parsed, &shorty)) != nullptr) {
103 lst.push_back(shorty);
104 }
105
106 return lst;
107 }
108
109 protected:
110 // Shorthands for the ShortyFieldType constants.
111 // The letters are the same as JNI letters, with kS_ being a lambda since \ is not available.
112 static constexpr ShortyFieldType kSZ = ShortyFieldType::kBoolean;
113 static constexpr ShortyFieldType kSB = ShortyFieldType::kByte;
114 static constexpr ShortyFieldType kSC = ShortyFieldType::kChar;
115 static constexpr ShortyFieldType kSS = ShortyFieldType::kShort;
116 static constexpr ShortyFieldType kSI = ShortyFieldType::kInt;
117 static constexpr ShortyFieldType kSF = ShortyFieldType::kFloat;
118 static constexpr ShortyFieldType kSJ = ShortyFieldType::kLong;
119 static constexpr ShortyFieldType kSD = ShortyFieldType::kDouble;
120 static constexpr ShortyFieldType kSL = ShortyFieldType::kObject;
121 static constexpr ShortyFieldType kS_ = ShortyFieldType::kLambda;
122 };
123
TEST_F(ShortyFieldTypeTest,TestMaybeCreate)124 TEST_F(ShortyFieldTypeTest, TestMaybeCreate) {
125 ShortyFieldType shorty;
126
127 std::vector<char> shorties = {'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D', 'L', '\\'};
128
129 // All valid 'shorty' characters are created successfully.
130 for (const char c : shorties) {
131 EXPECT_TRUE(ShortyFieldType::MaybeCreate(c, &shorty)) << c;
132 EXPECT_EQ(c, static_cast<char>(c));
133 }
134
135 // All other characters can never be created.
136 for (unsigned char c = 0; c < std::numeric_limits<unsigned char>::max(); ++c) {
137 // Skip the valid characters.
138 if (std::find(shorties.begin(), shorties.end(), c) != shorties.end()) { continue; }
139 // All invalid characters should fail.
140 EXPECT_FALSE(ShortyFieldType::MaybeCreate(static_cast<char>(c), &shorty)) << c;
141 }
142 } // TEST_F
143
TEST_F(ShortyFieldTypeTest,TestCreateFromFieldTypeDescriptor)144 TEST_F(ShortyFieldTypeTest, TestCreateFromFieldTypeDescriptor) {
145 // Sample input.
146 std::vector<const char*> lengthies = {
147 "Z", "B", "C", "S", "I", "F", "J", "D", "LObject;", "\\Closure;",
148 "[Z", "[[B", "[[LObject;"
149 };
150
151 // Expected output.
152 std::vector<ShortyFieldType> expected = {
153 ShortyFieldType::kBoolean,
154 ShortyFieldType::kByte,
155 ShortyFieldType::kChar,
156 ShortyFieldType::kShort,
157 ShortyFieldType::kInt,
158 ShortyFieldType::kFloat,
159 ShortyFieldType::kLong,
160 ShortyFieldType::kDouble,
161 ShortyFieldType::kObject,
162 ShortyFieldType::kLambda,
163 // Arrays are always treated as objects.
164 ShortyFieldType::kObject,
165 ShortyFieldType::kObject,
166 ShortyFieldType::kObject,
167 };
168
169 // All valid lengthy types are correctly turned into the expected shorty type.
170 for (size_t i = 0; i < lengthies.size(); ++i) {
171 EXPECT_EQ(expected[i], ShortyFieldType::CreateFromFieldTypeDescriptor(lengthies[i]));
172 }
173 } // TEST_F
174
TEST_F(ShortyFieldTypeTest,TestParseFromFieldTypeDescriptor)175 TEST_F(ShortyFieldTypeTest, TestParseFromFieldTypeDescriptor) {
176 // Sample input.
177 std::vector<const char*> lengthies = {
178 // Empty list
179 "",
180 // Primitives
181 "Z", "B", "C", "S", "I", "F", "J", "D",
182 // Non-primitives
183 "LObject;", "\\Closure;",
184 // Arrays. The biggest PITA.
185 "[Z", "[[B", "[[LObject;", "[[[[\\Closure;",
186 // Multiple things at once:
187 "ZBCSIFJD",
188 "LObject;LObject;SSI",
189 "[[ZDDZ",
190 "[[LObject;[[Z[F\\Closure;LObject;",
191 };
192
193 // Expected output.
194 std::vector<std::vector<ShortyFieldType>> expected = {
195 // Empty list
196 {},
197 // Primitives
198 {kSZ}, {kSB}, {kSC}, {kSS}, {kSI}, {kSF}, {kSJ}, {kSD},
199 // Non-primitives.
200 { ShortyFieldType::kObject }, { ShortyFieldType::kLambda },
201 // Arrays are always treated as objects.
202 { kSL }, { kSL }, { kSL }, { kSL },
203 // Multiple things at once:
204 { kSZ, kSB, kSC, kSS, kSI, kSF, kSJ, kSD },
205 { kSL, kSL, kSS, kSS, kSI },
206 { kSL, kSD, kSD, kSZ },
207 { kSL, kSL, kSL, kS_, kSL },
208 };
209
210 // Sanity check that the expected/actual lists are the same size.. when adding new entries.
211 ASSERT_EQ(expected.size(), lengthies.size());
212
213 // All valid lengthy types are correctly turned into the expected shorty type.
214 for (size_t i = 0; i < expected.size(); ++i) {
215 const std::vector<ShortyFieldType>& expected_list = expected[i];
216 std::vector<ShortyFieldType> actual_list = ParseLongTypeDescriptorsToList(lengthies[i]);
217 EXPECT_TRUE(AreListsEqual(expected_list, actual_list));
218 }
219 } // TEST_F
220
221 // Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
222 template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
223 struct ShortyTypeCharacteristics {
224 bool is_primitive_ = false;
225 bool is_primitive_narrow_ = false;
226 bool is_primitive_wide_ = false;
227 bool is_object_ = false;
228 bool is_lambda_ = false;
229 size_t size_ = sizeof(T);
230 bool is_dynamic_sized_ = false;
231
CheckExpectsart::lambda::ShortyTypeCharacteristics232 void CheckExpects() {
233 ShortyFieldType shorty = kShortyEnum;
234
235 // Test the main non-parsing-related ShortyFieldType characteristics.
236 EXPECT_EQ(is_primitive_, shorty.IsPrimitive());
237 EXPECT_EQ(is_primitive_narrow_, shorty.IsPrimitiveNarrow());
238 EXPECT_EQ(is_primitive_wide_, shorty.IsPrimitiveWide());
239 EXPECT_EQ(is_object_, shorty.IsObject());
240 EXPECT_EQ(is_lambda_, shorty.IsLambda());
241 EXPECT_EQ(size_, shorty.GetStaticSize());
242 EXPECT_EQ(is_dynamic_sized_, !shorty.IsStaticSize());
243
244 // Test compile-time ShortyFieldTypeTraits.
245 EXPECT_TRUE(ShortyFieldTypeTraits::IsType<T>());
246 EXPECT_EQ(is_primitive_, ShortyFieldTypeTraits::IsPrimitiveType<T>());
247 EXPECT_EQ(is_primitive_narrow_, ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>());
248 EXPECT_EQ(is_primitive_wide_, ShortyFieldTypeTraits::IsPrimitiveWideType<T>());
249 EXPECT_EQ(is_object_, ShortyFieldTypeTraits::IsObjectType<T>());
250 EXPECT_EQ(is_lambda_, ShortyFieldTypeTraits::IsLambdaType<T>());
251
252 // Test compile-time ShortyFieldType selectors
253 static_assert(std::is_same<T, typename ShortyFieldTypeSelectType<kShortyEnum>::type>::value,
254 "ShortyFieldType Enum->Type incorrect mapping");
255 auto kActualEnum = ShortyFieldTypeSelectEnum<T>::value; // Do not ODR-use, avoid linker error.
256 EXPECT_EQ(kShortyEnum, kActualEnum);
257 }
258 };
259
TEST_F(ShortyFieldTypeTest,TestCharacteristicsAndTraits)260 TEST_F(ShortyFieldTypeTest, TestCharacteristicsAndTraits) {
261 // Boolean test
262 {
263 SCOPED_TRACE("boolean");
264 ShortyTypeCharacteristics<bool, ShortyFieldType::kBoolean> chars;
265 chars.is_primitive_ = true;
266 chars.is_primitive_narrow_ = true;
267 chars.CheckExpects();
268 }
269
270 // Byte test
271 {
272 SCOPED_TRACE("byte");
273 ShortyTypeCharacteristics<int8_t, ShortyFieldType::kByte> chars;
274 chars.is_primitive_ = true;
275 chars.is_primitive_narrow_ = true;
276 chars.CheckExpects();
277 }
278
279 // Char test
280 {
281 SCOPED_TRACE("char");
282 ShortyTypeCharacteristics<uint16_t, ShortyFieldType::kChar> chars; // Char is unsigned.
283 chars.is_primitive_ = true;
284 chars.is_primitive_narrow_ = true;
285 chars.CheckExpects();
286 }
287
288 // Short test
289 {
290 SCOPED_TRACE("short");
291 ShortyTypeCharacteristics<int16_t, ShortyFieldType::kShort> chars;
292 chars.is_primitive_ = true;
293 chars.is_primitive_narrow_ = true;
294 chars.CheckExpects();
295 }
296
297 // Int test
298 {
299 SCOPED_TRACE("int");
300 ShortyTypeCharacteristics<int32_t, ShortyFieldType::kInt> chars;
301 chars.is_primitive_ = true;
302 chars.is_primitive_narrow_ = true;
303 chars.CheckExpects();
304 }
305
306 // Long test
307 {
308 SCOPED_TRACE("long");
309 ShortyTypeCharacteristics<int64_t, ShortyFieldType::kLong> chars;
310 chars.is_primitive_ = true;
311 chars.is_primitive_wide_ = true;
312 chars.CheckExpects();
313 }
314
315 // Float test
316 {
317 SCOPED_TRACE("float");
318 ShortyTypeCharacteristics<float, ShortyFieldType::kFloat> chars;
319 chars.is_primitive_ = true;
320 chars.is_primitive_narrow_ = true;
321 chars.CheckExpects();
322 }
323
324 // Double test
325 {
326 SCOPED_TRACE("double");
327 ShortyTypeCharacteristics<double, ShortyFieldType::kDouble> chars;
328 chars.is_primitive_ = true;
329 chars.is_primitive_wide_ = true;
330 chars.CheckExpects();
331 }
332
333 // Object test
334 {
335 SCOPED_TRACE("object");
336 ShortyTypeCharacteristics<mirror::Object*, ShortyFieldType::kObject> chars;
337 chars.is_object_ = true;
338 chars.size_ = kObjectReferenceSize;
339 chars.CheckExpects();
340 EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::CompressedReference<mirror::Object>));
341 }
342
343 // Lambda test
344 {
345 SCOPED_TRACE("lambda");
346 ShortyTypeCharacteristics<Closure*, ShortyFieldType::kLambda> chars;
347 chars.is_lambda_ = true;
348 chars.is_dynamic_sized_ = true;
349 chars.CheckExpects();
350 }
351 }
352
353 } // namespace lambda
354 } // namespace art
355