Skip to content

Commit 0887c11

Browse files
committed
fix: Register Swift classes with both native names
The Objective-C runtime doesn't behave consistently with Swift class names. Sometimes the mangled name can only be used, other times the demangled one. To avoid hardcoding any logic which may get easily broken in the future, we are adding both names in the global tables and are using the mangled name in signatures. Observation which lead to this change: * `objc_getClass(const char *)` - accepts both mangled and demangled names of Swift classes which are not nested in another class. Nested classes however can be discovered only by their mangled name. * `class_getName` - returns the demangled name for Swift classes which aren't nested, and the mangled one for nested. * On Mac Catalyst `class_getName` returns the mangled names no matter whether the class is nested or not. * Update the `metadata-generator` submodule * Add an optional demangled name for every metadata entity * Add a test case with a nested Swift-like class
1 parent 325e639 commit 0887c11

6 files changed

Lines changed: 72 additions & 14 deletions

File tree

src/NativeScript/Metadata/Metadata.h

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ inline UInt8 getMinorVersion(UInt8 encodedVersion) {
6363

6464
// Bit indices in flags section
6565
enum MetaFlags {
66+
HasDemangledName = 8,
6667
HasName = 7,
6768
// IsIosAppExtensionAvailable = 6, the flag exists in metadata generator but we never use it in the runtime
6869
FunctionReturnsUnmanaged = 3,
@@ -319,7 +320,7 @@ struct GlobalTable {
319320
return buckets.sizeInBytes();
320321
}
321322

322-
static const char* getName(const Meta& meta);
323+
static bool compareName(const Meta& meta, const char* identifierString, size_t length);
323324
};
324325

325326
struct ModuleTable {
@@ -546,22 +547,28 @@ struct LibraryMeta {
546547
}
547548
};
548549

549-
struct JsNameAndName {
550-
String jsName;
551-
String name;
550+
enum NameIndex {
551+
JsName,
552+
Name,
553+
DemangledName,
554+
NameIndexCount,
555+
};
556+
557+
struct JsNameAndNativeNames {
558+
String strings[NameIndexCount];
552559
};
553560

554561
union MetaNames {
555562
String name;
556-
PtrTo<JsNameAndName> names;
563+
PtrTo<JsNameAndNativeNames> names;
557564
};
558565

559566
struct Meta {
560567

561568
private:
562569
MetaNames _names;
563570
PtrTo<ModuleMeta> _topLevelModule;
564-
UInt8 _flags;
571+
UInt16 _flags;
565572
UInt8 _introduced;
566573

567574
public:
@@ -577,16 +584,24 @@ struct Meta {
577584
return this->flag(MetaFlags::HasName);
578585
}
579586

587+
bool hasDemangledName() const {
588+
return this->flag(MetaFlags::HasDemangledName);
589+
}
590+
580591
bool flag(int index) const {
581592
return (this->_flags & (1 << index)) > 0;
582593
}
583594

584595
const char* jsName() const {
585-
return (this->hasName()) ? this->_names.names->jsName.valuePtr() : this->_names.name.valuePtr();
596+
return this->getNameByIndex(JsName);
586597
}
587598

588599
const char* name() const {
589-
return (this->hasName()) ? this->_names.names->name.valuePtr() : this->_names.name.valuePtr();
600+
return this->getNameByIndex(Name);
601+
}
602+
603+
const char* demangledName() const {
604+
return this->getNameByIndex(DemangledName);
590605
}
591606

592607
/**
@@ -605,6 +620,24 @@ struct Meta {
605620
* > have been introduced in this or prior version;
606621
*/
607622
bool isAvailable() const;
623+
624+
private:
625+
const char* getNameByIndex(enum NameIndex index) const {
626+
int i = index;
627+
if (!this->hasName() && !this->hasDemangledName()) {
628+
return this->_names.name.valuePtr();
629+
}
630+
631+
if (!this->hasDemangledName() && i >= DemangledName) {
632+
i--;
633+
}
634+
635+
if (!this->hasName() && i >= Name) {
636+
i--;
637+
}
638+
639+
return this->_names.names.value().strings[i].valuePtr();
640+
}
608641
};
609642

610643
struct RecordMeta : Meta {

src/NativeScript/Metadata/MetadataInlines.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,17 +101,21 @@ const Meta* GlobalTable<TYPE>::findMeta(const char* identifierString, size_t len
101101
const ArrayOfPtrTo<Meta>& bucketContent = buckets[bucketIndex].value();
102102
for (ArrayOfPtrTo<Meta>::iterator it = bucketContent.begin(); it != bucketContent.end(); it++) {
103103
const Meta* meta = (*it).valuePtr();
104-
const char* name = getName(*meta);
105-
if (compareIdentifiers(name, identifierString, length) == 0) {
104+
if (this->compareName(*meta, identifierString, length)) {
106105
return onlyIfAvailable ? (meta->isAvailable() ? meta : nullptr) : meta;
107106
}
108107
}
109108
return nullptr;
110109
}
111110

112-
template <GlobalTableType TYPE>
113-
inline const char* GlobalTable<TYPE>::getName(const Meta& meta) {
114-
return TYPE == GlobalTableType::ByJsName ? meta.jsName() : meta.name();
111+
template <>
112+
inline bool GlobalTable<ByJsName>::compareName(const Meta& meta, const char* identifierString, size_t length) {
113+
return compareIdentifiers(meta.jsName(), identifierString, length) == 0;
114+
}
115+
116+
template <>
117+
inline bool GlobalTable<ByNativeName>::compareName(const Meta& meta, const char* identifierString, size_t length) {
118+
return compareIdentifiers(meta.name(), identifierString, length) == 0 || (meta.hasDemangledName() && compareIdentifiers(meta.demangledName(), identifierString, length) == 0);
115119
}
116120

117121
// GlobalTable::iterator

src/metadata-generator

Submodule metadata-generator updated 126 files

tests/TestFixtures/Interfaces/TNSSwiftLike.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ __attribute__((objc_runtime_name("_TtC17NativeScriptTests12TNSSwiftLike")))
1313

1414
@end
1515

16+
__attribute__((objc_runtime_name("_TtCC17NativeScriptTests12TNSSwiftLike5Inner")))
17+
@interface TNSSwiftLikeInner : NSObject
18+
19+
@end
20+
1621
@interface TNSSwiftLikeFactory : NSObject
1722
+ (TNSSwiftLike*)create;
23+
+ (TNSSwiftLikeInner*)createInner;
1824
@end
1925

2026
NS_ASSUME_NONNULL_END

tests/TestFixtures/Interfaces/TNSSwiftLike.m

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ @implementation TNSSwiftLike
1111

1212
@end
1313

14+
@implementation TNSSwiftLikeInner
15+
16+
@end
17+
1418
@implementation TNSSwiftLikeFactory
1519
+ (TNSSwiftLike*)create {
1620
return [TNSSwiftLike new];
1721
}
22+
23+
+ (TNSSwiftLikeInner*)createInner {
24+
return [TNSSwiftLikeInner new];
25+
}
1826
@end

tests/TestRunner/app/MetadataTests.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,11 @@ describe("Metadata", function () {
1212
expect(swiftLikeObj.constructor.name).toBe("NativeScriptTests.TNSSwiftLike");
1313
expect(NSString.stringWithUTF8String(class_getName(swiftLikeObj.constructor)).toString()).toBe("NativeScriptTests.TNSSwiftLike");
1414
});
15+
16+
it("Objects from nested Swift classes should be marshalled correctly", function () {
17+
const swiftLikeInnerObj = TNSSwiftLikeFactory.createInner();
18+
expect(swiftLikeInnerObj.constructor).toBe(global.TNSSwiftLikeInner);
19+
expect(swiftLikeInnerObj.constructor.name).toBe("_TtCC17NativeScriptTests12TNSSwiftLike5Inner");
20+
expect(NSString.stringWithUTF8String(class_getName(swiftLikeInnerObj.constructor)).toString(), "_TtCC17NativeScriptTests12TNSSwiftLike5Inner");
21+
});
1522
});

0 commit comments

Comments
 (0)