Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/NativeScript/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ set(HEADER_FILES
Marshalling/Reference/IndexedRefPrototype.h
Marshalling/Reference/ExtVectorTypeInstance.h
Metadata/Metadata.h
Metadata/MetadataInlines.h
Metadata/KnownUnknownClassPair.h
NativeScript-Prefix.h
NativeScript.h
Expand Down
29 changes: 10 additions & 19 deletions src/NativeScript/GlobalObject.mm
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
if (symbolName == nullptr)
return false;

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

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

switch (symbolMeta->type()) {
case Interface: {
auto interfaceMeta = static_cast<const InterfaceMeta*>(symbolMeta);
Class klass = objc_getClass(symbolMeta->name());
if (!klass) {
SymbolLoader::instance().ensureModule(symbolMeta->topLevelModule());
klass = objc_getClass(symbolMeta->name());
}

if (klass) {
auto constructor = globalObject->_typeFactory.get()->getObjCNativeConstructor(globalObject, symbolMeta->jsName(), ProtocolMetas());
auto constructor = globalObject->_typeFactory.get()->getObjCNativeConstructor(globalObject, ConstructorKey(klass), interfaceMeta);
strongSymbolWrapper = constructor;
globalObject->_objCConstructors.insert({ ConstructorKey(klass), constructor });
}
Expand Down Expand Up @@ -437,8 +438,8 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
// Once we start grouping declarations by modules, this can be safely restored.
void GlobalObject::getOwnPropertyNames(JSObject* object, ExecState* execState, PropertyNameArray& propertyNames, EnumerationMode enumerationMode) {
if (!jsCast<GlobalObject*>(object)->hasDebugger()) {
const GlobalTable* globalTable = MetaFile::instance()->globalTable();
for (const Meta* meta : *globalTable) {
auto globalTableJs = MetaFile::instance()->globalTableJs();
for (const Meta* meta : *globalTableJs) {
if (meta->isAvailable()) {
propertyNames.add(Identifier::fromString(execState, meta->jsName()));
}
Expand All @@ -458,7 +459,7 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
return kvp->second;
}

const InterfaceMeta* meta = MetaFile::instance()->globalTable()->findInterfaceMeta(class_getName(klass));
const InterfaceMeta* meta = MetaFile::instance()->globalTableNativeInterfaces()->findInterfaceMeta(class_getName(klass));
if (!searchBaseClasses && meta == nullptr) {
return Strong<ObjCConstructorBase>();
}
Expand All @@ -477,15 +478,15 @@ static void runLoopBeforeWaitingPerformWork(CFRunLoopObserverRef observer, CFRun
Class firstBaseWithMeta = klass;
while (!meta) {
firstBaseWithMeta = class_getSuperclass(firstBaseWithMeta);
meta = MetaFile::instance()->globalTable()->findInterfaceMeta(class_getName(firstBaseWithMeta));
meta = MetaFile::instance()->globalTableNativeInterfaces()->findInterfaceMeta(class_getName(firstBaseWithMeta));
}

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

CString protocolName = protocol_getName(aProtocol);
const Meta* meta = MetaFile::instance()->globalTable()->findMeta(protocolName.data());
if (meta && meta->type() != MetaType::ProtocolType) {
WTF::String newProtocolname = WTF::String::format("%sProtocol", protocolName.data());

size_t protocolIndex = 2;
while (objc_getProtocol(newProtocolname.utf8().data())) {
newProtocolname = WTF::String::format("%sProtocol%d", protocolName.data(), protocolIndex++);
}

meta = MetaFile::instance()->globalTable()->findMeta(newProtocolname.utf8().data());
}
const Meta* meta = MetaFile::instance()->globalTableNativeProtocols()->findMeta(protocolName.data());
ASSERT(meta && meta->type() == MetaType::ProtocolType);

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

return meta;
Expand Down
38 changes: 29 additions & 9 deletions src/NativeScript/Metadata/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,16 @@ template <typename T>
using ArrayOfPtrTo = Array<PtrTo<T>>;
using String = PtrTo<char>;

enum GlobalTableType {
ByJsName,
ByNativeName,
};

template <GlobalTableType TYPE>
struct GlobalTable {
class iterator {
private:
const GlobalTable* _globalTable;
const GlobalTable<TYPE>* _globalTable;
int _topLevelIndex;
int _bucketIndex;

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

public:
iterator(const GlobalTable* globalTable)
iterator(const GlobalTable<TYPE>* globalTable)
: iterator(globalTable, 0, 0) {
findNext();
}

iterator(const GlobalTable* globalTable, int32_t topLevelIndex, int32_t bucketIndex)
iterator(const GlobalTable<TYPE>* globalTable, int32_t topLevelIndex, int32_t bucketIndex)
: _globalTable(globalTable)
, _topLevelIndex(topLevelIndex)
, _bucketIndex(bucketIndex) {
Expand Down Expand Up @@ -312,6 +318,8 @@ struct GlobalTable {
int sizeInBytes() const {
return buckets.sizeInBytes();
}

static const char* getName(const Meta& meta);
};

struct ModuleTable {
Expand All @@ -324,19 +332,29 @@ struct ModuleTable {

struct MetaFile {
private:
GlobalTable _globalTable;
GlobalTable<GlobalTableType::ByJsName> _globalTableJs;

public:
static MetaFile* instance();

static MetaFile* setInstance(void* metadataPtr);

const GlobalTable* globalTable() const {
return &this->_globalTable;
const GlobalTable<GlobalTableType::ByJsName>* globalTableJs() const {
return &this->_globalTableJs;
}

const GlobalTable<GlobalTableType::ByNativeName>* globalTableNativeProtocols() const {
const GlobalTable<GlobalTableType::ByJsName>* gt = this->globalTableJs();
return reinterpret_cast<const GlobalTable<GlobalTableType::ByNativeName>*>(offset(gt, gt->sizeInBytes()));
}

const GlobalTable<GlobalTableType::ByNativeName>* globalTableNativeInterfaces() const {
const GlobalTable<GlobalTableType::ByNativeName>* gt = this->globalTableNativeProtocols();
return reinterpret_cast<const GlobalTable<GlobalTableType::ByNativeName>*>(offset(gt, gt->sizeInBytes()));
}

const ModuleTable* topLevelModulesTable() const {
const GlobalTable* gt = this->globalTable();
const GlobalTable<GlobalTableType::ByNativeName>* gt = this->globalTableNativeInterfaces();
return reinterpret_cast<const ModuleTable*>(offset(gt, gt->sizeInBytes()));
}

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

const InterfaceMeta* baseMeta() const {
if (this->baseName() != nullptr) {
const InterfaceMeta* baseMeta = MetaFile::instance()->globalTable()->findInterfaceMeta(this->baseName());
const InterfaceMeta* baseMeta = MetaFile::instance()->globalTableJs()->findInterfaceMeta(this->baseName());
return baseMeta;
}

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

} // namespace Metadata

#include "MetadataInlines.h"

#endif /* defined(__NativeScript__Metadata__) */
131 changes: 1 addition & 130 deletions src/NativeScript/Metadata/Metadata.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,93 +39,6 @@ static UInt8 getSystemVersion() {
return result;
}

static int compareIdentifiers(const char* nullTerminated, const char* notNullTerminated, size_t length) {
int result = strncmp(nullTerminated, notNullTerminated, length);
return (result == 0) ? strlen(nullTerminated) - length : result;
}

const InterfaceMeta* GlobalTable::findInterfaceMeta(WTF::StringImpl* identifier) const {
return this->findInterfaceMeta(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash());
}

const InterfaceMeta* GlobalTable::findInterfaceMeta(const char* identifierString) const {
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
return this->findInterfaceMeta(identifierString, strlen(identifierString), hash);
}

const InterfaceMeta* GlobalTable::findInterfaceMeta(const char* identifierString, size_t length, unsigned hash) const {
const Meta* meta = MetaFile::instance()->globalTable()->findMeta(identifierString, length, hash, /*onlyIfAvailable*/ false);
if (meta == nullptr) {
return nullptr;
}

// Meta should be an interface, but it could also be a protocol in case of a
// private interface having the same name as a public protocol
assert(meta->type() == MetaType::Interface || (meta->type() == MetaType::ProtocolType && objc_getClass(meta->name()) != nullptr && objc_getProtocol(meta->name()) != nullptr));

if (meta->type() != MetaType::Interface) {
return nullptr;
}

const InterfaceMeta* interfaceMeta = static_cast<const InterfaceMeta*>(meta);
if (interfaceMeta->isAvailable()) {
return interfaceMeta;
} else {
const char* baseName = interfaceMeta->baseName();

NSLog(@"** \"%s\" introduced in iOS SDK %d.%d is currently unavailable, attempting to load its base: \"%s\". **",
std::string(identifierString, length).c_str(),
getMajorVersion(interfaceMeta->introducedIn()),
getMinorVersion(interfaceMeta->introducedIn()),
baseName);

return this->findInterfaceMeta(baseName);
}
}

const ProtocolMeta* GlobalTable::findProtocol(WTF::StringImpl* identifier) const {
return this->findProtocol(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash());
}

const ProtocolMeta* GlobalTable::findProtocol(const char* identifierString) const {
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
return this->findProtocol(identifierString, strlen(identifierString), hash);
}

const ProtocolMeta* GlobalTable::findProtocol(const char* identifierString, size_t length, unsigned hash) const {
// Do not check for availability when returning a protocol. Apple regularly create new protocols and move
// existing interface members there (e.g. iOS 12.0 introduced the UIFocusItemScrollableContainer protocol
// in UIKit which contained members that have existed in UIScrollView since iOS 2.0)

auto meta = this->findMeta(identifierString, length, hash, /*onlyIfAvailable*/ false);
ASSERT(!meta || meta->type() == ProtocolType);
return static_cast<const ProtocolMeta*>(meta);
}

const Meta* GlobalTable::findMeta(WTF::StringImpl* identifier, bool onlyIfAvailable) const {
return this->findMeta(reinterpret_cast<const char*>(identifier->utf8().data()), identifier->length(), identifier->hash(), onlyIfAvailable);
}

const Meta* GlobalTable::findMeta(const char* identifierString, bool onlyIfAvailable) const {
unsigned hash = WTF::StringHasher::computeHashAndMaskTop8Bits<LChar>(reinterpret_cast<const LChar*>(identifierString));
return this->findMeta(identifierString, strlen(identifierString), hash, onlyIfAvailable);
}

const Meta* GlobalTable::findMeta(const char* identifierString, size_t length, unsigned hash, bool onlyIfAvailable) const {
int bucketIndex = hash % buckets.count;
if (this->buckets[bucketIndex].isNull()) {
return nullptr;
}
const ArrayOfPtrTo<Meta>& bucketContent = buckets[bucketIndex].value();
for (ArrayOfPtrTo<Meta>::iterator it = bucketContent.begin(); it != bucketContent.end(); it++) {
const Meta* meta = (*it).valuePtr();
if (compareIdentifiers(meta->jsName(), identifierString, length) == 0) {
return onlyIfAvailable ? (meta->isAvailable() ? meta : nullptr) : meta;
}
}
return nullptr;
}

// Meta
bool Meta::isAvailable() const {
UInt8 introducedIn = this->introducedIn();
Expand Down Expand Up @@ -369,7 +282,7 @@ void collectInheritanceChainMembers(const char* identifier, size_t length, Membe
vector<const MethodMeta*> BaseClassMeta::initializersWithProtocols(vector<const MethodMeta*>& container, KnownUnknownClassPair klasses, const ProtocolMetas& additionalProtocols) const {
this->initializers(container, klasses);
for (Array<String>::iterator it = this->protocols->begin(); it != this->protocols->end(); it++) {
const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTable()->findProtocol((*it).valuePtr());
const ProtocolMeta* protocolMeta = MetaFile::instance()->globalTableJs()->findProtocol((*it).valuePtr());
if (protocolMeta != nullptr)
protocolMeta->initializersWithProtocols(container, klasses, ProtocolMetas());
}
Expand All @@ -379,48 +292,6 @@ void collectInheritanceChainMembers(const char* identifier, size_t length, Membe
return container;
}

const Meta* GlobalTable::iterator::getCurrent() {
return this->_globalTable->buckets[_topLevelIndex].value()[_bucketIndex].valuePtr();
}

GlobalTable::iterator& GlobalTable::iterator::operator++() {
this->_bucketIndex++;
this->findNext();
return *this;
}

const Meta* GlobalTable::iterator::operator*() {
return this->getCurrent();
}

bool GlobalTable::iterator::operator==(const iterator& other) const {
return _globalTable == other._globalTable && _topLevelIndex == other._topLevelIndex && _bucketIndex == other._bucketIndex;
}

bool GlobalTable::iterator::operator!=(const iterator& other) const {
return !(*this == other);
}

void GlobalTable::iterator::findNext() {
if (this->_topLevelIndex == this->_globalTable->buckets.count) {
return;
}

do {
if (!this->_globalTable->buckets[_topLevelIndex].isNull()) {
int bucketLength = this->_globalTable->buckets[_topLevelIndex].value().count;
while (this->_bucketIndex < bucketLength) {
if (this->getCurrent() != nullptr) {
return;
}
this->_bucketIndex++;
}
}
this->_bucketIndex = 0;
this->_topLevelIndex++;
} while (this->_topLevelIndex < this->_globalTable->buckets.count);
}

static MetaFile* metaFileInstance(nullptr);

MetaFile* MetaFile::instance() {
Expand Down
Loading