diff --git a/include/swift/Reflection/ReflectionContext.h b/include/swift/Reflection/ReflectionContext.h index c02321b4d1c21..08dc0f1b6a8d1 100644 --- a/include/swift/Reflection/ReflectionContext.h +++ b/include/swift/Reflection/ReflectionContext.h @@ -520,15 +520,14 @@ class ReflectionContext readMetadataAndValueOpaqueExistential(ExistentialAddress); if (!OptMetaAndValue) return false; - RemoteAddress MetadataAddress = OptMetaAndValue->first; - RemoteAddress ValueAddress = OptMetaAndValue->second; - auto InstanceTR = readTypeFromMetadata(MetadataAddress.getAddressData()); + auto InstanceTR = readTypeFromMetadata( + OptMetaAndValue->MetadataAddress.getAddressData()); if (!InstanceTR) return false; *OutInstanceTR = InstanceTR; - *OutInstanceAddress = ValueAddress; + *OutInstanceAddress = OptMetaAndValue->PayloadAddress; return true; } case RecordKind::ErrorExistential: { @@ -537,16 +536,15 @@ class ReflectionContext if (!OptMetaAndValue) return false; - RemoteAddress InstanceMetadataAddress = OptMetaAndValue->first; - RemoteAddress InstanceAddress = OptMetaAndValue->second; + // FIXME: Check third value, 'IsBridgedError' - auto InstanceTR = - readTypeFromMetadata(InstanceMetadataAddress.getAddressData()); + auto InstanceTR = readTypeFromMetadata( + OptMetaAndValue->MetadataAddress.getAddressData()); if (!InstanceTR) return false; *OutInstanceTR = InstanceTR; - *OutInstanceAddress = RemoteAddress(InstanceAddress); + *OutInstanceAddress = OptMetaAndValue->PayloadAddress; return true; } default: diff --git a/include/swift/Remote/MetadataReader.h b/include/swift/Remote/MetadataReader.h index 78888e0f039a7..3355360b60f9e 100644 --- a/include/swift/Remote/MetadataReader.h +++ b/include/swift/Remote/MetadataReader.h @@ -86,6 +86,26 @@ struct delete_with_free { } }; +/// A structure representing an opened existential type. +struct RemoteExistential { + /// The payload's concrete type metadata. + RemoteAddress MetadataAddress; + + /// The address of the payload value. + RemoteAddress PayloadAddress; + + /// True if this is an NSError instance transparently bridged to an Error + /// existential. + bool IsBridgedError; + + RemoteExistential(RemoteAddress MetadataAddress, + RemoteAddress PayloadAddress, + bool IsBridgedError=false) + : MetadataAddress(MetadataAddress), + PayloadAddress(PayloadAddress), + IsBridgedError(IsBridgedError) {} +}; + /// A generic reader of metadata. /// /// BuilderType must implement a particular interface which is currently @@ -327,8 +347,10 @@ class MetadataReader { } /// Given a pointer to a known-error existential, attempt to discover the - /// pointer to its metadata address and its value address. - Optional> + /// pointer to its metadata address, its value address, and whether this + /// is a toll-free-bridged NSError or an actual Error existential wrapper + /// around a native Swift value. + Optional readMetadataAndValueErrorExistential(RemoteAddress ExistentialAddress) { // An pointer to an error existential is always an heap object. auto MetadataAddress = @@ -337,6 +359,7 @@ class MetadataReader { return None; bool isObjC = false; + bool isBridged = false; // If we can determine the Objective-C class name, this is probably an // error existential with NSError-compatible layout. @@ -344,6 +367,8 @@ class MetadataReader { if (readObjCClassName(*MetadataAddress, ObjCClassName)) { if (ObjCClassName == "__SwiftNativeNSError") isObjC = true; + else + isBridged = true; } else { // Otherwise, we can check to see if this is a class metadata with the // kind value's least significant bit set, which indicates a pure @@ -356,6 +381,13 @@ class MetadataReader { isObjC = ClassMeta->isPureObjC(); } + if (isBridged) { + // NSError instances don't need to be unwrapped. + return RemoteExistential(RemoteAddress(*MetadataAddress), + ExistentialAddress, + isBridged); + } + // In addition to the isa pointer and two 32-bit reference counts, if the // error existential is layout-compatible with NSError, we also need to // skip over its three word-sized fields: the error code, the domain, @@ -387,14 +419,15 @@ class MetadataReader { auto Offset = (sizeof(HeapObject) + AlignmentMask) & ~AlignmentMask; InstanceAddress += Offset; - return Optional>( - {RemoteAddress(*InstanceMetadataAddress), - RemoteAddress(InstanceAddress)}); + return RemoteExistential( + RemoteAddress(*InstanceMetadataAddress), + RemoteAddress(InstanceAddress), + isBridged); } /// Given a known-opaque existential, attemp to discover the pointer to its /// metadata address and its value. - Optional> + Optional readMetadataAndValueOpaqueExistential(RemoteAddress ExistentialAddress) { // OpaqueExistentialContainer is the layout of an opaque existential. // `Type` is the pointer to the metadata. @@ -414,8 +447,8 @@ class MetadataReader { // Inline representation (the value fits in the existential container). // So, the value starts at the first word of the container. if (VWT->isValueInline()) - return Optional>( - {RemoteAddress(MetadataAddress), ExistentialAddress}); + return RemoteExistential(RemoteAddress(MetadataAddress), + ExistentialAddress); // Non-inline (box'ed) representation. // The first word of the container stores the address to the box. @@ -426,8 +459,8 @@ class MetadataReader { auto AlignmentMask = VWT->getAlignmentMask(); auto Offset = (sizeof(HeapObject) + AlignmentMask) & ~AlignmentMask; auto StartOfValue = BoxAddress + Offset; - return Optional>( - {RemoteAddress(MetadataAddress), RemoteAddress(StartOfValue)}); + return RemoteExistential(RemoteAddress(MetadataAddress), + RemoteAddress(StartOfValue)); } /// Read a protocol from a reference to said protocol. @@ -496,28 +529,8 @@ class MetadataReader { if (!Meta) return BuiltType(); switch (Meta->getKind()) { - case MetadataKind::Class: { - auto classMeta = cast>(Meta); - if (!classMeta->isTypeMetadata()) { - std::string className; - if (!readObjCClassName(MetadataAddress, className)) - return BuiltType(); - - BuiltType BuiltObjCClass = Builder.createObjCClassType(std::move(className)); - if (!BuiltObjCClass) { - // Try the superclass. - if (!classMeta->Superclass) - return BuiltType(); - - BuiltObjCClass = readTypeFromMetadata(classMeta->Superclass, - skipArtificialSubclasses); - } - - TypeCache[MetadataAddress] = BuiltObjCClass; - return BuiltObjCClass; - } - return readNominalTypeFromMetadata(Meta, skipArtificialSubclasses); - } + case MetadataKind::Class: + return readNominalTypeFromClassMetadata(Meta, skipArtificialSubclasses); case MetadataKind::Struct: case MetadataKind::Enum: case MetadataKind::Optional: @@ -2283,6 +2296,30 @@ class MetadataReader { return nominal; } + BuiltType readNominalTypeFromClassMetadata(MetadataRef origMetadata, + bool skipArtificialSubclasses = false) { + auto classMeta = cast>(origMetadata); + if (classMeta->isTypeMetadata()) + return readNominalTypeFromMetadata(origMetadata, skipArtificialSubclasses); + + std::string className; + if (!readObjCClassName(origMetadata.getAddress(), className)) + return BuiltType(); + + BuiltType BuiltObjCClass = Builder.createObjCClassType(std::move(className)); + if (!BuiltObjCClass) { + // Try the superclass. + if (!classMeta->Superclass) + return BuiltType(); + + BuiltObjCClass = readTypeFromMetadata(classMeta->Superclass, + skipArtificialSubclasses); + } + + TypeCache[origMetadata.getAddress()] = BuiltObjCClass; + return BuiltObjCClass; + } + /// Given that the remote process is running the non-fragile Apple runtime, /// grab the ro-data from a class pointer. StoredPointer readObjCRODataPtr(StoredPointer classAddress) { diff --git a/include/swift/RemoteAST/RemoteAST.h b/include/swift/RemoteAST/RemoteAST.h index 8cf81f0bf4331..e2c2aeb7c8755 100644 --- a/include/swift/RemoteAST/RemoteAST.h +++ b/include/swift/RemoteAST/RemoteAST.h @@ -23,6 +23,7 @@ #include "swift/Remote/MemoryReader.h" #include "swift/Basic/LLVM.h" #include "swift/ABI/MetadataValues.h" +#include "swift/AST/Type.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringRef.h" @@ -31,7 +32,6 @@ namespace swift { class ASTContext; class NominalTypeDecl; -class Type; namespace remoteAST { @@ -136,6 +136,21 @@ class Result { } }; +/// A structure representing an opened existential value. +struct OpenedExistential { + /// The concrete type of the value inside the existential. + Type InstanceType; + + /// The address of the payload. + /// + /// Note: If the concrete type is a class type, this is the address of the + /// instance and not the address of the reference to the instance. + remote::RemoteAddress PayloadAddress; + + OpenedExistential(Type InstanceType, remote::RemoteAddress PayloadAddress) + : InstanceType(InstanceType), PayloadAddress(PayloadAddress) {} +}; + /// A context for performing an operation relating the remote process with /// the AST. This may be discarded and recreated at any time without danger, /// but reusing a context across multiple calls may allow some redundant work @@ -212,11 +227,17 @@ class RemoteASTContext { Result getHeapMetadataForObject(remote::RemoteAddress address); + /// 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 + getDynamicTypeAndAddressForError(remote::RemoteAddress object); + /// Given an existential and its static type, resolve its dynamic /// type and address. A single step of unwrapping is performed, i.e. if the /// value stored inside the existential is itself an existential, the /// caller can decide whether to iterate itself. - Result> + Result getDynamicTypeAndAddressForExistential(remote::RemoteAddress address, Type staticType); }; diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 99bea1d425ca0..36ee3577bb862 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -104,7 +104,9 @@ class RemoteASTContextImpl { getDeclForRemoteNominalTypeDescriptor(RemoteAddress descriptor) = 0; virtual Result getHeapMetadataForObject(RemoteAddress object) = 0; - virtual Result> + virtual Result + getDynamicTypeAndAddressForError(RemoteAddress object) = 0; + virtual Result getDynamicTypeAndAddressForExistential(RemoteAddress object, Type staticType) = 0; @@ -476,58 +478,89 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { return getFailure(); } - Result> + Result getDynamicTypeAndAddressClassExistential(RemoteAddress object) { auto pointerval = Reader.readPointerValue(object.getAddressData()); if (!pointerval) - return getFailure>(); + return getFailure(); auto result = Reader.readMetadataFromInstance(*pointerval); if (!result) - return getFailure>(); + return getFailure(); auto typeResult = Reader.readTypeFromMetadata(result.getValue()); if (!typeResult) - return getFailure>(); - return std::make_pair(std::move(typeResult), - RemoteAddress(*pointerval)); + return getFailure(); + return OpenedExistential(std::move(typeResult), + RemoteAddress(*pointerval)); } - Result> - getDynamicTypeAndAddressErrorExistential(RemoteAddress object) { - auto pointerval = Reader.readPointerValue(object.getAddressData()); - if (!pointerval) - return getFailure>(); + Result + getDynamicTypeAndAddressErrorExistential(RemoteAddress object, + bool dereference=true) { + if (dereference) { + auto pointerval = Reader.readPointerValue(object.getAddressData()); + if (!pointerval) + return getFailure(); + object = RemoteAddress(*pointerval); + } + auto result = - Reader.readMetadataAndValueErrorExistential(RemoteAddress(*pointerval)); + Reader.readMetadataAndValueErrorExistential(object); if (!result) - return getFailure>(); - RemoteAddress metadataAddress = result->first; - RemoteAddress valueAddress = result->second; + return getFailure(); auto typeResult = - Reader.readTypeFromMetadata(metadataAddress.getAddressData()); + Reader.readTypeFromMetadata(result->MetadataAddress.getAddressData()); if (!typeResult) - return getFailure>(); - return std::make_pair(std::move(typeResult), - std::move(valueAddress)); + return getFailure(); + + + // 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.readPointerValue( + payloadAddress.getAddressData()); + if (!pointerval) + return getFailure(); + + payloadAddress = RemoteAddress(*pointerval); + } + + return OpenedExistential(std::move(typeResult), + std::move(payloadAddress)); } - Result> + Result getDynamicTypeAndAddressOpaqueExistential(RemoteAddress object) { auto result = Reader.readMetadataAndValueOpaqueExistential(object); if (!result) - return getFailure>(); - RemoteAddress metadataAddress = result->first; - RemoteAddress valueAddress = result->second; + return getFailure(); auto typeResult = - Reader.readTypeFromMetadata(metadataAddress.getAddressData()); + Reader.readTypeFromMetadata(result->MetadataAddress.getAddressData()); if (!typeResult) - return getFailure>(); - return std::make_pair(std::move(typeResult), - std::move(valueAddress)); + return getFailure(); + + // 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.readPointerValue( + payloadAddress.getAddressData()); + if (!pointerval) + return getFailure(); + + payloadAddress = RemoteAddress(*pointerval); + } + + return OpenedExistential(std::move(typeResult), + std::move(payloadAddress)); } - Result> + Result getDynamicTypeAndAddressExistentialMetatype(RemoteAddress object) { // The value of the address is just the input address. // The type is obtained through the following sequence of steps: @@ -536,27 +569,36 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl { // 3) Wrapping the resolved type in an existential metatype. auto pointerval = Reader.readPointerValue(object.getAddressData()); if (!pointerval) - return getFailure>(); + return getFailure(); auto typeResult = Reader.readTypeFromMetadata(*pointerval); if (!typeResult) - return getFailure>(); + return getFailure(); auto wrappedType = ExistentialMetatypeType::get(typeResult); if (!wrappedType) - return getFailure>(); - return std::make_pair(std::move(wrappedType), - std::move(object)); + return getFailure(); + 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 + 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> + Result getDynamicTypeAndAddressForExistential(RemoteAddress object, Type staticType) override { // If this is not an existential, give up. if (!staticType->isAnyExistentialType()) - return getFailure>(); + return getFailure(); // Handle the case where this is an ExistentialMetatype. if (!staticType->isExistentialType()) @@ -632,7 +674,13 @@ RemoteASTContext::getHeapMetadataForObject(remote::RemoteAddress address) { return asImpl(Impl)->getHeapMetadataForObject(address); } -Result> +Result +RemoteASTContext::getDynamicTypeAndAddressForError( + remote::RemoteAddress address) { + return asImpl(Impl)->getDynamicTypeAndAddressForError(address); +} + +Result RemoteASTContext::getDynamicTypeAndAddressForExistential( remote::RemoteAddress address, Type staticType) { return asImpl(Impl)->getDynamicTypeAndAddressForExistential(address, diff --git a/test/RemoteAST/existentials_objc.swift b/test/RemoteAST/existentials_objc.swift index 131c6af29051a..922b4aa527749 100644 --- a/test/RemoteAST/existentials_objc.swift +++ b/test/RemoteAST/existentials_objc.swift @@ -31,3 +31,14 @@ printDynamicTypeAndAddressForExistential(NSString("hello") as AnyObject) // CHECK: NSString printDynamicTypeAndAddressForExistential(NSString("hello") as AnyObject) + +// Bridged NSError. +class ClassError : NSError { + required init(coder: NSCoder) { fatalError() } + init() { + super.init(domain: "ClassError", code: 10, userInfo: [:]) + } +} + +// CHECK: ClassError +printDynamicTypeAndAddressForExistential(ClassError() as Error) diff --git a/tools/swift-remoteast-test/swift-remoteast-test.cpp b/tools/swift-remoteast-test/swift-remoteast-test.cpp index 60fe9cb6c4f82..259bdb1a0b418 100644 --- a/tools/swift-remoteast-test/swift-remoteast-test.cpp +++ b/tools/swift-remoteast-test/swift-remoteast-test.cpp @@ -15,6 +15,7 @@ #include "swift/RemoteAST/RemoteAST.h" #include "swift/Remote/InProcessMemoryReader.h" +#include "swift/Remote/MetadataReader.h" #include "swift/Runtime/Metadata.h" #include "swift/Frontend/Frontend.h" #include "swift/FrontendTool/FrontendTool.h" @@ -38,7 +39,7 @@ using namespace swift::remote; using namespace swift::remoteAST; /// The context for the code we're running. Set by the observer. -static ASTContext *Context = nullptr; +static ASTContext *context = nullptr; /// The RemoteAST for the code we're running. std::shared_ptr reader; @@ -49,7 +50,7 @@ static RemoteASTContext &getRemoteASTContext() { return *remoteContext; std::shared_ptr reader(new InProcessMemoryReader()); - remoteContext.reset(new RemoteASTContext(*Context, std::move(reader))); + remoteContext.reset(new RemoteASTContext(*context, std::move(reader))); return *remoteContext; } @@ -166,7 +167,7 @@ printDynamicTypeAndAddressForExistential(void *object, RemoteAddress(object), staticTypeResult.getValue()); if (result) { out << "found type: "; - result.getValue().first.print(out); + result.getValue().InstanceType.print(out); out << "\n"; } else { out << result.getFailure().render() << '\n'; @@ -177,7 +178,7 @@ namespace { struct Observer : public FrontendObserver { void configuredCompiler(CompilerInstance &instance) override { - Context = &instance.getASTContext(); + context = &instance.getASTContext(); } };