blob: f0cad6a4dc1a215d48a7d2674d44c6bfbdecc65e [file] [log] [blame]
//===--- KeyPaths.cpp - Key path helper symbols ---------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"
#include <cstdint>
#include <cstring>
using namespace swift;
SWIFT_RUNTIME_EXPORT SWIFT_CC(swift)
void swift_copyKeyPathTrivialIndices(const void *src, void *dest, size_t bytes) {
memcpy(dest, src, bytes);
}
SWIFT_CC(swift)
static bool equateGenericArguments(const void *a, const void *b, size_t bytes) {
// Generic arguments can't affect equality, since an equivalent key path may
// have been formed in a fully concrete context without capturing generic
// arguments.
return true;
}
SWIFT_CC(swift)
static intptr_t hashGenericArguments(const void *src, size_t bytes) {
// Generic arguments can't affect equality, since an equivalent key path may
// have been formed in a fully concrete context without capturing generic
// arguments. The implementation recognizes a hash value return of '0' as
// "no effect on the hash".
return 0;
}
/// A prefab witness table for computed key path components that only include
/// captured generic arguments.
SWIFT_RUNTIME_EXPORT
void *(swift_keyPathGenericWitnessTable[]) = {
nullptr, // no destructor necessary
(void*)(uintptr_t)swift_copyKeyPathTrivialIndices,
(void*)(uintptr_t)equateGenericArguments,
(void*)(uintptr_t)hashGenericArguments,
};
/****************************************************************************/
/** Projection functions ****************************************************/
/****************************************************************************/
namespace {
struct AddrAndOwner {
OpaqueValue *Addr;
HeapObject *Owner;
};
}
// These functions are all implemented in the stdlib. Their type
// parameters are passed impliictly in the isa of the key path.
extern "C"
SWIFT_CC(swift) void
swift_getAtKeyPath(SWIFT_INDIRECT_RESULT void *result,
const OpaqueValue *root, void *keyPath);
extern "C"
SWIFT_CC(swift) AddrAndOwner
_swift_modifyAtWritableKeyPath_impl(OpaqueValue *root, void *keyPath);
extern "C"
SWIFT_CC(swift) AddrAndOwner
_swift_modifyAtReferenceWritableKeyPath_impl(const OpaqueValue *root,
void *keyPath);
namespace {
struct YieldOnceTemporary {
const Metadata *Type;
// Yield-once buffers can't be memcpy'ed, so it doesn't matter that
// isValueInline() returns false for non-bitwise-takable types --- but
// it doesn't hurt, either.
ValueBuffer Buffer;
YieldOnceTemporary(const Metadata *type) : Type(type) {}
static OpaqueValue *allocateIn(const Metadata *type,
YieldOnceBuffer *buffer) {
auto *temp =
new (reinterpret_cast<void*>(buffer)) YieldOnceTemporary(type);
return type->allocateBufferIn(&temp->Buffer);
}
static void destroyAndDeallocateIn(YieldOnceBuffer *buffer) {
auto *temp = reinterpret_cast<YieldOnceTemporary*>(buffer);
temp->Type->vw_destroy(temp->Type->projectBufferFrom(&temp->Buffer));
temp->Type->deallocateBufferIn(&temp->Buffer);
}
};
static_assert(sizeof(YieldOnceTemporary) <= sizeof(YieldOnceBuffer) &&
alignof(YieldOnceTemporary) <= alignof(YieldOnceBuffer),
"temporary doesn't fit in a YieldOnceBuffer");
}
static SWIFT_CC(swift)
void _destroy_temporary_continuation(YieldOnceBuffer *buffer, bool forUnwind) {
YieldOnceTemporary::destroyAndDeallocateIn(buffer);
}
// The resilient offset to the start of KeyPath's class-specific data.
extern "C" size_t MANGLE_SYM(s7KeyPathCMo);
YieldOnceResult<const OpaqueValue*>
swift::swift_readAtKeyPath(YieldOnceBuffer *buffer,
const OpaqueValue *root, void *keyPath) {
// The Value type parameter is passed in the class of the key path object.
// KeyPath is a native class, so we can just load its metadata directly
// even on ObjC-interop targets.
const Metadata *keyPathType = static_cast<HeapObject*>(keyPath)->metadata;
// To find the generic arguments, we just have to find the class-specific
// data section of the class; the generic arguments are always at the start
// of that.
//
// We use the resilient access pattern because it's easy; since we're within
// KeyPath's resilience domain, that's not really necessary, and it would
// be totally valid to hard-code an offset.
auto keyPathGenericArgs =
reinterpret_cast<const Metadata * const *>(
reinterpret_cast<const char*>(keyPathType) + MANGLE_SYM(s7KeyPathCMo));
const Metadata *valueTy = keyPathGenericArgs[1];
// Allocate the buffer.
auto result = YieldOnceTemporary::allocateIn(valueTy, buffer);
// Read into the buffer.
swift_getAtKeyPath(result, root, keyPath);
// Return a continuation that destroys the value in the buffer
// and deallocates it.
return { &_destroy_temporary_continuation, result };
}
static SWIFT_CC(swift)
void _release_owner_continuation(YieldOnceBuffer *buffer, bool forUnwind) {
swift_unknownObjectRelease(buffer->Data[0]);
}
YieldOnceResult<OpaqueValue*>
swift::swift_modifyAtWritableKeyPath(YieldOnceBuffer *buffer,
OpaqueValue *root, void *keyPath) {
auto addrAndOwner =
_swift_modifyAtWritableKeyPath_impl(root, keyPath);
buffer->Data[0] = addrAndOwner.Owner;
return { &_release_owner_continuation, addrAndOwner.Addr };
}
YieldOnceResult<OpaqueValue*>
swift::swift_modifyAtReferenceWritableKeyPath(YieldOnceBuffer *buffer,
const OpaqueValue *root,
void *keyPath) {
auto addrAndOwner =
_swift_modifyAtReferenceWritableKeyPath_impl(root, keyPath);
buffer->Data[0] = addrAndOwner.Owner;
return { &_release_owner_continuation, addrAndOwner.Addr };
}