[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
Calendar.swift
CharacterSet.swift
CheckClass.mm
CheckClass.swift
Codable.swift
Collections+DataProtocol.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>
+ (void)_noteProcessHasUsedKVOSwiftOverlay;
@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
ExistentialContainer.cpp
Float16Support.cpp
FoundationSupport.cpp
Heap.cpp
HeapObject.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: %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-run %t/a.out | %FileCheck %s
// RUN: %target-run %t/a.out
// REQUIRES: executable_test
// REQUIRES: objc_interop
import Foundation
import StdlibUnittest
class SwiftClass {}
@@ -35,36 +36,57 @@ struct DEF<T> {
class InnerClass : NSObject {}
}
let suite = TestSuite("check_class_for_archiving")
defer { runAllTests() }
let op: Int32 = 0 // archiving
// CHECK: SwiftClass: 0
print("SwiftClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(SwiftClass.self, operation: op))")
// CHECK: ObjcClass: 0
print("ObjcClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ObjcClass.self, operation: op))")
// CHECK: NamedClass1: 0
print("NamedClass1: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass1.self, operation: op))")
// CHECK: NamedClass2: 0
print("NamedClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass2.self, operation: op))")
// CHECK: DerivedClass: 0
print("DerivedClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))")
// CHECK: DerivedClassWithName: 0
print("DerivedClassWithName: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))")
// CHECK: NSKeyedUnarchiver: 0
print("NSKeyedUnarchiver: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NSKeyedUnarchiver.self, operation: op))")
if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
// CHECK: PrivateClass: 2
print("PrivateClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op))")
// CHECK: GenericClass: 1
print("GenericClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(GenericClass<Int>.self, operation: op))")
// CHECK: InnerClass: 2
print("InnerClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ABC.InnerClass.self, operation: op))")
// CHECK: InnerClass2: 1
print("InnerClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DEF<Int>.InnerClass.self, operation: op))")
} else {
// Disable the checks for older OSes because of rdar://problem/50504765
print("PrivateClass: 2")
print("GenericClass: 1")
print("InnerClass: 2")
print("InnerClass2: 1")
suite.test("SwiftClass") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(SwiftClass.self, operation: op))
}
suite.test("ObjcClass") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ObjcClass.self, operation: op))
}
suite.test("NamedClass1") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass1.self, operation: op))
}
suite.test("NamedClass2") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass2.self, operation: op))
}
suite.test("DerivedClass") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))
}
suite.test("DerivedClassWithName") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClassWithName.self, operation: op))
}
suite.test("NSKeyedUnarchiver") {
expectEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NSKeyedUnarchiver.self, operation: op))
}
// Disable negative tests on older OSes because of rdar://problem/50504765
if #available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *) {
suite.test("PrivateClass") {
expectNotEqual(0, NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op))
}
}
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 {}
func checkArchiving(_ cls: AnyObject.Type) {
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: 0)
func _check(_ label: String, _ cls: AnyObject.Type, _ op: CInt) {
NSLog("--%@ start", label)
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: op)
NSLog("--%@ end", label)
}
func checkUnarchiving(_ cls: AnyObject.Type) {
NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(cls, operation: 1)
func checkArchiving(_ label: String, _ cls: AnyObject.Type) {
_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]]--
checkArchiving(SwiftClass.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --SwiftClass start
checkArchiving("SwiftClass", SwiftClass.self)
// CHECK-NEXT: --SwiftClass end
private class ArchivedTwice {}
checkArchiving(ArchivedTwice.self)
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.+}}).ArchivedTwice' with mangled runtime name '_TtC{{.+[0-9]+}}ArchivedTwice'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
// CHECK-NEXT: @objc(ABCArchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --ArchivedTwice1 start
checkArchiving("ArchivedTwice1", ArchivedTwice.self)
// CHECK: Attempting to archive Swift class '_Test.({{.+}}).ArchivedTwice' with {{.+}} runtime name '_TtC{{.+[0-9]+}}ArchivedTwice'
// CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
// CHECK-LABEL: --ArchivedTwice2 start
checkArchiving("ArchivedTwice2", ArchivedTwice.self)
// CHECK-NEXT: --ArchivedTwice2 end
private class UnarchivedTwice {}
checkUnarchiving(UnarchivedTwice.self)
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedTwice' with mangled runtime name '_TtC{{.+[0-9]+}}UnarchivedTwice'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
// CHECK-NEXT: @objc(ABCUnarchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(UnarchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --UnarchivedTwice1 start
checkUnarchiving("UnarchivedTwice1", UnarchivedTwice.self)
// CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedTwice' with {{.+}} runtime name '_TtC{{.+[0-9]+}}UnarchivedTwice'
// CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
// CHECK-LABEL: --UnarchivedTwice2 start
checkUnarchiving("UnarchivedTwice2", UnarchivedTwice.self)
// CHECK-NEXT: --UnarchivedTwice2 end
private class ArchivedThenUnarchived {}
checkArchiving(ArchivedThenUnarchived.self)
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.+}}).ArchivedThenUnarchived' with mangled runtime name '_TtC{{.+[0-9]+}}ArchivedThenUnarchived'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived)
// CHECK-NEXT: @objc(ABCArchivedThenUnarchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(ArchivedThenUnarchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --ArchivedThenUnarchived1 start
checkArchiving("ArchivedThenUnarchived1", ArchivedThenUnarchived.self)
// CHECK: Attempting to archive Swift class '_Test.({{.+}}).ArchivedThenUnarchived' with {{.+}} runtime name '_TtC{{.+[0-9]+}}ArchivedThenUnarchived'
// CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived)
// CHECK-LABEL: --ArchivedThenUnarchived2 start
checkUnarchiving("ArchivedThenUnarchived2", ArchivedThenUnarchived.self)
// CHECK-NEXT: --ArchivedThenUnarchived2 end
private class UnarchivedThenArchived {}
checkUnarchiving(UnarchivedThenArchived.self)
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedThenArchived' with mangled runtime name '_TtC{{.+[0-9]+}}UnarchivedThenArchived'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived)
// CHECK-NEXT: @objc(ABCUnarchivedThenArchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(UnarchivedThenArchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --UnarchivedThenArchived1 start
checkUnarchiving("UnarchivedThenArchived1", UnarchivedThenArchived.self)
// CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).UnarchivedThenArchived' with {{.+}} runtime name '_TtC{{.+[0-9]+}}UnarchivedThenArchived'
// CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived)
class Outer {
// CHECK-LABEL: --UnarchivedThenArchived2 start
checkArchiving("UnarchivedThenArchived2", UnarchivedThenArchived.self)
// CHECK-NEXT: --UnarchivedThenArchived2 end
private class Outer {
class ArchivedTwice {}
class UnarchivedTwice {}
class ArchivedThenUnarchived {}
class UnarchivedThenArchived {}
}
checkArchiving(Outer.ArchivedTwice.self)
// CHECK-NEXT: Attempting to archive Swift class '_Test.Outer.ArchivedTwice'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
// CHECK-NEXT: @objc(ABCArchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(Outer.ArchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Outer.ArchivedTwice1 start
checkArchiving("Outer.ArchivedTwice1", Outer.ArchivedTwice.self)
// CHECK: Attempting to archive Swift class '_Test.({{.+}}).Outer.ArchivedTwice'
// CHECK: @objc(_TtC{{.+[0-9]+}}ArchivedTwice)
checkUnarchiving(Outer.UnarchivedTwice.self)
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.Outer.UnarchivedTwice'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
// CHECK-NEXT: @objc(ABCUnarchivedTwice)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.UnarchivedTwice.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Outer.ArchivedTwice2 start
checkArchiving("Outer.ArchivedTwice2", Outer.ArchivedTwice.self)
// CHECK-NEXT: --Outer.ArchivedTwice2 end
checkArchiving(Outer.ArchivedThenUnarchived.self)
// CHECK-NEXT: Attempting to archive Swift class '_Test.Outer.ArchivedThenUnarchived'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}ArchivedThenUnarchived)
// CHECK-NEXT: @objc(ABCArchivedThenUnarchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkUnarchiving(Outer.ArchivedThenUnarchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Outer.UnarchivedTwice1 start
checkUnarchiving("Outer.UnarchivedTwice1", Outer.UnarchivedTwice.self)
// CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).Outer.UnarchivedTwice'
// CHECK: @objc(_TtC{{.+[0-9]+}}UnarchivedTwice)
checkUnarchiving(Outer.UnarchivedThenArchived.self)
// CHECK-NEXT: Attempting to unarchive Swift class '_Test.Outer.UnarchivedThenArchived'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}UnarchivedThenArchived)
// CHECK-NEXT: @objc(ABCUnarchivedThenArchived)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(Outer.UnarchivedThenArchived.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Outer.UnarchivedTwice2 start
checkUnarchiving("Outer.UnarchivedTwice2", Outer.UnarchivedTwice.self)
// CHECK-NEXT: --Outer.UnarchivedTwice2 end
// CHECK-LABEL: --Outer.ArchivedThenUnarchived1 start
checkArchiving("Outer.ArchivedThenUnarchived1", Outer.ArchivedThenUnarchived.self)
// 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 {}
checkArchiving(.self)
// CHECK-NEXT: Attempting to archive Swift class '_Test.({{.*}}).'
// CHECK-NEXT: @objc(_TtC{{.+[0-9]+}}9)
// CHECK-NEXT: @objc(ABCMyModel)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --Japanese1 start
checkArchiving("Japanese1", .self)
// CHECK: Attempting to archive Swift class '_Test.({{.*}}).'
// 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-NEXT: Attempting to archive generic Swift class '_Test.ArchivedTwiceGeneric<Swift.Int>' with mangled runtime name '_TtGC5_Test20ArchivedTwiceGenericSi_'
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:)
// CHECK-SAME: _TtGC5_Test20ArchivedTwiceGenericSi_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
mark() // CHECK-NEXT: --[[@LINE]]--
checkArchiving(ArchivedTwiceGeneric<Int>.self)
mark() // CHECK-NEXT: --[[@LINE]]--
// CHECK-LABEL: --LocalArchived start
checkArchiving("LocalArchived", LocalArchived.self)
// CHECK: Attempting to archive Swift class '_Test.({{.+}}).LocalArchived'
checkArchiving(ArchivedTwiceGeneric<NSObject>.self)
// CHECK-NEXT: Attempting to archive generic Swift class '_Test.ArchivedTwiceGeneric<__C.NSObject>' with mangled runtime name '_TtGC5_Test20ArchivedTwiceGenericCSo8NSObject_'
// CHECK-NEXT: NSKeyedUnarchiver.setClass(_:forClassName:)
// CHECK-SAME: _TtGC5_Test20ArchivedTwiceGenericCSo8NSObject_
// CHECK-NEXT: NSKeyedArchiver.setClassName(_:for:)
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]]--
// CHECK-LABEL: --LocalUnarchived start
checkUnarchiving("LocalUnarchived", LocalUnarchived.self)
// CHECK: Attempting to unarchive Swift class '_Test.({{.+}}).LocalUnarchived'
}
someFunction()