Skip to content

Commit c9c13b9

Browse files
authored
Merge pull request #1218 from NativeScript/bektchiev/demangle-swift-class-names
fix(runtime): Lookup into native MD tables when searching by name
2 parents ff8359d + d6a78e1 commit c9c13b9

File tree

15 files changed

+291
-173
lines changed

15 files changed

+291
-173
lines changed

src/NativeScript/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ set(HEADER_FILES
5757
Marshalling/Reference/IndexedRefPrototype.h
5858
Marshalling/Reference/ExtVectorTypeInstance.h
5959
Metadata/Metadata.h
60+
Metadata/MetadataInlines.h
6061
Metadata/KnownUnknownClassPair.h
6162
NativeScript-Prefix.h
6263
NativeScript.h

src/NativeScript/GlobalObject.mm

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
322322
if (symbolName == nullptr)
323323
return false;
324324

325-
const Meta* symbolMeta = Metadata::MetaFile::instance()->globalTable()->findMeta(symbolName);
325+
const Meta* symbolMeta = Metadata::MetaFile::instance()->globalTableJs()->findMeta(symbolName);
326326
if (symbolMeta == nullptr)
327327
return false;
328328

@@ -331,14 +331,15 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
331331

332332
switch (symbolMeta->type()) {
333333
case Interface: {
334+
auto interfaceMeta = static_cast<const InterfaceMeta*>(symbolMeta);
334335
Class klass = objc_getClass(symbolMeta->name());
335336
if (!klass) {
336337
SymbolLoader::instance().ensureModule(symbolMeta->topLevelModule());
337338
klass = objc_getClass(symbolMeta->name());
338339
}
339340

340341
if (klass) {
341-
auto constructor = globalObject->_typeFactory.get()->getObjCNativeConstructor(globalObject, symbolMeta->jsName(), ProtocolMetas());
342+
auto constructor = globalObject->_typeFactory.get()->getObjCNativeConstructor(globalObject, ConstructorKey(klass), interfaceMeta);
342343
strongSymbolWrapper = constructor;
343344
globalObject->_objCConstructors.insert({ ConstructorKey(klass), constructor });
344345
}
@@ -437,8 +438,8 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
437438
// Once we start grouping declarations by modules, this can be safely restored.
438439
void GlobalObject::getOwnPropertyNames(JSObject* object, ExecState* execState, PropertyNameArray& propertyNames, EnumerationMode enumerationMode) {
439440
if (!jsCast<GlobalObject*>(object)->hasDebugger()) {
440-
const GlobalTable* globalTable = MetaFile::instance()->globalTable();
441-
for (const Meta* meta : *globalTable) {
441+
auto globalTableJs = MetaFile::instance()->globalTableJs();
442+
for (const Meta* meta : *globalTableJs) {
442443
if (meta->isAvailable()) {
443444
propertyNames.add(Identifier::fromString(execState, meta->jsName()));
444445
}
@@ -458,7 +459,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
458459
return kvp->second;
459460
}
460461

461-
const InterfaceMeta* meta = MetaFile::instance()->globalTable()->findInterfaceMeta(class_getName(klass));
462+
const InterfaceMeta* meta = MetaFile::instance()->globalTableNativeInterfaces()->findInterfaceMeta(class_getName(klass));
462463
if (!searchBaseClasses && meta == nullptr) {
463464
return Strong<ObjCConstructorBase>();
464465
}
@@ -477,15 +478,15 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
477478
Class firstBaseWithMeta = klass;
478479
while (!meta) {
479480
firstBaseWithMeta = class_getSuperclass(firstBaseWithMeta);
480-
meta = MetaFile::instance()->globalTable()->findInterfaceMeta(class_getName(firstBaseWithMeta));
481+
meta = MetaFile::instance()->globalTableNativeInterfaces()->findInterfaceMeta(class_getName(firstBaseWithMeta));
481482
}
482483

483484
ConstructorKey fallbackConstructorKey(firstBaseWithMeta, klass, protocols);
484485
// Use the hinted fallback if:
485486
// 1) It is more concrete than the first base class with meta; or is unrelated to it
486487
// and 2) It has metadata which is available on the current device
487488
if (fallback && fallback != klass && fallback != firstBaseWithMeta && ([fallback isSubclassOfClass:firstBaseWithMeta] || ![firstBaseWithMeta isSubclassOfClass:fallback])) {
488-
if (auto metadata = MetaFile::instance()->globalTable()->findInterfaceMeta(class_getName(fallback))) {
489+
if (auto metadata = MetaFile::instance()->globalTableNativeInterfaces()->findInterfaceMeta(class_getName(fallback))) {
489490
// We have a hinted fallback class and it has metadata. Treat instances as if they are inheriting from the fallback class.
490491
// This way all members known from the metadata will be exposed to JS (if the actual class implements them).
491492
fallbackConstructorKey = ConstructorKey(fallback, klass, protocols); // fallback is known (coming from a public header), the actual returned type is unknown (without metadata)
@@ -522,17 +523,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
522523
}
523524

524525
CString protocolName = protocol_getName(aProtocol);
525-
const Meta* meta = MetaFile::instance()->globalTable()->findMeta(protocolName.data());
526-
if (meta && meta->type() != MetaType::ProtocolType) {
527-
WTF::String newProtocolname = WTF::String::format("%sProtocol", protocolName.data());
528-
529-
size_t protocolIndex = 2;
530-
while (objc_getProtocol(newProtocolname.utf8().data())) {
531-
newProtocolname = WTF::String::format("%sProtocol%d", protocolName.data(), protocolIndex++);
532-
}
533-
534-
meta = MetaFile::instance()->globalTable()->findMeta(newProtocolname.utf8().data());
535-
}
526+
const Meta* meta = MetaFile::instance()->globalTableNativeProtocols()->findMeta(protocolName.data());
536527
ASSERT(meta && meta->type() == MetaType::ProtocolType);
537528

538529
auto protocolWrapper = createProtocolWrapper(this, static_cast<const ProtocolMeta*>(meta), aProtocol);
@@ -591,7 +582,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
591582
const Meta* getUIApplicationMainMeta() {
592583
static const Meta* meta = nullptr;
593584
if (!meta) {
594-
meta = Metadata::MetaFile::instance()->globalTable()->findMeta("UIApplicationMain");
585+
meta = Metadata::MetaFile::instance()->globalTableJs()->findMeta("UIApplicationMain");
595586
}
596587

597588
return meta;

src/NativeScript/Metadata/Metadata.h

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,16 @@ template <typename T>
242242
using ArrayOfPtrTo = Array<PtrTo<T>>;
243243
using String = PtrTo<char>;
244244

245+
enum GlobalTableType {
246+
ByJsName,
247+
ByNativeName,
248+
};
249+
250+
template <GlobalTableType TYPE>
245251
struct GlobalTable {
246252
class iterator {
247253
private:
248-
const GlobalTable* _globalTable;
254+
const GlobalTable<TYPE>* _globalTable;
249255
int _topLevelIndex;
250256
int _bucketIndex;
251257

@@ -254,12 +260,12 @@ struct GlobalTable {
254260
const Meta* getCurrent();
255261

256262
public:
257-
iterator(const GlobalTable* globalTable)
263+
iterator(const GlobalTable<TYPE>* globalTable)
258264
: iterator(globalTable, 0, 0) {
259265
findNext();
260266
}
261267

262-
iterator(const GlobalTable* globalTable, int32_t topLevelIndex, int32_t bucketIndex)
268+
iterator(const GlobalTable<TYPE>* globalTable, int32_t topLevelIndex, int32_t bucketIndex)
263269
: _globalTable(globalTable)
264270
, _topLevelIndex(topLevelIndex)
265271
, _bucketIndex(bucketIndex) {
@@ -312,6 +318,8 @@ struct GlobalTable {
312318
int sizeInBytes() const {
313319
return buckets.sizeInBytes();
314320
}
321+
322+
static const char* getName(const Meta& meta);
315323
};
316324

317325
struct ModuleTable {
@@ -324,19 +332,29 @@ struct ModuleTable {
324332

325333
struct MetaFile {
326334
private:
327-
GlobalTable _globalTable;
335+
GlobalTable<GlobalTableType::ByJsName> _globalTableJs;
328336

329337
public:
330338
static MetaFile* instance();
331339

332340
static MetaFile* setInstance(void* metadataPtr);
333341

334-
const GlobalTable* globalTable() const {
335-
return &this->_globalTable;
342+
const GlobalTable<GlobalTableType::ByJsName>* globalTableJs() const {
343+
return &this->_globalTableJs;
344+
}
345+
346+
const GlobalTable<GlobalTableType::ByNativeName>* globalTableNativeProtocols() const {
347+
const GlobalTable<GlobalTableType::ByJsName>* gt = this->globalTableJs();
348+
return reinterpret_cast<const GlobalTable<GlobalTableType::ByNativeName>*>(offset(gt, gt->sizeInBytes()));
349+
}
350+
351+
const GlobalTable<GlobalTableType::ByNativeName>* globalTableNativeInterfaces() const {
352+
const GlobalTable<GlobalTableType::ByNativeName>* gt = this->globalTableNativeProtocols();
353+
return reinterpret_cast<const GlobalTable<GlobalTableType::ByNativeName>*>(offset(gt, gt->sizeInBytes()));
336354
}
337355

338356
const ModuleTable* topLevelModulesTable() const {
339-
const GlobalTable* gt = this->globalTable();
357+
const GlobalTable<GlobalTableType::ByNativeName>* gt = this->globalTableNativeInterfaces();
340358
return reinterpret_cast<const ModuleTable*>(offset(gt, gt->sizeInBytes()));
341359
}
342360

@@ -771,7 +789,7 @@ struct BaseClassMeta : Meta {
771789
template <typename T>
772790
void forEachProtocol(const T& fun, const ProtocolMetas* additionalProtocols) const {
773791
for (Array<String>::iterator it = this->protocols->begin(); it != this->protocols->end(); ++it) {
774-
if (const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTable()->findProtocol((*it).valuePtr())) {
792+
if (const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTableJs()->findProtocol((*it).valuePtr())) {
775793
fun(protocolMeta);
776794
}
777795
}
@@ -988,7 +1006,7 @@ struct InterfaceMeta : BaseClassMeta {
9881006

9891007
const InterfaceMeta* baseMeta() const {
9901008
if (this->baseName() != nullptr) {
991-
const InterfaceMeta* baseMeta = MetaFile::instance()->globalTable()->findInterfaceMeta(this->baseName());
1009+
const InterfaceMeta* baseMeta = MetaFile::instance()->globalTableJs()->findInterfaceMeta(this->baseName());
9921010
return baseMeta;
9931011
}
9941012

@@ -1000,4 +1018,6 @@ struct InterfaceMeta : BaseClassMeta {
10001018

10011019
} // namespace Metadata
10021020

1021+
#include "MetadataInlines.h"
1022+
10031023
#endif /* defined(__NativeScript__Metadata__) */

src/NativeScript/Metadata/Metadata.mm

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -39,93 +39,6 @@ static UInt8 getSystemVersion() {
3939
return result;
4040
}
4141

42-
static int compareIdentifiers(const char* nullTerminated, const char* notNullTerminated, size_t length) {
43-
int result = strncmp(nullTerminated, notNullTerminated, length);
44-
return (result == 0) ? strlen(nullTerminated) - length : result;
45-
}
46-
47-
const InterfaceMeta* GlobalTable::findInterfaceMeta(WTF::StringImpl* identifier) const {
48-
return this->findInterfaceMeta(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash());
49-
}
50-
51-
const InterfaceMeta* GlobalTable::findInterfaceMeta(const char* identifierString) const {
52-
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
53-
return this->findInterfaceMeta(identifierString, strlen(identifierString), hash);
54-
}
55-
56-
const InterfaceMeta* GlobalTable::findInterfaceMeta(const char* identifierString, size_t length, unsigned hash) const {
57-
const Meta* meta = MetaFile::instance()->globalTable()->findMeta(identifierString, length, hash, /*onlyIfAvailable*/ false);
58-
if (meta == nullptr) {
59-
return nullptr;
60-
}
61-
62-
// Meta should be an interface, but it could also be a protocol in case of a
63-
// private interface having the same name as a public protocol
64-
assert(meta->type() == MetaType::Interface || (meta->type() == MetaType::ProtocolType && objc_getClass(meta->name()) != nullptr && objc_getProtocol(meta->name()) != nullptr));
65-
66-
if (meta->type() != MetaType::Interface) {
67-
return nullptr;
68-
}
69-
70-
const InterfaceMeta* interfaceMeta = static_cast<const InterfaceMeta*>(meta);
71-
if (interfaceMeta->isAvailable()) {
72-
return interfaceMeta;
73-
} else {
74-
const char* baseName = interfaceMeta->baseName();
75-
76-
NSLog(@"** \"%s\" introduced in iOS SDK %d.%d is currently unavailable, attempting to load its base: \"%s\". **",
77-
std::string(identifierString, length).c_str(),
78-
getMajorVersion(interfaceMeta->introducedIn()),
79-
getMinorVersion(interfaceMeta->introducedIn()),
80-
baseName);
81-
82-
return this->findInterfaceMeta(baseName);
83-
}
84-
}
85-
86-
const ProtocolMeta* GlobalTable::findProtocol(WTF::StringImpl* identifier) const {
87-
return this->findProtocol(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash());
88-
}
89-
90-
const ProtocolMeta* GlobalTable::findProtocol(const char* identifierString) const {
91-
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
92-
return this->findProtocol(identifierString, strlen(identifierString), hash);
93-
}
94-
95-
const ProtocolMeta* GlobalTable::findProtocol(const char* identifierString, size_t length, unsigned hash) const {
96-
// Do not check for availability when returning a protocol. Apple regularly create new protocols and move
97-
// existing interface members there (e.g. iOS 12.0 introduced the UIFocusItemScrollableContainer protocol
98-
// in UIKit which contained members that have existed in UIScrollView since iOS 2.0)
99-
100-
auto meta = this->findMeta(identifierString, length, hash, /*onlyIfAvailable*/ false);
101-
ASSERT(!meta || meta->type() == ProtocolType);
102-
return static_cast<const ProtocolMeta*>(meta);
103-
}
104-
105-
const Meta* GlobalTable::findMeta(WTF::StringImpl* identifier, bool onlyIfAvailable) const {
106-
return this->findMeta(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash(), onlyIfAvailable);
107-
}
108-
109-
const Meta* GlobalTable::findMeta(const char* identifierString, bool onlyIfAvailable) const {
110-
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
111-
return this->findMeta(identifierString, strlen(identifierString), hash, onlyIfAvailable);
112-
}
113-
114-
const Meta* GlobalTable::findMeta(const char* identifierString, size_t length, unsigned hash, bool onlyIfAvailable) const {
115-
int bucketIndex = hash % buckets.count;
116-
if (this->buckets[bucketIndex].isNull()) {
117-
return nullptr;
118-
}
119-
const ArrayOfPtrTo<Meta>& bucketContent = buckets[bucketIndex].value();
120-
for (ArrayOfPtrTo<Meta>::iterator it = bucketContent.begin(); it != bucketContent.end(); it++) {
121-
const Meta* meta = (*it).valuePtr();
122-
if (compareIdentifiers(meta->jsName(), identifierString, length) == 0) {
123-
return onlyIfAvailable ? (meta->isAvailable() ? meta : nullptr) : meta;
124-
}
125-
}
126-
return nullptr;
127-
}
128-
12942
// Meta
13043
bool Meta::isAvailable() const {
13144
UInt8 introducedIn = this->introducedIn();
@@ -369,7 +282,7 @@ void collectInheritanceChainMembers(const char* identifier, size_t length, Membe
369282
vector<const MethodMeta*> BaseClassMeta::initializersWithProtocols(vector<const MethodMeta*>& container, KnownUnknownClassPair klasses, const ProtocolMetas& additionalProtocols) const {
370283
this->initializers(container, klasses);
371284
for (Array<String>::iterator it = this->protocols->begin(); it != this->protocols->end(); it++) {
372-
const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTable()->findProtocol((*it).valuePtr());
285+
const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTableJs()->findProtocol((*it).valuePtr());
373286
if (protocolMeta != nullptr)
374287
protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas());
375288
}
@@ -379,48 +292,6 @@ void collectInheritanceChainMembers(const char* identifier, size_t length, Membe
379292
return container;
380293
}
381294

382-
const Meta* GlobalTable::iterator::getCurrent() {
383-
return this->_globalTable->buckets[_topLevelIndex].value()[_bucketIndex].valuePtr();
384-
}
385-
386-
GlobalTable::iterator& GlobalTable::iterator::operator++() {
387-
this->_bucketIndex++;
388-
this->findNext();
389-
return *this;
390-
}
391-
392-
const Meta* GlobalTable::iterator::operator*() {
393-
return this->getCurrent();
394-
}
395-
396-
bool GlobalTable::iterator::operator==(const iterator& other) const {
397-
return _globalTable == other._globalTable && _topLevelIndex == other._topLevelIndex && _bucketIndex == other._bucketIndex;
398-
}
399-
400-
bool GlobalTable::iterator::operator!=(const iterator& other) const {
401-
return !(*this == other);
402-
}
403-
404-
void GlobalTable::iterator::findNext() {
405-
if (this->_topLevelIndex == this->_globalTable->buckets.count) {
406-
return;
407-
}
408-
409-
do {
410-
if (!this->_globalTable->buckets[_topLevelIndex].isNull()) {
411-
int bucketLength = this->_globalTable->buckets[_topLevelIndex].value().count;
412-
while (this->_bucketIndex < bucketLength) {
413-
if (this->getCurrent() != nullptr) {
414-
return;
415-
}
416-
this->_bucketIndex++;
417-
}
418-
}
419-
this->_bucketIndex = 0;
420-
this->_topLevelIndex++;
421-
} while (this->_topLevelIndex < this->_globalTable->buckets.count);
422-
}
423-
424295
static MetaFile* metaFileInstance(nullptr);
425296

426297
MetaFile* MetaFile::instance() {

0 commit comments

Comments
 (0)