| //===--- ExistentialMetadataImpl.h - Existential metadata -------*- C++ -*-===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Declarations used to implement value witnesses for Swift |
| // existential types. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_RUNTIME_EXISTENTIALMETADATAIMPL_H |
| #define SWIFT_RUNTIME_EXISTENTIALMETADATAIMPL_H |
| |
| #include "MetadataImpl.h" |
| |
| namespace swift { |
| namespace metadataimpl { |
| |
| /// A common base class for opaque-existential and class-existential boxes. |
| template<typename Impl> |
| struct LLVM_LIBRARY_VISIBILITY ExistentialBoxBase { |
| }; |
| |
| /// A common base class for fixed and non-fixed opaque-existential box |
| /// implementations. |
| struct LLVM_LIBRARY_VISIBILITY OpaqueExistentialBoxBase |
| : ExistentialBoxBase<OpaqueExistentialBoxBase> { |
| template <class Container, class... A> |
| static void destroy(Container *value, A... args) { |
| auto *type = value->getType(); |
| auto *vwt = type->getValueWitnesses(); |
| if (vwt->isValueInline()) { |
| // destroy(&valueBuffer) |
| type->vw_destroy( |
| reinterpret_cast<OpaqueValue *>(value->getBuffer(args...))); |
| } else { |
| // release(valueBuffer[0]) |
| swift_release( |
| *reinterpret_cast<HeapObject **>(value->getBuffer(args...))); |
| } |
| } |
| |
| enum class Dest { |
| Assign, |
| Init, |
| }; |
| enum class Source { |
| Copy, |
| Take |
| }; |
| |
| template <class Container, class... A> |
| static void copyReference(Container *dest, Container *src, Dest d, Source s, |
| A... args) { |
| auto *destRefAddr = |
| reinterpret_cast<HeapObject **>(dest->getBuffer(args...)); |
| |
| // Load the source reference. |
| auto *srcRef = *reinterpret_cast<HeapObject **>(src->getBuffer(args...)); |
| |
| // Load the old destination reference so we can release it later if this is |
| // an assignment. |
| HeapObject *destRef = d == Dest::Assign ? *destRefAddr : nullptr; |
| |
| // Do the assignment. |
| *destRefAddr = srcRef; |
| |
| // If we copy the source retain the reference. |
| if (s == Source::Copy) |
| swift_retain(srcRef); |
| |
| // If we have an assignment release the old reference. |
| if (d == Dest::Assign) |
| swift_release(destRef); |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithCopy(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| auto *type = src->getType(); |
| auto *vwt = type->getValueWitnesses(); |
| |
| if (vwt->isValueInline()) { |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| |
| type->vw_initializeWithCopy(destValue, srcValue); |
| } else { |
| // initWithCopy of the reference to the cow box. |
| copyReference(dest, src, Dest::Init, Source::Copy, args...); |
| } |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithTake(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| auto *type = src->getType(); |
| auto *vwt = type->getValueWitnesses(); |
| |
| if (vwt->isValueInline()) { |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| |
| type->vw_initializeWithTake(destValue, srcValue); |
| } else { |
| // initWithTake of the reference to the cow box. |
| copyReference(dest, src, Dest::Init, Source::Take, args...); |
| } |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithCopy(Container *dest, Container *src, |
| A... args) { |
| auto srcType = src->getType(); |
| auto destType = dest->getType(); |
| if (src == dest) |
| return dest; |
| if (srcType == destType) { |
| // Types match. |
| auto *vwt = srcType->getValueWitnesses(); |
| |
| if (vwt->isValueInline()) { |
| // Inline. |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| // assignWithCopy. |
| srcType->vw_assignWithCopy(destValue, srcValue); |
| } else { |
| // Outline (boxed value). |
| // assignWithCopy. |
| copyReference(dest, src, Dest::Assign, Source::Copy, args...); |
| } |
| } else { |
| // Different types. |
| auto *destVwt = destType->getValueWitnesses(); |
| auto *srcVwt = srcType->getValueWitnesses(); |
| if (destVwt->isValueInline()) { |
| // Inline destination value. |
| ValueBuffer tmpBuffer; |
| auto *opaqueTmpBuffer = reinterpret_cast<OpaqueValue *>(&tmpBuffer); |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| |
| // Move dest value asside so we can destroy it later. |
| destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); |
| |
| src->copyTypeInto(dest, args...); |
| if (srcVwt->isValueInline()) { |
| // Inline src value. |
| |
| srcType->vw_initializeWithCopy(destValue, srcValue); |
| } else { |
| // Outline src value. |
| |
| // initWithCopy of reference to cow box. |
| copyReference(dest, src, Dest::Init, Source::Copy, args...); |
| } |
| |
| // Finally, destroy the old dest value. |
| destType->vw_destroy(opaqueTmpBuffer); |
| } else { |
| // Outline destination value. |
| |
| // Get the dest reference so we can release it later. |
| auto *destRef = |
| *reinterpret_cast<HeapObject **>(dest->getBuffer(args...)); |
| |
| src->copyTypeInto(dest, args...); |
| if (srcVwt->isValueInline()) { |
| |
| // initWithCopy. |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| srcType->vw_initializeWithCopy(destValue, srcValue); |
| } else { |
| |
| // initWithCopy of reference to cow box. |
| copyReference(dest, src, Dest::Init, Source::Copy, args...); |
| } |
| |
| // Release dest reference. |
| swift_release(destRef); |
| } |
| } |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithTake(Container *dest, Container *src, |
| A... args) { |
| auto srcType = src->getType(); |
| auto destType = dest->getType(); |
| if (src == dest) |
| return dest; |
| |
| if (srcType == destType) { |
| // Types match. |
| |
| auto *vwt = srcType->getValueWitnesses(); |
| if (vwt->isValueInline()) { |
| // Inline. |
| |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| // assignWithTake. |
| srcType->vw_assignWithTake(destValue, srcValue); |
| } else { |
| // Outline (boxed value). |
| |
| // assignWithTake of reference to cow box. |
| copyReference(dest, src, Dest::Assign, Source::Take, args...); |
| } |
| } else { |
| // Different types. |
| |
| auto *destVwt = destType->getValueWitnesses(); |
| auto *srcVwt = srcType->getValueWitnesses(); |
| if (destVwt->isValueInline()) { |
| // Inline destination value. |
| |
| ValueBuffer tmpBuffer; |
| auto *opaqueTmpBuffer = reinterpret_cast<OpaqueValue *>(&tmpBuffer); |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| |
| // Move dest value asside. |
| destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); |
| |
| src->copyTypeInto(dest, args...); |
| if (srcVwt->isValueInline()) { |
| // Inline src value. |
| |
| srcType->vw_initializeWithTake(destValue, srcValue); |
| } else { |
| // Outline src value. |
| |
| // initWithTake of reference to cow box. |
| copyReference(dest, src, Dest::Init, Source::Take, args...); |
| } |
| |
| // Destroy old dest value. |
| destType->vw_destroy(opaqueTmpBuffer); |
| } else { |
| // Outline destination value. |
| |
| // Get the old dest reference. |
| auto *destRef = |
| *reinterpret_cast<HeapObject **>(dest->getBuffer(args...)); |
| |
| src->copyTypeInto(dest, args...); |
| if (srcVwt->isValueInline()) { |
| // initWithCopy. |
| |
| auto *destValue = |
| reinterpret_cast<OpaqueValue *>(dest->getBuffer(args...)); |
| auto *srcValue = |
| reinterpret_cast<OpaqueValue *>(src->getBuffer(args...)); |
| // initWithTake. |
| srcType->vw_initializeWithTake(destValue, srcValue); |
| } else { |
| |
| // initWithTake of reference to cow box. |
| copyReference(dest, src, Dest::Init, Source::Take, args...); |
| } |
| |
| // Release old dest reference. |
| swift_release(destRef); |
| } |
| } |
| return dest; |
| } |
| }; |
| |
| /// The basic layout of an opaque existential with a fixed number of |
| /// witness tables. Note that the WitnessTables field is accessed via |
| /// spooky action from Header. |
| template <unsigned NumWitnessTables> |
| struct LLVM_LIBRARY_VISIBILITY FixedOpaqueExistentialContainer { |
| OpaqueExistentialContainer Header; |
| const void *WitnessTables[NumWitnessTables]; |
| }; |
| // We need to be able to instantiate for NumWitnessTables==0, which |
| // requires an explicit specialization. |
| template <> |
| struct FixedOpaqueExistentialContainer<0> { |
| OpaqueExistentialContainer Header; |
| }; |
| |
| /// A box implementation class for an opaque existential type with |
| /// a fixed number of witness tables. |
| template <unsigned NumWitnessTables> |
| struct LLVM_LIBRARY_VISIBILITY OpaqueExistentialBox |
| : OpaqueExistentialBoxBase { |
| struct Container : FixedOpaqueExistentialContainer<NumWitnessTables> { |
| const Metadata *getType() const { |
| return this->Header.Type; |
| } |
| ValueBuffer *getBuffer() { |
| return &this->Header.Buffer; |
| } |
| void copyTypeInto(Container *dest) const { |
| this->Header.copyTypeInto(&dest->Header, NumWitnessTables); |
| } |
| |
| static size_t getContainerStride() { |
| return sizeof(Container); |
| } |
| }; |
| using type = Container; |
| |
| static constexpr size_t size = sizeof(Container); |
| static constexpr size_t alignment = alignof(Container); |
| static constexpr size_t stride = sizeof(Container); |
| static constexpr size_t isPOD = false; |
| static constexpr bool isBitwiseTakable = false; |
| static constexpr unsigned numExtraInhabitants = 0; |
| }; |
| |
| /// A non-fixed box implementation class for an opaque existential |
| /// type with a dynamic number of witness tables. |
| struct LLVM_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox |
| : OpaqueExistentialBoxBase { |
| struct Container { |
| OpaqueExistentialContainer Header; |
| |
| const Metadata *getType() { |
| return Header.Type; |
| } |
| ValueBuffer *getBuffer(const Metadata *self) { |
| return &Header.Buffer; |
| } |
| void copyTypeInto(Container *dest, const Metadata *self) { |
| Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); |
| } |
| |
| static unsigned getNumWitnessTables(const Metadata *self) { |
| auto castSelf = static_cast<const ExistentialTypeMetadata*>(self); |
| return castSelf->Flags.getNumWitnessTables(); |
| } |
| |
| static size_t getAlignment(unsigned numWitnessTables) { |
| return std::max(alignof(void*), alignof(ValueBuffer)); |
| } |
| static size_t getSize(unsigned numWitnessTables) { |
| constexpr size_t base = sizeof(OpaqueExistentialContainer); |
| static_assert(base > 0, "stride needs base size > 0"); |
| return base + numWitnessTables * sizeof(void*); |
| } |
| static size_t getStride(unsigned numWitnessTables) { |
| return getSize(numWitnessTables); |
| } |
| |
| static size_t getContainerStride(const Metadata *self) { |
| return getStride(getNumWitnessTables(self)); |
| } |
| }; |
| |
| using type = Container; |
| static constexpr unsigned numExtraInhabitants = 0; |
| }; |
| |
| /// A common base class for fixed and non-fixed class-existential box |
| /// implementations. |
| struct LLVM_LIBRARY_VISIBILITY ClassExistentialBoxBase |
| : ExistentialBoxBase<ClassExistentialBoxBase> { |
| static constexpr unsigned numExtraInhabitants = |
| swift_getHeapObjectExtraInhabitantCount(); |
| |
| template <class Container, class... A> |
| static void destroy(Container *value, A... args) { |
| swift_unknownRelease(*value->getValueSlot()); |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithCopy(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| auto newValue = *src->getValueSlot(); |
| *dest->getValueSlot() = newValue; |
| swift_unknownRetain(newValue); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithTake(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| *dest->getValueSlot() = *src->getValueSlot(); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithCopy(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| auto newValue = *src->getValueSlot(); |
| auto oldValue = *dest->getValueSlot(); |
| *dest->getValueSlot() = newValue; |
| swift_unknownRetain(newValue); |
| swift_unknownRelease(oldValue); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithTake(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| auto newValue = *src->getValueSlot(); |
| auto oldValue = *dest->getValueSlot(); |
| *dest->getValueSlot() = newValue; |
| swift_unknownRelease(oldValue); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static void storeExtraInhabitant(Container *dest, int index, A... args) { |
| swift_storeHeapObjectExtraInhabitant((HeapObject**) dest->getValueSlot(), |
| index); |
| } |
| |
| template <class Container, class... A> |
| static int getExtraInhabitantIndex(const Container *src, A... args) { |
| return swift_getHeapObjectExtraInhabitantIndex( |
| (HeapObject* const *) src->getValueSlot()); |
| } |
| |
| }; |
| |
| /// A box implementation class for an existential container with |
| /// a class constraint and a fixed number of protocol witness tables. |
| template <unsigned NumWitnessTables> |
| struct LLVM_LIBRARY_VISIBILITY ClassExistentialBox |
| : ClassExistentialBoxBase { |
| struct Container { |
| ClassExistentialContainer Header; |
| const void *TypeInfo[NumWitnessTables]; |
| |
| void copyTypeInto(Container *dest) const { |
| for (unsigned i = 0; i != NumWitnessTables; ++i) |
| dest->TypeInfo[i] = TypeInfo[i]; |
| } |
| void **getValueSlot() { return &Header.Value; } |
| void * const *getValueSlot() const { return &Header.Value; } |
| |
| static size_t getContainerStride() { return sizeof(Container); } |
| }; |
| |
| using type = Container; |
| |
| static constexpr size_t size = sizeof(Container); |
| static constexpr size_t alignment = alignof(Container); |
| static constexpr size_t stride = sizeof(Container); |
| static constexpr size_t isPOD = false; |
| static constexpr size_t isBitwiseTakable = true; |
| }; |
| |
| /// A non-fixed box implementation class for a class existential |
| /// type with a dynamic number of witness tables. |
| struct LLVM_LIBRARY_VISIBILITY NonFixedClassExistentialBox |
| : ClassExistentialBoxBase { |
| struct Container { |
| ClassExistentialContainer Header; |
| |
| static unsigned getNumWitnessTables(const Metadata *self) { |
| auto castSelf = static_cast<const ExistentialTypeMetadata*>(self); |
| return castSelf->Flags.getNumWitnessTables(); |
| } |
| |
| void copyTypeInto(Container *dest, const Metadata *self) { |
| Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); |
| } |
| |
| void **getValueSlot() { return &Header.Value; } |
| void * const *getValueSlot() const { return &Header.Value; } |
| |
| static size_t getAlignment(unsigned numWitnessTables) { |
| return alignof(void*); |
| } |
| static size_t getSize(unsigned numWitnessTables) { |
| constexpr size_t base = sizeof(ClassExistentialContainer); |
| static_assert(base > 0, "stride needs base size > 0"); |
| return base + numWitnessTables * sizeof(void*); |
| } |
| static size_t getStride(unsigned numWitnessTables) { |
| return getSize(numWitnessTables); |
| } |
| static size_t getContainerStride(const Metadata *self) { |
| return getStride(getNumWitnessTables(self)); |
| } |
| }; |
| using type = Container; |
| }; |
| |
| /// A common base class for fixed and non-fixed existential metatype box |
| /// implementations. |
| struct LLVM_LIBRARY_VISIBILITY ExistentialMetatypeBoxBase |
| : ExistentialBoxBase<ExistentialMetatypeBoxBase> { |
| static constexpr unsigned numExtraInhabitants = |
| swift_getHeapObjectExtraInhabitantCount(); |
| |
| template <class Container, class... A> |
| static void destroy(Container *value, A... args) { |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithCopy(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| *dest->getValueSlot() = *src->getValueSlot(); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *initializeWithTake(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| *dest->getValueSlot() = *src->getValueSlot(); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithCopy(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| *dest->getValueSlot() = *src->getValueSlot(); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static Container *assignWithTake(Container *dest, Container *src, |
| A... args) { |
| src->copyTypeInto(dest, args...); |
| *dest->getValueSlot() = *src->getValueSlot(); |
| return dest; |
| } |
| |
| template <class Container, class... A> |
| static void storeExtraInhabitant(Container *dest, int index, A... args) { |
| Metadata **MD = const_cast<Metadata **>(dest->getValueSlot()); |
| swift_storeHeapObjectExtraInhabitant(reinterpret_cast<HeapObject **>(MD), |
| index); |
| } |
| |
| template <class Container, class... A> |
| static int getExtraInhabitantIndex(const Container *src, A... args) { |
| Metadata **MD = const_cast<Metadata **>(src->getValueSlot()); |
| return swift_getHeapObjectExtraInhabitantIndex( |
| reinterpret_cast<HeapObject *const *>(MD)); |
| } |
| }; |
| |
| /// A box implementation class for an existential metatype container |
| /// with a fixed number of protocol witness tables. |
| template <unsigned NumWitnessTables> |
| struct LLVM_LIBRARY_VISIBILITY ExistentialMetatypeBox |
| : ExistentialMetatypeBoxBase { |
| struct Container { |
| ExistentialMetatypeContainer Header; |
| const void *TypeInfo[NumWitnessTables]; |
| |
| void copyTypeInto(Container *dest) const { |
| for (unsigned i = 0; i != NumWitnessTables; ++i) |
| dest->TypeInfo[i] = TypeInfo[i]; |
| } |
| const Metadata **getValueSlot() { return &Header.Value; } |
| const Metadata * const *getValueSlot() const { return &Header.Value; } |
| |
| static size_t getContainerStride() { return sizeof(Container); } |
| }; |
| |
| using type = Container; |
| |
| static constexpr size_t size = sizeof(Container); |
| static constexpr size_t alignment = alignof(Container); |
| static constexpr size_t stride = sizeof(Container); |
| static constexpr size_t isPOD = true; |
| static constexpr size_t isBitwiseTakable = true; |
| }; |
| |
| /// A non-fixed box implementation class for an existential metatype |
| /// type with a dynamic number of witness tables. |
| struct LLVM_LIBRARY_VISIBILITY NonFixedExistentialMetatypeBox |
| : ExistentialMetatypeBoxBase { |
| struct Container { |
| ExistentialMetatypeContainer Header; |
| |
| static unsigned getNumWitnessTables(const Metadata *self) { |
| auto castSelf = static_cast<const ExistentialTypeMetadata*>(self); |
| return castSelf->Flags.getNumWitnessTables(); |
| } |
| |
| void copyTypeInto(Container *dest, const Metadata *self) { |
| Header.copyTypeInto(&dest->Header, getNumWitnessTables(self)); |
| } |
| |
| const Metadata **getValueSlot() { return &Header.Value; } |
| const Metadata * const *getValueSlot() const { return &Header.Value; } |
| |
| static size_t getAlignment(unsigned numWitnessTables) { |
| return alignof(void*); |
| } |
| static size_t getSize(unsigned numWitnessTables) { |
| constexpr size_t base = sizeof(ExistentialMetatypeContainer); |
| static_assert(base > 0, "stride needs base size > 0"); |
| return base + numWitnessTables * sizeof(void*); |
| } |
| static size_t getStride(unsigned numWitnessTables) { |
| return getSize(numWitnessTables); |
| } |
| static size_t getContainerStride(const Metadata *self) { |
| return getStride(getNumWitnessTables(self)); |
| } |
| }; |
| using type = Container; |
| }; |
| |
| } // end namespace metadataimpl |
| } // end namespace swift |
| |
| #endif /* SWIFT_RUNTIME_EXISTENTIALMETADATAIMPL_H */ |