Files
swift-mirror/lib/RemoteAST/RemoteAST.cpp
Slava Pestov bd3b45138f RemoteAST: More consistent behavior of existentials inside classes
LLDB calls getDynamicTypeAndAddressForExistential() on an existential
value without knowing if its a class existential or opaque existential.

Class existentials return the address of the instance itself here,
whereas opaque existentials always returned the address of the
payload value.

This meant the caller could not usefully operate on the payload value
if it was of class type, because there was no way of knowing if the
extra dereference had occurred or not.

Now, always load the reference if the wrapped type is a class, even
if the existential is opaque.

Will be tested on the lldb side with another change I'm working on.
2019-02-12 14:59:57 -05:00

667 lines
24 KiB
C++

//===--- RemoteAST.cpp ----------------------------------------------------===//
//
// 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 file implements the RemoteAST interface.
//
//===----------------------------------------------------------------------===//
#include "swift/RemoteAST/RemoteAST.h"
#include "swift/Remote/MetadataReader.h"
#include "swift/Strings.h"
#include "swift/Subsystems.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/ASTDemangler.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/GenericSignature.h"
#include "swift/AST/Module.h"
#include "swift/AST/NameLookup.h"
#include "swift/AST/SubstitutionMap.h"
#include "swift/AST/Types.h"
#include "swift/AST/TypeRepr.h"
#include "swift/Basic/Mangler.h"
#include "swift/ClangImporter/ClangImporter.h"
#include "swift/Demangling/Demangler.h"
#include "llvm/ADT/StringSwitch.h"
// TODO: Develop a proper interface for this.
#include "swift/AST/IRGenOptions.h"
#include "swift/AST/SILOptions.h"
#include "swift/SIL/SILModule.h"
#include "../IRGen/IRGenModule.h"
#include "../IRGen/FixedTypeInfo.h"
#include "../IRGen/GenClass.h"
#include "../IRGen/GenStruct.h"
#include "../IRGen/GenTuple.h"
#include "../IRGen/MemberAccessStrategy.h"
using namespace swift;
using namespace swift::remote;
using namespace swift::remoteAST;
using irgen::Alignment;
using irgen::Size;
static inline RemoteAddress operator+(RemoteAddress address, Size offset) {
return RemoteAddress(address.getAddressData() + offset.getValue());
}
namespace {
/// A "minimal" class for querying IRGen.
struct IRGenContext {
IRGenOptions IROpts;
SILOptions SILOpts;
std::unique_ptr<SILModule> SILMod;
llvm::LLVMContext LLVMContext;
irgen::IRGenerator IRGen;
irgen::IRGenModule IGM;
private:
IRGenContext(ASTContext &ctx, ModuleDecl *module)
: IROpts(createIRGenOptions()),
SILMod(SILModule::createEmptyModule(module, SILOpts)),
IRGen(IROpts, *SILMod),
IGM(IRGen, IRGen.createTargetMachine(), LLVMContext) {}
static IRGenOptions createIRGenOptions() {
IRGenOptions IROpts;
return IROpts;
}
public:
static std::unique_ptr<IRGenContext>
create(ASTContext &ctx, DeclContext *nominalDC) {
auto module = nominalDC->getParentModule();
return std::unique_ptr<IRGenContext>(new IRGenContext(ctx, module));
}
};
/// The basic implementation of the RemoteASTContext interface.
/// The template subclasses do target-specific logic.
class RemoteASTContextImpl {
std::unique_ptr<IRGenContext> IRGen;
Optional<Failure> CurFailure;
public:
RemoteASTContextImpl() = default;
virtual ~RemoteASTContextImpl() = default;
virtual Result<Type>
getTypeForRemoteTypeMetadata(RemoteAddress metadata, bool skipArtificial) = 0;
virtual Result<MetadataKind>
getKindForRemoteTypeMetadata(RemoteAddress metadata) = 0;
virtual Result<NominalTypeDecl*>
getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) = 0;
virtual Result<RemoteAddress>
getHeapMetadataForObject(RemoteAddress object) = 0;
virtual Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressForExistential(RemoteAddress object,
Type staticType) = 0;
Result<uint64_t>
getOffsetOfMember(Type type, RemoteAddress optMetadata, StringRef memberName){
// Sanity check: obviously invalid arguments.
if (!type || memberName.empty())
return Result<uint64_t>::emplaceFailure(Failure::BadArgument);
// Sanity check: if the caller gave us a dependent type, there's no way
// we can handle that.
if (type->hasTypeParameter() || type->hasArchetype())
return Result<uint64_t>::emplaceFailure(Failure::DependentArgument);
// Split into cases.
if (auto typeDecl = type->getNominalOrBoundGenericNominal()) {
return getOffsetOfField(type, typeDecl, optMetadata, memberName);
} else if (auto tupleType = type->getAs<TupleType>()) {
return getOffsetOfTupleElement(tupleType, optMetadata, memberName);
} else {
return Result<uint64_t>::emplaceFailure(Failure::TypeHasNoSuchMember,
memberName);
}
}
protected:
template <class T, class DefaultFailureKindTy, class... DefaultFailureArgTys>
Result<T> getFailureAsResult(DefaultFailureKindTy defaultFailureKind,
DefaultFailureArgTys &&...defaultFailureArgs) {
// If we already have a failure, use that.
if (CurFailure) {
Result<T> result = std::move(*CurFailure);
CurFailure.reset();
return result;
}
// Otherwise, use the default failure.
return Result<T>::emplaceFailure(defaultFailureKind,
std::forward<DefaultFailureArgTys>(defaultFailureArgs)...);
}
template <class T>
Result<T> getFailure() {
return getFailureAsResult<T>(Failure::Unknown);
}
template <class T, class KindTy, class... ArgTys>
Result<T> fail(KindTy kind, ArgTys &&...args) {
return Result<T>::emplaceFailure(kind, std::forward<ArgTys>(args)...);
}
private:
virtual ASTBuilder &getBuilder() = 0;
virtual MemoryReader &getReader() = 0;
virtual bool readWordOffset(RemoteAddress address, int64_t *offset) = 0;
virtual std::unique_ptr<IRGenContext> createIRGenContext() = 0;
virtual Result<uint64_t>
getOffsetOfTupleElementFromMetadata(RemoteAddress metadata,
unsigned elementIndex) = 0;
virtual Result<uint64_t>
getOffsetOfFieldFromMetadata(RemoteAddress metadata,
StringRef memberName) = 0;
IRGenContext *getIRGen() {
if (!IRGen) IRGen = createIRGenContext();
return IRGen.get();
}
Result<uint64_t>
getOffsetOfField(Type type, NominalTypeDecl *typeDecl,
RemoteAddress optMetadata, StringRef memberName) {
if (!isa<StructDecl>(typeDecl) && !isa<ClassDecl>(typeDecl))
return fail<uint64_t>(Failure::Unimplemented,
"access members of this kind of type");
// Try to find the member.
VarDecl *member = findField(typeDecl, memberName);
// If we found a member, try to find its offset statically.
if (member && member->hasStorage() && !typeDecl->isResilient()) {
if (auto irgen = getIRGen()) {
return getOffsetOfFieldFromIRGen(irgen->IGM, type, typeDecl,
optMetadata, member);
}
}
// Try searching the metadata for a member with the given name.
if (optMetadata) {
return getOffsetOfFieldFromMetadata(optMetadata, memberName);
}
// Okay, that's everything we know how to try.
// Use a specialized diagnostic if we couldn't find any such member.
if (!member) {
return fail<uint64_t>(Failure::TypeHasNoSuchMember, memberName);
}
return fail<uint64_t>(Failure::Unknown);
}
/// Look for an instance property of the given nominal type that's
/// known to be stored.
VarDecl *findField(NominalTypeDecl *typeDecl, StringRef memberName) {
for (auto field : typeDecl->getStoredProperties()) {
if (field->getName().str() == memberName)
return field;
}
return nullptr;
}
using MemberAccessStrategy = irgen::MemberAccessStrategy;
Result<uint64_t>
getOffsetOfFieldFromIRGen(irgen::IRGenModule &IGM, Type type,
NominalTypeDecl *typeDecl,
RemoteAddress optMetadata, VarDecl *member) {
SILType loweredTy = IGM.getSILTypes().getLoweredType(type);
MemberAccessStrategy strategy =
(isa<StructDecl>(typeDecl)
? getPhysicalStructMemberAccessStrategy(IGM, loweredTy, member)
: getPhysicalClassMemberAccessStrategy(IGM, loweredTy, member));
switch (strategy.getKind()) {
case MemberAccessStrategy::Kind::Complex:
return fail<uint64_t>(Failure::Unimplemented,
"access members with complex storage");
case MemberAccessStrategy::Kind::DirectFixed:
return uint64_t(strategy.getDirectOffset().getValue());
case MemberAccessStrategy::Kind::DirectGlobal: {
RemoteAddress directOffsetAddress =
getReader().getSymbolAddress(strategy.getDirectGlobalSymbol());
if (!directOffsetAddress)
return getFailure<uint64_t>();
return readDirectOffset(directOffsetAddress,
strategy.getDirectOffsetKind());
}
case MemberAccessStrategy::Kind::IndirectFixed: {
// We can't apply indirect offsets without metadata.
if (!optMetadata)
return fail<uint64_t>(Failure::Unimplemented,
"access generically-offset members without "
"metadata");
Size indirectOffset = strategy.getIndirectOffset();
return readIndirectOffset(optMetadata, indirectOffset,
strategy.getDirectOffsetKind());
}
case MemberAccessStrategy::Kind::IndirectGlobal: {
// We can't apply indirect offsets without metadata.
if (!optMetadata)
return fail<uint64_t>(Failure::Unimplemented,
"access generically-offset members without "
"metadata");
RemoteAddress indirectOffsetAddress =
getReader().getSymbolAddress(strategy.getIndirectGlobalSymbol());
Size indirectOffset;
if (!readOffset(indirectOffsetAddress,
strategy.getIndirectOffsetKind(),
indirectOffset))
return getFailure<uint64_t>();
return readIndirectOffset(optMetadata, indirectOffset,
strategy.getDirectOffsetKind());
}
}
llvm_unreachable("bad member MemberAccessStrategy");
}
bool readOffset(RemoteAddress address,
MemberAccessStrategy::OffsetKind kind,
Size &offset) {
switch (kind) {
case MemberAccessStrategy::OffsetKind::Bytes_Word: {
int64_t rawOffset;
if (!readWordOffset(address, &rawOffset))
return false;
offset = Size(rawOffset);
return true;
}
}
llvm_unreachable("bad offset kind");
}
Result<uint64_t> readIndirectOffset(RemoteAddress metadata,
Size indirectOffset,
MemberAccessStrategy::OffsetKind kind) {
RemoteAddress directOffsetAddress = metadata + indirectOffset;
return readDirectOffset(directOffsetAddress, kind);
}
Result<uint64_t> readDirectOffset(RemoteAddress directOffsetAddress,
MemberAccessStrategy::OffsetKind kind) {
Size directOffset;
if (!readOffset(directOffsetAddress, kind, directOffset))
return getFailure<uint64_t>();
return uint64_t(directOffset.getValue());
}
/// Read the
Result<uint64_t>
getOffsetOfTupleElement(TupleType *type, RemoteAddress optMetadata,
StringRef memberName) {
// Check that the member "name" is a valid index into the tuple.
unsigned targetIndex;
if (memberName.getAsInteger(10, targetIndex) ||
targetIndex >= type->getNumElements())
return fail<uint64_t>(Failure::TypeHasNoSuchMember, memberName);
// Fast path: element 0 is always at offset 0.
if (targetIndex == 0) return uint64_t(0);
// Create an IRGen instance.
auto irgen = getIRGen();
if (!irgen) return Result<uint64_t>::emplaceFailure(Failure::Unknown);
auto &IGM = irgen->IGM;
SILType loweredTy = IGM.getSILTypes().getLoweredType(type);
// If the type has a statically fixed offset, return that.
if (auto offset =
irgen::getFixedTupleElementOffset(IGM, loweredTy, targetIndex))
return offset->getValue();
// If we have metadata, go load from that.
if (optMetadata)
return getOffsetOfTupleElementFromMetadata(optMetadata, targetIndex);
// Okay, reproduce tuple layout.
// Find the last element with a known offset. Note that we don't
// have to ask IRGen about element 0 because we know its size is zero.
Size lastOffset = Size(0);
unsigned lastIndex = targetIndex;
for (--lastIndex; lastIndex != 0; --lastIndex) {
if (auto offset =
irgen::getFixedTupleElementOffset(IGM, loweredTy, lastIndex)) {
lastOffset = *offset;
break;
}
}
// Okay, iteratively build up from there.
for (; ; ++lastIndex) {
// Try to get the size and alignment of this element.
SILType eltTy = loweredTy.getTupleElementType(lastIndex);
auto sizeAndAlignment = getTypeSizeAndAlignment(IGM, eltTy);
if (!sizeAndAlignment) return getFailure<uint64_t>();
// Round up to the alignment of the element.
lastOffset = lastOffset.roundUpToAlignment(sizeAndAlignment->second);
// If this is the target, we're done.
if (lastIndex == targetIndex)
return lastOffset.getValue();
// Otherwise, skip forward by the size of the element.
lastOffset += sizeAndAlignment->first;
}
llvm_unreachable("didn't reach target index");
}
/// Attempt to discover the size and alignment of the given type.
Optional<std::pair<Size, Alignment>>
getTypeSizeAndAlignment(irgen::IRGenModule &IGM, SILType eltTy) {
auto &eltTI = IGM.getTypeInfo(eltTy);
if (auto fixedTI = dyn_cast<irgen::FixedTypeInfo>(&eltTI)) {
return std::make_pair(fixedTI->getFixedSize(),
fixedTI->getFixedAlignment());
}
// TODO: handle resilient types
return None;
}
};
/// A template for generating target-specific implementations of the
/// RemoteASTContext interface.
template <class Runtime>
class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl {
MetadataReader<Runtime, ASTBuilder> Reader;
ASTBuilder &getBuilder() override {
return Reader.Builder;
}
MemoryReader &getReader() override {
return *Reader.Reader;
}
bool readWordOffset(RemoteAddress address, int64_t *extendedOffset) override {
using unsigned_size_t = typename Runtime::StoredSize;
using signed_size_t = typename std::make_signed<unsigned_size_t>::type;
signed_size_t offset;
if (!getReader().readInteger(address, &offset))
return false;
*extendedOffset = offset;
return true;
}
public:
RemoteASTContextConcreteImpl(std::shared_ptr<MemoryReader> &&reader,
ASTContext &ctx)
: Reader(std::move(reader), ctx) {}
Result<Type> getTypeForRemoteTypeMetadata(RemoteAddress metadata,
bool skipArtificial) override {
if (auto result = Reader.readTypeFromMetadata(metadata.getAddressData(),
skipArtificial))
return result;
return getFailure<Type>();
}
Result<MetadataKind>
getKindForRemoteTypeMetadata(RemoteAddress metadata) override {
auto result = Reader.readKindFromMetadata(metadata.getAddressData());
if (result)
return *result;
return getFailure<MetadataKind>();
}
Result<NominalTypeDecl*>
getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) override {
if (auto result =
Reader.readNominalTypeFromDescriptor(descriptor.getAddressData()))
return dyn_cast<NominalTypeDecl>((GenericTypeDecl *) result);
return getFailure<NominalTypeDecl*>();
}
std::unique_ptr<IRGenContext> createIRGenContext() override {
return IRGenContext::create(getBuilder().getASTContext(),
getBuilder().getNotionalDC());
}
Result<uint64_t>
getOffsetOfTupleElementFromMetadata(RemoteAddress metadata,
unsigned index) override {
typename Runtime::StoredSize offset;
if (Reader.readTupleElementOffset(metadata.getAddressData(),
index, &offset))
return uint64_t(offset);
return getFailure<uint64_t>();
}
Result<uint64_t>
getOffsetOfFieldFromMetadata(RemoteAddress metadata,
StringRef memberName) override {
// TODO: this would be useful for resilience
return fail<uint64_t>(Failure::Unimplemented,
"look up field offset by name");
}
Result<RemoteAddress>
getHeapMetadataForObject(RemoteAddress object) override {
auto result = Reader.readMetadataFromInstance(object.getAddressData());
if (result) return RemoteAddress(*result);
return getFailure<RemoteAddress>();
}
Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressClassExistential(RemoteAddress object) {
auto pointerval = Reader.readPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<std::pair<Type, RemoteAddress>>();
auto result = Reader.readMetadataFromInstance(*pointerval);
if (!result)
return getFailure<std::pair<Type, RemoteAddress>>();
auto typeResult = Reader.readTypeFromMetadata(result.getValue());
if (!typeResult)
return getFailure<std::pair<Type, RemoteAddress>>();
return std::make_pair<Type, RemoteAddress>(std::move(typeResult),
RemoteAddress(*pointerval));
}
Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressErrorExistential(RemoteAddress object) {
auto pointerval = Reader.readPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<std::pair<Type, RemoteAddress>>();
auto result =
Reader.readMetadataAndValueErrorExistential(RemoteAddress(*pointerval));
if (!result)
return getFailure<std::pair<Type, RemoteAddress>>();
RemoteAddress metadataAddress = std::get<0>(*result);
RemoteAddress valueAddress = std::get<1>(*result);
bool isBridged = std::get<2>(*result);
auto typeResult =
Reader.readTypeFromMetadata(metadataAddress.getAddressData());
if (!typeResult)
return getFailure<std::pair<Type, RemoteAddress>>();
// When the existential wraps a class type, LLDB expects that the
// address returned is the class instance itself and not the address
// of the reference.
if (!isBridged && typeResult->getClassOrBoundGenericClass()) {
auto pointerval = Reader.readPointerValue(valueAddress.getAddressData());
if (!pointerval)
return getFailure<std::pair<Type, RemoteAddress>>();
valueAddress = RemoteAddress(*pointerval);
}
return std::make_pair<Type, RemoteAddress>(std::move(typeResult),
std::move(valueAddress));
}
Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressOpaqueExistential(RemoteAddress object) {
auto result = Reader.readMetadataAndValueOpaqueExistential(object);
if (!result)
return getFailure<std::pair<Type, RemoteAddress>>();
RemoteAddress metadataAddress = result->first;
RemoteAddress valueAddress = result->second;
auto typeResult =
Reader.readTypeFromMetadata(metadataAddress.getAddressData());
if (!typeResult)
return getFailure<std::pair<Type, RemoteAddress>>();
// When the existential wraps a class type, LLDB expects that the
// address returned is the class instance itself and not the address
// of the reference.
if (typeResult->getClassOrBoundGenericClass()) {
auto pointerval = Reader.readPointerValue(valueAddress.getAddressData());
if (!pointerval)
return getFailure<std::pair<Type, RemoteAddress>>();
valueAddress = RemoteAddress(*pointerval);
}
return std::make_pair<Type, RemoteAddress>(std::move(typeResult),
std::move(valueAddress));
}
Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressExistentialMetatype(RemoteAddress object) {
// The value of the address is just the input address.
// The type is obtained through the following sequence of steps:
// 1) Loading a pointer from the input address
// 2) Reading it as metadata and resolving the type
// 3) Wrapping the resolved type in an existential metatype.
auto pointerval = Reader.readPointerValue(object.getAddressData());
if (!pointerval)
return getFailure<std::pair<Type, RemoteAddress>>();
auto typeResult = Reader.readTypeFromMetadata(*pointerval);
if (!typeResult)
return getFailure<std::pair<Type, RemoteAddress>>();
auto wrappedType = ExistentialMetatypeType::get(typeResult);
if (!wrappedType)
return getFailure<std::pair<Type, RemoteAddress>>();
return std::make_pair<Type, RemoteAddress>(std::move(wrappedType),
std::move(object));
}
/// Resolve the dynamic type and the value address of an existential,
/// given its address and its static type. For class and error existentials,
/// this API takes a pointer to the instance reference rather than the
/// instance reference itself.
Result<std::pair<Type, RemoteAddress>>
getDynamicTypeAndAddressForExistential(RemoteAddress object,
Type staticType) override {
// If this is not an existential, give up.
if (!staticType->isAnyExistentialType())
return getFailure<std::pair<Type, RemoteAddress>>();
// Handle the case where this is an ExistentialMetatype.
if (!staticType->isExistentialType())
return getDynamicTypeAndAddressExistentialMetatype(object);
// This should be an existential type at this point.
auto layout = staticType->getExistentialLayout();
switch (layout.getKind()) {
case ExistentialLayout::Kind::Class:
return getDynamicTypeAndAddressClassExistential(object);
case ExistentialLayout::Kind::Error:
return getDynamicTypeAndAddressErrorExistential(object);
case ExistentialLayout::Kind::Opaque:
return getDynamicTypeAndAddressOpaqueExistential(object);
}
llvm_unreachable("invalid type kind");
}
};
} // end anonymous namespace
static RemoteASTContextImpl *createImpl(ASTContext &ctx,
std::shared_ptr<MemoryReader> &&reader) {
auto &target = ctx.LangOpts.Target;
assert(target.isArch32Bit() || target.isArch64Bit());
if (target.isArch32Bit()) {
using Target = External<RuntimeTarget<4>>;
return new RemoteASTContextConcreteImpl<Target>(std::move(reader), ctx);
} else {
using Target = External<RuntimeTarget<8>>;
return new RemoteASTContextConcreteImpl<Target>(std::move(reader), ctx);
}
}
static RemoteASTContextImpl *asImpl(void *impl) {
return static_cast<RemoteASTContextImpl*>(impl);
}
RemoteASTContext::RemoteASTContext(ASTContext &ctx,
std::shared_ptr<MemoryReader> reader)
: Impl(createImpl(ctx, std::move(reader))) {
}
RemoteASTContext::~RemoteASTContext() {
delete asImpl(Impl);
}
Result<Type>
RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address,
bool skipArtificial) {
return asImpl(Impl)->getTypeForRemoteTypeMetadata(address, skipArtificial);
}
Result<MetadataKind>
RemoteASTContext::getKindForRemoteTypeMetadata(remote::RemoteAddress address) {
return asImpl(Impl)->getKindForRemoteTypeMetadata(address);
}
Result<NominalTypeDecl *>
RemoteASTContext::getDeclForRemoteNominalTypeDescriptor(RemoteAddress address) {
return asImpl(Impl)->getDeclForRemoteNominalTypeDescriptor(address);
}
Result<uint64_t>
RemoteASTContext::getOffsetOfMember(Type type, RemoteAddress optMetadata,
StringRef memberName) {
return asImpl(Impl)->getOffsetOfMember(type, optMetadata, memberName);
}
Result<remote::RemoteAddress>
RemoteASTContext::getHeapMetadataForObject(remote::RemoteAddress address) {
return asImpl(Impl)->getHeapMetadataForObject(address);
}
Result<std::pair<Type, remote::RemoteAddress>>
RemoteASTContext::getDynamicTypeAndAddressForExistential(
remote::RemoteAddress address, Type staticType) {
return asImpl(Impl)->getDynamicTypeAndAddressForExistential(address,
staticType);
}