//===--- 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" #include "swift/Runtime/ExistentialContainer.h" namespace swift { namespace metadataimpl { /// A common base class for opaque-existential and class-existential boxes. template struct SWIFT_LIBRARY_VISIBILITY ExistentialBoxBase { template static void copyBytes(Container *dest, Container *src, A... args) { memcpy(dest, src, src->getContainerStride(args...)); } template static void copyTypeBytes(Container *dest, Container *src, A...args) { auto valueBytes = src->getValueSize(); auto toCopy = src->getContainerStride(args...) - valueBytes; memcpy((char *)dest + valueBytes, (char *)src + valueBytes, toCopy); } }; /// A common base class for fixed and non-fixed opaque-existential box /// implementations. struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBoxBase : ExistentialBoxBase { template 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(value->getBuffer(args...))); } else { // release(valueBuffer[0]) swift_release( *reinterpret_cast(value->getBuffer(args...))); } } enum class Dest { Assign, Init, }; enum class Source { Copy, Take }; template static void copyReference(Container *dest, Container *src, Dest d, Source s, A... args) { auto *destRefAddr = reinterpret_cast(dest->getBuffer(args...)); // Load the source reference. auto *srcRef = *reinterpret_cast(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 static Container *initializeWithCopy(Container *dest, Container *src, A... args) { copyTypeBytes(dest, src, args...); auto *type = src->getType(); auto *vwt = type->getValueWitnesses(); if (vwt->isValueInline()) { auto *destValue = reinterpret_cast(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(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 static Container *initializeWithTake(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template 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(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(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(&tmpBuffer); auto *destValue = reinterpret_cast(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(src->getBuffer(args...)); // Move dest value aside so we can destroy it later. destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); copyTypeBytes(dest, src, 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(dest->getBuffer(args...)); copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // initWithCopy. auto *destValue = reinterpret_cast(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(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 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(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(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(&tmpBuffer); auto *destValue = reinterpret_cast(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(src->getBuffer(args...)); // Move dest value aside. destType->vw_initializeWithTake(opaqueTmpBuffer, destValue); copyTypeBytes(dest, src, 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(dest->getBuffer(args...)); copyTypeBytes(dest, src, args...); if (srcVwt->isValueInline()) { // initWithCopy. auto *destValue = reinterpret_cast(dest->getBuffer(args...)); auto *srcValue = reinterpret_cast(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 struct SWIFT_LIBRARY_VISIBILITY FixedOpaqueExistentialContainer { OpaqueExistentialContainer Header; const void *WitnessTables[NumWitnessTables]; static size_t getValueSize() { return sizeof(Header.Buffer); } }; // We need to be able to instantiate for NumWitnessTables==0, which // requires an explicit specialization. template <> struct FixedOpaqueExistentialContainer<0> { OpaqueExistentialContainer Header; static size_t getValueSize() { return sizeof(Header.Buffer); } }; /// A box implementation class for an opaque existential type with /// a fixed number of witness tables. template struct SWIFT_LIBRARY_VISIBILITY OpaqueExistentialBox : OpaqueExistentialBoxBase { struct Container : FixedOpaqueExistentialContainer { const Metadata *getType() const { return this->Header.Type; } ValueBuffer *getBuffer() { return &this->Header.Buffer; } 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 = true; static constexpr unsigned numExtraInhabitants = swift_getHeapObjectExtraInhabitantCount(); static void storeExtraInhabitantTag(Container *dest, unsigned tag) { swift_storeHeapObjectExtraInhabitant( const_cast( reinterpret_cast(&dest->Header.Type)), tag - 1); } static unsigned getExtraInhabitantTag(const Container *src) { return swift_getHeapObjectExtraInhabitantIndex(const_cast( reinterpret_cast(&src->Header.Type))) + 1; } }; /// A non-fixed box implementation class for an opaque existential /// type with a dynamic number of witness tables. struct SWIFT_LIBRARY_VISIBILITY NonFixedOpaqueExistentialBox : OpaqueExistentialBoxBase { struct Container { OpaqueExistentialContainer Header; const Metadata *getType() { return Header.Type; } ValueBuffer *getBuffer(const Metadata *self) { return &Header.Buffer; } static unsigned getNumWitnessTables(const Metadata *self) { auto castSelf = static_cast(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 self->vw_stride(); } static size_t getValueSize() { return sizeof(Header.Buffer); } }; using type = Container; static constexpr unsigned numExtraInhabitants = swift_getHeapObjectExtraInhabitantCount(); static void storeExtraInhabitantTag(Container *dest, unsigned tag) { swift_storeHeapObjectExtraInhabitant( (HeapObject**)(uintptr_t)&dest->Header.Type, tag - 1); } static unsigned getExtraInhabitantTag(const Container *src) { return swift_getHeapObjectExtraInhabitantIndex( (HeapObject* const *)(uintptr_t)&src->Header.Type) + 1; } }; /// A common base class for fixed and non-fixed class-existential box /// implementations. struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBoxBase : ExistentialBoxBase { static constexpr unsigned numExtraInhabitants = swift_getHeapObjectExtraInhabitantCount(); template static void destroy(Container *value, A... args) { swift_unknownObjectRelease(*value->getValueSlot()); } template static Container *initializeWithCopy(Container *dest, Container *src, A... args) { auto newValue = *src->getValueSlot(); copyBytes(dest, src, args...); swift_unknownObjectRetain(newValue); return dest; } template static Container *initializeWithTake(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template static Container *assignWithCopy(Container *dest, Container *src, A... args) { auto newValue = *src->getValueSlot(); auto oldValue = *dest->getValueSlot(); copyBytes(dest, src, args...); swift_unknownObjectRetain(newValue); swift_unknownObjectRelease(oldValue); return dest; } template static Container *assignWithTake(Container *dest, Container *src, A... args) { auto oldValue = *dest->getValueSlot(); copyBytes(dest, src, args...); swift_unknownObjectRelease(oldValue); return dest; } template static void storeExtraInhabitantTag(Container *dest, unsigned tag, A... args) { swift_storeHeapObjectExtraInhabitant((HeapObject**) dest->getValueSlot(), tag - 1); } template static int getExtraInhabitantTag(const Container *src, A... args) { return swift_getHeapObjectExtraInhabitantIndex( (HeapObject* const *) src->getValueSlot()) + 1; } }; /// A box implementation class for an existential container with /// a class constraint and a fixed number of protocol witness tables. template struct SWIFT_LIBRARY_VISIBILITY ClassExistentialBox : ClassExistentialBoxBase { struct Container { ClassExistentialContainer Header; const void *TypeInfo[NumWitnessTables]; 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 SWIFT_LIBRARY_VISIBILITY NonFixedClassExistentialBox : ClassExistentialBoxBase { struct Container { ClassExistentialContainer Header; static unsigned getNumWitnessTables(const Metadata *self) { auto castSelf = static_cast(self); return castSelf->Flags.getNumWitnessTables(); } 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 self->vw_stride(); } }; using type = Container; }; /// A common base class for fixed and non-fixed existential metatype box /// implementations. struct SWIFT_LIBRARY_VISIBILITY ExistentialMetatypeBoxBase : ExistentialBoxBase { static constexpr unsigned numExtraInhabitants = swift_getHeapObjectExtraInhabitantCount(); template static void destroy(Container *value, A... args) { } template static Container *initializeWithCopy(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template static Container *initializeWithTake(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template static Container *assignWithCopy(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template static Container *assignWithTake(Container *dest, Container *src, A... args) { copyBytes(dest, src, args...); return dest; } template static void storeExtraInhabitantTag(Container *dest, unsigned tag, A... args) { Metadata **MD = const_cast(dest->getValueSlot()); swift_storeHeapObjectExtraInhabitant(reinterpret_cast(MD), tag - 1); } template static int getExtraInhabitantTag(const Container *src, A... args) { Metadata **MD = const_cast(src->getValueSlot()); return swift_getHeapObjectExtraInhabitantIndex( reinterpret_cast(MD)) + 1; } }; /// A box implementation class for an existential metatype container /// with a fixed number of protocol witness tables. template struct SWIFT_LIBRARY_VISIBILITY ExistentialMetatypeBox : ExistentialMetatypeBoxBase { struct Container { ExistentialMetatypeContainer Header; const void *TypeInfo[NumWitnessTables]; 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 SWIFT_LIBRARY_VISIBILITY NonFixedExistentialMetatypeBox : ExistentialMetatypeBoxBase { struct Container { ExistentialMetatypeContainer Header; static unsigned getNumWitnessTables(const Metadata *self) { auto castSelf = static_cast(self); return castSelf->Flags.getNumWitnessTables(); } 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 self->vw_stride(); } }; using type = Container; }; } // end namespace metadataimpl } // end namespace swift #endif /* SWIFT_RUNTIME_EXISTENTIALMETADATAIMPL_H */