mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Apply a blanket pass of including `new` for the placement new allocation and namespacing the call to the global placement new allocator. This should repair the Android ARMv7 builds.
384 lines
13 KiB
Plaintext
384 lines
13 KiB
Plaintext
//===--- SwiftValue.mm - Boxed Swift value class --------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This implements the Objective-C class that is used to carry Swift values
|
|
// that have been bridged to Objective-C objects without special handling.
|
|
// The class is opaque to user code, but is `NSObject`- and `NSCopying`-
|
|
// conforming and is understood by the Swift runtime for dynamic casting
|
|
// back to the contained type.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Runtime/Config.h"
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
#include "SwiftObject.h"
|
|
#include "SwiftValue.h"
|
|
#include "swift/Basic/Lazy.h"
|
|
#include "swift/Runtime/Casting.h"
|
|
#include "swift/Runtime/HeapObject.h"
|
|
#include "swift/Runtime/Metadata.h"
|
|
#include "swift/Runtime/ObjCBridge.h"
|
|
#include "swift/Runtime/Debug.h"
|
|
#include "Private.h"
|
|
#include "SwiftHashableSupport.h"
|
|
#include <objc/runtime.h>
|
|
#include <Foundation/Foundation.h>
|
|
|
|
#include <new>
|
|
|
|
using namespace swift;
|
|
using namespace swift::hashable_support;
|
|
|
|
// TODO: Making this a SwiftObject subclass would let us use Swift refcounting,
|
|
// but we would need to be able to emit __SwiftValue's Objective-C class object
|
|
// with the Swift destructor pointer prefixed before it.
|
|
//
|
|
// The layout of `__SwiftValue` is:
|
|
// - object header,
|
|
// - `SwiftValueHeader` instance,
|
|
// - the payload, tail-allocated (the Swift value contained in this box).
|
|
//
|
|
// NOTE: older runtimes called this _SwiftValue. The two must
|
|
// coexist, so it was renamed. The old name must not be used in the new
|
|
// runtime.
|
|
@interface __SwiftValue : NSObject <NSCopying>
|
|
|
|
- (id)copyWithZone:(NSZone *)zone;
|
|
|
|
@end
|
|
|
|
/// The fixed-size ivars of `__SwiftValue`. The actual boxed value is
|
|
/// tail-allocated.
|
|
struct SwiftValueHeader {
|
|
/// The type of the value contained in the `__SwiftValue` box.
|
|
const Metadata *type;
|
|
|
|
/// The base type that introduces the `Hashable` conformance.
|
|
/// This member is only available for native Swift errors.
|
|
/// This member is lazily-initialized.
|
|
/// Instead of using it directly, call `getHashableBaseType()`.
|
|
mutable std::atomic<const Metadata *> hashableBaseType;
|
|
|
|
/// The witness table for `Hashable` conformance.
|
|
/// This member is only available for native Swift errors.
|
|
/// This member is lazily-initialized.
|
|
/// Instead of using it directly, call `getHashableConformance()`.
|
|
mutable std::atomic<const hashable_support::HashableWitnessTable *>
|
|
hashableConformance;
|
|
|
|
/// Get the base type that conforms to `Hashable`.
|
|
/// Returns NULL if the type does not conform.
|
|
const Metadata *getHashableBaseType() const;
|
|
|
|
/// Get the `Hashable` protocol witness table for the contained type.
|
|
/// Returns NULL if the type does not conform.
|
|
const hashable_support::HashableWitnessTable *getHashableConformance() const;
|
|
|
|
SwiftValueHeader()
|
|
: hashableBaseType(nullptr), hashableConformance(nullptr) {}
|
|
};
|
|
|
|
const Metadata *SwiftValueHeader::getHashableBaseType() const {
|
|
if (auto type = hashableBaseType.load(std::memory_order_acquire)) {
|
|
if (reinterpret_cast<uintptr_t>(type) == 1) {
|
|
return nullptr;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
const Metadata *expectedType = nullptr;
|
|
const Metadata *hashableBaseType = findHashableBaseType(type);
|
|
this->hashableBaseType.compare_exchange_strong(
|
|
expectedType, hashableBaseType ? hashableBaseType
|
|
: reinterpret_cast<const Metadata *>(1),
|
|
std::memory_order_acq_rel);
|
|
return type;
|
|
}
|
|
|
|
const hashable_support::HashableWitnessTable *
|
|
SwiftValueHeader::getHashableConformance() const {
|
|
if (auto wt = hashableConformance.load(std::memory_order_acquire)) {
|
|
if (reinterpret_cast<uintptr_t>(wt) == 1) {
|
|
return nullptr;
|
|
}
|
|
return wt;
|
|
}
|
|
|
|
const HashableWitnessTable *expectedWT = nullptr;
|
|
const HashableWitnessTable *wt =
|
|
reinterpret_cast<const HashableWitnessTable *>(
|
|
swift_conformsToProtocol(type, &HashableProtocolDescriptor));
|
|
hashableConformance.compare_exchange_strong(
|
|
expectedWT, wt ? wt : reinterpret_cast<const HashableWitnessTable *>(1),
|
|
std::memory_order_acq_rel);
|
|
return wt;
|
|
}
|
|
|
|
static constexpr const size_t SwiftValueHeaderOffset
|
|
= sizeof(Class); // isa pointer
|
|
static constexpr const size_t SwiftValueMinAlignMask
|
|
= alignof(Class) - 1;
|
|
/* TODO: If we're able to become a SwiftObject subclass in the future,
|
|
* change to this:
|
|
static constexpr const size_t SwiftValueHeaderOffset
|
|
= sizeof(HeapObject);
|
|
static constexpr const size_t SwiftValueMinAlignMask
|
|
= alignof(HeapObject) - 1;
|
|
*/
|
|
|
|
static Class _getSwiftValueClass() {
|
|
auto theClass = [__SwiftValue class];
|
|
// Fixed instance size of __SwiftValue should be same as object header.
|
|
assert(class_getInstanceSize(theClass) == SwiftValueHeaderOffset
|
|
&& "unexpected size of __SwiftValue?!");
|
|
return theClass;
|
|
}
|
|
|
|
static Class getSwiftValueClass() {
|
|
return SWIFT_LAZY_CONSTANT(_getSwiftValueClass());
|
|
}
|
|
|
|
static constexpr size_t getSwiftValuePayloadOffset(size_t alignMask) {
|
|
return (SwiftValueHeaderOffset + sizeof(SwiftValueHeader) + alignMask) &
|
|
~alignMask;
|
|
}
|
|
|
|
static SwiftValueHeader *getSwiftValueHeader(__SwiftValue *v) {
|
|
auto instanceBytes = reinterpret_cast<char *>(v);
|
|
return reinterpret_cast<SwiftValueHeader *>(instanceBytes +
|
|
SwiftValueHeaderOffset);
|
|
}
|
|
|
|
static OpaqueValue *getSwiftValuePayload(__SwiftValue *v, size_t alignMask) {
|
|
auto instanceBytes = reinterpret_cast<char *>(v);
|
|
return reinterpret_cast<OpaqueValue *>(instanceBytes +
|
|
getSwiftValuePayloadOffset(alignMask));
|
|
}
|
|
|
|
static size_t getSwiftValuePayloadAlignMask(const Metadata *type) {
|
|
return type->getValueWitnesses()->getAlignmentMask() | SwiftValueMinAlignMask;
|
|
}
|
|
|
|
const Metadata *swift::getSwiftValueTypeMetadata(__SwiftValue *v) {
|
|
return getSwiftValueHeader(v)->type;
|
|
}
|
|
|
|
std::pair<const Metadata *, const OpaqueValue *>
|
|
swift::getValueFromSwiftValue(__SwiftValue *v) {
|
|
auto instanceType = getSwiftValueTypeMetadata(v);
|
|
size_t alignMask = getSwiftValuePayloadAlignMask(instanceType);
|
|
return {instanceType, getSwiftValuePayload(v, alignMask)};
|
|
}
|
|
|
|
__SwiftValue *swift::bridgeAnythingToSwiftValueObject(OpaqueValue *src,
|
|
const Metadata *srcType,
|
|
bool consume) {
|
|
size_t alignMask = getSwiftValuePayloadAlignMask(srcType);
|
|
|
|
size_t totalSize =
|
|
getSwiftValuePayloadOffset(alignMask) + srcType->getValueWitnesses()->size;
|
|
|
|
void *instanceMemory = swift_slowAlloc(totalSize, alignMask);
|
|
__SwiftValue *instance
|
|
= objc_constructInstance(getSwiftValueClass(), instanceMemory);
|
|
/* TODO: If we're able to become a SwiftObject subclass in the future,
|
|
* change to this:
|
|
auto instance = swift_allocObject(getSwiftValueClass(), totalSize,
|
|
alignMask);
|
|
*/
|
|
|
|
auto header = getSwiftValueHeader(instance);
|
|
::new (header) SwiftValueHeader();
|
|
header->type = srcType;
|
|
|
|
auto payload = getSwiftValuePayload(instance, alignMask);
|
|
if (consume)
|
|
srcType->vw_initializeWithTake(payload, src);
|
|
else
|
|
srcType->vw_initializeWithCopy(payload, src);
|
|
|
|
return instance;
|
|
}
|
|
|
|
__SwiftValue *swift::getAsSwiftValue(id object) {
|
|
// __SwiftValue should have no subclasses or proxies. We can do an exact
|
|
// class check.
|
|
if (object_getClass(object) == getSwiftValueClass())
|
|
return object;
|
|
return nil;
|
|
}
|
|
|
|
bool
|
|
swift::findSwiftValueConformances(const ExistentialTypeMetadata *existentialType,
|
|
const WitnessTable **tablesBuffer) {
|
|
// __SwiftValue never satisfies a superclass constraint.
|
|
if (existentialType->getSuperclassConstraint() != nullptr)
|
|
return false;
|
|
|
|
Class cls = nullptr;
|
|
|
|
// Note that currently we never modify tablesBuffer because
|
|
// __SwiftValue doesn't conform to any protocols that need witness tables.
|
|
|
|
for (auto protocol : existentialType->getProtocols()) {
|
|
// __SwiftValue only conforms to ObjC protocols. We specifically
|
|
// don't want to say that __SwiftValue conforms to the Swift protocols
|
|
// that NSObject conforms to because that would create a situation
|
|
// where arguably an arbitrary type would conform to those protocols
|
|
// by way of coercion through __SwiftValue. Eventually we want to
|
|
// change __SwiftValue to not be an NSObject subclass at all.
|
|
|
|
if (!protocol.isObjC())
|
|
return false;
|
|
|
|
if (!cls) cls = _getSwiftValueClass();
|
|
|
|
// Check whether the class conforms to the protocol.
|
|
if (![cls conformsToProtocol: protocol.getObjCProtocol()])
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@implementation __SwiftValue
|
|
|
|
+ (instancetype)allocWithZone:(NSZone *)zone {
|
|
swift::crash("__SwiftValue cannot be instantiated");
|
|
}
|
|
|
|
- (id)copyWithZone:(NSZone *)zone {
|
|
// Instances are immutable, so we can just retain.
|
|
return objc_retain(self);
|
|
|
|
/* TODO: If we're able to become a SwiftObject subclass in the future,
|
|
* change to this:
|
|
swift_retain((HeapObject*)self);
|
|
return self;
|
|
*/
|
|
}
|
|
|
|
// Since we allocate using Swift's allocator to properly handle alignment,
|
|
// we need to deallocate ourselves instead of delegating to
|
|
// -[NSObject dealloc].
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
|
|
- (void)dealloc {
|
|
// TODO: If we're able to become a SwiftObject subclass in the future,
|
|
// this should move to the heap metadata destructor function.
|
|
|
|
// Destroy the contained value.
|
|
auto instanceType = getSwiftValueTypeMetadata(self);
|
|
size_t alignMask = getSwiftValuePayloadAlignMask(instanceType);
|
|
getSwiftValueHeader(self)->~SwiftValueHeader();
|
|
instanceType->vw_destroy(getSwiftValuePayload(self, alignMask));
|
|
|
|
// Deallocate ourselves.
|
|
objc_destructInstance(self);
|
|
auto totalSize = getSwiftValuePayloadOffset(alignMask) +
|
|
instanceType->getValueWitnesses()->size;
|
|
swift_slowDealloc(self, totalSize, alignMask);
|
|
}
|
|
#pragma clang diagnostic pop
|
|
|
|
- (BOOL)isEqual:(id)other {
|
|
if (self == other) {
|
|
return YES;
|
|
}
|
|
|
|
if (!other) {
|
|
return NO;
|
|
}
|
|
|
|
if (![other isKindOfClass:getSwiftValueClass()]) {
|
|
return NO;
|
|
}
|
|
|
|
auto selfHeader = getSwiftValueHeader(self);
|
|
auto otherHeader = getSwiftValueHeader(other);
|
|
|
|
auto hashableBaseType = selfHeader->getHashableBaseType();
|
|
if (!hashableBaseType ||
|
|
otherHeader->getHashableBaseType() != hashableBaseType) {
|
|
return NO;
|
|
}
|
|
|
|
auto hashableConformance = selfHeader->getHashableConformance();
|
|
if (!hashableConformance) {
|
|
return NO;
|
|
}
|
|
|
|
return _swift_stdlib_Hashable_isEqual_indirect(
|
|
getSwiftValuePayload(self,
|
|
getSwiftValuePayloadAlignMask(selfHeader->type)),
|
|
getSwiftValuePayload(other,
|
|
getSwiftValuePayloadAlignMask(otherHeader->type)),
|
|
hashableBaseType, hashableConformance);
|
|
}
|
|
|
|
- (NSUInteger)hash {
|
|
auto selfHeader = getSwiftValueHeader(self);
|
|
auto hashableConformance = selfHeader->getHashableConformance();
|
|
if (!hashableConformance) {
|
|
return (NSUInteger)self;
|
|
}
|
|
return _swift_stdlib_Hashable_hashValue_indirect(
|
|
getSwiftValuePayload(self,
|
|
getSwiftValuePayloadAlignMask(selfHeader->type)),
|
|
selfHeader->type, hashableConformance);
|
|
}
|
|
|
|
static id getValueDescription(__SwiftValue *self) {
|
|
const Metadata *type;
|
|
const OpaqueValue *value;
|
|
std::tie(type, value) = getValueFromSwiftValue(self);
|
|
|
|
// Copy the value, since it will be consumed by getSummary.
|
|
ValueBuffer copyBuf;
|
|
auto copy = type->allocateBufferIn(©Buf);
|
|
type->vw_initializeWithCopy(copy, const_cast<OpaqueValue *>(value));
|
|
|
|
id string = getDescription(copy, type);
|
|
type->deallocateBufferIn(©Buf);
|
|
return string;
|
|
}
|
|
|
|
- (id /* NSString */)description {
|
|
return getValueDescription(self);
|
|
}
|
|
- (id /* NSString */)debugDescription {
|
|
return getValueDescription(self);
|
|
}
|
|
|
|
// Private methods for debugging purposes.
|
|
|
|
- (const Metadata *)_swiftTypeMetadata {
|
|
return getSwiftValueTypeMetadata(self);
|
|
}
|
|
- (id /* NSString */)_swiftTypeName {
|
|
TypeNamePair typeName
|
|
= swift_getTypeName(getSwiftValueTypeMetadata(self), true);
|
|
id str = swift_stdlib_NSStringFromUTF8(typeName.data, typeName.length);
|
|
return [str autorelease];
|
|
}
|
|
- (const OpaqueValue *)_swiftValue {
|
|
return getValueFromSwiftValue(self).second;
|
|
}
|
|
|
|
@end
|
|
#endif
|
|
|
|
// TODO: We could pick specialized __SwiftValue subclasses for trivial types
|
|
// or for types with known size and alignment characteristics. Probably
|
|
// not enough of a real perf bottleneck to be worth it...
|