[Foundation] Update & simplify class name stability check

Move the ObjC class name stability check logic to the Swift runtime, exposing it as a new SPI called _swift_isObjCTypeNameSerializable.

Update the reporting logic. The ObjC names of generic classes are considered stable now, but private classes and classes defined in function bodies or other anonymous contexts are unstable by design.

On the overlay side, rewrite the check’s implementation in Swift and considerably simplify it.

rdar://57809977
This commit is contained in:
Karoy Lorentey
2020-07-02 01:08:05 -07:00
parent 3e8166eab5
commit f44cbe4697
9 changed files with 319 additions and 447 deletions

View File

@@ -0,0 +1,40 @@
//===--- FoundationSupport.cpp - Support functions for Foundation ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 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
//
//===----------------------------------------------------------------------===//
//
// Helper functions for the Foundation framework.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_RUNTIME_FOUNDATION_SUPPORT_H
#define SWIFT_RUNTIME_FOUNDATION_SUPPORT_H
#include "swift/Runtime/Config.h"
#if SWIFT_OBJC_INTEROP
#include <objc/runtime.h>
#ifdef __cplusplus
namespace swift { extern "C" {
#endif
/// Returns a boolean indicating whether the Objective-C name of a class type is
/// stable across executions, i.e., if the class name is safe to serialize. (The
/// names of private and local types are unstable.)
SWIFT_RUNTIME_STDLIB_SPI
bool _swift_isObjCTypeNameSerializable(Class theClass);
#ifdef __cplusplus
}} // extern "C", namespace swift
#endif
#endif // SWIFT_OBJC_INTEROP
#endif // SWIFT_RUNTIME_FOUNDATION_SUPPORT_H

View File

@@ -7,7 +7,7 @@ add_swift_target_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES
BundleLookup.mm BundleLookup.mm
Calendar.swift Calendar.swift
CharacterSet.swift CharacterSet.swift
CheckClass.mm CheckClass.swift
Codable.swift Codable.swift
Collections+DataProtocol.swift Collections+DataProtocol.swift
CombineTypealiases.swift CombineTypealiases.swift

View File

@@ -1,291 +0,0 @@
#import <Foundation/Foundation.h>
#include <objc/runtime.h>
#include "swift/Runtime/HeapObject.h"
#include "swift/Runtime/Metadata.h"
@interface NSKeyedUnarchiver (SwiftAdditions)
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls
operation:(int)operation
NS_SWIFT_NAME(_swift_checkClassAndWarnForKeyedArchiving(_:operation:));
@end
static bool isASCIIIdentifierChar(char c) {
if (c >= 'a' && c <= 'z') return true;
if (c >= 'A' && c <= 'Z') return true;
if (c >= '0' && c <= '9') return true;
if (c == '_') return true;
if (c == '$') return true;
return false;
}
template <typename T, size_t N>
static constexpr size_t arrayLength(T (&)[N]) { return N; }
static void logIfFirstOccurrence(Class objcClass, void (^log)(void)) {
static auto queue = dispatch_queue_create(
"SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue",
DISPATCH_QUEUE_SERIAL);
static NSHashTable *seenClasses = nil;
dispatch_sync(queue, ^{
// Will be NO when seenClasses is still nil.
if ([seenClasses containsObject:objcClass])
return;
if (!seenClasses) {
NSPointerFunctionsOptions options = 0;
options |= NSPointerFunctionsOpaqueMemory;
options |= NSPointerFunctionsObjectPointerPersonality;
seenClasses = [[NSHashTable alloc] initWithOptions:options capacity:16];
}
[seenClasses addObject:objcClass];
// Synchronize logging so that multiple lines aren't interleaved.
log();
});
}
namespace {
class StringRefLite {
StringRefLite(const char *data, size_t len) : data(data), length(len) {}
public:
const char *data;
size_t length;
StringRefLite() : data(nullptr), length(0) {}
template <size_t N>
StringRefLite(const char (&staticStr)[N]) : data(staticStr), length(N) {}
StringRefLite(swift::TypeNamePair rawValue)
: data(rawValue.data),
length(rawValue.length){}
NS_RETURNS_RETAINED
NSString *newNSStringNoCopy() const {
return [[NSString alloc] initWithBytesNoCopy:const_cast<char *>(data)
length:length
encoding:NSUTF8StringEncoding
freeWhenDone:NO];
}
const char &operator[](size_t offset) const {
assert(offset < length);
return data[offset];
}
StringRefLite slice(size_t from, size_t to) const {
assert(from <= to);
assert(to <= length);
return {data + from, to - from};
}
const char *begin() const {
return data;
}
const char *end() const {
return data + length;
}
};
}
/// Assume that a non-generic demangled class name always ends in ".MyClass"
/// or ".(MyClass plus extra info)".
static StringRefLite findBaseName(StringRefLite demangledName) {
size_t end = demangledName.length;
size_t parenCount = 0;
for (size_t i = end; i != 0; --i) {
switch (demangledName[i - 1]) {
case '.':
if (parenCount == 0) {
if (i != end && demangledName[i] == '(')
++i;
return demangledName.slice(i, end);
}
break;
case ')':
parenCount += 1;
break;
case '(':
if (parenCount > 0)
parenCount -= 1;
break;
case ' ':
end = i - 1;
break;
default:
break;
}
}
return {};
}
@implementation NSKeyedUnarchiver (SwiftAdditions)
/// Checks if class \p objcClass is good for archiving.
///
/// If not, a runtime warning is printed.
///
/// \param operation Specifies the archiving operation. Valid operations are:
/// 0: archiving
/// 1: unarchiving
/// \return Returns the status
/// 0: not a problem class (either non-Swift or has an explicit name)
/// 1: a Swift generic class
/// 2: a Swift non-generic class where adding @objc is valid
/// Future versions of this API will return nonzero values for additional cases
/// that mean the class shouldn't be archived.
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)objcClass
operation:(int)operation {
using namespace swift;
const ClassMetadata *theClass = (ClassMetadata *)objcClass;
// Is it a (real) swift class?
if (!theClass->isTypeMetadata() || theClass->isArtificialSubclass())
return 0;
// Does the class already have a custom name?
if (theClass->getFlags() & ClassFlags::HasCustomObjCName)
return 0;
// Is it a mangled name?
const char *className = class_getName(objcClass);
if (!(className[0] == '_' && className[1] == 'T'))
return 0;
// Is it a name in the form <module>.<class>? Note: the module name could
// start with "_T".
if (strchr(className, '.'))
return 0;
// Is it a generic class?
if (theClass->getDescription()->isGeneric()) {
logIfFirstOccurrence(objcClass, ^{
// Use actual NSStrings to force UTF-8.
StringRefLite demangledName = swift_getTypeName(theClass,
/*qualified*/true);
NSString *demangledString = demangledName.newNSStringNoCopy();
NSString *mangledString = NSStringFromClass(objcClass);
NSString *primaryMessage;
switch (operation) {
case 1:
primaryMessage = [[NSString alloc] initWithFormat:
@"Attempting to unarchive generic Swift class '%@' with mangled "
"runtime name '%@'. Runtime names for generic classes are "
"unstable and may change in the future, leading to "
"non-decodable data.", demangledString, mangledString];
break;
default:
primaryMessage = [[NSString alloc] initWithFormat:
@"Attempting to archive generic Swift class '%@' with mangled "
"runtime name '%@'. Runtime names for generic classes are "
"unstable and may change in the future, leading to "
"non-decodable data.", demangledString, mangledString];
break;
}
NSString *generatedNote = [[NSString alloc] initWithFormat:
@"To avoid this failure, create a concrete subclass and register "
"it with NSKeyedUnarchiver.setClass(_:forClassName:) instead, "
"using the name \"%@\".", mangledString];
const char *staticNote =
"If you need to produce archives compatible with older versions "
"of your program, use NSKeyedArchiver.setClassName(_:for:) as well.";
NSLog(@"%@", primaryMessage);
NSLog(@"%@", generatedNote);
NSLog(@"%s", staticNote);
RuntimeErrorDetails::Note notes[] = {
{ [generatedNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
{ staticNote, /*numFixIts*/0, /*fixIts*/nullptr },
};
RuntimeErrorDetails errorInfo = {};
errorInfo.version = RuntimeErrorDetails::currentVersion;
errorInfo.errorType = "nskeyedarchiver-incompatible-class";
errorInfo.notes = notes;
errorInfo.numNotes = arrayLength(notes);
_swift_reportToDebugger(RuntimeErrorFlagNone, [primaryMessage UTF8String],
&errorInfo);
[primaryMessage release];
[generatedNote release];
[demangledString release];
});
return 1;
}
// It's a swift class with a (compiler generated) mangled name, which should
// be written into an NSArchive.
logIfFirstOccurrence(objcClass, ^{
// Use actual NSStrings to force UTF-8.
StringRefLite demangledName = swift_getTypeName(theClass,/*qualified*/true);
NSString *demangledString = demangledName.newNSStringNoCopy();
NSString *mangledString = NSStringFromClass(objcClass);
NSString *primaryMessage;
switch (operation) {
case 1:
primaryMessage = [[NSString alloc] initWithFormat:
@"Attempting to unarchive Swift class '%@' with mangled runtime "
"name '%@'. The runtime name for this class is unstable and may "
"change in the future, leading to non-decodable data.",
demangledString, mangledString];
break;
default:
primaryMessage = [[NSString alloc] initWithFormat:
@"Attempting to archive Swift class '%@' with mangled runtime "
"name '%@'. The runtime name for this class is unstable and may "
"change in the future, leading to non-decodable data.",
demangledString, mangledString];
break;
}
NSString *firstNote = [[NSString alloc] initWithFormat:
@"You can use the 'objc' attribute to ensure that the name will not "
"change: \"@objc(%@)\"", mangledString];
StringRefLite baseName = findBaseName(demangledName);
// Offer a more generic message if the base name we found doesn't look like
// an ASCII identifier. This avoids printing names like "ABCモデル".
if (baseName.length == 0 ||
!std::all_of(baseName.begin(), baseName.end(), isASCIIIdentifierChar)) {
baseName = "MyModel";
}
NSString *secondNote = [[NSString alloc] initWithFormat:
@"If there are no existing archives containing this class, you should "
"choose a unique, prefixed name instead: \"@objc(ABC%1$.*2$s)\"",
baseName.data, (int)baseName.length];
NSLog(@"%@", primaryMessage);
NSLog(@"%@", firstNote);
NSLog(@"%@", secondNote);
// FIXME: We could suggest these as fix-its if we had source locations for
// the class.
RuntimeErrorDetails::Note notes[] = {
{ [firstNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
{ [secondNote UTF8String], /*numFixIts*/0, /*fixIts*/nullptr },
};
RuntimeErrorDetails errorInfo = {};
errorInfo.version = RuntimeErrorDetails::currentVersion;
errorInfo.errorType = "nskeyedarchiver-incompatible-class";
errorInfo.notes = notes;
errorInfo.numNotes = arrayLength(notes);
_swift_reportToDebugger(RuntimeErrorFlagNone, [primaryMessage UTF8String],
&errorInfo);
[primaryMessage release];
[firstNote release];
[secondNote release];
[demangledString release];
});
return 2;
}
@end

View File

@@ -0,0 +1,64 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 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
//
//===----------------------------------------------------------------------===//
@_exported import Foundation // Clang module
@_implementationOnly import _SwiftFoundationOverlayShims
import Dispatch
private let _queue = DispatchQueue(label: "com.apple.SwiftFoundation._checkClassAndWarnForKeyedArchivingQueue")
private var _seenClasses: Set<ObjectIdentifier> = []
private func _isClassFirstSeen(_ theClass: AnyClass) -> Bool {
_queue.sync {
let id = ObjectIdentifier(theClass)
return _seenClasses.insert(id).inserted
}
}
extension NSKeyedUnarchiver {
/// Checks if class `theClass` is good for archiving.
///
/// If not, a runtime warning is printed.
///
/// - Parameter operation: Specifies the archiving operation. Supported values
/// are 0 for archiving, and 1 for unarchiving.
/// - Returns: 0 if the given class is safe to archive, and non-zero if it
/// isn't.
@objc(_swift_checkClassAndWarnForKeyedArchiving:operation:)
@usableFromInline
internal class func _swift_checkClassAndWarnForKeyedArchiving(
_ theClass: AnyClass,
operation: CInt
) -> CInt {
if _swift_isObjCTypeNameSerializable(theClass) { return 0 }
if _isClassFirstSeen(theClass) {
let demangledName = String(reflecting: theClass)
let mangledName = NSStringFromClass(theClass)
let op = (operation == 1 ? "unarchive" : "archive")
let message = """
Attempting to \(op) Swift class '\(demangledName)' with unstable runtime name '\(mangledName)'.
The runtime name for this class may change in the future, leading to non-decodable data.
You can use the 'objc' attribute to ensure that the name will not change:
"@objc(\(mangledName))"
If there are no existing archives containing this class, you should choose a unique, prefixed name instead:
"@objc(ABCMyModel)"
"""
NSLog("%@", message)
_swift_reportToDebugger(0, message, nil)
}
return 1
}
}

View File

@@ -75,3 +75,8 @@ static inline _Bool _withStackOrHeapBuffer(size_t amount, void (__attribute__((n
@protocol _NSKVOCompatibilityShim <NSObject> @protocol _NSKVOCompatibilityShim <NSObject>
+ (void)_noteProcessHasUsedKVOSwiftOverlay; + (void)_noteProcessHasUsedKVOSwiftOverlay;
@end @end
// Exported by libswiftCore:
extern bool _swift_isObjCTypeNameSerializable(Class theClass);
extern void _swift_reportToDebugger(uintptr_t flags, const char *message, void *details);

View File

@@ -43,6 +43,7 @@ set(swift_runtime_sources
Exclusivity.cpp Exclusivity.cpp
ExistentialContainer.cpp ExistentialContainer.cpp
Float16Support.cpp Float16Support.cpp
FoundationSupport.cpp
Heap.cpp Heap.cpp
HeapObject.cpp HeapObject.cpp
ImageInspectionCommon.cpp ImageInspectionCommon.cpp

View File

@@ -0,0 +1,61 @@
//===--- FoundationSupport.cpp - Support functions for Foundation ---------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2020 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
//
//===----------------------------------------------------------------------===//
//
// Helper functions for the Foundation framework.
//
//===----------------------------------------------------------------------===//
#include "swift/Runtime/FoundationSupport.h"
#if SWIFT_OBJC_INTEROP
#include "swift/Runtime/Metadata.h"
#include "swift/Runtime/HeapObject.h"
using namespace swift;
/// Returns a boolean indicating whether the Objective-C name of a class type is
/// stable across executions, i.e., if the class name is safe to serialize. (The
/// names of private and local types are unstable.)
bool
swift::_swift_isObjCTypeNameSerializable(Class theClass) {
auto type = (AnyClassMetadata *)theClass;
switch (type->getKind()) {
case MetadataKind::ObjCClassWrapper:
case MetadataKind::ForeignClass:
return true;
case MetadataKind::Class: {
// Pure ObjC classes always have stable names.
if (type->isPureObjC())
return true;
auto cls = static_cast<const ClassMetadata *>(type);
// Peek through artificial subclasses.
if (cls->isArtificialSubclass()) {
cls = cls->Superclass;
}
// A custom ObjC name is always considered stable.
if (cls->getFlags() & ClassFlags::HasCustomObjCName)
return true;
// Otherwise the name is stable if the class has no anonymous ancestor context.
auto desc = static_cast<const ContextDescriptor *>(cls->getDescription());
while (desc) {
if (desc->getKind() == ContextDescriptorKind::Anonymous) {
return false;
}
desc = desc->Parent.get();
}
return true;
}
default:
return false;
}
}
#endif // SWIFT_OBJC_INTEROP

View File

@@ -1,12 +1,13 @@
// RUN: %empty-directory(%t) // RUN: %empty-directory(%t)
// RUN: %target-build-swift %s -module-name=_Test -import-objc-header %S/Inputs/check_class_for_archiving.h -o %t/a.out // RUN: %target-build-swift %s -module-name=_Test -import-objc-header %S/Inputs/check_class_for_archiving.h -o %t/a.out
// RUN: %target-codesign %t/a.out // RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s // RUN: %target-run %t/a.out
// REQUIRES: executable_test // REQUIRES: executable_test
// REQUIRES: objc_interop // REQUIRES: objc_interop
import Foundation import Foundation
import StdlibUnittest
class SwiftClass {} class SwiftClass {}
@@ -35,36 +36,57 @@ struct DEF<T> {
class InnerClass : NSObject {} class InnerClass : NSObject {}
} }
let suite = TestSuite("check_class_for_archiving")
defer { runAllTests() }
let op: Int32 = 0 // archiving let op: Int32 = 0 // archiving
// CHECK: SwiftClass: 0 suite.test("SwiftClass") {
print("SwiftClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(SwiftClass.self, operation: op))") expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(SwiftClass.self, operation: op))
// CHECK: ObjcClass: 0 }
print("ObjcClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ObjcClass.self, operation: op))") suite.test("ObjcClass") {
// CHECK: NamedClass1: 0 expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ObjcClass.self, operation: op))
print("NamedClass1: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass1.self, operation: op))") }
// CHECK: NamedClass2: 0 suite.test("NamedClass1") {
print("NamedClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass2.self, operation: op))") expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass1.self, operation: op))
// CHECK: DerivedClass: 0 }
print("DerivedClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))") suite.test("NamedClass2") {
// CHECK: DerivedClassWithName: 0 expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass2.self, operation: op))
print("DerivedClassWithName: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))") }
// CHECK: NSKeyedUnarchiver: 0 suite.test("DerivedClass") {
print("NSKeyedUnarchiver: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NSKeyedUnarchiver.self, operation: op))") expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))
}
if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) { suite.test("DerivedClassWithName") {
// CHECK: PrivateClass: 2 expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClassWithName.self, operation: op))
print("PrivateClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op))") }
// CHECK: GenericClass: 1 suite.test("NSKeyedUnarchiver") {
print("GenericClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(GenericClass<Int>.self, operation: op))") expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NSKeyedUnarchiver.self, operation: op))
// CHECK: InnerClass: 2 }
print("InnerClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ABC.InnerClass.self, operation: op))")
// CHECK: InnerClass2: 1 // Disable negative tests on older OSes because of rdar://problem/50504765
print("InnerClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DEF<Int>.InnerClass.self, operation: op))") if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
} else { suite.test("PrivateClass") {
// Disable the checks for older OSes because of rdar://problem/50504765 expectNotEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op))
print("PrivateClass: 2") }
print("GenericClass: 1") }
print("InnerClass: 2")
print("InnerClass2: 1") if #available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *) {
// Generic classes and nested classes were considered to have unstable names
// in earlier releases.
suite.test("GenericClass") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(GenericClass<Int>.self, operation: op))
}
suite.test("InnerClass") {
print(NSStringFromClass(ABC.InnerClass.self))
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ABC.InnerClass.self, operation: op))
}
suite.test("InnerClass2") {
print(NSStringFromClass(DEF<Int>.InnerClass.self))
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DEF<Int>.InnerClass.self, operation: op))
}
suite.test("LocalClass") {
class LocalClass: NSObject {}
expectNotEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(LocalClass.self, operation: op))
}
} }

View File

@@ -24,162 +24,132 @@ if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
class SwiftClass {} class SwiftClass {}
func checkArchiving(_ cls: AnyObject.Type) { func _check(_ label: String, _ cls: AnyObject.Type, _ op: CInt) {
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: 0) NSLog("--%@ start", label)
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: op)
NSLog("--%@ end", label)
} }
func checkUnarchiving(_ cls: AnyObject.Type) { func checkArchiving(_ label: String, _ cls: AnyObject.Type) {
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: 1) _check(label, cls, 0)
}
func checkUnarchiving(_ label: String, _ cls: AnyObject.Type) {
_check(label, cls, 1)
} }
func mark(line: Int32 = #line) {
NSLog("--%d--", line)
}
mark() // CHECK: --[[@LINE]]-- // CHECK-LABEL: --SwiftClass start
checkArchiving(SwiftClass.self) checkArchiving("SwiftClass", SwiftClass.self)
mark() // CHECK-NEXT: --[[@LINE]]-- // CHECK-NEXT: --SwiftClass end
private class ArchivedTwice {} private class ArchivedTwice {}
checkArchiving(ArchivedTwice.self) // CHECK-LABEL: --ArchivedTwice1 start
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.+}}).ArchivedTwice' with mangled runtime name '_TtC{{.+[0-9]+}}ArchivedTwice' checkArchiving("ArchivedTwice1", ArchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedTwice) // CHECK: Attempting to archive Swift class '_Test.({{.+}}).ArchivedTwice' with {{.+}} runtime name '_TtC{{.+[0-9]+}}ArchivedTwice'
// CHECK-NEXT: @objc(ABCArchivedTwice) // CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwice.self) // CHECK-LABEL: --ArchivedTwice2 start
mark() // CHECK-NEXT: --[[@LINE]]-- checkArchiving("ArchivedTwice2", ArchivedTwice.self)
// CHECK-NEXT: --ArchivedTwice2 end
private class UnarchivedTwice {} private class UnarchivedTwice {}
checkUnarchiving(UnarchivedTwice.self) // CHECK-LABEL: --UnarchivedTwice1 start
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedTwice' with mangled runtime name '_TtC{{.+[0-9]+}}UnarchivedTwice' checkUnarchiving("UnarchivedTwice1", UnarchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice) // CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedTwice' with {{.+}} runtime name '_TtC{{.+[0-9]+}}UnarchivedTwice'
// CHECK-NEXT: @objc(ABCUnarchivedTwice) // CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(UnarchivedTwice.self) // CHECK-LABEL: --UnarchivedTwice2 start
mark() // CHECK-NEXT: --[[@LINE]]-- checkUnarchiving("UnarchivedTwice2", UnarchivedTwice.self)
// CHECK-NEXT: --UnarchivedTwice2 end
private class ArchivedThenUnarchived {} private class ArchivedThenUnarchived {}
checkArchiving(ArchivedThenUnarchived.self) // CHECK-LABEL: --ArchivedThenUnarchived1 start
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.+}}).ArchivedThenUnarchived' with mangled runtime name '_TtC{{.+[0-9]+}}ArchivedThenUnarchived' checkArchiving("ArchivedThenUnarchived1", ArchivedThenUnarchived.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived) // CHECK: Attempting to archive Swift class '_Test.({{.+}}).ArchivedThenUnarchived' with {{.+}} runtime name '_TtC{{.+[0-9]+}}ArchivedThenUnarchived'
// CHECK-NEXT: @objc(ABCArchivedThenUnarchived) // CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(ArchivedThenUnarchived.self) // CHECK-LABEL: --ArchivedThenUnarchived2 start
mark() // CHECK-NEXT: --[[@LINE]]-- checkUnarchiving("ArchivedThenUnarchived2", ArchivedThenUnarchived.self)
// CHECK-NEXT: --ArchivedThenUnarchived2 end
private class UnarchivedThenArchived {} private class UnarchivedThenArchived {}
checkUnarchiving(UnarchivedThenArchived.self) // CHECK-LABEL: --UnarchivedThenArchived1 start
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedThenArchived' with mangled runtime name '_TtC{{.+[0-9]+}}UnarchivedThenArchived' checkUnarchiving("UnarchivedThenArchived1", UnarchivedThenArchived.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived) // CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedThenArchived' with {{.+}} runtime name '_TtC{{.+[0-9]+}}UnarchivedThenArchived'
// CHECK-NEXT: @objc(ABCUnarchivedThenArchived) // CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(UnarchivedThenArchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
class Outer { // CHECK-LABEL: --UnarchivedThenArchived2 start
checkArchiving("UnarchivedThenArchived2", UnarchivedThenArchived.self)
// CHECK-NEXT: --UnarchivedThenArchived2 end
private class Outer {
class ArchivedTwice {} class ArchivedTwice {}
class UnarchivedTwice {} class UnarchivedTwice {}
class ArchivedThenUnarchived {} class ArchivedThenUnarchived {}
class UnarchivedThenArchived {} class UnarchivedThenArchived {}
} }
checkArchiving(Outer.ArchivedTwice.self) // CHECK-LABEL: --Outer.ArchivedTwice1 start
// CHECK-NEXT: Attempting to archive Swift class '_Test.Outer.ArchivedTwice' checkArchiving("Outer.ArchivedTwice1", Outer.ArchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedTwice) // CHECK: Attempting to archive Swift class '_Test.({{.+}}).Outer.ArchivedTwice'
// CHECK-NEXT: @objc(ABCArchivedTwice) // CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(Outer.ArchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.UnarchivedTwice.self) // CHECK-LABEL: --Outer.ArchivedTwice2 start
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.Outer.UnarchivedTwice' checkArchiving("Outer.ArchivedTwice2", Outer.ArchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice) // CHECK-NEXT: --Outer.ArchivedTwice2 end
// CHECK-NEXT: @objc(ABCUnarchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.UnarchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(Outer.ArchivedThenUnarchived.self) // CHECK-LABEL: --Outer.UnarchivedTwice1 start
// CHECK-NEXT: Attempting to archive Swift class '_Test.Outer.ArchivedThenUnarchived' checkUnarchiving("Outer.UnarchivedTwice1", Outer.UnarchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived) // CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).Outer.UnarchivedTwice'
// CHECK-NEXT: @objc(ABCArchivedThenUnarchived) // CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.ArchivedThenUnarchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.UnarchivedThenArchived.self) // CHECK-LABEL: --Outer.UnarchivedTwice2 start
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.Outer.UnarchivedThenArchived' checkUnarchiving("Outer.UnarchivedTwice2", Outer.UnarchivedTwice.self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived) // CHECK-NEXT: --Outer.UnarchivedTwice2 end
// CHECK-NEXT: @objc(ABCUnarchivedThenArchived)
mark() // CHECK-NEXT: --[[@LINE]]-- // CHECK-LABEL: --Outer.ArchivedThenUnarchived1 start
checkArchiving(Outer.UnarchivedThenArchived.self) checkArchiving("Outer.ArchivedThenUnarchived1", Outer.ArchivedThenUnarchived.self)
mark() // CHECK-NEXT: --[[@LINE]]-- // CHECK: Attempting to archive Swift class '_Test.({{.+}}).Outer.ArchivedThenUnarchived'
// CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived)
// CHECK-LABEL: --Outer.ArchivedThenUnarchived2 start
checkUnarchiving("Outer.ArchivedThenUnarchived2", Outer.ArchivedThenUnarchived.self)
// CHECK-NEXT: --Outer.ArchivedThenUnarchived2 end
// CHECK-LABEL: --Outer.UnarchivedThenArchived1 start
checkUnarchiving("Outer.UnarchivedThenArchived1", Outer.UnarchivedThenArchived.self)
// CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).Outer.UnarchivedThenArchived'
// CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived)
// CHECK-LABEL: --Outer.UnarchivedThenArchived2 start
checkArchiving("Outer.UnarchivedThenArchived2", Outer.UnarchivedThenArchived.self)
// CHECK-NEXT: --Outer.UnarchivedThenArchived2 end
private class {} private class {}
checkArchiving(.self) // CHECK-LABEL: --Japanese1 start
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.*}}).' checkArchiving("Japanese1", .self)
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}9) // CHECK: Attempting to archive Swift class '_Test.({{.*}}).'
// CHECK-NEXT: @objc(ABCMyModel)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Japanese2 start
checkArchiving("Japanese2", .self)
// CHECK-NEXT: --Japanese2 end
class ArchivedTwiceGeneric<T> {} func someFunction() {
class LocalArchived: NSObject {}
class LocalUnarchived: NSObject {}
checkArchiving(ArchivedTwiceGeneric<Int>.self) // CHECK-LABEL: --LocalArchived start
// CHECK-NEXT: Attempting to archive generic Swift class '_Test.ArchivedTwiceGeneric<Swift.Int>' with mangled runtime name '_TtGC5_Test20ArchivedTwiceGenericSi_' checkArchiving("LocalArchived", LocalArchived.self)
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:) // CHECK: Attempting to archive Swift class '_Test.({{.+}}).LocalArchived'
// CHECK-SAME: _TtGC5_Test20ArchivedTwiceGenericSi_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwiceGeneric<Int>.self)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwiceGeneric<NSObject>.self) // CHECK-LABEL: --LocalUnarchived start
// CHECK-NEXT: Attempting to archive generic Swift class '_Test.ArchivedTwiceGeneric<__C.NSObject>' with mangled runtime name '_TtGC5_Test20ArchivedTwiceGenericCSo8NSObject_' checkUnarchiving("LocalUnarchived", LocalUnarchived.self)
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:) // CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).LocalUnarchived'
// CHECK-SAME: _TtGC5_Test20ArchivedTwiceGenericCSo8NSObject_ }
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:) someFunction()
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwiceGeneric<NSObject>.self)
mark() // CHECK-NEXT: --[[@LINE]]--
class UnarchivedTwiceGeneric<T> {}
checkUnarchiving(UnarchivedTwiceGeneric<Int>.self)
// CHECK-NEXT: Attempting to unarchive generic Swift class '_Test.UnarchivedTwiceGeneric<Swift.Int>' with mangled runtime name '_TtGC5_Test22UnarchivedTwiceGenericSi_'
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:)
// CHECK-SAME: _TtGC5_Test22UnarchivedTwiceGenericSi_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(UnarchivedTwiceGeneric<Int>.self)
mark() // CHECK-NEXT: --[[@LINE]]--
class ArchivedThenUnarchivedGeneric<T> {}
checkArchiving(ArchivedThenUnarchivedGeneric<Int>.self)
// CHECK-NEXT: Attempting to archive generic Swift class '_Test.ArchivedThenUnarchivedGeneric<Swift.Int>' with mangled runtime name '_TtGC5_Test29ArchivedThenUnarchivedGenericSi_'
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:)
// CHECK-SAME: _TtGC5_Test29ArchivedThenUnarchivedGenericSi_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(ArchivedThenUnarchivedGeneric<Int>.self)
mark() // CHECK-NEXT: --[[@LINE]]--
class UnarchivedThenArchivedGeneric<T> {}
checkUnarchiving(UnarchivedThenArchivedGeneric<Int>.self)
// CHECK-NEXT: Attempting to unarchive generic Swift class '_Test.UnarchivedThenArchivedGeneric<Swift.Int>' with mangled runtime name '_TtGC5_Test29UnarchivedThenArchivedGenericSi_'
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:)
// CHECK-SAME: _TtGC5_Test29UnarchivedThenArchivedGenericSi_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(UnarchivedThenArchivedGeneric<Int>.self)
mark() // CHECK-NEXT: --[[@LINE]]--