blob: 32bade9b56c3cc1d0b897bae467699ee8ad7674c [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "lambda/shorty_field_type.h"
#include "mirror/object_reference.h"
#include "utils.h"
#include <numeric>
#include <stdint.h>
#include "gtest/gtest.h"
#define EXPECT_NULL(expected) EXPECT_EQ(reinterpret_cast<const void*>(expected), \
reinterpret_cast<void*>(nullptr));
namespace art {
namespace lambda {
class ShortyFieldTypeTest : public ::testing::Test {
public:
ShortyFieldTypeTest() = default;
~ShortyFieldTypeTest() = default;
protected:
static void SetUpTestCase() {
}
virtual void SetUp() {
}
static ::testing::AssertionResult IsResultSuccessful(bool result) {
if (result) {
return ::testing::AssertionSuccess();
} else {
return ::testing::AssertionFailure();
}
}
template <typename T>
static std::string ListToString(const T& list) {
std::stringstream stream;
stream << "[";
for (auto&& val : list) {
stream << val << ", ";
}
stream << "]";
return stream.str();
}
// Compare two vector-like types for equality.
template <typename T>
static ::testing::AssertionResult AreListsEqual(const T& expected, const T& actual) {
bool success = true;
std::stringstream stream;
if (expected.size() != actual.size()) {
success = false;
stream << "Expected list size: " << expected.size()
<< ", but got list size: " << actual.size();
stream << std::endl;
}
for (size_t j = 0; j < std::min(expected.size(), actual.size()); ++j) {
if (expected[j] != actual[j]) {
success = false;
stream << "Expected element '" << j << "' to be '" << expected[j] << "', but got actual: '"
<< actual[j] << "'.";
stream << std::endl;
}
}
if (success) {
return ::testing::AssertionSuccess();
}
stream << "Expected list was: " << ListToString(expected)
<< ", actual list was: " << ListToString(actual);
return ::testing::AssertionFailure() << stream.str();
}
static std::vector<ShortyFieldType> ParseLongTypeDescriptorsToList(const char* type_descriptor) {
std::vector<ShortyFieldType> lst;
ShortyFieldType shorty;
const char* parsed = type_descriptor;
while ((parsed = ShortyFieldType::ParseFromFieldTypeDescriptor(parsed, &shorty)) != nullptr) {
lst.push_back(shorty);
}
return lst;
}
protected:
// Shorthands for the ShortyFieldType constants.
// The letters are the same as JNI letters, with kS_ being a lambda since \ is not available.
static constexpr ShortyFieldType kSZ = ShortyFieldType::kBoolean;
static constexpr ShortyFieldType kSB = ShortyFieldType::kByte;
static constexpr ShortyFieldType kSC = ShortyFieldType::kChar;
static constexpr ShortyFieldType kSS = ShortyFieldType::kShort;
static constexpr ShortyFieldType kSI = ShortyFieldType::kInt;
static constexpr ShortyFieldType kSF = ShortyFieldType::kFloat;
static constexpr ShortyFieldType kSJ = ShortyFieldType::kLong;
static constexpr ShortyFieldType kSD = ShortyFieldType::kDouble;
static constexpr ShortyFieldType kSL = ShortyFieldType::kObject;
static constexpr ShortyFieldType kS_ = ShortyFieldType::kLambda;
};
TEST_F(ShortyFieldTypeTest, TestMaybeCreate) {
ShortyFieldType shorty;
std::vector<char> shorties = {'Z', 'B', 'C', 'S', 'I', 'F', 'J', 'D', 'L', '\\'};
// All valid 'shorty' characters are created successfully.
for (const char c : shorties) {
EXPECT_TRUE(ShortyFieldType::MaybeCreate(c, &shorty)) << c;
EXPECT_EQ(c, static_cast<char>(c));
}
// All other characters can never be created.
for (unsigned char c = 0; c < std::numeric_limits<unsigned char>::max(); ++c) {
// Skip the valid characters.
if (std::find(shorties.begin(), shorties.end(), c) != shorties.end()) { continue; }
// All invalid characters should fail.
EXPECT_FALSE(ShortyFieldType::MaybeCreate(static_cast<char>(c), &shorty)) << c;
}
} // TEST_F
TEST_F(ShortyFieldTypeTest, TestCreateFromFieldTypeDescriptor) {
// Sample input.
std::vector<const char*> lengthies = {
"Z", "B", "C", "S", "I", "F", "J", "D", "LObject;", "\\Closure;",
"[Z", "[[B", "[[LObject;"
};
// Expected output.
std::vector<ShortyFieldType> expected = {
ShortyFieldType::kBoolean,
ShortyFieldType::kByte,
ShortyFieldType::kChar,
ShortyFieldType::kShort,
ShortyFieldType::kInt,
ShortyFieldType::kFloat,
ShortyFieldType::kLong,
ShortyFieldType::kDouble,
ShortyFieldType::kObject,
ShortyFieldType::kLambda,
// Arrays are always treated as objects.
ShortyFieldType::kObject,
ShortyFieldType::kObject,
ShortyFieldType::kObject,
};
// All valid lengthy types are correctly turned into the expected shorty type.
for (size_t i = 0; i < lengthies.size(); ++i) {
EXPECT_EQ(expected[i], ShortyFieldType::CreateFromFieldTypeDescriptor(lengthies[i]));
}
} // TEST_F
TEST_F(ShortyFieldTypeTest, TestParseFromFieldTypeDescriptor) {
// Sample input.
std::vector<const char*> lengthies = {
// Empty list
"",
// Primitives
"Z", "B", "C", "S", "I", "F", "J", "D",
// Non-primitives
"LObject;", "\\Closure;",
// Arrays. The biggest PITA.
"[Z", "[[B", "[[LObject;", "[[[[\\Closure;",
// Multiple things at once:
"ZBCSIFJD",
"LObject;LObject;SSI",
"[[ZDDZ",
"[[LObject;[[Z[F\\Closure;LObject;",
};
// Expected output.
std::vector<std::vector<ShortyFieldType>> expected = {
// Empty list
{},
// Primitives
{kSZ}, {kSB}, {kSC}, {kSS}, {kSI}, {kSF}, {kSJ}, {kSD},
// Non-primitives.
{ ShortyFieldType::kObject }, { ShortyFieldType::kLambda },
// Arrays are always treated as objects.
{ kSL }, { kSL }, { kSL }, { kSL },
// Multiple things at once:
{ kSZ, kSB, kSC, kSS, kSI, kSF, kSJ, kSD },
{ kSL, kSL, kSS, kSS, kSI },
{ kSL, kSD, kSD, kSZ },
{ kSL, kSL, kSL, kS_, kSL },
};
// Sanity check that the expected/actual lists are the same size.. when adding new entries.
ASSERT_EQ(expected.size(), lengthies.size());
// All valid lengthy types are correctly turned into the expected shorty type.
for (size_t i = 0; i < expected.size(); ++i) {
const std::vector<ShortyFieldType>& expected_list = expected[i];
std::vector<ShortyFieldType> actual_list = ParseLongTypeDescriptorsToList(lengthies[i]);
EXPECT_TRUE(AreListsEqual(expected_list, actual_list));
}
} // TEST_F
// Helper class to probe a shorty's characteristics by minimizing copy-and-paste tests.
template <typename T, decltype(ShortyFieldType::kByte) kShortyEnum>
struct ShortyTypeCharacteristics {
bool is_primitive_ = false;
bool is_primitive_narrow_ = false;
bool is_primitive_wide_ = false;
bool is_object_ = false;
bool is_lambda_ = false;
size_t size_ = sizeof(T);
bool is_dynamic_sized_ = false;
void CheckExpects() {
ShortyFieldType shorty = kShortyEnum;
// Test the main non-parsing-related ShortyFieldType characteristics.
EXPECT_EQ(is_primitive_, shorty.IsPrimitive());
EXPECT_EQ(is_primitive_narrow_, shorty.IsPrimitiveNarrow());
EXPECT_EQ(is_primitive_wide_, shorty.IsPrimitiveWide());
EXPECT_EQ(is_object_, shorty.IsObject());
EXPECT_EQ(is_lambda_, shorty.IsLambda());
EXPECT_EQ(size_, shorty.GetStaticSize());
EXPECT_EQ(is_dynamic_sized_, !shorty.IsStaticSize());
// Test compile-time ShortyFieldTypeTraits.
EXPECT_TRUE(ShortyFieldTypeTraits::IsType<T>());
EXPECT_EQ(is_primitive_, ShortyFieldTypeTraits::IsPrimitiveType<T>());
EXPECT_EQ(is_primitive_narrow_, ShortyFieldTypeTraits::IsPrimitiveNarrowType<T>());
EXPECT_EQ(is_primitive_wide_, ShortyFieldTypeTraits::IsPrimitiveWideType<T>());
EXPECT_EQ(is_object_, ShortyFieldTypeTraits::IsObjectType<T>());
EXPECT_EQ(is_lambda_, ShortyFieldTypeTraits::IsLambdaType<T>());
// Test compile-time ShortyFieldType selectors
static_assert(std::is_same<T, typename ShortyFieldTypeSelectType<kShortyEnum>::type>::value,
"ShortyFieldType Enum->Type incorrect mapping");
auto kActualEnum = ShortyFieldTypeSelectEnum<T>::value; // Do not ODR-use, avoid linker error.
EXPECT_EQ(kShortyEnum, kActualEnum);
}
};
TEST_F(ShortyFieldTypeTest, TestCharacteristicsAndTraits) {
// Boolean test
{
SCOPED_TRACE("boolean");
ShortyTypeCharacteristics<bool, ShortyFieldType::kBoolean> chars;
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Byte test
{
SCOPED_TRACE("byte");
ShortyTypeCharacteristics<int8_t, ShortyFieldType::kByte> chars;
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Char test
{
SCOPED_TRACE("char");
ShortyTypeCharacteristics<uint16_t, ShortyFieldType::kChar> chars; // Char is unsigned.
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Short test
{
SCOPED_TRACE("short");
ShortyTypeCharacteristics<int16_t, ShortyFieldType::kShort> chars;
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Int test
{
SCOPED_TRACE("int");
ShortyTypeCharacteristics<int32_t, ShortyFieldType::kInt> chars;
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Long test
{
SCOPED_TRACE("long");
ShortyTypeCharacteristics<int64_t, ShortyFieldType::kLong> chars;
chars.is_primitive_ = true;
chars.is_primitive_wide_ = true;
chars.CheckExpects();
}
// Float test
{
SCOPED_TRACE("float");
ShortyTypeCharacteristics<float, ShortyFieldType::kFloat> chars;
chars.is_primitive_ = true;
chars.is_primitive_narrow_ = true;
chars.CheckExpects();
}
// Double test
{
SCOPED_TRACE("double");
ShortyTypeCharacteristics<double, ShortyFieldType::kDouble> chars;
chars.is_primitive_ = true;
chars.is_primitive_wide_ = true;
chars.CheckExpects();
}
// Object test
{
SCOPED_TRACE("object");
ShortyTypeCharacteristics<mirror::Object*, ShortyFieldType::kObject> chars;
chars.is_object_ = true;
chars.size_ = kObjectReferenceSize;
chars.CheckExpects();
EXPECT_EQ(kObjectReferenceSize, sizeof(mirror::CompressedReference<mirror::Object>));
}
// Lambda test
{
SCOPED_TRACE("lambda");
ShortyTypeCharacteristics<Closure*, ShortyFieldType::kLambda> chars;
chars.is_lambda_ = true;
chars.is_dynamic_sized_ = true;
chars.CheckExpects();
}
}
} // namespace lambda
} // namespace art