C++ Class objc_getClass(constchar *aClassName) { if (!aClassName) returnNil;
// NO unconnected, YES class handler return look_up_class(aClassName, NO, YES); }
。。。。
/*********************************************************************** * look_up_class * Look up a class by name, and realize it. * Locking: acquires runtimeLock **********************************************************************/ staticBOOL empty_getClass(constchar *name, Class *outClass) { *outClass = nil; returnNO; }
Class look_up_class(constchar *name, bool includeUnconnected __attribute__((unused)), bool includeClassHandler __attribute__((unused))) { if (!name) returnnil;
Class result; bool unrealized; { runtimeLock.lock(); result = getClassExceptSomeSwift(name); unrealized = result && !result->isRealized(); if (unrealized) { result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock); // runtimeLock is now unlocked } else { runtimeLock.unlock(); } }
if (!result) { // Ask Swift about its un-instantiated classes.
// We use thread-local storage to prevent infinite recursion // if the hook function provokes another lookup of the same name // (for example, if the hook calls objc_allocateClassPair)
auto *tls = _objc_fetch_pthread_data(true);
// Stop if this thread is already looking up this name. for (unsigned i = 0; i < tls->classNameLookupsUsed; i++) { if (0 == strcmp(name, tls->classNameLookups[i])) { returnnil; } }
// Save this lookup in tls. if (tls->classNameLookupsUsed == tls->classNameLookupsAllocated) { tls->classNameLookupsAllocated = (tls->classNameLookupsAllocated * 2 ?: 1); size_t size = tls->classNameLookupsAllocated * sizeof(tls->classNameLookups[0]); tls->classNameLookups = (constchar **) realloc(tls->classNameLookups, size); } tls->classNameLookups[tls->classNameLookupsUsed++] = name;
// Call the hook. Class swiftcls = nil; if (GetClassHook.get()(name, &swiftcls)) { ASSERT(swiftcls->isRealized()); result = swiftcls; }
// Erase the name from tls. unsigned slot = --tls->classNameLookupsUsed; ASSERT(slot >= 0 && slot < tls->classNameLookupsAllocated); ASSERT(name == tls->classNameLookups[slot]); tls->classNameLookups[slot] = nil; }
C++ OBJC_EXPORT Method _Nullable class_getClassMethod(Class _Nullable cls, SEL_Nonnull name)
。。。。。 NEVER_INLINE IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior) { constIMP forward_imp = (IMP)_objc_msgForward_impcache; IMP imp = nil; Class curClass;
runtimeLock.assertUnlocked();
if (slowpath(!cls->isInitialized())) { // The first message sent to a class is often +new or +alloc, or +self // which goes through objc_opt_* or various optimized entry points. // // However, the class isn't realized/initialized yet at this point, // and the optimized entry points fall down through objc_msgSend, // which ends up here. // // We really want to avoid caching these, as it can cause IMP caches // to be made with a single entry forever. // // Note that this check is racy as several threads might try to // message a given class for the first time at the same time, // in which case we might cache anyway. behavior |= LOOKUP_NOCACHE; }
// runtimeLock is held during isRealized and isInitialized checking // to prevent races against concurrent realization.
// runtimeLock is held during method search to make // method-lookup + cache-fill atomic with respect to method addition. // Otherwise, a category could be added but ignored indefinitely because // the cache was re-filled with the old value after the cache flush on // behalf of the category.
runtimeLock.lock();
// We don't want people to be able to craft a binary blob that looks like // a class but really isn't one and do a CFI attack. // // To make these harder we want to make sure this is a class that was // either built into the binary or legitimately registered through // objc_duplicateClass, objc_initializeClassPair or objc_allocateClassPair. checkIsKnownClass(cls);
cls = realizeAndInitializeIfNeeded_locked(inst, cls, behavior & LOOKUP_INITIALIZE); // runtimeLock may have been dropped but is now locked again runtimeLock.assertLocked(); curClass = cls;
// The code used to lookup the class's cache again right after // we take the lock but for the vast majority of the cases // evidence shows this is a miss most of the time, hence a time loss. // // The only codepath calling into this without having performed some // kind of cache lookup is class_getInstanceMethod().
if (slowpath((curClass = curClass->getSuperclass()) == nil)) { // No implementation found, and method resolver didn't help. // Use forwarding. imp = forward_imp; break; } }
// Halt if there is a cycle in the superclass chain. if (slowpath(--attempts == 0)) { _objc_fatal("Memory corruption in class list."); }
// Superclass cache. imp = cache_getImp(curClass, sel); if (slowpath(imp == forward_imp)) { // Found a forward:: entry in a superclass. // Stop searching, but don't cache yet; call method // resolver for this class first. break; } if (fastpath(imp)) { // Found the method in a superclass. Cache it in this class. goto done; } }
// No implementation found. Try method resolver once.