Skip to content

Commit 1e38b4b

Browse files
authored
Merge pull request #10893 from jrose-apple/are-you-having-issues
[stdlib] Surface NSKeyedArchiver issues in Xcode.
2 parents b14e6fc + fd3b585 commit 1e38b4b

File tree

6 files changed

+100
-35
lines changed

6 files changed

+100
-35
lines changed

include/swift/Runtime/Debug.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,13 @@ enum: uintptr_t {
207207

208208
/// Debugger hook. Calling this stops the debugger with a message and details
209209
/// about the issues.
210-
void reportToDebugger(uintptr_t flags, const char *message,
211-
RuntimeErrorDetails *details = nullptr);
210+
///
211+
/// This is not considered a finalized runtime entry point at this time. Do not
212+
/// emit calls to it from arbitrary Swift code; it's only meant for libraries
213+
/// that ship with the runtime (i.e. the stdlib and overlays).
214+
SWIFT_RUNTIME_EXPORT
215+
void _swift_reportToDebugger(uintptr_t flags, const char *message,
216+
RuntimeErrorDetails *details = nullptr);
212217

213218
SWIFT_RUNTIME_EXPORT
214219
bool _swift_reportFatalErrorsToDebugger;

stdlib/public/SDK/Foundation/CheckClass.mm

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ static bool isASCIIIdentifierChar(char c) {
2020
return false;
2121
}
2222

23+
template <typename T, size_t N>
24+
static constexpr size_t arrayLength(T (&)[N]) { return N; }
25+
2326
static void logIfFirstOccurrence(Class objcClass, void (^log)(void)) {
2427
static auto queue = dispatch_queue_create(
2528
"SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue",
@@ -164,26 +167,52 @@ + (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
164167
/*qualified*/true);
165168
NSString *demangledString = demangledName.newNSStringNoCopy();
166169
NSString *mangledString = NSStringFromClass(objcClass);
170+
171+
NSString *primaryMessage;
167172
switch (operation) {
168173
case 1:
169-
NSLog(@"Attempting to unarchive generic Swift class '%@' with mangled "
170-
"runtime name '%@'. Runtime names for generic classes are "
171-
"unstable and may change in the future, leading to "
172-
"non-decodable data.", demangledString, mangledString);
174+
primaryMessage = [[NSString alloc] initWithFormat:
175+
@"Attempting to unarchive generic Swift class '%@' with mangled "
176+
"runtime name '%@'. Runtime names for generic classes are "
177+
"unstable and may change in the future, leading to "
178+
"non-decodable data.", demangledString, mangledString];
173179
break;
174180
default:
175-
NSLog(@"Attempting to archive generic Swift class '%@' with mangled "
176-
"runtime name '%@'. Runtime names for generic classes are "
177-
"unstable and may change in the future, leading to "
178-
"non-decodable data.", demangledString, mangledString);
181+
primaryMessage = [[NSString alloc] initWithFormat:
182+
@"Attempting to archive generic Swift class '%@' with mangled "
183+
"runtime name '%@'. Runtime names for generic classes are "
184+
"unstable and may change in the future, leading to "
185+
"non-decodable data.", demangledString, mangledString];
179186
break;
180187
}
181-
NSLog(@"To avoid this failure, create a concrete subclass and register "
182-
"it with NSKeyedUnarchiver.setClass(_:forClassName:) instead, "
183-
"using the name \"%@\".", mangledString);
184-
NSLog(@"If you need to produce archives compatible with older versions "
185-
"of your program, use NSKeyedArchiver.setClassName(_:for:) "
186-
"as well.");
188+
NSString *generatedNote = [[NSString alloc] initWithFormat:
189+
@"To avoid this failure, create a concrete subclass and register "
190+
"it with NSKeyedUnarchiver.setClass(_:forClassName:) instead, "
191+
"using the name \"%@\".", mangledString];
192+
const char *staticNote =
193+
"If you need to produce archives compatible with older versions "
194+
"of your program, use NSKeyedArchiver.setClassName(_:for:) as well.";
195+
196+
NSLog(@"%@", primaryMessage);
197+
NSLog(@"%@", generatedNote);
198+
NSLog(@"%s", staticNote);
199+
200+
RuntimeErrorDetails::Note notes[] = {
201+
{ [generatedNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
202+
{ staticNote, /*numFixIts*/0, /*fixIts*/nullptr },
203+
};
204+
205+
RuntimeErrorDetails errorInfo = {};
206+
errorInfo.version = RuntimeErrorDetails::currentVersion;
207+
errorInfo.errorType = "nskeyedarchiver-incompatible-class";
208+
errorInfo.notes = notes;
209+
errorInfo.numNotes = arrayLength(notes);
210+
211+
_swift_reportToDebugger(RuntimeErrorFlagNone, [primaryMessage UTF8String],
212+
&errorInfo);
213+
214+
[primaryMessage release];
215+
[generatedNote release];
187216
[demangledString release];
188217
});
189218
return 1;
@@ -196,23 +225,28 @@ + (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
196225
StringRefLite demangledName = swift_getTypeName(theClass,/*qualified*/true);
197226
NSString *demangledString = demangledName.newNSStringNoCopy();
198227
NSString *mangledString = NSStringFromClass(objcClass);
228+
229+
NSString *primaryMessage;
199230
switch (operation) {
200231
case 1:
201-
NSLog(@"Attempting to unarchive Swift class '%@' with mangled runtime "
202-
"name '%@'. The runtime name for this class is unstable and may "
203-
"change in the future, leading to non-decodable data.",
204-
demangledString, mangledString);
232+
primaryMessage = [[NSString alloc] initWithFormat:
233+
@"Attempting to unarchive Swift class '%@' with mangled runtime "
234+
"name '%@'. The runtime name for this class is unstable and may "
235+
"change in the future, leading to non-decodable data.",
236+
demangledString, mangledString];
205237
break;
206238
default:
207-
NSLog(@"Attempting to archive Swift class '%@' with mangled runtime "
208-
"name '%@'. The runtime name for this class is unstable and may "
209-
"change in the future, leading to non-decodable data.",
210-
demangledString, mangledString);
239+
primaryMessage = [[NSString alloc] initWithFormat:
240+
@"Attempting to archive Swift class '%@' with mangled runtime "
241+
"name '%@'. The runtime name for this class is unstable and may "
242+
"change in the future, leading to non-decodable data.",
243+
demangledString, mangledString];
211244
break;
212245
}
213-
[demangledString release];
214-
NSLog(@"You can use the 'objc' attribute to ensure that the name will not "
215-
"change: \"@objc(%@)\"", mangledString);
246+
247+
NSString *firstNote = [[NSString alloc] initWithFormat:
248+
@"You can use the 'objc' attribute to ensure that the name will not "
249+
"change: \"@objc(%@)\"", mangledString];
216250

217251
StringRefLite baseName = findBaseName(demangledName);
218252
// Offer a more generic message if the base name we found doesn't look like
@@ -222,9 +256,35 @@ + (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
222256
baseName = "MyModel";
223257
}
224258

225-
NSLog(@"If there are no existing archives containing this class, you "
226-
"should choose a unique, prefixed name instead: "
227-
"\"@objc(ABC%1$.*2$s)\"", baseName.data, (int)baseName.length);
259+
NSString *secondNote = [[NSString alloc] initWithFormat:
260+
@"If there are no existing archives containing this class, you should "
261+
"choose a unique, prefixed name instead: \"@objc(ABC%1$.*2$s)\"",
262+
baseName.data, (int)baseName.length];
263+
264+
NSLog(@"%@", primaryMessage);
265+
NSLog(@"%@", firstNote);
266+
NSLog(@"%@", secondNote);
267+
268+
// FIXME: We could suggest these as fix-its if we had source locations for
269+
// the class.
270+
RuntimeErrorDetails::Note notes[] = {
271+
{ [firstNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
272+
{ [secondNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
273+
};
274+
275+
RuntimeErrorDetails errorInfo = {};
276+
errorInfo.version = RuntimeErrorDetails::currentVersion;
277+
errorInfo.errorType = "nskeyedarchiver-incompatible-class";
278+
errorInfo.notes = notes;
279+
errorInfo.numNotes = arrayLength(notes);
280+
281+
_swift_reportToDebugger(RuntimeErrorFlagNone, [primaryMessage UTF8String],
282+
&errorInfo);
283+
284+
[primaryMessage release];
285+
[firstNote release];
286+
[secondNote release];
287+
[demangledString release];
228288
});
229289
return 2;
230290
}

stdlib/public/runtime/Errors.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,8 @@ void _swift_runtime_on_report(uintptr_t flags, const char *message,
256256
);
257257
}
258258

259-
void swift::reportToDebugger(uintptr_t flags, const char *message,
260-
RuntimeErrorDetails *details) {
259+
void swift::_swift_reportToDebugger(uintptr_t flags, const char *message,
260+
RuntimeErrorDetails *details) {
261261
_swift_runtime_on_report(flags, message, details);
262262
}
263263

stdlib/public/runtime/Exclusivity.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ static void reportExclusivityConflict(ExclusivityFlags oldAction, void *oldPC,
124124
uintptr_t flags = RuntimeErrorFlagNone;
125125
if (!keepGoing)
126126
flags = RuntimeErrorFlagFatal;
127-
reportToDebugger(flags, message, &details);
127+
_swift_reportToDebugger(flags, message, &details);
128128

129129
if (keepGoing) {
130130
return;

stdlib/public/runtime/SwiftObject.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1494,7 +1494,7 @@ void swift_objc_swift3ImplicitObjCEntrypoint(id self, SEL selector,
14941494
uintptr_t runtime_error_flags = RuntimeErrorFlagNone;
14951495
if (reporter == swift::fatalError)
14961496
runtime_error_flags = RuntimeErrorFlagFatal;
1497-
reportToDebugger(runtime_error_flags, message, &details);
1497+
_swift_reportToDebugger(runtime_error_flags, message, &details);
14981498

14991499
reporter(flags,
15001500
"*** %s:%zu:%zu: %s; add explicit '@objc' to the declaration to "

stdlib/public/stubs/Assert.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ static void logPrefixAndMessageToDebugger(
6565
} else {
6666
swift_asprintf(&debuggerMessage, "%.*s", prefixLength, prefix);
6767
}
68-
reportToDebugger(RuntimeErrorFlagFatal, debuggerMessage);
68+
_swift_reportToDebugger(RuntimeErrorFlagFatal, debuggerMessage);
6969
free(debuggerMessage);
7070
}
7171

0 commit comments

Comments
 (0)