Files
swift-mirror/unittests/runtime/Metadata.cpp
2014-11-17 17:15:12 +00:00

546 lines
19 KiB
C++

//===- swift/unittests/runtime/Metadata.cpp - Metadata tests --------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/Metadata.h"
#include "gtest/gtest.h"
#include <vector>
#include <functional>
#include <pthread.h>
#include <semaphore.h>
using namespace swift;
// Race testing.
template <typename T>
struct RaceArgs {
std::function<T()> code;
sem_t *ready;
pthread_rwlock_t *go;
};
void *RaceThunk(void *vargs) {
RaceArgs<void*> *args = static_cast<RaceArgs<void*> *>(vargs);
// Signal ready. Wait for go.
sem_post(args->ready);
pthread_rwlock_rdlock(args->go);
return args->code();
}
/// RaceTest(code) runs code in many threads simultaneously,
/// and returns a vector of all returned results.
template <typename T>
std::vector<T>
RaceTest(std::function<T()> code)
{
const unsigned threadCount = 64;
sem_t ready;
pthread_rwlock_t go;
sem_init(&ready, 0, 0);
pthread_rwlock_init(&go, NULL);
pthread_rwlock_wrlock(&go);
// Create the threads.
pthread_t threads[threadCount];
std::vector<RaceArgs<T>> args(threadCount, {code, &ready, &go});
for (unsigned i = 0; i < threadCount; i++) {
pthread_create(&threads[i], nullptr, &RaceThunk, &args[i]);
}
// Wait for all test threads to reach ready.
for (unsigned i = 0; i < threadCount; i++) {
sem_wait(&ready);
}
// Race!
pthread_rwlock_unlock(&go);
// Collect results.
std::vector<T> results;
for (unsigned i = 0; i < threadCount; i++) {
void *result;
pthread_join(threads[i], &result);
results.push_back(static_cast<T>(result));
}
sem_destroy(&ready);
pthread_rwlock_destroy(&go);
return results;
}
/// RaceTest_ExpectEqual(code) runs code in many threads simultaneously,
/// verifies that they all returned the same value, and returns that value.
template<typename T>
T RaceTest_ExpectEqual(std::function<T()> code)
{
auto results = RaceTest<T>(code);
auto r0 = results[0];
for (auto r : results) {
EXPECT_EQ(r0, r);
}
return r0;
}
/// Some unique global pointers.
char Global1 = 0;
char Global2 = 0;
char Global3 = 0;
/// The general structure of a generic metadata.
template <unsigned NumFields>
struct GenericMetadataTest {
GenericMetadata Header;
void *Fields[NumFields];
};
GenericMetadataTest<3> MetadataTest1 = {
// Header
{
// allocation function
[](GenericMetadata *pattern, const void *args) {
auto metadata = swift_allocateGenericValueMetadata(pattern, args);
auto metadataWords = reinterpret_cast<const void**>(metadata);
auto argsWords = reinterpret_cast<const void* const*>(args);
metadataWords[2] = argsWords[0];
return metadata;
},
3 * sizeof(void*), // metadata size
1, // num arguments
0, // address point
{} // private data
},
// Fields
{
(void*) MetadataKind::Struct,
&Global1,
nullptr
}
};
TEST(MetadataTest, getGenericMetadata) {
auto metadataTemplate = (GenericMetadata*) &MetadataTest1;
void *args[] = { &Global2 };
auto result1 = RaceTest_ExpectEqual<const Metadata *>(
[&]() -> const Metadata * {
auto inst = swift_getGenericMetadata(metadataTemplate, args);
auto fields = reinterpret_cast<void * const *>(inst);
EXPECT_EQ((void*) MetadataKind::Struct, fields[0]);
EXPECT_EQ(&Global1, fields[1]);
EXPECT_EQ(&Global2, fields[2]);
return inst;
});
args[0] = &Global3;
RaceTest_ExpectEqual<const Metadata *>(
[&]() -> const Metadata * {
auto inst = swift_getGenericMetadata(metadataTemplate, args);
EXPECT_NE(inst, result1);
auto fields = reinterpret_cast<void * const *>(inst);
EXPECT_EQ((void*) MetadataKind::Struct, fields[0]);
EXPECT_EQ(&Global1, fields[1]);
EXPECT_EQ(&Global3, fields[2]);
return inst;
});
}
FullMetadata<ClassMetadata> MetadataTest2 = {
{ { nullptr }, { &_TWVBo } },
{ { { MetadataKind::Class } }, nullptr, 0, ClassFlags(), nullptr, 0, 0, 0, 0, 0 }
};
TEST(MetadataTest, getMetatypeMetadata) {
auto inst1 = RaceTest_ExpectEqual<const MetatypeMetadata *>(
[&]() -> const MetatypeMetadata * {
auto inst = swift_getMetatypeMetadata(&_TMdBi64_.base);
EXPECT_EQ(sizeof(void*), inst->getValueWitnesses()->size);
return inst;
});
auto inst2 = RaceTest_ExpectEqual<const MetatypeMetadata *>(
[&]() -> const MetatypeMetadata * {
auto inst = swift_getMetatypeMetadata(&_TMdBi32_.base);
EXPECT_EQ(sizeof(void*), inst->getValueWitnesses()->size);
return inst;
});
auto inst3 = RaceTest_ExpectEqual<const MetatypeMetadata *>(
[&]() -> const MetatypeMetadata * {
auto inst = swift_getMetatypeMetadata(&MetadataTest2);
EXPECT_EQ(sizeof(void*), inst->getValueWitnesses()->size);
return inst;
});
auto inst4 = RaceTest_ExpectEqual<const MetatypeMetadata *>(
[&]() -> const MetatypeMetadata * {
auto inst = swift_getMetatypeMetadata(inst3);
EXPECT_EQ(sizeof(void*), inst->getValueWitnesses()->size);
return inst;
});
auto inst5 = RaceTest_ExpectEqual<const MetatypeMetadata *>(
[&]() -> const MetatypeMetadata * {
auto inst = swift_getMetatypeMetadata(inst1);
EXPECT_EQ(sizeof(void*), inst->getValueWitnesses()->size);
return inst;
});
// After all this, the instance type fields should still be valid.
ASSERT_EQ(&_TMdBi64_.base, inst1->InstanceType);
ASSERT_EQ(&_TMdBi32_.base, inst2->InstanceType);
ASSERT_EQ(&MetadataTest2, inst3->InstanceType);
ASSERT_EQ(inst3, inst4->InstanceType);
ASSERT_EQ(inst1, inst5->InstanceType);
}
ProtocolDescriptor ProtocolA{
"_TMp8Metadata9ProtocolA",
nullptr,
ProtocolDescriptorFlags()
.withSwift(true)
.withClassConstraint(ProtocolClassConstraint::Any)
.withNeedsWitnessTable(true)
};
ProtocolDescriptor ProtocolB{
"_TMp8Metadata9ProtocolB",
nullptr,
ProtocolDescriptorFlags()
.withSwift(true)
.withClassConstraint(ProtocolClassConstraint::Any)
.withNeedsWitnessTable(true)
};
ProtocolDescriptor ProtocolClassConstrained{
"_TMp8Metadata24ProtocolClassConstrained",
nullptr,
ProtocolDescriptorFlags()
.withSwift(true)
.withClassConstraint(ProtocolClassConstraint::Class)
.withNeedsWitnessTable(true)
};
ProtocolDescriptor ProtocolNoWitnessTable{
"_TMp8Metadata22ProtocolNoWitnessTable",
nullptr,
ProtocolDescriptorFlags()
.withSwift(true)
.withClassConstraint(ProtocolClassConstraint::Class)
.withNeedsWitnessTable(false)
};
static const ExistentialTypeMetadata *test_getExistentialMetadata(
std::initializer_list<const ProtocolDescriptor *> descriptors)
{
std::vector<const ProtocolDescriptor *> mutDescriptors(descriptors);
return swift_getExistentialTypeMetadata(mutDescriptors.size(),
mutDescriptors.data());
}
TEST(MetadataTest, getExistentialMetadata) {
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto any = test_getExistentialMetadata({});
EXPECT_EQ(MetadataKind::Existential, any->getKind());
EXPECT_EQ(0U, any->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, any->Flags.getClassConstraint());
EXPECT_EQ(0U, any->Protocols.NumProtocols);
return any;
});
auto exA = RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto a = test_getExistentialMetadata({&ProtocolA});
EXPECT_EQ(MetadataKind::Existential, a->getKind());
EXPECT_EQ(1U, a->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, a->Flags.getClassConstraint());
EXPECT_EQ(1U, a->Protocols.NumProtocols);
EXPECT_EQ(&ProtocolA, a->Protocols[0]);
return a;
});
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto b = test_getExistentialMetadata({&ProtocolB});
EXPECT_NE(exA, b);
EXPECT_EQ(MetadataKind::Existential, b->getKind());
EXPECT_EQ(1U, b->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, b->Flags.getClassConstraint());
EXPECT_EQ(1U, b->Protocols.NumProtocols);
EXPECT_EQ(&ProtocolB, b->Protocols[0]);
return b;
});
// protocol compositions are order-invariant
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ab = test_getExistentialMetadata({&ProtocolA, &ProtocolB});
auto ba = test_getExistentialMetadata({&ProtocolB, &ProtocolA});
EXPECT_EQ(ab, ba);
EXPECT_EQ(MetadataKind::Existential, ab->getKind());
EXPECT_EQ(2U, ab->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, ab->Flags.getClassConstraint());
EXPECT_EQ(2U, ab->Protocols.NumProtocols);
EXPECT_TRUE(
(ab->Protocols[0]==&ProtocolA && ab->Protocols[1]==&ProtocolB)
|| (ab->Protocols[0]==&ProtocolB && ab->Protocols[1]==&ProtocolA));
return ab;
});
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto classConstrained
= test_getExistentialMetadata({&ProtocolClassConstrained});
EXPECT_EQ(MetadataKind::Existential, classConstrained->getKind());
EXPECT_EQ(1U, classConstrained->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Class,
classConstrained->Flags.getClassConstraint());
EXPECT_EQ(1U, classConstrained->Protocols.NumProtocols);
EXPECT_EQ(&ProtocolClassConstrained, classConstrained->Protocols[0]);
return classConstrained;
});
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto noWitnessTable
= test_getExistentialMetadata({&ProtocolNoWitnessTable});
EXPECT_EQ(MetadataKind::Existential, noWitnessTable->getKind());
EXPECT_EQ(0U, noWitnessTable->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Class,
noWitnessTable->Flags.getClassConstraint());
EXPECT_EQ(1U, noWitnessTable->Protocols.NumProtocols);
EXPECT_EQ(&ProtocolNoWitnessTable, noWitnessTable->Protocols[0]);
return noWitnessTable;
});
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto mixedWitnessTable
= test_getExistentialMetadata({&ProtocolNoWitnessTable,
&ProtocolA, &ProtocolB});
EXPECT_EQ(MetadataKind::Existential, mixedWitnessTable->getKind());
EXPECT_EQ(2U, mixedWitnessTable->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Class,
mixedWitnessTable->Flags.getClassConstraint());
EXPECT_EQ(3U, mixedWitnessTable->Protocols.NumProtocols);
return mixedWitnessTable;
});
}
static void destroySuperclass(HeapObject *toDestroy) {}
struct {
void *Prefix[4];
FullMetadata<ClassMetadata> Metadata;
} SuperclassWithPrefix = {
{ &Global1, &Global3, &Global2, &Global3 },
{ { { &destroySuperclass }, { &_TWVBo } },
{ { { MetadataKind::Class } }, nullptr, /*rodata*/ 1, ClassFlags(), nullptr,
0, 0, 0, sizeof(SuperclassWithPrefix),
sizeof(SuperclassWithPrefix.Prefix) + sizeof(HeapMetadataHeader) } }
};
ClassMetadata * const SuperclassWithPrefix_AddressPoint =
&SuperclassWithPrefix.Metadata;
static void destroySubclass(HeapObject *toDestroy) {}
struct {
GenericMetadata Header;
FullMetadata<ClassMetadata> Pattern;
void *Suffix[3];
} GenericSubclass = {
{
// allocation function
[](GenericMetadata *pattern, const void *args) -> Metadata* {
auto metadata =
swift_allocateGenericClassMetadata(pattern, args,
SuperclassWithPrefix_AddressPoint);
char *bytes = (char*) metadata + sizeof(ClassMetadata);
auto metadataWords = reinterpret_cast<const void**>(bytes);
auto argsWords = reinterpret_cast<const void* const *>(args);
metadataWords[2] = argsWords[0];
return metadata;
},
sizeof(GenericSubclass.Pattern) + sizeof(GenericSubclass.Suffix), // pattern size
1, // num arguments
sizeof(HeapMetadataHeader), // address point
{} // private data
},
{ { { &destroySubclass }, { &_TWVBo } },
{ { { MetadataKind::Class } }, nullptr, /*rodata*/ 1, ClassFlags(), nullptr,
0, 0, 0, sizeof(GenericSubclass.Pattern) + sizeof(GenericSubclass.Suffix),
sizeof(HeapMetadataHeader) } },
{ &Global2, &Global1, &Global2 }
};
TEST(MetadataTest, getGenericMetadata_SuperclassWithUnexpectedPrefix) {
auto metadataTemplate = &GenericSubclass.Header;
void *args[] = { &Global3 };
RaceTest_ExpectEqual<const ClassMetadata *>(
[&]() -> const ClassMetadata * {
auto inst = static_cast<const ClassMetadata*>(
swift_getGenericMetadata(metadataTemplate, args));
void * const *fields = reinterpret_cast<void * const *>(inst);
// Assert that we copied the extra prefix data from the superclass.
EXPECT_EQ(&Global1, fields[-6]);
EXPECT_EQ(&Global3, fields[-5]);
EXPECT_EQ(&Global2, fields[-4]);
EXPECT_EQ(&Global3, fields[-3]);
// Assert that we copied the shared prefix data from the subclass.
EXPECT_EQ((void*) &destroySubclass, fields[-2]);
EXPECT_EQ(&_TWVBo, fields[-1]);
// Assert that we set the superclass field.
EXPECT_EQ(SuperclassWithPrefix_AddressPoint, fields[1]);
// Assert that we copied the subclass suffix data.
auto suffix = (void * const *) ((char*) inst + sizeof(ClassMetadata));
EXPECT_EQ(&Global2, suffix[0]);
EXPECT_EQ(&Global1, suffix[1]);
// This should have been overwritten by the creation function.
EXPECT_EQ(&Global3, suffix[2]);
EXPECT_EQ(7 * sizeof(void*) + sizeof(GenericSubclass.Pattern),
inst->getClassSize());
EXPECT_EQ(4 * sizeof(void*) + sizeof(HeapMetadataHeader),
inst->getClassAddressPoint());
// These are all expected to be equal.
return inst;
});
}
static ProtocolDescriptor OpaqueProto1 = { "OpaqueProto1", nullptr,
ProtocolDescriptorFlags().withSwift(true).withNeedsWitnessTable(true)
.withClassConstraint(ProtocolClassConstraint::Any)
};
static ProtocolDescriptor OpaqueProto2 = { "OpaqueProto2", nullptr,
ProtocolDescriptorFlags().withSwift(true).withNeedsWitnessTable(true)
.withClassConstraint(ProtocolClassConstraint::Any)
};
static ProtocolDescriptor OpaqueProto3 = { "OpaqueProto3", nullptr,
ProtocolDescriptorFlags().withSwift(true).withNeedsWitnessTable(true)
.withClassConstraint(ProtocolClassConstraint::Any)
};
static ProtocolDescriptor ClassProto1 = { "ClassProto1", nullptr,
ProtocolDescriptorFlags().withSwift(true).withNeedsWitnessTable(true)
.withClassConstraint(ProtocolClassConstraint::Class)
};
TEST(MetadataTest, getExistentialTypeMetadata_opaque) {
const ProtocolDescriptor *protoList1[] = {
&OpaqueProto1
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex1 = swift_getExistentialTypeMetadata(1, protoList1);
EXPECT_EQ(MetadataKind::Existential, ex1->getKind());
EXPECT_EQ(5 * sizeof(void*), ex1->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex1->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex1->getValueWitnesses()->isPOD());
EXPECT_FALSE(ex1->getValueWitnesses()->isBitwiseTakable());
return ex1;
});
const ProtocolDescriptor *protoList2[] = {
&OpaqueProto1, &OpaqueProto2
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex2 = swift_getExistentialTypeMetadata(2, protoList2);
EXPECT_EQ(MetadataKind::Existential, ex2->getKind());
EXPECT_EQ(6 * sizeof(void*), ex2->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex2->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex2->getValueWitnesses()->isPOD());
EXPECT_FALSE(ex2->getValueWitnesses()->isBitwiseTakable());
return ex2;
});
const ProtocolDescriptor *protoList3[] = {
&OpaqueProto1, &OpaqueProto2, &OpaqueProto3
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex3 = swift_getExistentialTypeMetadata(3, protoList3);
EXPECT_EQ(MetadataKind::Existential, ex3->getKind());
EXPECT_EQ(7 * sizeof(void*), ex3->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex3->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex3->getValueWitnesses()->isPOD());
EXPECT_FALSE(ex3->getValueWitnesses()->isBitwiseTakable());
return ex3;
});
}
TEST(MetadataTest, getExistentialTypeMetadata_class) {
const ProtocolDescriptor *protoList1[] = {
&ClassProto1
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex1 = swift_getExistentialTypeMetadata(1, protoList1);
EXPECT_EQ(MetadataKind::Existential, ex1->getKind());
EXPECT_EQ(2 * sizeof(void*), ex1->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex1->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex1->getValueWitnesses()->isPOD());
EXPECT_TRUE(ex1->getValueWitnesses()->isBitwiseTakable());
return ex1;
});
const ProtocolDescriptor *protoList2[] = {
&OpaqueProto1, &ClassProto1
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex2 = swift_getExistentialTypeMetadata(2, protoList2);
EXPECT_EQ(MetadataKind::Existential, ex2->getKind());
EXPECT_EQ(3 * sizeof(void*), ex2->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex2->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex2->getValueWitnesses()->isPOD());
EXPECT_TRUE(ex2->getValueWitnesses()->isBitwiseTakable());
return ex2;
});
const ProtocolDescriptor *protoList3[] = {
&OpaqueProto1, &OpaqueProto2, &ClassProto1
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
auto ex3 = swift_getExistentialTypeMetadata(3, protoList3);
EXPECT_EQ(MetadataKind::Existential, ex3->getKind());
EXPECT_EQ(4 * sizeof(void*), ex3->getValueWitnesses()->getSize());
EXPECT_EQ(alignof(void*), ex3->getValueWitnesses()->getAlignment());
EXPECT_FALSE(ex3->getValueWitnesses()->isPOD());
EXPECT_TRUE(ex3->getValueWitnesses()->isBitwiseTakable());
return ex3;
});
}