Skip to content

Commit 76ebaee

Browse files
committed
MetadataReader: Add support for NSError toll-free bridging
An Error existential value can directly store a reference to an NSError instance without wrapping it in an Error container. When "projecting" such an existential, the dynamic type is the NSError's isa pointer, and the payload is the address of the instance itself.
1 parent 5824b0f commit 76ebaee

File tree

4 files changed

+36
-8
lines changed

4 files changed

+36
-8
lines changed

include/swift/Reflection/ReflectionContext.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,9 @@ class ReflectionContext
537537
if (!OptMetaAndValue)
538538
return false;
539539

540-
RemoteAddress InstanceMetadataAddress = OptMetaAndValue->first;
541-
RemoteAddress InstanceAddress = OptMetaAndValue->second;
540+
RemoteAddress InstanceMetadataAddress = std::get<0>(*OptMetaAndValue);
541+
RemoteAddress InstanceAddress = std::get<1>(*OptMetaAndValue);
542+
// FIXME: Check third value, 'isBridged'
542543

543544
auto InstanceTR =
544545
readTypeFromMetadata(InstanceMetadataAddress.getAddressData());

include/swift/Remote/MetadataReader.h

+18-4
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,10 @@ class MetadataReader {
327327
}
328328

329329
/// Given a pointer to a known-error existential, attempt to discover the
330-
/// pointer to its metadata address and its value address.
331-
Optional<std::pair<RemoteAddress, RemoteAddress>>
330+
/// pointer to its metadata address, its value address, and whether this
331+
/// is a toll-free-bridged NSError or an actual Error existential wrapper
332+
/// around a native Swift value.
333+
Optional<std::tuple<RemoteAddress, RemoteAddress, bool>>
332334
readMetadataAndValueErrorExistential(RemoteAddress ExistentialAddress) {
333335
// An pointer to an error existential is always an heap object.
334336
auto MetadataAddress =
@@ -337,13 +339,16 @@ class MetadataReader {
337339
return None;
338340

339341
bool isObjC = false;
342+
bool isBridged = false;
340343

341344
// If we can determine the Objective-C class name, this is probably an
342345
// error existential with NSError-compatible layout.
343346
std::string ObjCClassName;
344347
if (readObjCClassName(*MetadataAddress, ObjCClassName)) {
345348
if (ObjCClassName == "__SwiftNativeNSError")
346349
isObjC = true;
350+
else
351+
isBridged = true;
347352
} else {
348353
// Otherwise, we can check to see if this is a class metadata with the
349354
// kind value's least significant bit set, which indicates a pure
@@ -356,6 +361,14 @@ class MetadataReader {
356361
isObjC = ClassMeta->isPureObjC();
357362
}
358363

364+
if (isBridged) {
365+
// NSError instances don't need to be unwrapped.
366+
return Optional<std::tuple<RemoteAddress, RemoteAddress, bool>>(
367+
{RemoteAddress(*MetadataAddress),
368+
ExistentialAddress,
369+
isBridged});
370+
}
371+
359372
// In addition to the isa pointer and two 32-bit reference counts, if the
360373
// error existential is layout-compatible with NSError, we also need to
361374
// skip over its three word-sized fields: the error code, the domain,
@@ -387,9 +400,10 @@ class MetadataReader {
387400
auto Offset = (sizeof(HeapObject) + AlignmentMask) & ~AlignmentMask;
388401
InstanceAddress += Offset;
389402

390-
return Optional<std::pair<RemoteAddress, RemoteAddress>>(
403+
return Optional<std::tuple<RemoteAddress, RemoteAddress, bool>>(
391404
{RemoteAddress(*InstanceMetadataAddress),
392-
RemoteAddress(InstanceAddress)});
405+
RemoteAddress(InstanceAddress),
406+
isBridged});
393407
}
394408

395409
/// Given a known-opaque existential, attemp to discover the pointer to its

lib/RemoteAST/RemoteAST.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -500,8 +500,10 @@ class RemoteASTContextConcreteImpl final : public RemoteASTContextImpl {
500500
Reader.readMetadataAndValueErrorExistential(RemoteAddress(*pointerval));
501501
if (!result)
502502
return getFailure<std::pair<Type, RemoteAddress>>();
503-
RemoteAddress metadataAddress = result->first;
504-
RemoteAddress valueAddress = result->second;
503+
504+
RemoteAddress metadataAddress = std::get<0>(*result);
505+
RemoteAddress valueAddress = std::get<1>(*result);
506+
bool isBridged = std::get<2>(*result);
505507

506508
auto typeResult =
507509
Reader.readTypeFromMetadata(metadataAddress.getAddressData());

test/RemoteAST/existentials_objc.swift

+11
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,14 @@ printDynamicTypeAndAddressForExistential(NSString("hello") as AnyObject)
3131
3232
// CHECK: NSString
3333
printDynamicTypeAndAddressForExistential(NSString("hello") as AnyObject)
34+
35+
// Bridged NSError.
36+
class ClassError : NSError {
37+
required init(coder: NSCoder) { fatalError() }
38+
init() {
39+
super.init(domain: "ClassError", code: 10, userInfo: [:])
40+
}
41+
}
42+
43+
// CHECK: ClassError
44+
printDynamicTypeAndAddressForExistential(ClassError() as Error)

0 commit comments

Comments
 (0)