mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add an extra opaque field to AddressSpace, which can be used by clients of RemoteInspection to distinguish between different address spaces. LLDB employs an optimization where it reads memory from files instead of the running process whenever it can to speed up memory reads (these can be slow when debugging something over a network). To do this, it needs to keep track whether an address originated from a process or a file. It currently distinguishes addresses by setting an unused high bit on the address, but because of pointer authentication this is not a reliable solution. In order to keep this optimization working, this patch adds an extra opaque AddressSpace field to RemoteAddress, which LLDB can use on its own implementation of MemoryReader to distinguish between addresses. This patch is NFC for the other RemoteInspection clients, as it adds extra information to RemoteAddress, which is entirely optional and if unused should not change the behavior of the library. Although this patch is quite big the changes are largely mechanical, replacing threading StoredPointer with RemoteAddress. rdar://148361743 (cherry picked from commit58df5534d2) (cherry picked from commit8f3862b5e7)
729 lines
26 KiB
C++
729 lines
26 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/TypeRepr.h"
|
|
#include "swift/AST/Types.h"
|
|
#include "swift/Basic/Assertions.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 address + offset.getValue();
|
|
}
|
|
|
|
namespace {
|
|
|
|
/// A "minimal" class for querying IRGen.
|
|
struct IRGenContext {
|
|
const IRGenOptions IROpts;
|
|
SILOptions SILOpts;
|
|
Lowering::TypeConverter TC;
|
|
std::unique_ptr<SILModule> SILMod;
|
|
irgen::IRGenerator IRGen;
|
|
irgen::IRGenModule IGM;
|
|
|
|
private:
|
|
IRGenContext(ASTContext &ctx, ModuleDecl *module)
|
|
: IROpts(createIRGenOptions()),
|
|
TC(*module),
|
|
SILMod(SILModule::createEmptyModule(module, TC, SILOpts)),
|
|
IRGen(IROpts, *SILMod),
|
|
IGM(IRGen, IRGen.createTargetMachine()) {}
|
|
|
|
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;
|
|
std::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<OpenedExistential>
|
|
getDynamicTypeAndAddressForError(RemoteAddress object) = 0;
|
|
virtual Result<OpenedExistential>
|
|
getDynamicTypeAndAddressForExistential(RemoteAddress object,
|
|
Type staticType) = 0;
|
|
virtual Result<Type>
|
|
getUnderlyingTypeForOpaqueType(remote::RemoteAddress opaqueDescriptor,
|
|
SubstitutionMap substitutions,
|
|
unsigned ordinal) = 0;
|
|
Result<uint64_t>
|
|
getOffsetOfMember(Type type, RemoteAddress optMetadata, StringRef memberName){
|
|
// Soundness check: obviously invalid arguments.
|
|
if (!type || memberName.empty())
|
|
return Result<uint64_t>::emplaceFailure(Failure::BadArgument);
|
|
|
|
// Soundness 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.str());
|
|
}
|
|
}
|
|
|
|
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.str());
|
|
}
|
|
|
|
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.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.str());
|
|
|
|
// 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.getLoweredType(type);
|
|
|
|
// Only the runtime metadata knows the offsets of resilient members.
|
|
auto &typeInfo = IGM.getTypeInfo(loweredTy);
|
|
if (!isa<irgen::FixedTypeInfo>(&typeInfo))
|
|
return Result<uint64_t>::emplaceFailure(Failure::NotFixedLayout);
|
|
|
|
// 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.
|
|
std::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 std::nullopt;
|
|
}
|
|
};
|
|
|
|
/// 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, GenericSignature()) {}
|
|
|
|
Result<Type> getTypeForRemoteTypeMetadata(RemoteAddress metadata,
|
|
bool skipArtificial) override {
|
|
if (auto result = Reader.readTypeFromMetadata(metadata, skipArtificial))
|
|
return result;
|
|
return getFailure<Type>();
|
|
}
|
|
|
|
Result<MetadataKind>
|
|
getKindForRemoteTypeMetadata(RemoteAddress metadata) override {
|
|
auto result = Reader.readKindFromMetadata(metadata);
|
|
if (result)
|
|
return *result;
|
|
return getFailure<MetadataKind>();
|
|
}
|
|
|
|
Result<NominalTypeDecl*>
|
|
getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) override {
|
|
if (auto result = Reader.readNominalTypeFromDescriptor(descriptor))
|
|
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, 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);
|
|
if (result) return RemoteAddress(*result);
|
|
return getFailure<RemoteAddress>();
|
|
}
|
|
|
|
Result<OpenedExistential>
|
|
getDynamicTypeAndAddressClassExistential(RemoteAddress object) {
|
|
auto pointerval = Reader.readResolvedPointerValue(object);
|
|
if (!pointerval)
|
|
return getFailure<OpenedExistential>();
|
|
auto result = Reader.readMetadataFromInstance(*pointerval);
|
|
;
|
|
if (!result)
|
|
return getFailure<OpenedExistential>();
|
|
auto typeResult = Reader.readTypeFromMetadata(result.value());
|
|
if (!typeResult)
|
|
return getFailure<OpenedExistential>();
|
|
return OpenedExistential(std::move(typeResult),
|
|
RemoteAddress(*pointerval));
|
|
}
|
|
|
|
Result<OpenedExistential>
|
|
getDynamicTypeAndAddressErrorExistential(RemoteAddress object,
|
|
bool dereference=true) {
|
|
if (dereference) {
|
|
auto pointerval = Reader.readResolvedPointerValue(object);
|
|
if (!pointerval)
|
|
return getFailure<OpenedExistential>();
|
|
object = RemoteAddress(*pointerval);
|
|
}
|
|
|
|
auto result =
|
|
Reader.readMetadataAndValueErrorExistential(object);
|
|
if (!result)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
auto typeResult = Reader.readTypeFromMetadata(result->MetadataAddress);
|
|
if (!typeResult)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
|
|
// 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.
|
|
auto payloadAddress = result->PayloadAddress;
|
|
if (!result->IsBridgedError &&
|
|
typeResult->getClassOrBoundGenericClass()) {
|
|
auto pointerval = Reader.readResolvedPointerValue(payloadAddress);
|
|
if (!pointerval)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
payloadAddress = RemoteAddress(*pointerval);
|
|
}
|
|
|
|
return OpenedExistential(std::move(typeResult),
|
|
std::move(payloadAddress));
|
|
}
|
|
|
|
Result<OpenedExistential>
|
|
getDynamicTypeAndAddressOpaqueExistential(RemoteAddress object) {
|
|
auto result = Reader.readMetadataAndValueOpaqueExistential(object);
|
|
if (!result)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
auto typeResult = Reader.readTypeFromMetadata(result->MetadataAddress);
|
|
if (!typeResult)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
// 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.
|
|
auto payloadAddress = result->PayloadAddress;
|
|
if (typeResult->getClassOrBoundGenericClass()) {
|
|
auto pointerval = Reader.readResolvedPointerValue(payloadAddress);
|
|
if (!pointerval)
|
|
return getFailure<OpenedExistential>();
|
|
|
|
payloadAddress = RemoteAddress(*pointerval);
|
|
}
|
|
|
|
return OpenedExistential(std::move(typeResult),
|
|
std::move(payloadAddress));
|
|
}
|
|
|
|
Result<OpenedExistential>
|
|
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.readResolvedPointerValue(object);
|
|
if (!pointerval)
|
|
return getFailure<OpenedExistential>();
|
|
auto typeResult = Reader.readTypeFromMetadata(*pointerval);
|
|
if (!typeResult)
|
|
return getFailure<OpenedExistential>();
|
|
auto wrappedType = ExistentialMetatypeType::get(typeResult);
|
|
if (!wrappedType)
|
|
return getFailure<OpenedExistential>();
|
|
return OpenedExistential(std::move(wrappedType),
|
|
std::move(object));
|
|
}
|
|
|
|
/// Resolve the dynamic type and the value address of an error existential
|
|
/// object, Unlike getDynamicTypeAndAddressForExistential(), this function
|
|
/// takes the address of the instance and not the address of the reference.
|
|
Result<OpenedExistential>
|
|
getDynamicTypeAndAddressForError(RemoteAddress object) override {
|
|
return getDynamicTypeAndAddressErrorExistential(object,
|
|
/*dereference=*/false);
|
|
}
|
|
|
|
/// 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<OpenedExistential>
|
|
getDynamicTypeAndAddressForExistential(RemoteAddress object,
|
|
Type staticType) override {
|
|
// If this is not an existential, give up.
|
|
if (!staticType->isAnyExistentialType())
|
|
return getFailure<OpenedExistential>();
|
|
|
|
// 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");
|
|
}
|
|
|
|
Result<Type>
|
|
getUnderlyingTypeForOpaqueType(remote::RemoteAddress opaqueDescriptor,
|
|
SubstitutionMap substitutions,
|
|
unsigned ordinal) override {
|
|
auto underlyingType = Reader
|
|
.readUnderlyingTypeForOpaqueTypeDescriptor(
|
|
opaqueDescriptor, ordinal)
|
|
.getType();
|
|
|
|
if (!underlyingType)
|
|
return getFailure<Type>();
|
|
|
|
return underlyingType.subst(substitutions);
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static RemoteASTContextImpl *createImpl(ASTContext &ctx,
|
|
std::shared_ptr<MemoryReader> &&reader) {
|
|
auto &target = ctx.LangOpts.Target;
|
|
assert(target.isArch32Bit() || target.isArch64Bit());
|
|
bool objcInterop = ctx.LangOpts.EnableObjCInterop;
|
|
|
|
if (target.isArch32Bit()) {
|
|
if (objcInterop) {
|
|
using Target = External<WithObjCInterop<RuntimeTarget<4>>>;
|
|
return new RemoteASTContextConcreteImpl<Target>(std::move(reader), ctx);
|
|
} else {
|
|
using Target = External<NoObjCInterop<RuntimeTarget<4>>>;
|
|
return new RemoteASTContextConcreteImpl<Target>(std::move(reader), ctx);
|
|
}
|
|
} else {
|
|
if (objcInterop) {
|
|
using Target = External<WithObjCInterop<RuntimeTarget<8>>>;
|
|
return new RemoteASTContextConcreteImpl<Target>(std::move(reader), ctx);
|
|
} else {
|
|
using Target = External<NoObjCInterop<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<OpenedExistential>
|
|
RemoteASTContext::getDynamicTypeAndAddressForError(
|
|
remote::RemoteAddress address) {
|
|
return asImpl(Impl)->getDynamicTypeAndAddressForError(address);
|
|
}
|
|
|
|
Result<OpenedExistential>
|
|
RemoteASTContext::getDynamicTypeAndAddressForExistential(
|
|
remote::RemoteAddress address, Type staticType) {
|
|
return asImpl(Impl)->getDynamicTypeAndAddressForExistential(address,
|
|
staticType);
|
|
}
|
|
|
|
Result<Type>
|
|
RemoteASTContext::getUnderlyingTypeForOpaqueType(
|
|
remote::RemoteAddress opaqueDescriptor,
|
|
SubstitutionMap substitutions,
|
|
unsigned ordinal) {
|
|
return asImpl(Impl)->getUnderlyingTypeForOpaqueType(opaqueDescriptor,
|
|
substitutions, ordinal);
|
|
}
|