12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360 |
- /*
- * Copyright (c) 2010-2012 Apple Inc. All rights reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- #include "objc-private.h"
- #include "NSObject.h"
- #include "objc-weak.h"
- #include "llvm-DenseMap.h"
- #include "NSObject.h"
- #include <malloc/malloc.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <mach/mach.h>
- #include <mach-o/dyld.h>
- #include <mach-o/nlist.h>
- #include <sys/types.h>
- #include <sys/mman.h>
- #include <libkern/OSAtomic.h>
- #include <Block.h>
- #include <map>
- #include <execinfo.h>
- @interface NSInvocation
- - (SEL)selector;
- @end
- /***********************************************************************
- * Weak ivar support
- **********************************************************************/
- static id defaultBadAllocHandler(Class cls)
- {
- _objc_fatal("attempt to allocate object of class '%s' failed",
- cls->nameForLogging());
- }
- static id(*badAllocHandler)(Class) = &defaultBadAllocHandler;
- static id callBadAllocHandler(Class cls)
- {
- // fixme add re-entrancy protection in case allocation fails inside handler
- return (*badAllocHandler)(cls);
- }
- void _objc_setBadAllocHandler(id(*newHandler)(Class))
- {
- badAllocHandler = newHandler;
- }
- namespace {
- // The order of these bits is important.
- #define SIDE_TABLE_WEAKLY_REFERENCED (1UL<<0)
- #define SIDE_TABLE_DEALLOCATING (1UL<<1) // MSB-ward of weak bit
- #define SIDE_TABLE_RC_ONE (1UL<<2) // MSB-ward of deallocating bit
- #define SIDE_TABLE_RC_PINNED (1UL<<(WORD_BITS-1))
- #define SIDE_TABLE_RC_SHIFT 2
- #define SIDE_TABLE_FLAG_MASK (SIDE_TABLE_RC_ONE-1)
- // RefcountMap disguises its pointers because we
- // don't want the table to act as a root for `leaks`.
- typedef objc::DenseMap<DisguisedPtr<objc_object>,size_t,true> RefcountMap;
- // Template parameters.
- enum HaveOld { DontHaveOld = false, DoHaveOld = true };
- enum HaveNew { DontHaveNew = false, DoHaveNew = true };
- struct SideTable {
- spinlock_t slock;
- RefcountMap refcnts;
- weak_table_t weak_table;
- SideTable() {
- memset(&weak_table, 0, sizeof(weak_table));
- }
- ~SideTable() {
- _objc_fatal("Do not delete SideTable.");
- }
- void lock() { slock.lock(); }
- void unlock() { slock.unlock(); }
- void forceReset() { slock.forceReset(); }
- // Address-ordered lock discipline for a pair of side tables.
- template<HaveOld, HaveNew>
- static void lockTwo(SideTable *lock1, SideTable *lock2);
- template<HaveOld, HaveNew>
- static void unlockTwo(SideTable *lock1, SideTable *lock2);
- };
- template<>
- void SideTable::lockTwo<DoHaveOld, DoHaveNew>
- (SideTable *lock1, SideTable *lock2)
- {
- spinlock_t::lockTwo(&lock1->slock, &lock2->slock);
- }
- template<>
- void SideTable::lockTwo<DoHaveOld, DontHaveNew>
- (SideTable *lock1, SideTable *)
- {
- lock1->lock();
- }
- template<>
- void SideTable::lockTwo<DontHaveOld, DoHaveNew>
- (SideTable *, SideTable *lock2)
- {
- lock2->lock();
- }
- template<>
- void SideTable::unlockTwo<DoHaveOld, DoHaveNew>
- (SideTable *lock1, SideTable *lock2)
- {
- spinlock_t::unlockTwo(&lock1->slock, &lock2->slock);
- }
- template<>
- void SideTable::unlockTwo<DoHaveOld, DontHaveNew>
- (SideTable *lock1, SideTable *)
- {
- lock1->unlock();
- }
- template<>
- void SideTable::unlockTwo<DontHaveOld, DoHaveNew>
- (SideTable *, SideTable *lock2)
- {
- lock2->unlock();
- }
- // We cannot use a C++ static initializer to initialize SideTables because
- // libc calls us before our C++ initializers run. We also don't want a global
- // pointer to this struct because of the extra indirection.
- // Do it the hard way.
- alignas(StripedMap<SideTable>) static uint8_t
- SideTableBuf[sizeof(StripedMap<SideTable>)];
- static void SideTableInit() {
- new (SideTableBuf) StripedMap<SideTable>();
- }
- static StripedMap<SideTable>& SideTables() {
- return *reinterpret_cast<StripedMap<SideTable>*>(SideTableBuf);
- }
- // anonymous namespace
- };
- void SideTableLockAll() {
- SideTables().lockAll();
- }
- void SideTableUnlockAll() {
- SideTables().unlockAll();
- }
- void SideTableForceResetAll() {
- SideTables().forceResetAll();
- }
- void SideTableDefineLockOrder() {
- SideTables().defineLockOrder();
- }
- void SideTableLocksPrecedeLock(const void *newlock) {
- SideTables().precedeLock(newlock);
- }
- void SideTableLocksSucceedLock(const void *oldlock) {
- SideTables().succeedLock(oldlock);
- }
- void SideTableLocksPrecedeLocks(StripedMap<spinlock_t>& newlocks) {
- int i = 0;
- const void *newlock;
- while ((newlock = newlocks.getLock(i++))) {
- SideTables().precedeLock(newlock);
- }
- }
- void SideTableLocksSucceedLocks(StripedMap<spinlock_t>& oldlocks) {
- int i = 0;
- const void *oldlock;
- while ((oldlock = oldlocks.getLock(i++))) {
- SideTables().succeedLock(oldlock);
- }
- }
- //
- // The -fobjc-arc flag causes the compiler to issue calls to objc_{retain/release/autorelease/retain_block}
- //
- id objc_retainBlock(id x) {
- return (id)_Block_copy(x);
- }
- //
- // The following SHOULD be called by the compiler directly, but the request hasn't been made yet :-)
- //
- BOOL objc_should_deallocate(id object) {
- return YES;
- }
- id
- objc_retain_autorelease(id obj)
- {
- return objc_autorelease(objc_retain(obj));
- }
- void
- objc_storeStrong(id *location, id obj)
- {
- id prev = *location;
- if (obj == prev) {
- return;
- }
- objc_retain(obj);
- *location = obj;
- objc_release(prev);
- }
- // Update a weak variable.
- // If HaveOld is true, the variable has an existing value
- // that needs to be cleaned up. This value might be nil.
- // If HaveNew is true, there is a new value that needs to be
- // assigned into the variable. This value might be nil.
- // If CrashIfDeallocating is true, the process is halted if newObj is
- // deallocating or newObj's class does not support weak references.
- // If CrashIfDeallocating is false, nil is stored instead.
- enum CrashIfDeallocating {
- DontCrashIfDeallocating = false, DoCrashIfDeallocating = true
- };
- template <HaveOld haveOld, HaveNew haveNew,
- CrashIfDeallocating crashIfDeallocating>
- static id
- storeWeak(id *location, objc_object *newObj)
- {
- assert(haveOld || haveNew);
- if (!haveNew) assert(newObj == nil);
- Class previouslyInitializedClass = nil;
- id oldObj;
- SideTable *oldTable;
- SideTable *newTable;
- // Acquire locks for old and new values.
- // Order by lock address to prevent lock ordering problems.
- // Retry if the old value changes underneath us.
- retry:
- if (haveOld) {
- oldObj = *location;
- oldTable = &SideTables()[oldObj];
- } else {
- oldTable = nil;
- }
- if (haveNew) {
- newTable = &SideTables()[newObj];
- } else {
- newTable = nil;
- }
- SideTable::lockTwo<haveOld, haveNew>(oldTable, newTable);
- if (haveOld && *location != oldObj) {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- goto retry;
- }
- // Prevent a deadlock between the weak reference machinery
- // and the +initialize machinery by ensuring that no
- // weakly-referenced object has an un-+initialized isa.
- if (haveNew && newObj) {
- Class cls = newObj->getIsa();
- if (cls != previouslyInitializedClass &&
- !((objc_class *)cls)->isInitialized())
- {
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- _class_initialize(_class_getNonMetaClass(cls, (id)newObj));
- // If this class is finished with +initialize then we're good.
- // If this class is still running +initialize on this thread
- // (i.e. +initialize called storeWeak on an instance of itself)
- // then we may proceed but it will appear initializing and
- // not yet initialized to the check above.
- // Instead set previouslyInitializedClass to recognize it on retry.
- previouslyInitializedClass = cls;
- goto retry;
- }
- }
- // Clean up old value, if any.
- if (haveOld) {
- weak_unregister_no_lock(&oldTable->weak_table, oldObj, location);
- }
- // Assign new value, if any.
- if (haveNew) {
- newObj = (objc_object *)
- weak_register_no_lock(&newTable->weak_table, (id)newObj, location,
- crashIfDeallocating);
- // weak_register_no_lock returns nil if weak store should be rejected
- // Set is-weakly-referenced bit in refcount table.
- if (newObj && !newObj->isTaggedPointer()) {
- newObj->setWeaklyReferenced_nolock();
- }
- // Do not set *location anywhere else. That would introduce a race.
- *location = (id)newObj;
- }
- else {
- // No new value. The storage is not changed.
- }
-
- SideTable::unlockTwo<haveOld, haveNew>(oldTable, newTable);
- return (id)newObj;
- }
- /**
- * This function stores a new value into a __weak variable. It would
- * be used anywhere a __weak variable is the target of an assignment.
- *
- * @param location The address of the weak pointer itself
- * @param newObj The new object this weak ptr should now point to
- *
- * @return \e newObj
- */
- id
- objc_storeWeak(id *location, id newObj)
- {
- return storeWeak<DoHaveOld, DoHaveNew, DoCrashIfDeallocating>
- (location, (objc_object *)newObj);
- }
- /**
- * This function stores a new value into a __weak variable.
- * If the new object is deallocating or the new object's class
- * does not support weak references, stores nil instead.
- *
- * @param location The address of the weak pointer itself
- * @param newObj The new object this weak ptr should now point to
- *
- * @return The value stored (either the new object or nil)
- */
- id
- objc_storeWeakOrNil(id *location, id newObj)
- {
- return storeWeak<DoHaveOld, DoHaveNew, DontCrashIfDeallocating>
- (location, (objc_object *)newObj);
- }
- /**
- * Initialize a fresh weak pointer to some object location.
- * It would be used for code like:
- *
- * (The nil case)
- * __weak id weakPtr;
- * (The non-nil case)
- * NSObject *o = ...;
- * __weak id weakPtr = o;
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the weak variable. (Concurrent weak clear is safe.)
- *
- * @param location Address of __weak ptr.
- * @param newObj Object ptr.
- */
- id
- objc_initWeak(id *location, id newObj)
- {
- if (!newObj) {
- *location = nil;
- return nil;
- }
- return storeWeak<DontHaveOld, DoHaveNew, DoCrashIfDeallocating>
- (location, (objc_object*)newObj);
- }
- id
- objc_initWeakOrNil(id *location, id newObj)
- {
- if (!newObj) {
- *location = nil;
- return nil;
- }
- return storeWeak<DontHaveOld, DoHaveNew, DontCrashIfDeallocating>
- (location, (objc_object*)newObj);
- }
- /**
- * Destroys the relationship between a weak pointer
- * and the object it is referencing in the internal weak
- * table. If the weak pointer is not referencing anything,
- * there is no need to edit the weak table.
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the weak variable. (Concurrent weak clear is safe.)
- *
- * @param location The weak pointer address.
- */
- void
- objc_destroyWeak(id *location)
- {
- (void)storeWeak<DoHaveOld, DontHaveNew, DontCrashIfDeallocating>
- (location, nil);
- }
- /*
- Once upon a time we eagerly cleared *location if we saw the object
- was deallocating. This confuses code like NSPointerFunctions which
- tries to pre-flight the raw storage and assumes if the storage is
- zero then the weak system is done interfering. That is false: the
- weak system is still going to check and clear the storage later.
- This can cause objc_weak_error complaints and crashes.
- So we now don't touch the storage until deallocation completes.
- */
- id
- objc_loadWeakRetained(id *location)
- {
- id obj;
- id result;
- Class cls;
- SideTable *table;
-
- retry:
- // fixme std::atomic this load
- obj = *location;
- if (!obj) return nil;
- if (obj->isTaggedPointer()) return obj;
-
- table = &SideTables()[obj];
-
- table->lock();
- if (*location != obj) {
- table->unlock();
- goto retry;
- }
-
- result = obj;
- cls = obj->ISA();
- if (! cls->hasCustomRR()) {
- // Fast case. We know +initialize is complete because
- // default-RR can never be set before then.
- assert(cls->isInitialized());
- if (! obj->rootTryRetain()) {
- result = nil;
- }
- }
- else {
- // Slow case. We must check for +initialize and call it outside
- // the lock if necessary in order to avoid deadlocks.
- if (cls->isInitialized() || _thisThreadIsInitializingClass(cls)) {
- BOOL (*tryRetain)(id, SEL) = (BOOL(*)(id, SEL))
- class_getMethodImplementation(cls, SEL_retainWeakReference);
- if ((IMP)tryRetain == _objc_msgForward) {
- result = nil;
- }
- else if (! (*tryRetain)(obj, SEL_retainWeakReference)) {
- result = nil;
- }
- }
- else {
- table->unlock();
- _class_initialize(cls);
- goto retry;
- }
- }
-
- table->unlock();
- return result;
- }
- /**
- * This loads the object referenced by a weak pointer and returns it, after
- * retaining and autoreleasing the object to ensure that it stays alive
- * long enough for the caller to use it. This function would be used
- * anywhere a __weak variable is used in an expression.
- *
- * @param location The weak pointer address
- *
- * @return The object pointed to by \e location, or \c nil if \e location is \c nil.
- */
- id
- objc_loadWeak(id *location)
- {
- if (!*location) return nil;
- return objc_autorelease(objc_loadWeakRetained(location));
- }
- /**
- * This function copies a weak pointer from one location to another,
- * when the destination doesn't already contain a weak pointer. It
- * would be used for code like:
- *
- * __weak id src = ...;
- * __weak id dst = src;
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to the destination variable. (Concurrent weak clear is safe.)
- *
- * @param dst The destination variable.
- * @param src The source variable.
- */
- void
- objc_copyWeak(id *dst, id *src)
- {
- id obj = objc_loadWeakRetained(src);
- objc_initWeak(dst, obj);
- objc_release(obj);
- }
- /**
- * Move a weak pointer from one location to another.
- * Before the move, the destination must be uninitialized.
- * After the move, the source is nil.
- *
- * This function IS NOT thread-safe with respect to concurrent
- * modifications to either weak variable. (Concurrent weak clear is safe.)
- *
- */
- void
- objc_moveWeak(id *dst, id *src)
- {
- objc_copyWeak(dst, src);
- objc_destroyWeak(src);
- *src = nil;
- }
- /***********************************************************************
- Autorelease pool implementation
- A thread's autorelease pool is a stack of pointers.
- Each pointer is either an object to release, or POOL_BOUNDARY which is
- an autorelease pool boundary.
- A pool token is a pointer to the POOL_BOUNDARY for that pool. When
- the pool is popped, every object hotter than the sentinel is released.
- The stack is divided into a doubly-linked list of pages. Pages are added
- and deleted as necessary.
- Thread-local storage points to the hot page, where newly autoreleased
- objects are stored.
- **********************************************************************/
- // Set this to 1 to mprotect() autorelease pool contents
- #define PROTECT_AUTORELEASEPOOL 0
- // Set this to 1 to validate the entire autorelease pool header all the time
- // (i.e. use check() instead of fastcheck() everywhere)
- #define CHECK_AUTORELEASEPOOL (DEBUG)
- BREAKPOINT_FUNCTION(void objc_autoreleaseNoPool(id obj));
- BREAKPOINT_FUNCTION(void objc_autoreleasePoolInvalid(const void *token));
- namespace {
- struct magic_t {
- static const uint32_t M0 = 0xA1A1A1A1;
- # define M1 "AUTORELEASE!"
- static const size_t M1_len = 12;
- uint32_t m[4];
-
- magic_t() {
- assert(M1_len == strlen(M1));
- assert(M1_len == 3 * sizeof(m[1]));
- m[0] = M0;
- strncpy((char *)&m[1], M1, M1_len);
- }
- ~magic_t() {
- m[0] = m[1] = m[2] = m[3] = 0;
- }
- bool check() const {
- return (m[0] == M0 && 0 == strncmp((char *)&m[1], M1, M1_len));
- }
- bool fastcheck() const {
- #if CHECK_AUTORELEASEPOOL
- return check();
- #else
- return (m[0] == M0);
- #endif
- }
- # undef M1
- };
-
- class AutoreleasePoolPage
- {
- // EMPTY_POOL_PLACEHOLDER is stored in TLS when exactly one pool is
- // pushed and it has never contained any objects. This saves memory
- // when the top level (i.e. libdispatch) pushes and pops pools but
- // never uses them.
- # define EMPTY_POOL_PLACEHOLDER ((id*)1)
- # define POOL_BOUNDARY nil
- static pthread_key_t const key = AUTORELEASE_POOL_KEY;
- static uint8_t const SCRIBBLE = 0xA3; // 0xA3A3A3A3 after releasing
- static size_t const SIZE =
- #if PROTECT_AUTORELEASEPOOL
- PAGE_MAX_SIZE; // must be multiple of vm page size
- #else
- PAGE_MAX_SIZE; // size and alignment, power of 2
- #endif
- static size_t const COUNT = SIZE / sizeof(id);
- magic_t const magic;
- id *next;
- pthread_t const thread;
- AutoreleasePoolPage * const parent;
- AutoreleasePoolPage *child;
- uint32_t const depth;
- uint32_t hiwat;
- // SIZE-sizeof(*this) bytes of contents follow
- static void * operator new(size_t size) {
- return malloc_zone_memalign(malloc_default_zone(), SIZE, SIZE);
- }
- static void operator delete(void * p) {
- return free(p);
- }
- inline void protect() {
- #if PROTECT_AUTORELEASEPOOL
- mprotect(this, SIZE, PROT_READ);
- check();
- #endif
- }
- inline void unprotect() {
- #if PROTECT_AUTORELEASEPOOL
- check();
- mprotect(this, SIZE, PROT_READ | PROT_WRITE);
- #endif
- }
- AutoreleasePoolPage(AutoreleasePoolPage *newParent)
- : magic(), next(begin()), thread(pthread_self()),
- parent(newParent), child(nil),
- depth(parent ? 1+parent->depth : 0),
- hiwat(parent ? parent->hiwat : 0)
- {
- if (parent) {
- parent->check();
- assert(!parent->child);
- parent->unprotect();
- parent->child = this;
- parent->protect();
- }
- protect();
- }
- ~AutoreleasePoolPage()
- {
- check();
- unprotect();
- assert(empty());
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- assert(!child);
- }
- void busted(bool die = true)
- {
- magic_t right;
- (die ? _objc_fatal : _objc_inform)
- ("autorelease pool page %p corrupted\n"
- " magic 0x%08x 0x%08x 0x%08x 0x%08x\n"
- " should be 0x%08x 0x%08x 0x%08x 0x%08x\n"
- " pthread %p\n"
- " should be %p\n",
- this,
- magic.m[0], magic.m[1], magic.m[2], magic.m[3],
- right.m[0], right.m[1], right.m[2], right.m[3],
- this->thread, pthread_self());
- }
- void check(bool die = true)
- {
- if (!magic.check() || !pthread_equal(thread, pthread_self())) {
- busted(die);
- }
- }
- void fastcheck(bool die = true)
- {
- #if CHECK_AUTORELEASEPOOL
- check(die);
- #else
- if (! magic.fastcheck()) {
- busted(die);
- }
- #endif
- }
- id * begin() {
- return (id *) ((uint8_t *)this+sizeof(*this));
- }
- id * end() {
- return (id *) ((uint8_t *)this+SIZE);
- }
- bool empty() {
- return next == begin();
- }
- bool full() {
- return next == end();
- }
- bool lessThanHalfFull() {
- return (next - begin() < (end() - begin()) / 2);
- }
- id *add(id obj)
- {
- assert(!full());
- unprotect();
- id *ret = next; // faster than `return next-1` because of aliasing
- *next++ = obj;
- protect();
- return ret;
- }
- void releaseAll()
- {
- releaseUntil(begin());
- }
- void releaseUntil(id *stop)
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
-
- while (this->next != stop) {
- // Restart from hotPage() every time, in case -release
- // autoreleased more objects
- AutoreleasePoolPage *page = hotPage();
- // fixme I think this `while` can be `if`, but I can't prove it
- while (page->empty()) {
- page = page->parent;
- setHotPage(page);
- }
- page->unprotect();
- id obj = *--page->next;
- memset((void*)page->next, SCRIBBLE, sizeof(*page->next));
- page->protect();
- if (obj != POOL_BOUNDARY) {
- objc_release(obj);
- }
- }
- setHotPage(this);
- #if DEBUG
- // we expect any children to be completely empty
- for (AutoreleasePoolPage *page = child; page; page = page->child) {
- assert(page->empty());
- }
- #endif
- }
- void kill()
- {
- // Not recursive: we don't want to blow out the stack
- // if a thread accumulates a stupendous amount of garbage
- AutoreleasePoolPage *page = this;
- while (page->child) page = page->child;
- AutoreleasePoolPage *deathptr;
- do {
- deathptr = page;
- page = page->parent;
- if (page) {
- page->unprotect();
- page->child = nil;
- page->protect();
- }
- delete deathptr;
- } while (deathptr != this);
- }
- static void tls_dealloc(void *p)
- {
- if (p == (void*)EMPTY_POOL_PLACEHOLDER) {
- // No objects or pool pages to clean up here.
- return;
- }
- // reinstate TLS value while we work
- setHotPage((AutoreleasePoolPage *)p);
- if (AutoreleasePoolPage *page = coldPage()) {
- if (!page->empty()) pop(page->begin()); // pop all of the pools
- if (DebugMissingPools || DebugPoolAllocation) {
- // pop() killed the pages already
- } else {
- page->kill(); // free all of the pages
- }
- }
-
- // clear TLS value so TLS destruction doesn't loop
- setHotPage(nil);
- }
- static AutoreleasePoolPage *pageForPointer(const void *p)
- {
- return pageForPointer((uintptr_t)p);
- }
- static AutoreleasePoolPage *pageForPointer(uintptr_t p)
- {
- AutoreleasePoolPage *result;
- uintptr_t offset = p % SIZE;
- assert(offset >= sizeof(AutoreleasePoolPage));
- result = (AutoreleasePoolPage *)(p - offset);
- result->fastcheck();
- return result;
- }
- static inline bool haveEmptyPoolPlaceholder()
- {
- id *tls = (id *)tls_get_direct(key);
- return (tls == EMPTY_POOL_PLACEHOLDER);
- }
- static inline id* setEmptyPoolPlaceholder()
- {
- assert(tls_get_direct(key) == nil);
- tls_set_direct(key, (void *)EMPTY_POOL_PLACEHOLDER);
- return EMPTY_POOL_PLACEHOLDER;
- }
- static inline AutoreleasePoolPage *hotPage()
- {
- AutoreleasePoolPage *result = (AutoreleasePoolPage *)
- tls_get_direct(key);
- if ((id *)result == EMPTY_POOL_PLACEHOLDER) return nil;
- if (result) result->fastcheck();
- return result;
- }
- static inline void setHotPage(AutoreleasePoolPage *page)
- {
- if (page) page->fastcheck();
- tls_set_direct(key, (void *)page);
- }
- static inline AutoreleasePoolPage *coldPage()
- {
- AutoreleasePoolPage *result = hotPage();
- if (result) {
- while (result->parent) {
- result = result->parent;
- result->fastcheck();
- }
- }
- return result;
- }
- static inline id *autoreleaseFast(id obj)
- {
- AutoreleasePoolPage *page = hotPage();
- if (page && !page->full()) {
- return page->add(obj);
- } else if (page) {
- return autoreleaseFullPage(obj, page);
- } else {
- return autoreleaseNoPage(obj);
- }
- }
- static __attribute__((noinline))
- id *autoreleaseFullPage(id obj, AutoreleasePoolPage *page)
- {
- // The hot page is full.
- // Step to the next non-full page, adding a new page if necessary.
- // Then add the object to that page.
- assert(page == hotPage());
- assert(page->full() || DebugPoolAllocation);
- do {
- if (page->child) page = page->child;
- else page = new AutoreleasePoolPage(page);
- } while (page->full());
- setHotPage(page);
- return page->add(obj);
- }
- static __attribute__((noinline))
- id *autoreleaseNoPage(id obj)
- {
- // "No page" could mean no pool has been pushed
- // or an empty placeholder pool has been pushed and has no contents yet
- assert(!hotPage());
- bool pushExtraBoundary = false;
- if (haveEmptyPoolPlaceholder()) {
- // We are pushing a second pool over the empty placeholder pool
- // or pushing the first object into the empty placeholder pool.
- // Before doing that, push a pool boundary on behalf of the pool
- // that is currently represented by the empty placeholder.
- pushExtraBoundary = true;
- }
- else if (obj != POOL_BOUNDARY && DebugMissingPools) {
- // We are pushing an object with no pool in place,
- // and no-pool debugging was requested by environment.
- _objc_inform("MISSING POOLS: (%p) Object %p of class %s "
- "autoreleased with no pool in place - "
- "just leaking - break on "
- "objc_autoreleaseNoPool() to debug",
- pthread_self(), (void*)obj, object_getClassName(obj));
- objc_autoreleaseNoPool(obj);
- return nil;
- }
- else if (obj == POOL_BOUNDARY && !DebugPoolAllocation) {
- // We are pushing a pool with no pool in place,
- // and alloc-per-pool debugging was not requested.
- // Install and return the empty pool placeholder.
- return setEmptyPoolPlaceholder();
- }
- // We are pushing an object or a non-placeholder'd pool.
- // Install the first page.
- AutoreleasePoolPage *page = new AutoreleasePoolPage(nil);
- setHotPage(page);
-
- // Push a boundary on behalf of the previously-placeholder'd pool.
- if (pushExtraBoundary) {
- page->add(POOL_BOUNDARY);
- }
-
- // Push the requested object or pool.
- return page->add(obj);
- }
- static __attribute__((noinline))
- id *autoreleaseNewPage(id obj)
- {
- AutoreleasePoolPage *page = hotPage();
- if (page) return autoreleaseFullPage(obj, page);
- else return autoreleaseNoPage(obj);
- }
- public:
- static inline id autorelease(id obj)
- {
- assert(obj);
- assert(!obj->isTaggedPointer());
- id *dest __unused = autoreleaseFast(obj);
- assert(!dest || dest == EMPTY_POOL_PLACEHOLDER || *dest == obj);
- return obj;
- }
- static inline void *push()
- {
- id *dest;
- if (DebugPoolAllocation) {
- // Each autorelease pool starts on a new pool page.
- dest = autoreleaseNewPage(POOL_BOUNDARY);
- } else {
- dest = autoreleaseFast(POOL_BOUNDARY);
- }
- assert(dest == EMPTY_POOL_PLACEHOLDER || *dest == POOL_BOUNDARY);
- return dest;
- }
- static void badPop(void *token)
- {
- // Error. For bincompat purposes this is not
- // fatal in executables built with old SDKs.
- if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
- // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
- _objc_fatal
- ("Invalid or prematurely-freed autorelease pool %p.", token);
- }
- // Old SDK. Bad pop is warned once.
- static bool complained = false;
- if (!complained) {
- complained = true;
- _objc_inform_now_and_on_crash
- ("Invalid or prematurely-freed autorelease pool %p. "
- "Set a breakpoint on objc_autoreleasePoolInvalid to debug. "
- "Proceeding anyway because the app is old "
- "(SDK version " SDK_FORMAT "). Memory errors are likely.",
- token, FORMAT_SDK(sdkVersion()));
- }
- objc_autoreleasePoolInvalid(token);
- }
-
- static inline void pop(void *token)
- {
- AutoreleasePoolPage *page;
- id *stop;
- if (token == (void*)EMPTY_POOL_PLACEHOLDER) {
- // Popping the top-level placeholder pool.
- if (hotPage()) {
- // Pool was used. Pop its contents normally.
- // Pool pages remain allocated for re-use as usual.
- pop(coldPage()->begin());
- } else {
- // Pool was never used. Clear the placeholder.
- setHotPage(nil);
- }
- return;
- }
- page = pageForPointer(token);
- stop = (id *)token;
- if (*stop != POOL_BOUNDARY) {
- if (stop == page->begin() && !page->parent) {
- // Start of coldest page may correctly not be POOL_BOUNDARY:
- // 1. top-level pool is popped, leaving the cold page in place
- // 2. an object is autoreleased with no pool
- } else {
- // Error. For bincompat purposes this is not
- // fatal in executables built with old SDKs.
- return badPop(token);
- }
- }
- if (PrintPoolHiwat) printHiwat();
- page->releaseUntil(stop);
- // memory: delete empty children
- if (DebugPoolAllocation && page->empty()) {
- // special case: delete everything during page-per-pool debugging
- AutoreleasePoolPage *parent = page->parent;
- page->kill();
- setHotPage(parent);
- } else if (DebugMissingPools && page->empty() && !page->parent) {
- // special case: delete everything for pop(top)
- // when debugging missing autorelease pools
- page->kill();
- setHotPage(nil);
- }
- else if (page->child) {
- // hysteresis: keep one empty child if page is more than half full
- if (page->lessThanHalfFull()) {
- page->child->kill();
- }
- else if (page->child->child) {
- page->child->child->kill();
- }
- }
- }
- static void init()
- {
- int r __unused = pthread_key_init_np(AutoreleasePoolPage::key,
- AutoreleasePoolPage::tls_dealloc);
- assert(r == 0);
- }
- void print()
- {
- _objc_inform("[%p] ................ PAGE %s %s %s", this,
- full() ? "(full)" : "",
- this == hotPage() ? "(hot)" : "",
- this == coldPage() ? "(cold)" : "");
- check(false);
- for (id *p = begin(); p < next; p++) {
- if (*p == POOL_BOUNDARY) {
- _objc_inform("[%p] ################ POOL %p", p, p);
- } else {
- _objc_inform("[%p] %#16lx %s",
- p, (unsigned long)*p, object_getClassName(*p));
- }
- }
- }
- static void printAll()
- {
- _objc_inform("##############");
- _objc_inform("AUTORELEASE POOLS for thread %p", pthread_self());
- AutoreleasePoolPage *page;
- ptrdiff_t objects = 0;
- for (page = coldPage(); page; page = page->child) {
- objects += page->next - page->begin();
- }
- _objc_inform("%llu releases pending.", (unsigned long long)objects);
- if (haveEmptyPoolPlaceholder()) {
- _objc_inform("[%p] ................ PAGE (placeholder)",
- EMPTY_POOL_PLACEHOLDER);
- _objc_inform("[%p] ################ POOL (placeholder)",
- EMPTY_POOL_PLACEHOLDER);
- }
- else {
- for (page = coldPage(); page; page = page->child) {
- page->print();
- }
- }
- _objc_inform("##############");
- }
- static void printHiwat()
- {
- // Check and propagate high water mark
- // Ignore high water marks under 256 to suppress noise.
- AutoreleasePoolPage *p = hotPage();
- uint32_t mark = p->depth*COUNT + (uint32_t)(p->next - p->begin());
- if (mark > p->hiwat && mark > 256) {
- for( ; p; p = p->parent) {
- p->unprotect();
- p->hiwat = mark;
- p->protect();
- }
-
- _objc_inform("POOL HIGHWATER: new high water mark of %u "
- "pending releases for thread %p:",
- mark, pthread_self());
-
- void *stack[128];
- int count = backtrace(stack, sizeof(stack)/sizeof(stack[0]));
- char **sym = backtrace_symbols(stack, count);
- for (int i = 0; i < count; i++) {
- _objc_inform("POOL HIGHWATER: %s", sym[i]);
- }
- free(sym);
- }
- }
- #undef POOL_BOUNDARY
- };
- // anonymous namespace
- };
- /***********************************************************************
- * Slow paths for inline control
- **********************************************************************/
- #if SUPPORT_NONPOINTER_ISA
- NEVER_INLINE id
- objc_object::rootRetain_overflow(bool tryRetain)
- {
- return rootRetain(tryRetain, true);
- }
- NEVER_INLINE bool
- objc_object::rootRelease_underflow(bool performDealloc)
- {
- return rootRelease(performDealloc, true);
- }
- // Slow path of clearDeallocating()
- // for objects with nonpointer isa
- // that were ever weakly referenced
- // or whose retain count ever overflowed to the side table.
- NEVER_INLINE void
- objc_object::clearDeallocating_slow()
- {
- assert(isa.nonpointer && (isa.weakly_referenced || isa.has_sidetable_rc));
- SideTable& table = SideTables()[this];
- table.lock();
- if (isa.weakly_referenced) {
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- if (isa.has_sidetable_rc) {
- table.refcnts.erase(this);
- }
- table.unlock();
- }
- #endif
- __attribute__((noinline,used))
- id
- objc_object::rootAutorelease2()
- {
- assert(!isTaggedPointer());
- return AutoreleasePoolPage::autorelease((id)this);
- }
- BREAKPOINT_FUNCTION(
- void objc_overrelease_during_dealloc_error(void)
- );
- NEVER_INLINE
- bool
- objc_object::overrelease_error()
- {
- _objc_inform_now_and_on_crash("%s object %p overreleased while already deallocating; break on objc_overrelease_during_dealloc_error to debug", object_getClassName((id)this), this);
- objc_overrelease_during_dealloc_error();
- return false; // allow rootRelease() to tail-call this
- }
- /***********************************************************************
- * Retain count operations for side table.
- **********************************************************************/
- #if DEBUG
- // Used to assert that an object is not present in the side table.
- bool
- objc_object::sidetable_present()
- {
- bool result = false;
- SideTable& table = SideTables()[this];
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) result = true;
- if (weak_is_registered_no_lock(&table.weak_table, (id)this)) result = true;
- table.unlock();
- return result;
- }
- #endif
- #if SUPPORT_NONPOINTER_ISA
- void
- objc_object::sidetable_lock()
- {
- SideTable& table = SideTables()[this];
- table.lock();
- }
- void
- objc_object::sidetable_unlock()
- {
- SideTable& table = SideTables()[this];
- table.unlock();
- }
- // Move the entire retain count to the side table,
- // as well as isDeallocating and weaklyReferenced.
- void
- objc_object::sidetable_moveExtraRC_nolock(size_t extra_rc,
- bool isDeallocating,
- bool weaklyReferenced)
- {
- assert(!isa.nonpointer); // should already be changed to raw pointer
- SideTable& table = SideTables()[this];
- size_t& refcntStorage = table.refcnts[this];
- size_t oldRefcnt = refcntStorage;
- // not deallocating - that was in the isa
- assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- uintptr_t carry;
- size_t refcnt = addc(oldRefcnt, extra_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
- if (carry) refcnt = SIDE_TABLE_RC_PINNED;
- if (isDeallocating) refcnt |= SIDE_TABLE_DEALLOCATING;
- if (weaklyReferenced) refcnt |= SIDE_TABLE_WEAKLY_REFERENCED;
- refcntStorage = refcnt;
- }
- // Move some retain counts to the side table from the isa field.
- // Returns true if the object is now pinned.
- bool
- objc_object::sidetable_addExtraRC_nolock(size_t delta_rc)
- {
- assert(isa.nonpointer);
- SideTable& table = SideTables()[this];
- size_t& refcntStorage = table.refcnts[this];
- size_t oldRefcnt = refcntStorage;
- // isa-side bits should not be set here
- assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- if (oldRefcnt & SIDE_TABLE_RC_PINNED) return true;
- uintptr_t carry;
- size_t newRefcnt =
- addc(oldRefcnt, delta_rc << SIDE_TABLE_RC_SHIFT, 0, &carry);
- if (carry) {
- refcntStorage =
- SIDE_TABLE_RC_PINNED | (oldRefcnt & SIDE_TABLE_FLAG_MASK);
- return true;
- }
- else {
- refcntStorage = newRefcnt;
- return false;
- }
- }
- // Move some retain counts from the side table to the isa field.
- // Returns the actual count subtracted, which may be less than the request.
- size_t
- objc_object::sidetable_subExtraRC_nolock(size_t delta_rc)
- {
- assert(isa.nonpointer);
- SideTable& table = SideTables()[this];
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end() || it->second == 0) {
- // Side table retain count is zero. Can't borrow.
- return 0;
- }
- size_t oldRefcnt = it->second;
- // isa-side bits should not be set here
- assert((oldRefcnt & SIDE_TABLE_DEALLOCATING) == 0);
- assert((oldRefcnt & SIDE_TABLE_WEAKLY_REFERENCED) == 0);
- size_t newRefcnt = oldRefcnt - (delta_rc << SIDE_TABLE_RC_SHIFT);
- assert(oldRefcnt > newRefcnt); // shouldn't underflow
- it->second = newRefcnt;
- return delta_rc;
- }
- size_t
- objc_object::sidetable_getExtraRC_nolock()
- {
- assert(isa.nonpointer);
- SideTable& table = SideTables()[this];
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end()) return 0;
- else return it->second >> SIDE_TABLE_RC_SHIFT;
- }
- // SUPPORT_NONPOINTER_ISA
- #endif
- id
- objc_object::sidetable_retain()
- {
- #if SUPPORT_NONPOINTER_ISA
- assert(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
-
- table.lock();
- size_t& refcntStorage = table.refcnts[this];
- if (! (refcntStorage & SIDE_TABLE_RC_PINNED)) {
- refcntStorage += SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- return (id)this;
- }
- bool
- objc_object::sidetable_tryRetain()
- {
- #if SUPPORT_NONPOINTER_ISA
- assert(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- // NO SPINLOCK HERE
- // _objc_rootTryRetain() is called exclusively by _objc_loadWeak(),
- // which already acquired the lock on our behalf.
- // fixme can't do this efficiently with os_lock_handoff_s
- // if (table.slock == 0) {
- // _objc_fatal("Do not call -_tryRetain.");
- // }
- bool result = true;
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end()) {
- table.refcnts[this] = SIDE_TABLE_RC_ONE;
- } else if (it->second & SIDE_TABLE_DEALLOCATING) {
- result = false;
- } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
- it->second += SIDE_TABLE_RC_ONE;
- }
-
- return result;
- }
- uintptr_t
- objc_object::sidetable_retainCount()
- {
- SideTable& table = SideTables()[this];
- size_t refcnt_result = 1;
-
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- // this is valid for SIDE_TABLE_RC_PINNED too
- refcnt_result += it->second >> SIDE_TABLE_RC_SHIFT;
- }
- table.unlock();
- return refcnt_result;
- }
- bool
- objc_object::sidetable_isDeallocating()
- {
- SideTable& table = SideTables()[this];
- // NO SPINLOCK HERE
- // _objc_rootIsDeallocating() is called exclusively by _objc_storeWeak(),
- // which already acquired the lock on our behalf.
- // fixme can't do this efficiently with os_lock_handoff_s
- // if (table.slock == 0) {
- // _objc_fatal("Do not call -_isDeallocating.");
- // }
- RefcountMap::iterator it = table.refcnts.find(this);
- return (it != table.refcnts.end()) && (it->second & SIDE_TABLE_DEALLOCATING);
- }
- bool
- objc_object::sidetable_isWeaklyReferenced()
- {
- bool result = false;
- SideTable& table = SideTables()[this];
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- result = it->second & SIDE_TABLE_WEAKLY_REFERENCED;
- }
- table.unlock();
- return result;
- }
- void
- objc_object::sidetable_setWeaklyReferenced_nolock()
- {
- #if SUPPORT_NONPOINTER_ISA
- assert(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- table.refcnts[this] |= SIDE_TABLE_WEAKLY_REFERENCED;
- }
- // rdar://20206767
- // return uintptr_t instead of bool so that the various raw-isa
- // -release paths all return zero in eax
- uintptr_t
- objc_object::sidetable_release(bool performDealloc)
- {
- #if SUPPORT_NONPOINTER_ISA
- assert(!isa.nonpointer);
- #endif
- SideTable& table = SideTables()[this];
- bool do_dealloc = false;
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it == table.refcnts.end()) {
- do_dealloc = true;
- table.refcnts[this] = SIDE_TABLE_DEALLOCATING;
- } else if (it->second < SIDE_TABLE_DEALLOCATING) {
- // SIDE_TABLE_WEAKLY_REFERENCED may be set. Don't change it.
- do_dealloc = true;
- it->second |= SIDE_TABLE_DEALLOCATING;
- } else if (! (it->second & SIDE_TABLE_RC_PINNED)) {
- it->second -= SIDE_TABLE_RC_ONE;
- }
- table.unlock();
- if (do_dealloc && performDealloc) {
- ((void(*)(objc_object *, SEL))objc_msgSend)(this, SEL_dealloc);
- }
- return do_dealloc;
- }
- void
- objc_object::sidetable_clearDeallocating()
- {
- SideTable& table = SideTables()[this];
- // clear any weak table items
- // clear extra retain count and deallocating bit
- // (fixme warn or abort if extra retain count == 0 ?)
- table.lock();
- RefcountMap::iterator it = table.refcnts.find(this);
- if (it != table.refcnts.end()) {
- if (it->second & SIDE_TABLE_WEAKLY_REFERENCED) {
- weak_clear_no_lock(&table.weak_table, (id)this);
- }
- table.refcnts.erase(it);
- }
- table.unlock();
- }
- /***********************************************************************
- * Optimized retain/release/autorelease entrypoints
- **********************************************************************/
- #if __OBJC2__
- __attribute__((aligned(16)))
- id
- objc_retain(id obj)
- {
- if (!obj) return obj;
- if (obj->isTaggedPointer()) return obj;
- return obj->retain();
- }
- __attribute__((aligned(16)))
- void
- objc_release(id obj)
- {
- if (!obj) return;
- if (obj->isTaggedPointer()) return;
- return obj->release();
- }
- __attribute__((aligned(16)))
- id
- objc_autorelease(id obj)
- {
- if (!obj) return obj;
- if (obj->isTaggedPointer()) return obj;
- return obj->autorelease();
- }
- // OBJC2
- #else
- // not OBJC2
- id objc_retain(id obj) { return [obj retain]; }
- void objc_release(id obj) { [obj release]; }
- id objc_autorelease(id obj) { return [obj autorelease]; }
- #endif
- /***********************************************************************
- * Basic operations for root class implementations a.k.a. _objc_root*()
- **********************************************************************/
- bool
- _objc_rootTryRetain(id obj)
- {
- assert(obj);
- return obj->rootTryRetain();
- }
- bool
- _objc_rootIsDeallocating(id obj)
- {
- assert(obj);
- return obj->rootIsDeallocating();
- }
- void
- objc_clear_deallocating(id obj)
- {
- assert(obj);
- if (obj->isTaggedPointer()) return;
- obj->clearDeallocating();
- }
- bool
- _objc_rootReleaseWasZero(id obj)
- {
- assert(obj);
- return obj->rootReleaseShouldDealloc();
- }
- id
- _objc_rootAutorelease(id obj)
- {
- assert(obj);
- return obj->rootAutorelease();
- }
- uintptr_t
- _objc_rootRetainCount(id obj)
- {
- assert(obj);
- return obj->rootRetainCount();
- }
- id
- _objc_rootRetain(id obj)
- {
- assert(obj);
- return obj->rootRetain();
- }
- void
- _objc_rootRelease(id obj)
- {
- assert(obj);
- obj->rootRelease();
- }
- id
- _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone)
- {
- id obj;
- #if __OBJC2__
- // allocWithZone under __OBJC2__ ignores the zone parameter
- (void)zone;
- obj = class_createInstance(cls, 0);
- #else
- if (!zone) {
- obj = class_createInstance(cls, 0);
- }
- else {
- obj = class_createInstanceFromZone(cls, 0, zone);
- }
- #endif
- if (slowpath(!obj)) obj = callBadAllocHandler(cls);
- return obj;
- }
- // Call [cls alloc] or [cls allocWithZone:nil], with appropriate
- // shortcutting optimizations.
- static ALWAYS_INLINE id
- callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
- {
- if (slowpath(checkNil && !cls)) return nil;
- #if __OBJC2__
- if (fastpath(!cls->ISA()->hasCustomAWZ())) {
- // No alloc/allocWithZone implementation. Go straight to the allocator.
- // fixme store hasCustomAWZ in the non-meta class and
- // add it to canAllocFast's summary
- if (fastpath(cls->canAllocFast())) {
- // No ctors, raw isa, etc. Go straight to the metal.
- bool dtor = cls->hasCxxDtor();
- id obj = (id)calloc(1, cls->bits.fastInstanceSize());
- if (slowpath(!obj)) return callBadAllocHandler(cls);
- obj->initInstanceIsa(cls, dtor);
- return obj;
- }
- else {
- // Has ctor or raw isa or something. Use the slower path.
- id obj = class_createInstance(cls, 0);
- if (slowpath(!obj)) return callBadAllocHandler(cls);
- return obj;
- }
- }
- #endif
- // No shortcuts available.
- if (allocWithZone) return [cls allocWithZone:nil];
- return [cls alloc];
- }
- // Base class implementation of +alloc. cls is not nil.
- // Calls [cls allocWithZone:nil].
- id
- _objc_rootAlloc(Class cls)
- {
- return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
- }
- // Calls [cls alloc].
- id
- objc_alloc(Class cls)
- {
- return callAlloc(cls, true/*checkNil*/, false/*allocWithZone*/);
- }
- // Calls [cls allocWithZone:nil].
- id
- objc_allocWithZone(Class cls)
- {
- return callAlloc(cls, true/*checkNil*/, true/*allocWithZone*/);
- }
- void
- _objc_rootDealloc(id obj)
- {
- assert(obj);
- obj->rootDealloc();
- }
- void
- _objc_rootFinalize(id obj __unused)
- {
- assert(obj);
- _objc_fatal("_objc_rootFinalize called with garbage collection off");
- }
- id
- _objc_rootInit(id obj)
- {
- // In practice, it will be hard to rely on this function.
- // Many classes do not properly chain -init calls.
- return obj;
- }
- malloc_zone_t *
- _objc_rootZone(id obj)
- {
- (void)obj;
- #if __OBJC2__
- // allocWithZone under __OBJC2__ ignores the zone parameter
- return malloc_default_zone();
- #else
- malloc_zone_t *rval = malloc_zone_from_ptr(obj);
- return rval ? rval : malloc_default_zone();
- #endif
- }
- uintptr_t
- _objc_rootHash(id obj)
- {
- return (uintptr_t)obj;
- }
- void *
- objc_autoreleasePoolPush(void)
- {
- return AutoreleasePoolPage::push();
- }
- void
- objc_autoreleasePoolPop(void *ctxt)
- {
- AutoreleasePoolPage::pop(ctxt);
- }
- void *
- _objc_autoreleasePoolPush(void)
- {
- return objc_autoreleasePoolPush();
- }
- void
- _objc_autoreleasePoolPop(void *ctxt)
- {
- objc_autoreleasePoolPop(ctxt);
- }
- void
- _objc_autoreleasePoolPrint(void)
- {
- AutoreleasePoolPage::printAll();
- }
- // Same as objc_release but suitable for tail-calling
- // if you need the value back and don't want to push a frame before this point.
- __attribute__((noinline))
- static id
- objc_releaseAndReturn(id obj)
- {
- objc_release(obj);
- return obj;
- }
- // Same as objc_retainAutorelease but suitable for tail-calling
- // if you don't want to push a frame before this point.
- __attribute__((noinline))
- static id
- objc_retainAutoreleaseAndReturn(id obj)
- {
- return objc_retainAutorelease(obj);
- }
- // Prepare a value at +1 for return through a +0 autoreleasing convention.
- id
- objc_autoreleaseReturnValue(id obj)
- {
- if (prepareOptimizedReturn(ReturnAtPlus1)) return obj;
- return objc_autorelease(obj);
- }
- // Prepare a value at +0 for return through a +0 autoreleasing convention.
- id
- objc_retainAutoreleaseReturnValue(id obj)
- {
- if (prepareOptimizedReturn(ReturnAtPlus0)) return obj;
- // not objc_autoreleaseReturnValue(objc_retain(obj))
- // because we don't need another optimization attempt
- return objc_retainAutoreleaseAndReturn(obj);
- }
- // Accept a value returned through a +0 autoreleasing convention for use at +1.
- id
- objc_retainAutoreleasedReturnValue(id obj)
- {
- if (acceptOptimizedReturn() == ReturnAtPlus1) return obj;
- return objc_retain(obj);
- }
- // Accept a value returned through a +0 autoreleasing convention for use at +0.
- id
- objc_unsafeClaimAutoreleasedReturnValue(id obj)
- {
- if (acceptOptimizedReturn() == ReturnAtPlus0) return obj;
- return objc_releaseAndReturn(obj);
- }
- id
- objc_retainAutorelease(id obj)
- {
- return objc_autorelease(objc_retain(obj));
- }
- void
- _objc_deallocOnMainThreadHelper(void *context)
- {
- id obj = (id)context;
- [obj dealloc];
- }
- // convert objc_objectptr_t to id, callee must take ownership.
- id objc_retainedObject(objc_objectptr_t pointer) { return (id)pointer; }
- // convert objc_objectptr_t to id, without ownership transfer.
- id objc_unretainedObject(objc_objectptr_t pointer) { return (id)pointer; }
- // convert id to objc_objectptr_t, no ownership transfer.
- objc_objectptr_t objc_unretainedPointer(id object) { return object; }
- void arr_init(void)
- {
- AutoreleasePoolPage::init();
- SideTableInit();
- }
- #if SUPPORT_TAGGED_POINTERS
- // Placeholder for old debuggers. When they inspect an
- // extended tagged pointer object they will see this isa.
- @interface __NSUnrecognizedTaggedPointer : NSObject
- @end
- @implementation __NSUnrecognizedTaggedPointer
- +(void) load { }
- -(id) retain { return self; }
- -(oneway void) release { }
- -(id) autorelease { return self; }
- @end
- #endif
- @implementation NSObject
- + (void)load {
- }
- + (void)initialize {
- }
- + (id)self {
- return (id)self;
- }
- - (id)self {
- return self;
- }
- + (Class)class {
- return self;
- }
- - (Class)class {
- return object_getClass(self);
- }
- + (Class)superclass {
- return self->superclass;
- }
- - (Class)superclass {
- return [self class]->superclass;
- }
- + (BOOL)isMemberOfClass:(Class)cls {
- return object_getClass((id)self) == cls;
- }
- - (BOOL)isMemberOfClass:(Class)cls {
- return [self class] == cls;
- }
- + (BOOL)isKindOfClass:(Class)cls {
- for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- - (BOOL)isKindOfClass:(Class)cls {
- for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- + (BOOL)isSubclassOfClass:(Class)cls {
- for (Class tcls = self; tcls; tcls = tcls->superclass) {
- if (tcls == cls) return YES;
- }
- return NO;
- }
- + (BOOL)isAncestorOfObject:(NSObject *)obj {
- for (Class tcls = [obj class]; tcls; tcls = tcls->superclass) {
- if (tcls == self) return YES;
- }
- return NO;
- }
- + (BOOL)instancesRespondToSelector:(SEL)sel {
- if (!sel) return NO;
- return class_respondsToSelector(self, sel);
- }
- + (BOOL)respondsToSelector:(SEL)sel {
- if (!sel) return NO;
- return class_respondsToSelector_inst(object_getClass(self), sel, self);
- }
- - (BOOL)respondsToSelector:(SEL)sel {
- if (!sel) return NO;
- return class_respondsToSelector_inst([self class], sel, self);
- }
- + (BOOL)conformsToProtocol:(Protocol *)protocol {
- if (!protocol) return NO;
- for (Class tcls = self; tcls; tcls = tcls->superclass) {
- if (class_conformsToProtocol(tcls, protocol)) return YES;
- }
- return NO;
- }
- - (BOOL)conformsToProtocol:(Protocol *)protocol {
- if (!protocol) return NO;
- for (Class tcls = [self class]; tcls; tcls = tcls->superclass) {
- if (class_conformsToProtocol(tcls, protocol)) return YES;
- }
- return NO;
- }
- + (NSUInteger)hash {
- return _objc_rootHash(self);
- }
- - (NSUInteger)hash {
- return _objc_rootHash(self);
- }
- + (BOOL)isEqual:(id)obj {
- return obj == (id)self;
- }
- - (BOOL)isEqual:(id)obj {
- return obj == self;
- }
- + (BOOL)isFault {
- return NO;
- }
- - (BOOL)isFault {
- return NO;
- }
- + (BOOL)isProxy {
- return NO;
- }
- - (BOOL)isProxy {
- return NO;
- }
- + (IMP)instanceMethodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return class_getMethodImplementation(self, sel);
- }
- + (IMP)methodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return object_getMethodImplementation((id)self, sel);
- }
- - (IMP)methodForSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return object_getMethodImplementation(self, sel);
- }
- + (BOOL)resolveClassMethod:(SEL)sel {
- return NO;
- }
- + (BOOL)resolveInstanceMethod:(SEL)sel {
- return NO;
- }
- // Replaced by CF (throws an NSException)
- + (void)doesNotRecognizeSelector:(SEL)sel {
- _objc_fatal("+[%s %s]: unrecognized selector sent to instance %p",
- class_getName(self), sel_getName(sel), self);
- }
- // Replaced by CF (throws an NSException)
- - (void)doesNotRecognizeSelector:(SEL)sel {
- _objc_fatal("-[%s %s]: unrecognized selector sent to instance %p",
- object_getClassName(self), sel_getName(sel), self);
- }
- + (id)performSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL))objc_msgSend)((id)self, sel);
- }
- + (id)performSelector:(SEL)sel withObject:(id)obj {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id))objc_msgSend)((id)self, sel, obj);
- }
- + (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id, id))objc_msgSend)((id)self, sel, obj1, obj2);
- }
- - (id)performSelector:(SEL)sel {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL))objc_msgSend)(self, sel);
- }
- - (id)performSelector:(SEL)sel withObject:(id)obj {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id))objc_msgSend)(self, sel, obj);
- }
- - (id)performSelector:(SEL)sel withObject:(id)obj1 withObject:(id)obj2 {
- if (!sel) [self doesNotRecognizeSelector:sel];
- return ((id(*)(id, SEL, id, id))objc_msgSend)(self, sel, obj1, obj2);
- }
- // Replaced by CF (returns an NSMethodSignature)
- + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)sel {
- _objc_fatal("+[NSObject instanceMethodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- // Replaced by CF (returns an NSMethodSignature)
- + (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
- _objc_fatal("+[NSObject methodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- // Replaced by CF (returns an NSMethodSignature)
- - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
- _objc_fatal("-[NSObject methodSignatureForSelector:] "
- "not available without CoreFoundation");
- }
- + (void)forwardInvocation:(NSInvocation *)invocation {
- [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
- }
- - (void)forwardInvocation:(NSInvocation *)invocation {
- [self doesNotRecognizeSelector:(invocation ? [invocation selector] : 0)];
- }
- + (id)forwardingTargetForSelector:(SEL)sel {
- return nil;
- }
- - (id)forwardingTargetForSelector:(SEL)sel {
- return nil;
- }
- // Replaced by CF (returns an NSString)
- + (NSString *)description {
- return nil;
- }
- // Replaced by CF (returns an NSString)
- - (NSString *)description {
- return nil;
- }
- + (NSString *)debugDescription {
- return [self description];
- }
- - (NSString *)debugDescription {
- return [self description];
- }
- + (id)new {
- return [callAlloc(self, false/*checkNil*/) init];
- }
- + (id)retain {
- return (id)self;
- }
- // Replaced by ObjectAlloc
- - (id)retain {
- return ((id)self)->rootRetain();
- }
- + (BOOL)_tryRetain {
- return YES;
- }
- // Replaced by ObjectAlloc
- - (BOOL)_tryRetain {
- return ((id)self)->rootTryRetain();
- }
- + (BOOL)_isDeallocating {
- return NO;
- }
- - (BOOL)_isDeallocating {
- return ((id)self)->rootIsDeallocating();
- }
- + (BOOL)allowsWeakReference {
- return YES;
- }
- + (BOOL)retainWeakReference {
- return YES;
- }
- - (BOOL)allowsWeakReference {
- return ! [self _isDeallocating];
- }
- - (BOOL)retainWeakReference {
- return [self _tryRetain];
- }
- + (oneway void)release {
- }
- // Replaced by ObjectAlloc
- - (oneway void)release {
- ((id)self)->rootRelease();
- }
- + (id)autorelease {
- return (id)self;
- }
- // Replaced by ObjectAlloc
- - (id)autorelease {
- return ((id)self)->rootAutorelease();
- }
- + (NSUInteger)retainCount {
- return ULONG_MAX;
- }
- - (NSUInteger)retainCount {
- return ((id)self)->rootRetainCount();
- }
- + (id)alloc {
- return _objc_rootAlloc(self);
- }
- // Replaced by ObjectAlloc
- + (id)allocWithZone:(struct _NSZone *)zone {
- return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
- }
- // Replaced by CF (throws an NSException)
- + (id)init {
- return (id)self;
- }
- - (id)init {
- return _objc_rootInit(self);
- }
- // Replaced by CF (throws an NSException)
- + (void)dealloc {
- }
- // Replaced by NSZombies
- - (void)dealloc {
- _objc_rootDealloc(self);
- }
- // Previously used by GC. Now a placeholder for binary compatibility.
- - (void) finalize {
- }
- + (struct _NSZone *)zone {
- return (struct _NSZone *)_objc_rootZone(self);
- }
- - (struct _NSZone *)zone {
- return (struct _NSZone *)_objc_rootZone(self);
- }
- + (id)copy {
- return (id)self;
- }
- + (id)copyWithZone:(struct _NSZone *)zone {
- return (id)self;
- }
- - (id)copy {
- return [(id)self copyWithZone:nil];
- }
- + (id)mutableCopy {
- return (id)self;
- }
- + (id)mutableCopyWithZone:(struct _NSZone *)zone {
- return (id)self;
- }
- - (id)mutableCopy {
- return [(id)self mutableCopyWithZone:nil];
- }
- @end
|