Skip to content

Commit d747f57

Browse files
eecksteinjrose-apple
authored andcommitted
runtime: add a runtime function _swift_checkClassAndWarnForKeyedArchiving
This function checks if a mangled class name is going to be written into an NSArchive. If yes, a warning should be printed and the return value should indicate that. TODO: print the actual warning rdar://problem/32414508 (cherry picked from commit 488c43c)
1 parent 8fbb2ca commit d747f57

File tree

6 files changed

+139
-0
lines changed

6 files changed

+139
-0
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ enum class ClassFlags : uint32_t {
9292

9393
/// Does this class use Swift 1.0 refcounting?
9494
UsesSwift1Refcounting = 0x2,
95+
96+
/// Has this class a custom name, specified with the @objc attribute?
97+
HasCustomObjCName = 0x4
9598
};
9699
inline bool operator&(ClassFlags a, ClassFlags b) {
97100
return (uint32_t(a) & uint32_t(b)) != 0;

lib/IRGen/GenMeta.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "swift/AST/CanTypeVisitor.h"
2020
#include "swift/AST/ExistentialLayout.h"
2121
#include "swift/AST/Decl.h"
22+
#include "swift/AST/Attr.h"
2223
#include "swift/AST/IRGenOptions.h"
2324
#include "swift/AST/SubstitutionMap.h"
2425
#include "swift/AST/Types.h"
@@ -3349,6 +3350,14 @@ namespace {
33493350
flags |= ClassFlags::UsesSwift1Refcounting;
33503351
}
33513352

3353+
DeclAttributes attrs = Target->getAttrs();
3354+
if (auto objc = attrs.getAttribute<ObjCAttr>()) {
3355+
if (objc->getName())
3356+
flags |= ClassFlags::HasCustomObjCName;
3357+
}
3358+
if (attrs.hasAttribute<ObjCRuntimeNameAttr>())
3359+
flags |= ClassFlags::HasCustomObjCName;
3360+
33523361
B.addInt32((uint32_t) flags);
33533362
}
33543363

stdlib/public/SDK/Foundation/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ add_swift_library(swiftFoundation ${SWIFT_SDK_OVERLAY_LIBRARY_BUILD_TYPES} IS_SD
5454
URLComponents.swift
5555
URLRequest.swift
5656
UUID.swift
57+
CheckClass.mm
5758

5859
SWIFT_COMPILE_FLAGS "${SWIFT_RUNTIME_SWIFT_COMPILE_FLAGS}" "-Xllvm" "-sil-inline-generics" "-Xllvm" "-sil-partial-specialization"
5960
LINK_FLAGS "${SWIFT_RUNTIME_SWIFT_LINK_FLAGS}"
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#import <Foundation/Foundation.h>
2+
3+
#include "swift/Runtime/Metadata.h"
4+
5+
@interface NSKeyedUnarchiver (SwiftAdditions)
6+
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls
7+
operation:(int)operation
8+
NS_SWIFT_NAME(_swift_checkClassAndWarnForKeyedArchiving(_:operation:));
9+
@end
10+
11+
@implementation NSKeyedUnarchiver (SwiftAdditions)
12+
13+
/// Checks if class \p cls is good for archiving.
14+
///
15+
/// If not, a runtime warning is printed.
16+
///
17+
/// \param operation Specifies the archiving operation. Valid operations are:
18+
/// 0: archiving
19+
/// 1: unarchiving
20+
/// \return Returns the status
21+
/// 0: not a problem class (either non-Swift or has an explicit name)
22+
/// 1: a Swift generic class
23+
/// 2: a Swift non-generic class where adding @objc is valid
24+
/// Future versions of this API will return nonzero values for additional cases
25+
/// that mean the class shouldn't be archived.
26+
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls
27+
operation:(int)operation {
28+
const swift::ClassMetadata *theClass = (swift::ClassMetadata *)cls;
29+
30+
// Is it a (real) swift class?
31+
if (!theClass->isTypeMetadata() || theClass->isArtificialSubclass())
32+
return 0;
33+
34+
// Does the class already have a custom name?
35+
if (theClass->getFlags() & swift::ClassFlags::HasCustomObjCName)
36+
return 0;
37+
38+
const char *className = [NSStringFromClass(cls) UTF8String];
39+
40+
// Is it a mangled name?
41+
if (!(className[0] == '_' && className[1] == 'T'))
42+
return 0;
43+
// Is it a name in the form <module>.<class>? Note: the module name could
44+
// start with "_T".
45+
if (strchr(className, '.'))
46+
return 0;
47+
48+
// Is it a generic class?
49+
if (theClass->getDescription()->GenericParams.isGeneric()) {
50+
// TODO: print a warning
51+
return 1;
52+
}
53+
54+
// It's a swift class with a (compiler generated) mangled name, which should
55+
// be written into an NSArchive.
56+
// TODO: print a warning
57+
return 2;
58+
}
59+
@end
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@interface NSKeyedUnarchiver (SwiftAdditions)
4+
+ (int)_swift_checkClassAndWarnForKeyedArchiving:(Class)cls operation:(int)operation
5+
NS_SWIFT_NAME(_swift_checkClassAndWarnForKeyedArchiving(_:operation:));
6+
@end
7+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -module-name=_Test -import-objc-header %S/Inputs/check_class_for_archiving.h -o %t/a.out
3+
// RUN: %target-run %t/a.out | %FileCheck %s
4+
5+
// REQUIRES: executable_test
6+
// REQUIRES: objc_interop
7+
8+
import Foundation
9+
10+
class SwiftClass {}
11+
12+
class ObjcClass : NSObject {}
13+
14+
private class PrivateClass : NSObject {}
15+
16+
@objc(named_class)
17+
private class NamedClass1 : NSObject {}
18+
19+
@objc(_T3nix11NamedClass2C)
20+
private class NamedClass2 : NSObject {}
21+
22+
class GenericClass<T> : NSObject {}
23+
24+
class DerivedClass : GenericClass<Int> {}
25+
26+
@objc(_T3nix20DerivedClassWithNameC)
27+
private class DerivedClassWithName : GenericClass<Int> {}
28+
29+
struct ABC {
30+
class InnerClass : NSObject {}
31+
}
32+
33+
struct DEF<T> {
34+
class InnerClass : NSObject {}
35+
}
36+
37+
let op: Int32 = 0 // archiving
38+
39+
// CHECK: SwiftClass: 0
40+
print("SwiftClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(SwiftClass.self, operation: op))")
41+
// CHECK: ObjcClass: 0
42+
print("ObjcClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ObjcClass.self, operation: op))")
43+
// CHECK: PrivateClass: 2
44+
print("PrivateClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(PrivateClass.self, operation: op))")
45+
// CHECK: NamedClass1: 0
46+
print("NamedClass1: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass1.self, operation: op))")
47+
// CHECK: NamedClass2: 0
48+
print("NamedClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NamedClass2.self, operation: op))")
49+
// CHECK: GenericClass: 1
50+
print("GenericClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(GenericClass<Int>.self, operation: op))")
51+
// CHECK: DerivedClass: 0
52+
print("DerivedClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))")
53+
// CHECK: DerivedClassWithName: 0
54+
print("DerivedClassWithName: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DerivedClass.self, operation: op))")
55+
// CHECK: InnerClass: 2
56+
print("InnerClass: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(ABC.InnerClass.self, operation: op))")
57+
// CHECK: InnerClass2: 1
58+
print("InnerClass2: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(DEF<Int>.InnerClass.self, operation: op))")
59+
// CHECK: NSKeyedUnarchiver: 0
60+
print("NSKeyedUnarchiver: \(NSKeyedUnarchiver._swift_checkClassAndWarnForKeyedArchiving(NSKeyedUnarchiver.self, operation: op))")

0 commit comments

Comments
 (0)