1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
|
//===--- 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/Bincompat.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 "swift/Threading/Mutex.h"
#include "Private.h"
#include "SwiftEquatableSupport.h"
#include "SwiftHashableSupport.h"
#include <objc/runtime.h>
#include <Foundation/Foundation.h>
#include <new>
#include <unordered_set>
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` or `Equatable` conformance.
/// This member is lazily-initialized.
/// Instead of using it directly, call `getHashableBaseType()` or `getEquatableBaseType()`
/// Value here is encoded:
/// * Least-significant bit set: This is an Equatable base type
/// * Least-significant bit not set: This is a Hashable base type
mutable std::atomic<uintptr_t> cachedBaseType;
/// The witness table for `Hashable` conformance.
/// This member is lazily-initialized.
/// Instead of using it directly, call `getHashableConformance()`.
/// Value here is encoded:
/// * Least-significant bit set: This is an Equatable conformance
/// * Least-significant bit not set: This is a Hashable conformance
mutable std::atomic<uintptr_t> cachedConformance;
/// Get the base type that conforms to `Hashable`.
/// Returns NULL if the type does not conform.
const Metadata *getHashableBaseType() const;
/// Get the base type that conforms to `Equatable`.
/// Returns NULL if the type does not conform.
const Metadata *getEquatableBaseType() 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;
/// Get the `Equatable` protocol witness table for the contained type.
/// Returns NULL if the type does not conform.
const equatable_support::EquatableWitnessTable *getEquatableConformance() const;
/// Populate the `cachedConformance` with the Hashable conformance
/// (if there is one), else the Equatable conformance.
/// Returns the encoded conformance: least-significant
/// bit is set if this is an Equatable conformance,
/// else it is a Hashable conformance. 0 (or 1) indicates
/// neither was found.
uintptr_t cacheHashableEquatableConformance() const;
SwiftValueHeader()
: cachedBaseType(0), cachedConformance(0) {}
};
// Set cachedConformance to the Hashable conformance if
// there is one, else the Equatable conformance.
// Also set cachedBaseType to the parent type that
// introduced the Hashable/Equatable conformance.
// The cached conformance and type are encoded:
// * If the LSbit is not set, it's the Hashable conformance
// * If the value is exactly 1, neither conformance is present
// * If the LSbit is 1, strip it and you'll have the Equatable conformance
// (Null indicates the cache has not been initialized yet;
// that will never be true on exit of this function.)
// Return: encoded cachedConformance value
uintptr_t
SwiftValueHeader::cacheHashableEquatableConformance() const {
// Relevant conformance and baseType
uintptr_t conformance;
uintptr_t baseType;
// First, see if it's Hashable
const HashableWitnessTable *hashable =
reinterpret_cast<const HashableWitnessTable *>(
swift_conformsToProtocolCommon(type, &HashableProtocolDescriptor));
if (hashable != nullptr) {
conformance = reinterpret_cast<uintptr_t>(hashable);
baseType = reinterpret_cast<uintptr_t>(findHashableBaseType(type));
} else {
// If not Hashable, maybe Equatable?
auto equatable =
swift_conformsToProtocolCommon(type, &equatable_support::EquatableProtocolDescriptor);
// Encode the equatable conformance
conformance = reinterpret_cast<uintptr_t>(equatable) | 1;
if (equatable != nullptr) {
// Find equatable base type
#if SWIFT_STDLIB_USE_RELATIVE_PROTOCOL_WITNESS_TABLES
const auto *description = lookThroughOptionalConditionalWitnessTable(
reinterpret_cast<const RelativeWitnessTable*>(equatable))
->getDescription();
#else
const auto *description = equatable->getDescription();
#endif
const Metadata *baseTypeThatConformsToEquatable =
findConformingSuperclass(type, description);
// Encode the equatable base type
baseType = reinterpret_cast<uintptr_t>(baseTypeThatConformsToEquatable) | 1;
} else {
baseType = 1; // Neither equatable nor hashable
}
}
// Set the conformance/baseType caches atomically
uintptr_t expectedConformance = 0;
cachedConformance.compare_exchange_strong(
expectedConformance, conformance, std::memory_order_acq_rel);
uintptr_t expectedType = 0;
cachedBaseType.compare_exchange_strong(
expectedType, baseType, std::memory_order_acq_rel);
return conformance;
}
const Metadata *SwiftValueHeader::getHashableBaseType() const {
auto type = cachedBaseType.load(std::memory_order_acquire);
if (type == 0) {
cacheHashableEquatableConformance();
type = cachedBaseType.load(std::memory_order_acquire);
}
if ((type & 1) == 0) {
// A Hashable conformance was found
return reinterpret_cast<const Metadata *>(type);
} else {
// Equatable conformance (or no conformance) found
return nullptr;
}
}
const Metadata *SwiftValueHeader::getEquatableBaseType() const {
auto type = cachedBaseType.load(std::memory_order_acquire);
if (type == 0) {
cacheHashableEquatableConformance();
type = cachedBaseType.load(std::memory_order_acquire);
}
if ((type & 1) == 0) {
// A Hashable conformance was found
return nullptr;
} else {
// An Equatable conformance (or neither) was found
return reinterpret_cast<const Metadata *>(type & ~1ULL);
}
}
const hashable_support::HashableWitnessTable *
SwiftValueHeader::getHashableConformance() const {
uintptr_t wt = cachedConformance.load(std::memory_order_acquire);
if (wt == 0) {
wt = cacheHashableEquatableConformance();
}
if ((wt & 1) == 0) {
// Hashable conformance found
return reinterpret_cast<const hashable_support::HashableWitnessTable *>(wt);
} else {
// Equatable conformance (or no conformance) found
return nullptr;
}
}
const equatable_support::EquatableWitnessTable *
SwiftValueHeader::getEquatableConformance() const {
uintptr_t wt = cachedConformance.load(std::memory_order_acquire);
if (wt == 0) {
wt = cacheHashableEquatableConformance();
}
if ((wt & 1) == 0) {
// Hashable conformance found
return nullptr;
} else {
// Equatable conformance (or no conformance) found
return reinterpret_cast<const equatable_support::EquatableWitnessTable *>(wt & ~1ULL);
}
}
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;
}
// `other` must also be a _SwiftValue box
if (![other isKindOfClass:getSwiftValueClass()]) {
return NO;
}
auto selfHeader = getSwiftValueHeader(self);
auto otherHeader = getSwiftValueHeader(other);
if (auto hashableConformance = selfHeader->getHashableConformance()) {
if (auto selfHashableBaseType = selfHeader->getHashableBaseType()) {
auto otherHashableBaseType = otherHeader->getHashableBaseType();
if (selfHashableBaseType == otherHashableBaseType) {
return _swift_stdlib_Hashable_isEqual_indirect(
getSwiftValuePayload(self,
getSwiftValuePayloadAlignMask(selfHeader->type)),
getSwiftValuePayload(other,
getSwiftValuePayloadAlignMask(otherHeader->type)),
selfHashableBaseType, hashableConformance);
}
}
}
if (auto equatableConformance = selfHeader->getEquatableConformance()) {
if (auto selfEquatableBaseType = selfHeader->getEquatableBaseType()) {
auto otherEquatableBaseType = otherHeader->getEquatableBaseType();
if (selfEquatableBaseType == otherEquatableBaseType) {
return _swift_stdlib_Equatable_isEqual_indirect(
getSwiftValuePayload(self,
getSwiftValuePayloadAlignMask(selfHeader->type)),
getSwiftValuePayload(other,
getSwiftValuePayloadAlignMask(otherHeader->type)),
selfEquatableBaseType, equatableConformance);
}
}
}
// Not Equatable, not Hashable, and not the same box
return NO;
}
- (NSUInteger)hash {
// If Swift type is Hashable, get the hash value from there
auto selfHeader = getSwiftValueHeader(self);
auto hashableConformance = selfHeader->getHashableConformance();
if (hashableConformance) {
return _swift_stdlib_Hashable_hashValue_indirect(
getSwiftValuePayload(self,
getSwiftValuePayloadAlignMask(selfHeader->type)),
selfHeader->type, hashableConformance);
}
// If Swift type is Equatable but not Hashable,
// we have to return something here that is compatible
// with the `isEqual:` above.
auto equatableConformance = selfHeader->getEquatableConformance();
if (equatableConformance) {
// Warn once per type about this
auto metadata = getSwiftValueTypeMetadata(self);
static Lazy<std::unordered_set<const Metadata *>> warned;
static LazyMutex warnedLock;
LazyMutex::ScopedLock guard(warnedLock);
auto result = warned.get().insert(metadata);
auto inserted = std::get<1>(result);
if (inserted) {
TypeNamePair typeName = swift_getTypeName(metadata, true);
warning(0,
"Obj-C `-hash` invoked on a Swift value of type `%s` that is Equatable but not Hashable; "
"this can lead to severe performance problems.\n",
typeName.data);
}
// Constant value (yuck!) is the only choice here
return (NSUInteger)1;
}
// If the Swift type is neither Equatable nor Hashable,
// then we can hash the identity, which should be pretty
// good in practice.
return (NSUInteger)self;
}
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...
|