123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923 |
- /*
- * Copyright (c) 2005-2009 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@
- */
- /***********************************************************************
- * objc-runtime-new.m
- * Support for new-ABI classes and images.
- **********************************************************************/
- #if __OBJC2__
- #include "DenseMapExtras.h"
- #include "objc-private.h"
- #include "objc-runtime-new.h"
- #include "objc-file.h"
- #include "objc-cache.h"
- #include <Block.h>
- #include <objc/message.h>
- #include <mach/shared_region.h>
- #define newprotocol(p) ((protocol_t *)p)
- static void disableTaggedPointers();
- static void detach_class(Class cls, bool isMeta);
- static void free_class(Class cls);
- static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace);
- static void adjustCustomFlagsForMethodChange(Class cls, method_t *meth);
- static method_t *search_method_list(const method_list_t *mlist, SEL sel);
- static bool method_lists_contains_any(method_list_t **mlists, method_list_t **end,
- SEL sels[], size_t selcount);
- static void flushCaches(Class cls);
- static void initializeTaggedPointerObfuscator(void);
- #if SUPPORT_FIXUP
- static void fixupMessageRef(message_ref_t *msg);
- #endif
- static Class realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock);
- static Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized);
- struct locstamped_category_t {
- category_t *cat;
- struct header_info *hi;
- };
- enum {
- ATTACH_CLASS = 1 << 0,
- ATTACH_METACLASS = 1 << 1,
- ATTACH_CLASS_AND_METACLASS = 1 << 2,
- ATTACH_EXISTING = 1 << 3,
- };
- static void attachCategories(Class cls, const struct locstamped_category_t *cats_list, uint32_t cats_count, int flags);
- /***********************************************************************
- * Lock management
- **********************************************************************/
- mutex_t runtimeLock;
- mutex_t selLock;
- #if CONFIG_USE_CACHE_LOCK
- mutex_t cacheUpdateLock;
- #endif
- recursive_mutex_t loadMethodLock;
- /***********************************************************************
- * Class structure decoding
- **********************************************************************/
- const uintptr_t objc_debug_class_rw_data_mask = FAST_DATA_MASK;
- /***********************************************************************
- * Non-pointer isa decoding
- **********************************************************************/
- #if SUPPORT_INDEXED_ISA
- // Indexed non-pointer isa.
- // These are used to mask the ISA and see if its got an index or not.
- const uintptr_t objc_debug_indexed_isa_magic_mask = ISA_INDEX_MAGIC_MASK;
- const uintptr_t objc_debug_indexed_isa_magic_value = ISA_INDEX_MAGIC_VALUE;
- // die if masks overlap
- STATIC_ASSERT((ISA_INDEX_MASK & ISA_INDEX_MAGIC_MASK) == 0);
- // die if magic is wrong
- STATIC_ASSERT((~ISA_INDEX_MAGIC_MASK & ISA_INDEX_MAGIC_VALUE) == 0);
- // Then these are used to extract the index from the ISA.
- const uintptr_t objc_debug_indexed_isa_index_mask = ISA_INDEX_MASK;
- const uintptr_t objc_debug_indexed_isa_index_shift = ISA_INDEX_SHIFT;
- asm("\n .globl _objc_absolute_indexed_isa_magic_mask" \
- "\n _objc_absolute_indexed_isa_magic_mask = " STRINGIFY2(ISA_INDEX_MAGIC_MASK));
- asm("\n .globl _objc_absolute_indexed_isa_magic_value" \
- "\n _objc_absolute_indexed_isa_magic_value = " STRINGIFY2(ISA_INDEX_MAGIC_VALUE));
- asm("\n .globl _objc_absolute_indexed_isa_index_mask" \
- "\n _objc_absolute_indexed_isa_index_mask = " STRINGIFY2(ISA_INDEX_MASK));
- asm("\n .globl _objc_absolute_indexed_isa_index_shift" \
- "\n _objc_absolute_indexed_isa_index_shift = " STRINGIFY2(ISA_INDEX_SHIFT));
- // And then we can use that index to get the class from this array. Note
- // the size is provided so that clients can ensure the index they get is in
- // bounds and not read off the end of the array.
- // Defined in the objc-msg-*.s files
- // const Class objc_indexed_classes[]
- // When we don't have enough bits to store a class*, we can instead store an
- // index in to this array. Classes are added here when they are realized.
- // Note, an index of 0 is illegal.
- uintptr_t objc_indexed_classes_count = 0;
- // SUPPORT_INDEXED_ISA
- #else
- // not SUPPORT_INDEXED_ISA
- // These variables exist but are all set to 0 so that they are ignored.
- const uintptr_t objc_debug_indexed_isa_magic_mask = 0;
- const uintptr_t objc_debug_indexed_isa_magic_value = 0;
- const uintptr_t objc_debug_indexed_isa_index_mask = 0;
- const uintptr_t objc_debug_indexed_isa_index_shift = 0;
- Class objc_indexed_classes[1] = { nil };
- uintptr_t objc_indexed_classes_count = 0;
- // not SUPPORT_INDEXED_ISA
- #endif
- #if SUPPORT_PACKED_ISA
- // Packed non-pointer isa.
- asm("\n .globl _objc_absolute_packed_isa_class_mask" \
- "\n _objc_absolute_packed_isa_class_mask = " STRINGIFY2(ISA_MASK));
- const uintptr_t objc_debug_isa_class_mask = ISA_MASK;
- const uintptr_t objc_debug_isa_magic_mask = ISA_MAGIC_MASK;
- const uintptr_t objc_debug_isa_magic_value = ISA_MAGIC_VALUE;
- // die if masks overlap
- STATIC_ASSERT((ISA_MASK & ISA_MAGIC_MASK) == 0);
- // die if magic is wrong
- STATIC_ASSERT((~ISA_MAGIC_MASK & ISA_MAGIC_VALUE) == 0);
- // die if virtual address space bound goes up
- STATIC_ASSERT((~ISA_MASK & MACH_VM_MAX_ADDRESS) == 0 ||
- ISA_MASK + sizeof(void*) == MACH_VM_MAX_ADDRESS);
- // SUPPORT_PACKED_ISA
- #else
- // not SUPPORT_PACKED_ISA
- // These variables exist but enforce pointer alignment only.
- const uintptr_t objc_debug_isa_class_mask = (~WORD_MASK);
- const uintptr_t objc_debug_isa_magic_mask = WORD_MASK;
- const uintptr_t objc_debug_isa_magic_value = 0;
- // not SUPPORT_PACKED_ISA
- #endif
- /***********************************************************************
- * Swift marker bits
- **********************************************************************/
- const uintptr_t objc_debug_swift_stable_abi_bit = FAST_IS_SWIFT_STABLE;
- /***********************************************************************
- * allocatedClasses
- * A table of all classes (and metaclasses) which have been allocated
- * with objc_allocateClassPair.
- **********************************************************************/
- namespace objc {
- static ExplicitInitDenseSet<Class> allocatedClasses;
- }
- /***********************************************************************
- * _firstRealizedClass
- * The root of all realized classes
- **********************************************************************/
- static Class _firstRealizedClass = nil;
- /*
- Low two bits of mlist->entsize is used as the fixed-up marker.
- PREOPTIMIZED VERSION:
- Method lists from shared cache are 1 (uniqued) or 3 (uniqued and sorted).
- (Protocol method lists are not sorted because of their extra parallel data)
- Runtime fixed-up method lists get 3.
- UN-PREOPTIMIZED VERSION:
- Method lists from shared cache are 1 (uniqued) or 3 (uniqued and sorted)
- Shared cache's sorting and uniquing are not trusted, but do affect the
- location of the selector name string.
- Runtime fixed-up method lists get 2.
- High two bits of protocol->flags is used as the fixed-up marker.
- PREOPTIMIZED VERSION:
- Protocols from shared cache are 1<<30.
- Runtime fixed-up protocols get 1<<30.
- UN-PREOPTIMIZED VERSION:
- Protocols from shared cache are 1<<30.
- Shared cache's fixups are not trusted.
- Runtime fixed-up protocols get 3<<30.
- */
- static uint32_t fixed_up_method_list = 3;
- static uint32_t uniqued_method_list = 1;
- static uint32_t fixed_up_protocol = PROTOCOL_FIXED_UP_1;
- static uint32_t canonical_protocol = PROTOCOL_IS_CANONICAL;
- void
- disableSharedCacheOptimizations(void)
- {
- fixed_up_method_list = 2;
- // It is safe to set uniqued method lists to 0 as we'll never call it unless
- // the method list was already in need of being fixed up
- uniqued_method_list = 0;
- fixed_up_protocol = PROTOCOL_FIXED_UP_1 | PROTOCOL_FIXED_UP_2;
- // Its safe to just set canonical protocol to 0 as we'll never call
- // clearIsCanonical() unless isCanonical() returned true, which can't happen
- // with a 0 mask
- canonical_protocol = 0;
- }
- bool method_list_t::isUniqued() const {
- return (flags() & uniqued_method_list) != 0;
- }
- bool method_list_t::isFixedUp() const {
- return flags() == fixed_up_method_list;
- }
- void method_list_t::setFixedUp() {
- runtimeLock.assertLocked();
- ASSERT(!isFixedUp());
- entsizeAndFlags = entsize() | fixed_up_method_list;
- }
- bool protocol_t::isFixedUp() const {
- return (flags & PROTOCOL_FIXED_UP_MASK) == fixed_up_protocol;
- }
- void protocol_t::setFixedUp() {
- runtimeLock.assertLocked();
- ASSERT(!isFixedUp());
- flags = (flags & ~PROTOCOL_FIXED_UP_MASK) | fixed_up_protocol;
- }
- bool protocol_t::isCanonical() const {
- return (flags & canonical_protocol) != 0;
- }
- void protocol_t::clearIsCanonical() {
- runtimeLock.assertLocked();
- ASSERT(isCanonical());
- flags = flags & ~canonical_protocol;
- }
- method_list_t **method_array_t::endCategoryMethodLists(Class cls)
- {
- method_list_t **mlists = beginLists();
- method_list_t **mlistsEnd = endLists();
-
- if (mlists == mlistsEnd || !cls->data()->ro->baseMethods())
- {
- // No methods, or no base methods.
- // Everything here is a category method.
- return mlistsEnd;
- }
-
- // Have base methods. Category methods are
- // everything except the last method list.
- return mlistsEnd - 1;
- }
- static const char *sel_cname(SEL sel)
- {
- return (const char *)(void *)sel;
- }
- static size_t protocol_list_size(const protocol_list_t *plist)
- {
- return sizeof(protocol_list_t) + plist->count * sizeof(protocol_t *);
- }
- static void try_free(const void *p)
- {
- if (p && malloc_size(p)) free((void *)p);
- }
- using ClassCopyFixupHandler = void (*)(Class _Nonnull oldClass,
- Class _Nonnull newClass);
- // Normally there's only one handler registered.
- static GlobalSmallVector<ClassCopyFixupHandler, 1> classCopyFixupHandlers;
- void _objc_setClassCopyFixupHandler(void (* _Nonnull newFixupHandler)
- (Class _Nonnull oldClass, Class _Nonnull newClass)) {
- mutex_locker_t lock(runtimeLock);
-
- classCopyFixupHandlers.append(newFixupHandler);
- }
- static Class
- alloc_class_for_subclass(Class supercls, size_t extraBytes)
- {
- if (!supercls || !supercls->isAnySwift()) {
- return _calloc_class(sizeof(objc_class) + extraBytes);
- }
- // Superclass is a Swift class. New subclass must duplicate its extra bits.
- // Allocate the new class, with space for super's prefix and suffix
- // and self's extraBytes.
- swift_class_t *swiftSupercls = (swift_class_t *)supercls;
- size_t superSize = swiftSupercls->classSize;
- void *superBits = swiftSupercls->baseAddress();
- void *bits = malloc(superSize + extraBytes);
- // Copy all of the superclass's data to the new class.
- memcpy(bits, superBits, superSize);
- // Erase the objc data and the Swift description in the new class.
- swift_class_t *swcls = (swift_class_t *)
- ((uint8_t *)bits + swiftSupercls->classAddressOffset);
- bzero(swcls, sizeof(objc_class));
- swcls->description = nil;
- for (auto handler : classCopyFixupHandlers) {
- handler(supercls, (Class)swcls);
- }
- // Mark this class as Swift-enhanced.
- if (supercls->isSwiftStable()) {
- swcls->bits.setIsSwiftStable();
- }
- if (supercls->isSwiftLegacy()) {
- swcls->bits.setIsSwiftLegacy();
- }
-
- return (Class)swcls;
- }
- /***********************************************************************
- * object_getIndexedIvars.
- **********************************************************************/
- void *object_getIndexedIvars(id obj)
- {
- uint8_t *base = (uint8_t *)obj;
- if (!obj) return nil;
- if (obj->isTaggedPointer()) return nil;
- if (!obj->isClass()) return base + obj->ISA()->alignedInstanceSize();
- Class cls = (Class)obj;
- if (!cls->isAnySwift()) return base + sizeof(objc_class);
-
- swift_class_t *swcls = (swift_class_t *)cls;
- return base - swcls->classAddressOffset + word_align(swcls->classSize);
- }
- /***********************************************************************
- * make_ro_writeable
- * Reallocates rw->ro if necessary to make it writeable.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static class_ro_t *make_ro_writeable(class_rw_t *rw)
- {
- runtimeLock.assertLocked();
- if (rw->flags & RW_COPIED_RO) {
- // already writeable, do nothing
- } else {
- rw->ro = rw->ro->duplicate();
- rw->flags |= RW_COPIED_RO;
- }
- return (class_ro_t *)rw->ro;
- }
- /***********************************************************************
- * dataSegmentsContain
- * Returns true if the given address lies within a data segment in any
- * loaded image.
- **********************************************************************/
- NEVER_INLINE
- static bool
- dataSegmentsContain(Class cls)
- {
- uint32_t index;
- if (objc::dataSegmentsRanges.find((uintptr_t)cls, index)) {
- // if the class is realized (hence has a class_rw_t),
- // memorize where we found the range
- if (cls->isRealized()) {
- cls->data()->witness = (uint16_t)index;
- }
- return true;
- }
- return false;
- }
- /***********************************************************************
- * isKnownClass
- * Return true if the class is known to the runtime (located within the
- * shared cache, within the data segment of a loaded image, or has been
- * allocated with obj_allocateClassPair).
- *
- * The result of this operation is cached on the class in a "witness"
- * value that is cheaply checked in the fastpath.
- **********************************************************************/
- ALWAYS_INLINE
- static bool
- isKnownClass(Class cls)
- {
- if (fastpath(objc::dataSegmentsRanges.contains(cls->data()->witness, (uintptr_t)cls))) {
- return true;
- }
- auto &set = objc::allocatedClasses.get();
- return set.find(cls) != set.end() || dataSegmentsContain(cls);
- }
- /***********************************************************************
- * addClassTableEntry
- * Add a class to the table of all classes. If addMeta is true,
- * automatically adds the metaclass of the class as well.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void
- addClassTableEntry(Class cls, bool addMeta = true)
- {
- runtimeLock.assertLocked();
- // This class is allowed to be a known class via the shared cache or via
- // data segments, but it is not allowed to be in the dynamic table already.
- auto &set = objc::allocatedClasses.get();
- ASSERT(set.find(cls) == set.end());
- if (!isKnownClass(cls))
- set.insert(cls);
- if (addMeta)
- addClassTableEntry(cls->ISA(), false);
- }
- /***********************************************************************
- * checkIsKnownClass
- * Checks the given class against the list of all known classes. Dies
- * with a fatal error if the class is not known.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- ALWAYS_INLINE
- static void
- checkIsKnownClass(Class cls)
- {
- if (slowpath(!isKnownClass(cls))) {
- _objc_fatal("Attempt to use unknown class %p.", cls);
- }
- }
- /***********************************************************************
- * classNSObject
- * Returns class NSObject.
- * Locking: none
- **********************************************************************/
- static Class classNSObject(void)
- {
- extern objc_class OBJC_CLASS_$_NSObject;
- return (Class)&OBJC_CLASS_$_NSObject;
- }
- static Class metaclassNSObject(void)
- {
- extern objc_class OBJC_METACLASS_$_NSObject;
- return (Class)&OBJC_METACLASS_$_NSObject;
- }
- /***********************************************************************
- * printReplacements
- * Implementation of PrintReplacedMethods / OBJC_PRINT_REPLACED_METHODS.
- * Warn about methods from cats that override other methods in cats or cls.
- * Assumes no methods from cats have been added to cls yet.
- **********************************************************************/
- __attribute__((cold, noinline))
- static void
- printReplacements(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count)
- {
- uint32_t c;
- bool isMeta = cls->isMetaClass();
- // Newest categories are LAST in cats
- // Later categories override earlier ones.
- for (c = 0; c < cats_count; c++) {
- category_t *cat = cats_list[c].cat;
- method_list_t *mlist = cat->methodsForMeta(isMeta);
- if (!mlist) continue;
- for (const auto& meth : *mlist) {
- SEL s = sel_registerName(sel_cname(meth.name));
- // Search for replaced methods in method lookup order.
- // Complain about the first duplicate only.
- // Look for method in earlier categories
- for (uint32_t c2 = 0; c2 < c; c2++) {
- category_t *cat2 = cats_list[c2].cat;
- const method_list_t *mlist2 = cat2->methodsForMeta(isMeta);
- if (!mlist2) continue;
- for (const auto& meth2 : *mlist2) {
- SEL s2 = sel_registerName(sel_cname(meth2.name));
- if (s == s2) {
- logReplacedMethod(cls->nameForLogging(), s,
- cls->isMetaClass(), cat->name,
- meth2.imp, meth.imp);
- goto complained;
- }
- }
- }
- // Look for method in cls
- for (const auto& meth2 : cls->data()->methods) {
- SEL s2 = sel_registerName(sel_cname(meth2.name));
- if (s == s2) {
- logReplacedMethod(cls->nameForLogging(), s,
- cls->isMetaClass(), cat->name,
- meth2.imp, meth.imp);
- goto complained;
- }
- }
- complained:
- ;
- }
- }
- }
- /***********************************************************************
- * unreasonableClassCount
- * Provides an upper bound for any iteration of classes,
- * to prevent spins when runtime metadata is corrupted.
- **********************************************************************/
- static unsigned unreasonableClassCount()
- {
- runtimeLock.assertLocked();
- int base = NXCountMapTable(gdb_objc_realized_classes) +
- getPreoptimizedClassUnreasonableCount();
- // Provide lots of slack here. Some iterations touch metaclasses too.
- // Some iterations backtrack (like realized class iteration).
- // We don't need an efficient bound, merely one that prevents spins.
- return (base + 1) * 16;
- }
- /***********************************************************************
- * Class enumerators
- * The passed in block returns `false` if subclasses can be skipped
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static inline void
- foreach_realized_class_and_subclass_2(Class top, unsigned &count,
- bool skip_metaclass,
- bool (^code)(Class) __attribute((noescape)))
- {
- Class cls = top;
- runtimeLock.assertLocked();
- ASSERT(top);
- while (1) {
- if (--count == 0) {
- _objc_fatal("Memory corruption in class list.");
- }
- bool skip_subclasses;
- if (skip_metaclass && cls->isMetaClass()) {
- skip_subclasses = true;
- } else {
- skip_subclasses = !code(cls);
- }
- if (!skip_subclasses && cls->data()->firstSubclass) {
- cls = cls->data()->firstSubclass;
- } else {
- while (!cls->data()->nextSiblingClass && cls != top) {
- cls = cls->superclass;
- if (--count == 0) {
- _objc_fatal("Memory corruption in class list.");
- }
- }
- if (cls == top) break;
- cls = cls->data()->nextSiblingClass;
- }
- }
- }
- // Enumerates a class and all of its realized subclasses.
- static void
- foreach_realized_class_and_subclass(Class top, bool (^code)(Class) __attribute((noescape)))
- {
- unsigned int count = unreasonableClassCount();
- foreach_realized_class_and_subclass_2(top, count, false, code);
- }
- // Enumerates all realized classes and metaclasses.
- static void
- foreach_realized_class_and_metaclass(bool (^code)(Class) __attribute((noescape)))
- {
- unsigned int count = unreasonableClassCount();
- for (Class top = _firstRealizedClass;
- top != nil;
- top = top->data()->nextSiblingClass)
- {
- foreach_realized_class_and_subclass_2(top, count, false, code);
- }
- }
- // Enumerates all realized classes (ignoring metaclasses).
- static void
- foreach_realized_class(bool (^code)(Class) __attribute((noescape)))
- {
- unsigned int count = unreasonableClassCount();
- for (Class top = _firstRealizedClass;
- top != nil;
- top = top->data()->nextSiblingClass)
- {
- foreach_realized_class_and_subclass_2(top, count, true, code);
- }
- }
- /***********************************************************************
- * Method Scanners / Optimization tracking
- * Implementation of scanning for various implementations of methods.
- **********************************************************************/
- namespace objc {
- enum SelectorBundle {
- AWZ,
- RR,
- Core,
- };
- namespace scanner {
- // The current state of NSObject swizzling for every scanner
- //
- // It allows for cheap checks of global swizzles, and also lets
- // things like IMP Swizzling before NSObject has been initialized
- // to be remembered, as setInitialized() would miss these.
- //
- // Every pair of bits describes a SelectorBundle.
- // even bits: is NSObject class swizzled for this bundle
- // odd bits: is NSObject meta class swizzled for this bundle
- static uintptr_t NSObjectSwizzledMask;
- static ALWAYS_INLINE uintptr_t
- swizzlingBit(SelectorBundle bundle, bool isMeta)
- {
- return 1UL << (2 * bundle + isMeta);
- }
- static void __attribute__((cold, noinline))
- printCustom(Class cls, SelectorBundle bundle, bool inherited)
- {
- static char const * const SelectorBundleName[] = {
- [AWZ] = "CUSTOM AWZ",
- [RR] = "CUSTOM RR",
- [Core] = "CUSTOM Core",
- };
- _objc_inform("%s: %s%s%s", SelectorBundleName[bundle],
- cls->nameForLogging(),
- cls->isMetaClass() ? " (meta)" : "",
- inherited ? " (inherited)" : "");
- }
- enum class Scope { Instances, Classes, Both };
- template <typename Traits, SelectorBundle Bundle, bool &ShouldPrint, Scope Domain = Scope::Both>
- class Mixin {
- // work around compiler being broken with templates using Class/objc_class,
- // probably some weird confusion with Class being builtin
- ALWAYS_INLINE static objc_class *as_objc_class(Class cls) {
- return (objc_class *)cls;
- }
- static void
- setCustomRecursively(Class cls, bool inherited = false)
- {
- foreach_realized_class_and_subclass(cls, [=](Class c){
- if (c != cls && !as_objc_class(c)->isInitialized()) {
- // Subclass not yet initialized. Wait for setInitialized() to do it
- return false;
- }
- if (Traits::isCustom(c)) {
- return false;
- }
- Traits::setCustom(c);
- if (ShouldPrint) {
- printCustom(cls, Bundle, inherited || c != cls);
- }
- return true;
- });
- }
- static bool
- isNSObjectSwizzled(bool isMeta)
- {
- return NSObjectSwizzledMask & swizzlingBit(Bundle, isMeta);
- }
- static void
- setNSObjectSwizzled(Class NSOClass, bool isMeta)
- {
- NSObjectSwizzledMask |= swizzlingBit(Bundle, isMeta);
- if (as_objc_class(NSOClass)->isInitialized()) {
- setCustomRecursively(NSOClass);
- }
- }
- static void
- scanChangedMethodForUnknownClass(const method_t *meth)
- {
- Class cls;
- cls = classNSObject();
- if (Domain != Scope::Classes && !isNSObjectSwizzled(NO)) {
- for (const auto &meth2: as_objc_class(cls)->data()->methods) {
- if (meth == &meth2) {
- setNSObjectSwizzled(cls, NO);
- break;
- }
- }
- }
- cls = metaclassNSObject();
- if (Domain != Scope::Instances && !isNSObjectSwizzled(YES)) {
- for (const auto &meth2: as_objc_class(cls)->data()->methods) {
- if (meth == &meth2) {
- setNSObjectSwizzled(cls, YES);
- break;
- }
- }
- }
- }
- static void
- scanAddedClassImpl(Class cls, bool isMeta)
- {
- Class NSOClass = (isMeta ? metaclassNSObject() : classNSObject());
- bool setCustom = NO, inherited = NO;
- if (isNSObjectSwizzled(isMeta)) {
- setCustom = YES;
- } else if (cls == NSOClass) {
- // NSObject is default but we need to check categories
- auto &methods = as_objc_class(cls)->data()->methods;
- setCustom = Traits::scanMethodLists(methods.beginCategoryMethodLists(),
- methods.endCategoryMethodLists(cls));
- } else if (!isMeta && !as_objc_class(cls)->superclass) {
- // Custom Root class
- setCustom = YES;
- } else if (Traits::isCustom(as_objc_class(cls)->superclass)) {
- // Superclass is custom, therefore we are too.
- setCustom = YES;
- inherited = YES;
- } else {
- // Not NSObject.
- auto &methods = as_objc_class(cls)->data()->methods;
- setCustom = Traits::scanMethodLists(methods.beginLists(),
- methods.endLists());
- }
- if (slowpath(setCustom)) {
- if (ShouldPrint) printCustom(cls, Bundle, inherited);
- } else {
- Traits::setDefault(cls);
- }
- }
- public:
- // Scan a class that is about to be marked Initialized for particular
- // bundles of selectors, and mark the class and its children
- // accordingly.
- //
- // This also handles inheriting properties from its superclass.
- //
- // Caller: objc_class::setInitialized()
- static void
- scanInitializedClass(Class cls, Class metacls)
- {
- if (Domain != Scope::Classes) {
- scanAddedClassImpl(cls, false);
- }
- if (Domain != Scope::Instances) {
- scanAddedClassImpl(metacls, true);
- }
- }
- // Inherit various properties from the superclass when a class
- // is being added to the graph.
- //
- // Caller: addSubclass()
- static void
- scanAddedSubClass(Class subcls, Class supercls)
- {
- if (slowpath(Traits::isCustom(supercls) && !Traits::isCustom(subcls))) {
- setCustomRecursively(subcls, true);
- }
- }
- // Scan Method lists for selectors that would override things
- // in a Bundle.
- //
- // This is used to detect when categories override problematic selectors
- // are injected in a class after it has been initialized.
- //
- // Caller: prepareMethodLists()
- static void
- scanAddedMethodLists(Class cls, method_list_t **mlists, int count)
- {
- if (slowpath(Traits::isCustom(cls))) {
- return;
- }
- if (slowpath(Traits::scanMethodLists(mlists, mlists + count))) {
- setCustomRecursively(cls);
- }
- }
- // Handle IMP Swizzling (the IMP for an exisiting method being changed).
- //
- // In almost all cases, IMP swizzling does not affect custom bits.
- // Custom search will already find the method whether or not
- // it is swizzled, so it does not transition from non-custom to custom.
- //
- // The only cases where IMP swizzling can affect the custom bits is
- // if the swizzled method is one of the methods that is assumed to be
- // non-custom. These special cases are listed in setInitialized().
- // We look for such cases here.
- //
- // Caller: Swizzling methods via adjustCustomFlagsForMethodChange()
- static void
- scanChangedMethod(Class cls, const method_t *meth)
- {
- if (fastpath(!Traits::isInterestingSelector(meth->name))) {
- return;
- }
- if (cls) {
- bool isMeta = as_objc_class(cls)->isMetaClass();
- if (isMeta && Domain != Scope::Instances) {
- if (cls == metaclassNSObject() && !isNSObjectSwizzled(isMeta)) {
- setNSObjectSwizzled(cls, isMeta);
- }
- }
- if (!isMeta && Domain != Scope::Classes) {
- if (cls == classNSObject() && !isNSObjectSwizzled(isMeta)) {
- setNSObjectSwizzled(cls, isMeta);
- }
- }
- } else {
- // We're called from method_exchangeImplementations, only NSObject
- // class and metaclass may be problematic (exchanging the default
- // builtin IMP of an interesting seleector, is a swizzling that,
- // may flip our scanned property. For other classes, the previous
- // value had already flipped the property).
- //
- // However, as we don't know the class, we need to scan all of
- // NSObject class and metaclass methods (this is SLOW).
- scanChangedMethodForUnknownClass(meth);
- }
- }
- };
- } // namespace scanner
- // AWZ methods: +alloc / +allocWithZone:
- struct AWZScanner : scanner::Mixin<AWZScanner, AWZ, PrintCustomAWZ, scanner::Scope::Classes> {
- static bool isCustom(Class cls) {
- return cls->hasCustomAWZ();
- }
- static void setCustom(Class cls) {
- cls->setHasCustomAWZ();
- }
- static void setDefault(Class cls) {
- cls->setHasDefaultAWZ();
- }
- static bool isInterestingSelector(SEL sel) {
- return sel == @selector(alloc) || sel == @selector(allocWithZone:);
- }
- static bool scanMethodLists(method_list_t **mlists, method_list_t **end) {
- SEL sels[2] = { @selector(alloc), @selector(allocWithZone:), };
- return method_lists_contains_any(mlists, end, sels, 2);
- }
- };
- // Retain/Release methods that are extremely rarely overridden
- //
- // retain/release/autorelease/retainCount/
- // _tryRetain/_isDeallocating/retainWeakReference/allowsWeakReference
- struct RRScanner : scanner::Mixin<RRScanner, RR, PrintCustomRR
- #if !SUPPORT_NONPOINTER_ISA
- , scanner::Scope::Instances
- #endif
- > {
- static bool isCustom(Class cls) {
- return cls->hasCustomRR();
- }
- static void setCustom(Class cls) {
- cls->setHasCustomRR();
- }
- static void setDefault(Class cls) {
- cls->setHasDefaultRR();
- }
- static bool isInterestingSelector(SEL sel) {
- return sel == @selector(retain) ||
- sel == @selector(release) ||
- sel == @selector(autorelease) ||
- sel == @selector(_tryRetain) ||
- sel == @selector(_isDeallocating) ||
- sel == @selector(retainCount) ||
- sel == @selector(allowsWeakReference) ||
- sel == @selector(retainWeakReference);
- }
- static bool scanMethodLists(method_list_t **mlists, method_list_t **end) {
- SEL sels[8] = {
- @selector(retain),
- @selector(release),
- @selector(autorelease),
- @selector(_tryRetain),
- @selector(_isDeallocating),
- @selector(retainCount),
- @selector(allowsWeakReference),
- @selector(retainWeakReference),
- };
- return method_lists_contains_any(mlists, end, sels, 8);
- }
- };
- // Core NSObject methods that are extremely rarely overridden
- //
- // +new, ±class, ±self, ±isKindOfClass:, ±respondsToSelector
- struct CoreScanner : scanner::Mixin<CoreScanner, Core, PrintCustomCore> {
- static bool isCustom(Class cls) {
- return cls->hasCustomCore();
- }
- static void setCustom(Class cls) {
- cls->setHasCustomCore();
- }
- static void setDefault(Class cls) {
- cls->setHasDefaultCore();
- }
- static bool isInterestingSelector(SEL sel) {
- return sel == @selector(new) ||
- sel == @selector(self) ||
- sel == @selector(class) ||
- sel == @selector(isKindOfClass:) ||
- sel == @selector(respondsToSelector:);
- }
- static bool scanMethodLists(method_list_t **mlists, method_list_t **end) {
- SEL sels[5] = {
- @selector(new),
- @selector(self),
- @selector(class),
- @selector(isKindOfClass:),
- @selector(respondsToSelector:)
- };
- return method_lists_contains_any(mlists, end, sels, 5);
- }
- };
- class category_list : nocopy_t {
- union {
- locstamped_category_t lc;
- struct {
- locstamped_category_t *array;
- // this aliases with locstamped_category_t::hi
- // which is an aliased pointer
- uint32_t is_array : 1;
- uint32_t count : 31;
- uint32_t size : 32;
- };
- } _u;
- public:
- category_list() : _u{{nullptr, nullptr}} { }
- category_list(locstamped_category_t lc) : _u{{lc}} { }
- category_list(category_list &&other) : category_list() {
- std::swap(_u, other._u);
- }
- ~category_list()
- {
- if (_u.is_array) {
- free(_u.array);
- }
- }
- uint32_t count() const
- {
- if (_u.is_array) return _u.count;
- return _u.lc.cat ? 1 : 0;
- }
- uint32_t arrayByteSize(uint32_t size) const
- {
- return sizeof(locstamped_category_t) * size;
- }
- const locstamped_category_t *array() const
- {
- return _u.is_array ? _u.array : &_u.lc;
- }
- void append(locstamped_category_t lc)
- {
- if (_u.is_array) {
- if (_u.count == _u.size) {
- // Have a typical malloc growth:
- // - size <= 8: grow by 2
- // - size <= 16: grow by 4
- // - size <= 32: grow by 8
- // ... etc
- _u.size += _u.size < 8 ? 2 : 1 << (fls(_u.size) - 2);
- _u.array = (locstamped_category_t *)reallocf(_u.array, arrayByteSize(_u.size));
- }
- _u.array[_u.count++] = lc;
- } else if (_u.lc.cat == NULL) {
- _u.lc = lc;
- } else {
- locstamped_category_t *arr = (locstamped_category_t *)malloc(arrayByteSize(2));
- arr[0] = _u.lc;
- arr[1] = lc;
- _u.array = arr;
- _u.is_array = true;
- _u.count = 2;
- _u.size = 2;
- }
- }
- void erase(category_t *cat)
- {
- if (_u.is_array) {
- for (int i = 0; i < _u.count; i++) {
- if (_u.array[i].cat == cat) {
- // shift entries to preserve list order
- memmove(&_u.array[i], &_u.array[i+1], arrayByteSize(_u.count - i - 1));
- return;
- }
- }
- } else if (_u.lc.cat == cat) {
- _u.lc.cat = NULL;
- _u.lc.hi = NULL;
- }
- }
- };
- class UnattachedCategories : public ExplicitInitDenseMap<Class, category_list>
- {
- public:
- void addForClass(locstamped_category_t lc, Class cls)
- {
- runtimeLock.assertLocked();
- if (slowpath(PrintConnecting)) {
- _objc_inform("CLASS: found category %c%s(%s)",
- cls->isMetaClass() ? '+' : '-',
- cls->nameForLogging(), lc.cat->name);
- }
- auto result = get().try_emplace(cls, lc);
- if (!result.second) {
- result.first->second.append(lc);
- }
- }
- void attachToClass(Class cls, Class previously, int flags)
- {
- runtimeLock.assertLocked();
- ASSERT((flags & ATTACH_CLASS) ||
- (flags & ATTACH_METACLASS) ||
- (flags & ATTACH_CLASS_AND_METACLASS));
- auto &map = get();
- auto it = map.find(previously);
- if (it != map.end()) {
- category_list &list = it->second;
- if (flags & ATTACH_CLASS_AND_METACLASS) {
- int otherFlags = flags & ~ATTACH_CLASS_AND_METACLASS;
- attachCategories(cls, list.array(), list.count(), otherFlags | ATTACH_CLASS);
- attachCategories(cls->ISA(), list.array(), list.count(), otherFlags | ATTACH_METACLASS);
- } else {
- attachCategories(cls, list.array(), list.count(), flags);
- }
- map.erase(it);
- }
- }
- void eraseCategoryForClass(category_t *cat, Class cls)
- {
- runtimeLock.assertLocked();
- auto &map = get();
- auto it = map.find(cls);
- if (it != map.end()) {
- category_list &list = it->second;
- list.erase(cat);
- if (list.count() == 0) {
- map.erase(it);
- }
- }
- }
- void eraseClass(Class cls)
- {
- runtimeLock.assertLocked();
- get().erase(cls);
- }
- };
- static UnattachedCategories unattachedCategories;
- } // namespace objc
- static bool isBundleClass(Class cls)
- {
- return cls->data()->ro->flags & RO_FROM_BUNDLE;
- }
- static void
- fixupMethodList(method_list_t *mlist, bool bundleCopy, bool sort)
- {
- runtimeLock.assertLocked();
- ASSERT(!mlist->isFixedUp());
- // fixme lock less in attachMethodLists ?
- // dyld3 may have already uniqued, but not sorted, the list
- if (!mlist->isUniqued()) {
- mutex_locker_t lock(selLock);
-
- // Unique selectors in list.
- for (auto& meth : *mlist) {
- const char *name = sel_cname(meth.name);
- meth.name = sel_registerNameNoLock(name, bundleCopy);
- }
- }
- // Sort by selector address.
- if (sort) {
- method_t::SortBySELAddress sorter;
- std::stable_sort(mlist->begin(), mlist->end(), sorter);
- }
-
- // Mark method list as uniqued and sorted
- mlist->setFixedUp();
- }
- static void
- prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount,
- bool baseMethods, bool methodsFromBundle)
- {
- runtimeLock.assertLocked();
- if (addedCount == 0) return;
- // There exist RR/AWZ/Core special cases for some class's base methods.
- // But this code should never need to scan base methods for RR/AWZ/Core:
- // default RR/AWZ/Core cannot be set before setInitialized().
- // Therefore we need not handle any special cases here.
- if (baseMethods) {
- ASSERT(cls->hasCustomAWZ() && cls->hasCustomRR() && cls->hasCustomCore());
- }
- // Add method lists to array.
- // Reallocate un-fixed method lists.
- // The new methods are PREPENDED to the method list array.
- for (int i = 0; i < addedCount; i++) {
- method_list_t *mlist = addedLists[i];
- ASSERT(mlist);
- // Fixup selectors if necessary
- if (!mlist->isFixedUp()) {
- fixupMethodList(mlist, methodsFromBundle, true/*sort*/);
- }
- }
- // If the class is initialized, then scan for method implementations
- // tracked by the class's flags. If it's not initialized yet,
- // then objc_class::setInitialized() will take care of it.
- if (cls->isInitialized()) {
- objc::AWZScanner::scanAddedMethodLists(cls, addedLists, addedCount);
- objc::RRScanner::scanAddedMethodLists(cls, addedLists, addedCount);
- objc::CoreScanner::scanAddedMethodLists(cls, addedLists, addedCount);
- }
- }
- // Attach method lists and properties and protocols from categories to a class.
- // Assumes the categories in cats are all loaded and sorted by load order,
- // oldest categories first.
- static void
- attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count,
- int flags)
- {
- if (slowpath(PrintReplacedMethods)) {
- printReplacements(cls, cats_list, cats_count);
- }
- if (slowpath(PrintConnecting)) {
- _objc_inform("CLASS: attaching %d categories to%s class '%s'%s",
- cats_count, (flags & ATTACH_EXISTING) ? " existing" : "",
- cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : "");
- }
- /*
- * Only a few classes have more than 64 categories during launch.
- * This uses a little stack, and avoids malloc.
- *
- * Categories must be added in the proper order, which is back
- * to front. To do that with the chunking, we iterate cats_list
- * from front to back, build up the local buffers backwards,
- * and call attachLists on the chunks. attachLists prepends the
- * lists, so the final result is in the expected order.
- */
- constexpr uint32_t ATTACH_BUFSIZ = 64;
- method_list_t *mlists[ATTACH_BUFSIZ];
- property_list_t *proplists[ATTACH_BUFSIZ];
- protocol_list_t *protolists[ATTACH_BUFSIZ];
- uint32_t mcount = 0;
- uint32_t propcount = 0;
- uint32_t protocount = 0;
- bool fromBundle = NO;
- bool isMeta = (flags & ATTACH_METACLASS);
- auto rw = cls->data();
- for (uint32_t i = 0; i < cats_count; i++) {
- auto& entry = cats_list[i];
- method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
- if (mlist) {
- if (mcount == ATTACH_BUFSIZ) {
- prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
- rw->methods.attachLists(mlists, mcount);
- mcount = 0;
- }
- mlists[ATTACH_BUFSIZ - ++mcount] = mlist;
- fromBundle |= entry.hi->isBundle();
- }
- property_list_t *proplist =
- entry.cat->propertiesForMeta(isMeta, entry.hi);
- if (proplist) {
- if (propcount == ATTACH_BUFSIZ) {
- rw->properties.attachLists(proplists, propcount);
- propcount = 0;
- }
- proplists[ATTACH_BUFSIZ - ++propcount] = proplist;
- }
- protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta);
- if (protolist) {
- if (protocount == ATTACH_BUFSIZ) {
- rw->protocols.attachLists(protolists, protocount);
- protocount = 0;
- }
- protolists[ATTACH_BUFSIZ - ++protocount] = protolist;
- }
- }
- if (mcount > 0) {
- prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle);
- rw->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount);
- if (flags & ATTACH_EXISTING) flushCaches(cls);
- }
- rw->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount);
- rw->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount);
- }
- /***********************************************************************
- * methodizeClass
- * Fixes up cls's method list, protocol list, and property list.
- * Attaches any outstanding categories.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void methodizeClass(Class cls, Class previously)
- {
- runtimeLock.assertLocked();
- bool isMeta = cls->isMetaClass();
- auto rw = cls->data();
- auto ro = rw->ro;
- // Methodizing for the first time
- if (PrintConnecting) {
- _objc_inform("CLASS: methodizing class '%s' %s",
- cls->nameForLogging(), isMeta ? "(meta)" : "");
- }
- // Install methods and properties that the class implements itself.
- method_list_t *list = ro->baseMethods();
- if (list) {
- prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls));
- rw->methods.attachLists(&list, 1);
- }
- property_list_t *proplist = ro->baseProperties;
- if (proplist) {
- rw->properties.attachLists(&proplist, 1);
- }
- protocol_list_t *protolist = ro->baseProtocols;
- if (protolist) {
- rw->protocols.attachLists(&protolist, 1);
- }
- // Root classes get bonus method implementations if they don't have
- // them already. These apply before category replacements.
- if (cls->isRootMetaclass()) {
- // root metaclass
- addMethod(cls, @selector(initialize), (IMP)&objc_noop_imp, "", NO);
- }
- // Attach categories.
- if (previously) {
- if (isMeta) {
- objc::unattachedCategories.attachToClass(cls, previously,
- ATTACH_METACLASS);
- } else {
- // When a class relocates, categories with class methods
- // may be registered on the class itself rather than on
- // the metaclass. Tell attachToClass to look for those.
- objc::unattachedCategories.attachToClass(cls, previously,
- ATTACH_CLASS_AND_METACLASS);
- }
- }
- objc::unattachedCategories.attachToClass(cls, cls,
- isMeta ? ATTACH_METACLASS : ATTACH_CLASS);
- #if DEBUG
- // Debug: sanity-check all SELs; log method list contents
- for (const auto& meth : rw->methods) {
- if (PrintConnecting) {
- _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-',
- cls->nameForLogging(), sel_getName(meth.name));
- }
- ASSERT(sel_registerName(sel_getName(meth.name)) == meth.name);
- }
- #endif
- }
- /***********************************************************************
- * nonMetaClasses
- * Returns the secondary metaclass => class map
- * Used for some cases of +initialize and +resolveClassMethod:.
- * This map does not contain all class and metaclass pairs. It only
- * contains metaclasses whose classes would be in the runtime-allocated
- * named-class table, but are not because some other class with the same name
- * is in that table.
- * Classes with no duplicates are not included.
- * Classes in the preoptimized named-class table are not included.
- * Classes whose duplicates are in the preoptimized table are not included.
- * Most code should use getMaybeUnrealizedNonMetaClass()
- * instead of reading this table.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static NXMapTable *nonmeta_class_map = nil;
- static NXMapTable *nonMetaClasses(void)
- {
- runtimeLock.assertLocked();
- if (nonmeta_class_map) return nonmeta_class_map;
- // nonmeta_class_map is typically small
- INIT_ONCE_PTR(nonmeta_class_map,
- NXCreateMapTable(NXPtrValueMapPrototype, 32),
- NXFreeMapTable(v));
- return nonmeta_class_map;
- }
- /***********************************************************************
- * addNonMetaClass
- * Adds metacls => cls to the secondary metaclass map
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void addNonMetaClass(Class cls)
- {
- runtimeLock.assertLocked();
- void *old;
- old = NXMapInsert(nonMetaClasses(), cls->ISA(), cls);
- ASSERT(!cls->isMetaClassMaybeUnrealized());
- ASSERT(cls->ISA()->isMetaClassMaybeUnrealized());
- ASSERT(!old);
- }
- static void removeNonMetaClass(Class cls)
- {
- runtimeLock.assertLocked();
- NXMapRemove(nonMetaClasses(), cls->ISA());
- }
- static bool scanMangledField(const char *&string, const char *end,
- const char *&field, int& length)
- {
- // Leading zero not allowed.
- if (*string == '0') return false;
- length = 0;
- field = string;
- while (field < end) {
- char c = *field;
- if (!isdigit(c)) break;
- field++;
- if (__builtin_smul_overflow(length, 10, &length)) return false;
- if (__builtin_sadd_overflow(length, c - '0', &length)) return false;
- }
- string = field + length;
- return length > 0 && string <= end;
- }
- /***********************************************************************
- * copySwiftV1DemangledName
- * Returns the pretty form of the given Swift-v1-mangled class or protocol name.
- * Returns nil if the string doesn't look like a mangled Swift v1 name.
- * The result must be freed with free().
- **********************************************************************/
- static char *copySwiftV1DemangledName(const char *string, bool isProtocol = false)
- {
- if (!string) return nil;
- // Swift mangling prefix.
- if (strncmp(string, isProtocol ? "_TtP" : "_TtC", 4) != 0) return nil;
- string += 4;
- const char *end = string + strlen(string);
- // Module name.
- const char *prefix;
- int prefixLength;
- if (string[0] == 's') {
- // "s" is the Swift module.
- prefix = "Swift";
- prefixLength = 5;
- string += 1;
- } else {
- if (! scanMangledField(string, end, prefix, prefixLength)) return nil;
- }
- // Class or protocol name.
- const char *suffix;
- int suffixLength;
- if (! scanMangledField(string, end, suffix, suffixLength)) return nil;
- if (isProtocol) {
- // Remainder must be "_".
- if (strcmp(string, "_") != 0) return nil;
- } else {
- // Remainder must be empty.
- if (string != end) return nil;
- }
- char *result;
- asprintf(&result, "%.*s.%.*s", prefixLength,prefix, suffixLength,suffix);
- return result;
- }
- /***********************************************************************
- * copySwiftV1MangledName
- * Returns the Swift 1.0 mangled form of the given class or protocol name.
- * Returns nil if the string doesn't look like an unmangled Swift name.
- * The result must be freed with free().
- **********************************************************************/
- static char *copySwiftV1MangledName(const char *string, bool isProtocol = false)
- {
- if (!string) return nil;
- size_t dotCount = 0;
- size_t dotIndex;
- const char *s;
- for (s = string; *s; s++) {
- if (*s == '.') {
- dotCount++;
- dotIndex = s - string;
- }
- }
- size_t stringLength = s - string;
- if (dotCount != 1 || dotIndex == 0 || dotIndex >= stringLength-1) {
- return nil;
- }
-
- const char *prefix = string;
- size_t prefixLength = dotIndex;
- const char *suffix = string + dotIndex + 1;
- size_t suffixLength = stringLength - (dotIndex + 1);
-
- char *name;
- if (prefixLength == 5 && memcmp(prefix, "Swift", 5) == 0) {
- asprintf(&name, "_Tt%cs%zu%.*s%s",
- isProtocol ? 'P' : 'C',
- suffixLength, (int)suffixLength, suffix,
- isProtocol ? "_" : "");
- } else {
- asprintf(&name, "_Tt%c%zu%.*s%zu%.*s%s",
- isProtocol ? 'P' : 'C',
- prefixLength, (int)prefixLength, prefix,
- suffixLength, (int)suffixLength, suffix,
- isProtocol ? "_" : "");
- }
- return name;
- }
- /***********************************************************************
- * getClassExceptSomeSwift
- * Looks up a class by name. The class MIGHT NOT be realized.
- * Demangled Swift names are recognized.
- * Classes known to the Swift runtime but not yet used are NOT recognized.
- * (such as subclasses of un-instantiated generics)
- * Use look_up_class() to find them as well.
- * Locking: runtimeLock must be read- or write-locked by the caller.
- **********************************************************************/
- // This is a misnomer: gdb_objc_realized_classes is actually a list of
- // named classes not in the dyld shared cache, whether realized or not.
- NXMapTable *gdb_objc_realized_classes; // exported for debuggers in objc-gdb.h
- uintptr_t objc_debug_realized_class_generation_count;
- static Class getClass_impl(const char *name)
- {
- runtimeLock.assertLocked();
- // allocated in _read_images
- ASSERT(gdb_objc_realized_classes);
- // Try runtime-allocated table
- Class result = (Class)NXMapGet(gdb_objc_realized_classes, name);
- if (result) return result;
- // Try table from dyld shared cache.
- // Note we do this last to handle the case where we dlopen'ed a shared cache
- // dylib with duplicates of classes already present in the main executable.
- // In that case, we put the class from the main executable in
- // gdb_objc_realized_classes and want to check that before considering any
- // newly loaded shared cache binaries.
- return getPreoptimizedClass(name);
- }
- static Class getClassExceptSomeSwift(const char *name)
- {
- runtimeLock.assertLocked();
- // Try name as-is
- Class result = getClass_impl(name);
- if (result) return result;
- // Try Swift-mangled equivalent of the given name.
- if (char *swName = copySwiftV1MangledName(name)) {
- result = getClass_impl(swName);
- free(swName);
- return result;
- }
- return nil;
- }
- /***********************************************************************
- * addNamedClass
- * Adds name => cls to the named non-meta class map.
- * Warns about duplicate class names and keeps the old mapping.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void addNamedClass(Class cls, const char *name, Class replacing = nil)
- {
- runtimeLock.assertLocked();
- Class old;
- if ((old = getClassExceptSomeSwift(name)) && old != replacing) {
- inform_duplicate(name, old, cls);
- // getMaybeUnrealizedNonMetaClass uses name lookups.
- // Classes not found by name lookup must be in the
- // secondary meta->nonmeta table.
- addNonMetaClass(cls);
- } else {
- NXMapInsert(gdb_objc_realized_classes, name, cls);
- }
- ASSERT(!(cls->data()->flags & RO_META));
- // wrong: constructed classes are already realized when they get here
- // ASSERT(!cls->isRealized());
- }
- /***********************************************************************
- * removeNamedClass
- * Removes cls from the name => cls map.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void removeNamedClass(Class cls, const char *name)
- {
- runtimeLock.assertLocked();
- ASSERT(!(cls->data()->flags & RO_META));
- if (cls == NXMapGet(gdb_objc_realized_classes, name)) {
- NXMapRemove(gdb_objc_realized_classes, name);
- } else {
- // cls has a name collision with another class - don't remove the other
- // but do remove cls from the secondary metaclass->class map.
- removeNonMetaClass(cls);
- }
- }
- /***********************************************************************
- * futureNamedClasses
- * Returns the classname => future class map for unrealized future classes.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static NXMapTable *future_named_class_map = nil;
- static NXMapTable *futureNamedClasses()
- {
- runtimeLock.assertLocked();
-
- if (future_named_class_map) return future_named_class_map;
- // future_named_class_map is big enough for CF's classes and a few others
- future_named_class_map =
- NXCreateMapTable(NXStrValueMapPrototype, 32);
- return future_named_class_map;
- }
- static bool haveFutureNamedClasses() {
- return future_named_class_map && NXCountMapTable(future_named_class_map);
- }
- /***********************************************************************
- * addFutureNamedClass
- * Installs cls as the class structure to use for the named class if it appears.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void addFutureNamedClass(const char *name, Class cls)
- {
- void *old;
- runtimeLock.assertLocked();
- if (PrintFuture) {
- _objc_inform("FUTURE: reserving %p for %s", (void*)cls, name);
- }
- class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
- class_ro_t *ro = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
- ro->name = strdupIfMutable(name);
- rw->ro = ro;
- cls->setData(rw);
- cls->data()->flags = RO_FUTURE;
- old = NXMapKeyCopyingInsert(futureNamedClasses(), name, cls);
- ASSERT(!old);
- }
- /***********************************************************************
- * popFutureNamedClass
- * Removes the named class from the unrealized future class list,
- * because it has been realized.
- * Returns nil if the name is not used by a future class.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static Class popFutureNamedClass(const char *name)
- {
- runtimeLock.assertLocked();
- Class cls = nil;
- if (future_named_class_map) {
- cls = (Class)NXMapKeyFreeingRemove(future_named_class_map, name);
- if (cls && NXCountMapTable(future_named_class_map) == 0) {
- NXFreeMapTable(future_named_class_map);
- future_named_class_map = nil;
- }
- }
- return cls;
- }
- /***********************************************************************
- * remappedClasses
- * Returns the oldClass => newClass map for realized future classes.
- * Returns the oldClass => nil map for ignored weak-linked classes.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static objc::DenseMap<Class, Class> *remappedClasses(bool create)
- {
- static objc::LazyInitDenseMap<Class, Class> remapped_class_map;
- runtimeLock.assertLocked();
- // start big enough to hold CF's classes and a few others
- return remapped_class_map.get(create, 32);
- }
- /***********************************************************************
- * noClassesRemapped
- * Returns YES if no classes have been remapped
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static bool noClassesRemapped(void)
- {
- runtimeLock.assertLocked();
- bool result = (remappedClasses(NO) == nil);
- #if DEBUG
- // Catch construction of an empty table, which defeats optimization.
- auto *map = remappedClasses(NO);
- if (map) ASSERT(map->size() > 0);
- #endif
- return result;
- }
- /***********************************************************************
- * addRemappedClass
- * newcls is a realized future class, replacing oldcls.
- * OR newcls is nil, replacing ignored weak-linked class oldcls.
- * Locking: runtimeLock must be write-locked by the caller
- **********************************************************************/
- static void addRemappedClass(Class oldcls, Class newcls)
- {
- runtimeLock.assertLocked();
- if (PrintFuture) {
- _objc_inform("FUTURE: using %p instead of %p for %s",
- (void*)newcls, (void*)oldcls, oldcls->nameForLogging());
- }
- auto result = remappedClasses(YES)->insert({ oldcls, newcls });
- #if DEBUG
- if (!std::get<1>(result)) {
- // An existing mapping was overwritten. This is not allowed
- // unless it was to nil.
- auto iterator = std::get<0>(result);
- auto value = std::get<1>(*iterator);
- ASSERT(value == nil);
- }
- #else
- (void)result;
- #endif
- }
- /***********************************************************************
- * remapClass
- * Returns the live class pointer for cls, which may be pointing to
- * a class struct that has been reallocated.
- * Returns nil if cls is ignored because of weak linking.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static Class remapClass(Class cls)
- {
- runtimeLock.assertLocked();
- if (!cls) return nil;
- auto *map = remappedClasses(NO);
- if (!map)
- return cls;
-
- auto iterator = map->find(cls);
- if (iterator == map->end())
- return cls;
- return std::get<1>(*iterator);
- }
- static Class remapClass(classref_t cls)
- {
- return remapClass((Class)cls);
- }
- Class _class_remap(Class cls)
- {
- mutex_locker_t lock(runtimeLock);
- return remapClass(cls);
- }
- /***********************************************************************
- * remapClassRef
- * Fix up a class ref, in case the class referenced has been reallocated
- * or is an ignored weak-linked class.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static void remapClassRef(Class *clsref)
- {
- runtimeLock.assertLocked();
- Class newcls = remapClass(*clsref);
- if (*clsref != newcls) *clsref = newcls;
- }
- _Nullable Class
- objc_loadClassref(_Nullable Class * _Nonnull clsref)
- {
- auto *atomicClsref = explicit_atomic<uintptr_t>::from_pointer((uintptr_t *)clsref);
-
- uintptr_t cls = atomicClsref->load(std::memory_order_relaxed);
- if (fastpath((cls & 1) == 0))
- return (Class)cls;
-
- auto stub = (stub_class_t *)(cls & ~1ULL);
- Class initialized = stub->initializer((Class)stub, nil);
- atomicClsref->store((uintptr_t)initialized, std::memory_order_relaxed);
- return initialized;
- }
- /***********************************************************************
- * getMaybeUnrealizedNonMetaClass
- * Return the ordinary class for this class or metaclass.
- * `inst` is an instance of `cls` or a subclass thereof, or nil.
- * Non-nil inst is faster.
- * The result may be unrealized.
- * Used by +initialize.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static Class getMaybeUnrealizedNonMetaClass(Class metacls, id inst)
- {
- static int total, named, secondary, sharedcache, dyld3;
- runtimeLock.assertLocked();
- ASSERT(metacls->isRealized());
- total++;
- // return cls itself if it's already a non-meta class
- if (!metacls->isMetaClass()) return metacls;
- // metacls really is a metaclass
- // which means inst (if any) is a class
- // special case for root metaclass
- // where inst == inst->ISA() == metacls is possible
- if (metacls->ISA() == metacls) {
- Class cls = metacls->superclass;
- ASSERT(cls->isRealized());
- ASSERT(!cls->isMetaClass());
- ASSERT(cls->ISA() == metacls);
- if (cls->ISA() == metacls) return cls;
- }
- // use inst if available
- if (inst) {
- Class cls = remapClass((Class)inst);
- // cls may be a subclass - find the real class for metacls
- // fixme this probably stops working once Swift starts
- // reallocating classes if cls is unrealized.
- while (cls) {
- if (cls->ISA() == metacls) {
- ASSERT(!cls->isMetaClassMaybeUnrealized());
- return cls;
- }
- cls = cls->superclass;
- }
- #if DEBUG
- _objc_fatal("cls is not an instance of metacls");
- #else
- // release build: be forgiving and fall through to slow lookups
- #endif
- }
- // try name lookup
- {
- Class cls = getClassExceptSomeSwift(metacls->mangledName());
- if (cls && cls->ISA() == metacls) {
- named++;
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %d/%d (%g%%) "
- "successful by-name metaclass lookups",
- named, total, named*100.0/total);
- }
- return cls;
- }
- }
- // try secondary table
- {
- Class cls = (Class)NXMapGet(nonMetaClasses(), metacls);
- if (cls) {
- secondary++;
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %d/%d (%g%%) "
- "successful secondary metaclass lookups",
- secondary, total, secondary*100.0/total);
- }
- ASSERT(cls->ISA() == metacls);
- return cls;
- }
- }
- // try the dyld closure table
- if (isPreoptimized())
- {
- // Try table from dyld closure first. It was built to ignore the dupes it
- // knows will come from the cache, so anything left in here was there when
- // we launched
- Class cls = nil;
- // Note, we have to pass the lambda directly here as otherwise we would try
- // message copy and autorelease.
- _dyld_for_each_objc_class(metacls->mangledName(),
- [&cls, metacls](void* classPtr, bool isLoaded, bool* stop) {
- // Skip images which aren't loaded. This supports the case where dyld
- // might soft link an image from the main binary so its possibly not
- // loaded yet.
- if (!isLoaded)
- return;
- // Found a loaded image with this class name, so check if its the right one
- Class result = (Class)classPtr;
- if (result->ISA() == metacls) {
- cls = result;
- *stop = true;
- }
- });
- if (cls) {
- dyld3++;
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %d/%d (%g%%) "
- "successful dyld closure metaclass lookups",
- dyld3, total, dyld3*100.0/total);
- }
- return cls;
- }
- }
- // try any duplicates in the dyld shared cache
- {
- Class cls = nil;
- int count;
- Class *classes = copyPreoptimizedClasses(metacls->mangledName(),&count);
- if (classes) {
- for (int i = 0; i < count; i++) {
- if (classes[i]->ISA() == metacls) {
- cls = classes[i];
- break;
- }
- }
- free(classes);
- }
- if (cls) {
- sharedcache++;
- if (PrintInitializing) {
- _objc_inform("INITIALIZE: %d/%d (%g%%) "
- "successful shared cache metaclass lookups",
- sharedcache, total, sharedcache*100.0/total);
- }
- return cls;
- }
- }
- _objc_fatal("no class for metaclass %p", (void*)metacls);
- }
- /***********************************************************************
- * class_initialize. Send the '+initialize' message on demand to any
- * uninitialized class. Force initialization of superclasses first.
- * inst is an instance of cls, or nil. Non-nil is better for performance.
- * Returns the class pointer. If the class was unrealized then
- * it may be reallocated.
- * Locking:
- * runtimeLock must be held by the caller
- * This function may drop the lock.
- * On exit the lock is re-acquired or dropped as requested by leaveLocked.
- **********************************************************************/
- static Class initializeAndMaybeRelock(Class cls, id inst,
- mutex_t& lock, bool leaveLocked)
- {
- lock.assertLocked();
- ASSERT(cls->isRealized());
- if (cls->isInitialized()) {
- if (!leaveLocked) lock.unlock();
- return cls;
- }
- // Find the non-meta class for cls, if it is not already one.
- // The +initialize message is sent to the non-meta class object.
- Class nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
- // Realize the non-meta class if necessary.
- if (nonmeta->isRealized()) {
- // nonmeta is cls, which was already realized
- // OR nonmeta is distinct, but is already realized
- // - nothing else to do
- lock.unlock();
- } else {
- nonmeta = realizeClassMaybeSwiftAndUnlock(nonmeta, lock);
- // runtimeLock is now unlocked
- // fixme Swift can't relocate the class today,
- // but someday it will:
- cls = object_getClass(nonmeta);
- }
- // runtimeLock is now unlocked, for +initialize dispatch
- ASSERT(nonmeta->isRealized());
- initializeNonMetaClass(nonmeta);
- if (leaveLocked) runtimeLock.lock();
- return cls;
- }
- // Locking: acquires runtimeLock
- Class class_initialize(Class cls, id obj)
- {
- runtimeLock.lock();
- return initializeAndMaybeRelock(cls, obj, runtimeLock, false);
- }
- // Locking: caller must hold runtimeLock; this may drop and re-acquire it
- static Class initializeAndLeaveLocked(Class cls, id obj, mutex_t& lock)
- {
- return initializeAndMaybeRelock(cls, obj, lock, true);
- }
- /***********************************************************************
- * addRootClass
- * Adds cls as a new realized root class.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void addRootClass(Class cls)
- {
- runtimeLock.assertLocked();
- ASSERT(cls->isRealized());
- objc_debug_realized_class_generation_count++;
-
- cls->data()->nextSiblingClass = _firstRealizedClass;
- _firstRealizedClass = cls;
- }
- static void removeRootClass(Class cls)
- {
- runtimeLock.assertLocked();
- objc_debug_realized_class_generation_count++;
-
- Class *classp;
- for (classp = &_firstRealizedClass;
- *classp != cls;
- classp = &(*classp)->data()->nextSiblingClass)
- { }
-
- *classp = (*classp)->data()->nextSiblingClass;
- }
- /***********************************************************************
- * addSubclass
- * Adds subcls as a subclass of supercls.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void addSubclass(Class supercls, Class subcls)
- {
- runtimeLock.assertLocked();
- if (supercls && subcls) {
- ASSERT(supercls->isRealized());
- ASSERT(subcls->isRealized());
- objc_debug_realized_class_generation_count++;
-
- subcls->data()->nextSiblingClass = supercls->data()->firstSubclass;
- supercls->data()->firstSubclass = subcls;
- if (supercls->hasCxxCtor()) {
- subcls->setHasCxxCtor();
- }
- if (supercls->hasCxxDtor()) {
- subcls->setHasCxxDtor();
- }
- objc::AWZScanner::scanAddedSubClass(subcls, supercls);
- objc::RRScanner::scanAddedSubClass(subcls, supercls);
- objc::CoreScanner::scanAddedSubClass(subcls, supercls);
- // Special case: instancesRequireRawIsa does not propagate
- // from root class to root metaclass
- if (supercls->instancesRequireRawIsa() && supercls->superclass) {
- subcls->setInstancesRequireRawIsaRecursively(true);
- }
- }
- }
- /***********************************************************************
- * removeSubclass
- * Removes subcls as a subclass of supercls.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void removeSubclass(Class supercls, Class subcls)
- {
- runtimeLock.assertLocked();
- ASSERT(supercls->isRealized());
- ASSERT(subcls->isRealized());
- ASSERT(subcls->superclass == supercls);
- objc_debug_realized_class_generation_count++;
-
- Class *cp;
- for (cp = &supercls->data()->firstSubclass;
- *cp && *cp != subcls;
- cp = &(*cp)->data()->nextSiblingClass)
- ;
- ASSERT(*cp == subcls);
- *cp = subcls->data()->nextSiblingClass;
- }
- /***********************************************************************
- * protocols
- * Returns the protocol name => protocol map for protocols.
- * Locking: runtimeLock must read- or write-locked by the caller
- **********************************************************************/
- static NXMapTable *protocols(void)
- {
- static NXMapTable *protocol_map = nil;
-
- runtimeLock.assertLocked();
- INIT_ONCE_PTR(protocol_map,
- NXCreateMapTable(NXStrValueMapPrototype, 16),
- NXFreeMapTable(v) );
- return protocol_map;
- }
- /***********************************************************************
- * getProtocol
- * Looks up a protocol by name. Demangled Swift names are recognized.
- * Locking: runtimeLock must be read- or write-locked by the caller.
- **********************************************************************/
- static NEVER_INLINE Protocol *getProtocol(const char *name)
- {
- runtimeLock.assertLocked();
- // Try name as-is.
- Protocol *result = (Protocol *)NXMapGet(protocols(), name);
- if (result) return result;
- // Try Swift-mangled equivalent of the given name.
- if (char *swName = copySwiftV1MangledName(name, true/*isProtocol*/)) {
- result = (Protocol *)NXMapGet(protocols(), swName);
- free(swName);
- if (result) return result;
- }
- // Try table from dyld shared cache
- // Temporarily check that we are using the new table. Eventually this check
- // will always be true.
- // FIXME: Remove this check when we can
- if (sharedCacheSupportsProtocolRoots()) {
- result = getPreoptimizedProtocol(name);
- if (result) return result;
- }
- return nil;
- }
- /***********************************************************************
- * remapProtocol
- * Returns the live protocol pointer for proto, which may be pointing to
- * a protocol struct that has been reallocated.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static ALWAYS_INLINE protocol_t *remapProtocol(protocol_ref_t proto)
- {
- runtimeLock.assertLocked();
- // Protocols in shared cache images have a canonical bit to mark that they
- // are the definition we should use
- if (((protocol_t *)proto)->isCanonical())
- return (protocol_t *)proto;
- protocol_t *newproto = (protocol_t *)
- getProtocol(((protocol_t *)proto)->mangledName);
- return newproto ? newproto : (protocol_t *)proto;
- }
- /***********************************************************************
- * remapProtocolRef
- * Fix up a protocol ref, in case the protocol referenced has been reallocated.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static size_t UnfixedProtocolReferences;
- static void remapProtocolRef(protocol_t **protoref)
- {
- runtimeLock.assertLocked();
- protocol_t *newproto = remapProtocol((protocol_ref_t)*protoref);
- if (*protoref != newproto) {
- *protoref = newproto;
- UnfixedProtocolReferences++;
- }
- }
- /***********************************************************************
- * moveIvars
- * Slides a class's ivars to accommodate the given superclass size.
- * Ivars are NOT compacted to compensate for a superclass that shrunk.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void moveIvars(class_ro_t *ro, uint32_t superSize)
- {
- runtimeLock.assertLocked();
- uint32_t diff;
- ASSERT(superSize > ro->instanceStart);
- diff = superSize - ro->instanceStart;
- if (ro->ivars) {
- // Find maximum alignment in this class's ivars
- uint32_t maxAlignment = 1;
- for (const auto& ivar : *ro->ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- uint32_t alignment = ivar.alignment();
- if (alignment > maxAlignment) maxAlignment = alignment;
- }
- // Compute a slide value that preserves that alignment
- uint32_t alignMask = maxAlignment - 1;
- diff = (diff + alignMask) & ~alignMask;
- // Slide all of this class's ivars en masse
- for (const auto& ivar : *ro->ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- uint32_t oldOffset = (uint32_t)*ivar.offset;
- uint32_t newOffset = oldOffset + diff;
- *ivar.offset = newOffset;
- if (PrintIvars) {
- _objc_inform("IVARS: offset %u -> %u for %s "
- "(size %u, align %u)",
- oldOffset, newOffset, ivar.name,
- ivar.size, ivar.alignment());
- }
- }
- }
- *(uint32_t *)&ro->instanceStart += diff;
- *(uint32_t *)&ro->instanceSize += diff;
- }
- static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro_t*& ro)
- {
- class_rw_t *rw = cls->data();
- ASSERT(supercls);
- ASSERT(!cls->isMetaClass());
- /* debug: print them all before sliding
- if (ro->ivars) {
- for (const auto& ivar : *ro->ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- _objc_inform("IVARS: %s.%s (offset %u, size %u, align %u)",
- ro->name, ivar.name,
- *ivar.offset, ivar.size, ivar.alignment());
- }
- }
- */
- // Non-fragile ivars - reconcile this class with its superclass
- const class_ro_t *super_ro = supercls->data()->ro;
-
- if (DebugNonFragileIvars) {
- // Debugging: Force non-fragile ivars to slide.
- // Intended to find compiler, runtime, and program bugs.
- // If it fails with this and works without, you have a problem.
-
- // Operation: Reset everything to 0 + misalignment.
- // Then force the normal sliding logic to push everything back.
-
- // Exceptions: root classes, metaclasses, *NSCF* classes,
- // __CF* classes, NSConstantString, NSSimpleCString
-
- // (already know it's not root because supercls != nil)
- const char *clsname = cls->mangledName();
- if (!strstr(clsname, "NSCF") &&
- 0 != strncmp(clsname, "__CF", 4) &&
- 0 != strcmp(clsname, "NSConstantString") &&
- 0 != strcmp(clsname, "NSSimpleCString"))
- {
- uint32_t oldStart = ro->instanceStart;
- class_ro_t *ro_w = make_ro_writeable(rw);
- ro = rw->ro;
-
- // Find max ivar alignment in class.
- // default to word size to simplify ivar update
- uint32_t alignment = 1<<WORD_SHIFT;
- if (ro->ivars) {
- for (const auto& ivar : *ro->ivars) {
- if (ivar.alignment() > alignment) {
- alignment = ivar.alignment();
- }
- }
- }
- uint32_t misalignment = ro->instanceStart % alignment;
- uint32_t delta = ro->instanceStart - misalignment;
- ro_w->instanceStart = misalignment;
- ro_w->instanceSize -= delta;
-
- if (PrintIvars) {
- _objc_inform("IVARS: DEBUG: forcing ivars for class '%s' "
- "to slide (instanceStart %zu -> %zu)",
- cls->nameForLogging(), (size_t)oldStart,
- (size_t)ro->instanceStart);
- }
-
- if (ro->ivars) {
- for (const auto& ivar : *ro->ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- *ivar.offset -= delta;
- }
- }
- }
- }
- if (ro->instanceStart >= super_ro->instanceSize) {
- // Superclass has not overgrown its space. We're done here.
- return;
- }
- // fixme can optimize for "class has no new ivars", etc
- if (ro->instanceStart < super_ro->instanceSize) {
- // Superclass has changed size. This class's ivars must move.
- // Also slide layout bits in parallel.
- // This code is incapable of compacting the subclass to
- // compensate for a superclass that shrunk, so don't do that.
- if (PrintIvars) {
- _objc_inform("IVARS: sliding ivars for class %s "
- "(superclass was %u bytes, now %u)",
- cls->nameForLogging(), ro->instanceStart,
- super_ro->instanceSize);
- }
- class_ro_t *ro_w = make_ro_writeable(rw);
- ro = rw->ro;
- moveIvars(ro_w, super_ro->instanceSize);
- gdb_objc_class_changed(cls, OBJC_CLASS_IVARS_CHANGED, ro->name);
- }
- }
- /***********************************************************************
- * realizeClassWithoutSwift
- * Performs first-time initialization on class cls,
- * including allocating its read-write data.
- * Does not perform any Swift-side initialization.
- * Returns the real class structure for the class.
- * Locking: runtimeLock must be write-locked by the caller
- **********************************************************************/
- static Class realizeClassWithoutSwift(Class cls, Class previously)
- {
- runtimeLock.assertLocked();
- const class_ro_t *ro;
- class_rw_t *rw;
- Class supercls;
- Class metacls;
- bool isMeta;
- // 如果类已经实现了,直接返回
- if (!cls) return nil;
- if (cls->isRealized()) return cls;
- ASSERT(cls == remapClass(cls));
- // fixme verify class is not in an un-dlopened part of the shared cache?
- // 编译期间,cls->data指向的是class_ro_t结构体
- // 因此可以强制转成class_ro_t
- ro = (const class_ro_t *)cls->data();
- if (ro->flags & RO_FUTURE) {
- // This was a future class. rw data is already allocated.
- rw = cls->data();
- ro = cls->data()->ro;
- cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE);
- } else {
- // Normal class. Allocate writeable class data.
- // 初始化class_rw_t结构体
- rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1);
- rw->ro = ro;
- rw->flags = RW_REALIZED|RW_REALIZING;
- cls->setData(rw);
- }
- isMeta = ro->flags & RO_META;
- #if FAST_CACHE_META
- if (isMeta) cls->cache.setBit(FAST_CACHE_META);
- #endif
- rw->version = isMeta ? 7 : 0; // old runtime went up to 6
- // Choose an index for this class.
- // Sets cls->instancesRequireRawIsa if indexes no more indexes are available
- cls->chooseClassArrayIndex();
- if (PrintConnecting) {
- _objc_inform("CLASS: realizing class '%s'%s %p %p #%u %s%s",
- cls->nameForLogging(), isMeta ? " (meta)" : "",
- (void*)cls, ro, cls->classArrayIndex(),
- cls->isSwiftStable() ? "(swift)" : "",
- cls->isSwiftLegacy() ? "(pre-stable swift)" : "");
- }
- // Realize superclass and metaclass, if they aren't already.
- // This needs to be done after RW_REALIZED is set above, for root classes.
- // This needs to be done after class index is chosen, for root metaclasses.
- // This assumes that none of those classes have Swift contents,
- // or that Swift's initializers have already been called.
- // fixme that assumption will be wrong if we add support
- // for ObjC subclasses of Swift classes.
- supercls = realizeClassWithoutSwift(remapClass(cls->superclass), nil);
- metacls = realizeClassWithoutSwift(remapClass(cls->ISA()), nil);
- #if SUPPORT_NONPOINTER_ISA
- if (isMeta) {
- // Metaclasses do not need any features from non pointer ISA
- // This allows for a faspath for classes in objc_retain/objc_release.
- cls->setInstancesRequireRawIsa();
- } else {
- // Disable non-pointer isa for some classes and/or platforms.
- // Set instancesRequireRawIsa.
- bool instancesRequireRawIsa = cls->instancesRequireRawIsa();
- bool rawIsaIsInherited = false;
- static bool hackedDispatch = false;
- if (DisableNonpointerIsa) {
- // Non-pointer isa disabled by environment or app SDK version
- instancesRequireRawIsa = true;
- }
- else if (!hackedDispatch && 0 == strcmp(ro->name, "OS_object"))
- {
- // hack for libdispatch et al - isa also acts as vtable pointer
- hackedDispatch = true;
- instancesRequireRawIsa = true;
- }
- else if (supercls && supercls->superclass &&
- supercls->instancesRequireRawIsa())
- {
- // This is also propagated by addSubclass()
- // but nonpointer isa setup needs it earlier.
- // Special case: instancesRequireRawIsa does not propagate
- // from root class to root metaclass
- instancesRequireRawIsa = true;
- rawIsaIsInherited = true;
- }
- if (instancesRequireRawIsa) {
- cls->setInstancesRequireRawIsaRecursively(rawIsaIsInherited);
- }
- }
- // SUPPORT_NONPOINTER_ISA
- #endif
- // Update superclass and metaclass in case of remapping
- cls->superclass = supercls;
- cls->initClassIsa(metacls);
- // Reconcile instance variable offsets / layout.
- // This may reallocate class_ro_t, updating our ro variable.
- if (supercls && !isMeta) reconcileInstanceVariables(cls, supercls, ro);
- // Set fastInstanceSize if it wasn't set already.
- cls->setInstanceSize(ro->instanceSize);
- // Copy some flags from ro to rw
- if (ro->flags & RO_HAS_CXX_STRUCTORS) {
- cls->setHasCxxDtor();
- if (! (ro->flags & RO_HAS_CXX_DTOR_ONLY)) {
- cls->setHasCxxCtor();
- }
- }
-
- // Propagate the associated objects forbidden flag from ro or from
- // the superclass.
- if ((ro->flags & RO_FORBIDS_ASSOCIATED_OBJECTS) ||
- (supercls && supercls->forbidsAssociatedObjects()))
- {
- rw->flags |= RW_FORBIDS_ASSOCIATED_OBJECTS;
- }
- // Connect this class to its superclass's subclass lists
- if (supercls) {
- addSubclass(supercls, cls);
- } else {
- addRootClass(cls);
- }
- // 将类实现的方法(包括分类)、属性和遵循的协议添加到class_rw_t结构体中的methods、properties、protocols列表中
- // Attach categories
- methodizeClass(cls, previously);
- return cls;
- }
- /***********************************************************************
- * _objc_realizeClassFromSwift
- * Called by Swift when it needs the ObjC part of a class to be realized.
- * There are four cases:
- * 1. cls != nil; previously == cls
- * Class cls is being realized in place
- * 2. cls != nil; previously == nil
- * Class cls is being constructed at runtime
- * 3. cls != nil; previously != cls
- * The class that was at previously has been reallocated to cls
- * 4. cls == nil, previously != nil
- * The class at previously is hereby disavowed
- *
- * Only variants #1 and #2 are supported today.
- *
- * Locking: acquires runtimeLock
- **********************************************************************/
- Class _objc_realizeClassFromSwift(Class cls, void *previously)
- {
- if (cls) {
- if (previously && previously != (void*)cls) {
- // #3: relocation
- mutex_locker_t lock(runtimeLock);
- addRemappedClass((Class)previously, cls);
- addClassTableEntry(cls);
- addNamedClass(cls, cls->mangledName(), /*replacing*/nil);
- return realizeClassWithoutSwift(cls, (Class)previously);
- } else {
- // #1 and #2: realization in place, or new class
- mutex_locker_t lock(runtimeLock);
- if (!previously) {
- // #2: new class
- cls = readClass(cls, false/*bundle*/, false/*shared cache*/);
- }
- // #1 and #2: realization in place, or new class
- // We ignore the Swift metadata initializer callback.
- // We assume that's all handled since we're being called from Swift.
- return realizeClassWithoutSwift(cls, nil);
- }
- }
- else {
- // #4: disavowal
- // In the future this will mean remapping the old address to nil
- // and if necessary removing the old address from any other tables.
- _objc_fatal("Swift requested that class %p be ignored, "
- "but libobjc does not support that.", previously);
- }
- }
- /***********************************************************************
- * realizeSwiftClass
- * Performs first-time initialization on class cls,
- * including allocating its read-write data,
- * and any Swift-side initialization.
- * Returns the real class structure for the class.
- * Locking: acquires runtimeLock indirectly
- **********************************************************************/
- static Class realizeSwiftClass(Class cls)
- {
- runtimeLock.assertUnlocked();
- // Some assumptions:
- // * Metaclasses never have a Swift initializer.
- // * Root classes never have a Swift initializer.
- // (These two together avoid initialization order problems at the root.)
- // * Unrealized non-Swift classes have no Swift ancestry.
- // * Unrealized Swift classes with no initializer have no ancestry that
- // does have the initializer.
- // (These two together mean we don't need to scan superclasses here
- // and we don't need to worry about Swift superclasses inside
- // realizeClassWithoutSwift()).
- // fixme some of these assumptions will be wrong
- // if we add support for ObjC sublasses of Swift classes.
- #if DEBUG
- runtimeLock.lock();
- ASSERT(remapClass(cls) == cls);
- ASSERT(cls->isSwiftStable_ButAllowLegacyForNow());
- ASSERT(!cls->isMetaClassMaybeUnrealized());
- ASSERT(cls->superclass);
- runtimeLock.unlock();
- #endif
- // Look for a Swift metadata initialization function
- // installed on the class. If it is present we call it.
- // That function in turn initializes the Swift metadata,
- // prepares the "compiler-generated" ObjC metadata if not
- // already present, and calls _objc_realizeSwiftClass() to finish
- // our own initialization.
- if (auto init = cls->swiftMetadataInitializer()) {
- if (PrintConnecting) {
- _objc_inform("CLASS: calling Swift metadata initializer "
- "for class '%s' (%p)", cls->nameForLogging(), cls);
- }
- Class newcls = init(cls, nil);
- // fixme someday Swift will need to relocate classes at this point,
- // but we don't accept that yet.
- if (cls != newcls) {
- mutex_locker_t lock(runtimeLock);
- addRemappedClass(cls, newcls);
- }
- return newcls;
- }
- else {
- // No Swift-side initialization callback.
- // Perform our own realization directly.
- mutex_locker_t lock(runtimeLock);
- return realizeClassWithoutSwift(cls, nil);
- }
- }
- /***********************************************************************
- * realizeClassMaybeSwift (MaybeRelock / AndUnlock / AndLeaveLocked)
- * Realize a class that might be a Swift class.
- * Returns the real class structure for the class.
- * Locking:
- * runtimeLock must be held on entry
- * runtimeLock may be dropped during execution
- * ...AndUnlock function leaves runtimeLock unlocked on exit
- * ...AndLeaveLocked re-acquires runtimeLock if it was dropped
- * This complication avoids repeated lock transitions in some cases.
- **********************************************************************/
- static Class
- realizeClassMaybeSwiftMaybeRelock(Class cls, mutex_t& lock, bool leaveLocked)
- {
- lock.assertLocked();
- if (!cls->isSwiftStable_ButAllowLegacyForNow()) {
- // Non-Swift class. Realize it now with the lock still held.
- // fixme wrong in the future for objc subclasses of swift classes
- realizeClassWithoutSwift(cls, nil);
- if (!leaveLocked) lock.unlock();
- } else {
- // Swift class. We need to drop locks and call the Swift
- // runtime to initialize it.
- lock.unlock();
- cls = realizeSwiftClass(cls);
- ASSERT(cls->isRealized()); // callback must have provoked realization
- if (leaveLocked) lock.lock();
- }
- return cls;
- }
- static Class
- realizeClassMaybeSwiftAndUnlock(Class cls, mutex_t& lock)
- {
- return realizeClassMaybeSwiftMaybeRelock(cls, lock, false);
- }
- static Class
- realizeClassMaybeSwiftAndLeaveLocked(Class cls, mutex_t& lock)
- {
- return realizeClassMaybeSwiftMaybeRelock(cls, lock, true);
- }
- /***********************************************************************
- * missingWeakSuperclass
- * Return YES if some superclass of cls was weak-linked and is missing.
- **********************************************************************/
- static bool
- missingWeakSuperclass(Class cls)
- {
- ASSERT(!cls->isRealized());
- if (!cls->superclass) {
- // superclass nil. This is normal for root classes only.
- return (!(cls->data()->flags & RO_ROOT));
- } else {
- // superclass not nil. Check if a higher superclass is missing.
- Class supercls = remapClass(cls->superclass);
- ASSERT(cls != cls->superclass);
- ASSERT(cls != supercls);
- if (!supercls) return YES;
- if (supercls->isRealized()) return NO;
- return missingWeakSuperclass(supercls);
- }
- }
- /***********************************************************************
- * realizeAllClassesInImage
- * Non-lazily realizes all unrealized classes in the given image.
- * Locking: runtimeLock must be held by the caller.
- * Locking: this function may drop and re-acquire the lock.
- **********************************************************************/
- static void realizeAllClassesInImage(header_info *hi)
- {
- runtimeLock.assertLocked();
- size_t count, i;
- classref_t const *classlist;
- if (hi->areAllClassesRealized()) return;
- classlist = _getObjc2ClassList(hi, &count);
- for (i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (cls) {
- realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
- }
- }
- hi->setAllClassesRealized(YES);
- }
- /***********************************************************************
- * realizeAllClasses
- * Non-lazily realizes all unrealized classes in all known images.
- * Locking: runtimeLock must be held by the caller.
- * Locking: this function may drop and re-acquire the lock.
- * Dropping the lock makes this function thread-unsafe with respect
- * to concurrent image unload, but the callers of this function
- * already ultimately do something that is also thread-unsafe with
- * respect to image unload (such as using the list of all classes).
- **********************************************************************/
- static void realizeAllClasses(void)
- {
- runtimeLock.assertLocked();
- header_info *hi;
- for (hi = FirstHeader; hi; hi = hi->getNext()) {
- realizeAllClassesInImage(hi); // may drop and re-acquire runtimeLock
- }
- }
- /***********************************************************************
- * _objc_allocateFutureClass
- * Allocate an unresolved future class for the given class name.
- * Returns any existing allocation if one was already made.
- * Assumes the named class doesn't exist yet.
- * Locking: acquires runtimeLock
- **********************************************************************/
- Class _objc_allocateFutureClass(const char *name)
- {
- mutex_locker_t lock(runtimeLock);
- Class cls;
- NXMapTable *map = futureNamedClasses();
- if ((cls = (Class)NXMapGet(map, name))) {
- // Already have a future class for this name.
- return cls;
- }
- cls = _calloc_class(sizeof(objc_class));
- addFutureNamedClass(name, cls);
- return cls;
- }
- /***********************************************************************
- * objc_getFutureClass. Return the id of the named class.
- * If the class does not exist, return an uninitialized class
- * structure that will be used for the class when and if it
- * does get loaded.
- * Not thread safe.
- **********************************************************************/
- Class objc_getFutureClass(const char *name)
- {
- Class cls;
- // YES unconnected, NO class handler
- // (unconnected is OK because it will someday be the real class)
- cls = look_up_class(name, YES, NO);
- if (cls) {
- if (PrintFuture) {
- _objc_inform("FUTURE: found %p already in use for %s",
- (void*)cls, name);
- }
- return cls;
- }
-
- // No class or future class with that name yet. Make one.
- // fixme not thread-safe with respect to
- // simultaneous library load or getFutureClass.
- return _objc_allocateFutureClass(name);
- }
- BOOL _class_isFutureClass(Class cls)
- {
- return cls && cls->isFuture();
- }
- /***********************************************************************
- * _objc_flush_caches
- * Flushes all caches.
- * (Historical behavior: flush caches for cls, its metaclass,
- * and subclasses thereof. Nil flushes all classes.)
- * Locking: acquires runtimeLock
- **********************************************************************/
- static void flushCaches(Class cls)
- {
- runtimeLock.assertLocked();
- #if CONFIG_USE_CACHE_LOCK
- mutex_locker_t lock(cacheUpdateLock);
- #endif
- if (cls) {
- foreach_realized_class_and_subclass(cls, [](Class c){
- cache_erase_nolock(c);
- return true;
- });
- }
- else {
- foreach_realized_class_and_metaclass([](Class c){
- cache_erase_nolock(c);
- return true;
- });
- }
- }
- void _objc_flush_caches(Class cls)
- {
- {
- mutex_locker_t lock(runtimeLock);
- flushCaches(cls);
- if (cls && cls->superclass && cls != cls->getIsa()) {
- flushCaches(cls->getIsa());
- } else {
- // cls is a root class or root metaclass. Its metaclass is itself
- // or a subclass so the metaclass caches were already flushed.
- }
- }
- if (!cls) {
- // collectALot if cls==nil
- #if CONFIG_USE_CACHE_LOCK
- mutex_locker_t lock(cacheUpdateLock);
- #else
- mutex_locker_t lock(runtimeLock);
- #endif
- cache_collect(true);
- }
- }
- /***********************************************************************
- * map_images
- * Process the given images which are being mapped in by dyld.
- * Calls ABI-agnostic code after taking ABI-specific locks.
- *
- * Locking: write-locks runtimeLock
- **********************************************************************/
- void
- map_images(unsigned count, const char * const paths[],
- const struct mach_header * const mhdrs[])
- {
- mutex_locker_t lock(runtimeLock);
- return map_images_nolock(count, paths, mhdrs);
- }
- /***********************************************************************
- * load_images
- * Process +load in the given images which are being mapped in by dyld.
- *
- * Locking: write-locks runtimeLock and loadMethodLock
- **********************************************************************/
- extern bool hasLoadMethods(const headerType *mhdr);
- extern void prepare_load_methods(const headerType *mhdr);
- void
- load_images(const char *path __unused, const struct mach_header *mh)
- {
- // Return without taking locks if there are no +load methods here.
- if (!hasLoadMethods((const headerType *)mh)) return;
- recursive_mutex_locker_t lock(loadMethodLock);
- // Discover load methods
- {
- mutex_locker_t lock2(runtimeLock);
- prepare_load_methods((const headerType *)mh);
- }
- // Call +load methods (without runtimeLock - re-entrant)
- call_load_methods();
- }
- /***********************************************************************
- * unmap_image
- * Process the given image which is about to be unmapped by dyld.
- *
- * Locking: write-locks runtimeLock and loadMethodLock
- **********************************************************************/
- void
- unmap_image(const char *path __unused, const struct mach_header *mh)
- {
- recursive_mutex_locker_t lock(loadMethodLock);
- mutex_locker_t lock2(runtimeLock);
- unmap_image_nolock(mh);
- }
- /***********************************************************************
- * mustReadClasses
- * Preflight check in advance of readClass() from an image.
- **********************************************************************/
- bool mustReadClasses(header_info *hi, bool hasDyldRoots)
- {
- const char *reason;
- // If the image is not preoptimized then we must read classes.
- if (!hi->hasPreoptimizedClasses()) {
- reason = nil; // Don't log this one because it is noisy.
- goto readthem;
- }
- // If iOS simulator then we must read classes.
- #if TARGET_OS_SIMULATOR
- reason = "the image is for iOS simulator";
- goto readthem;
- #endif
- ASSERT(!hi->isBundle()); // no MH_BUNDLE in shared cache
- // If the image may have missing weak superclasses then we must read classes
- if (!noMissingWeakSuperclasses() || hasDyldRoots) {
- reason = "the image may contain classes with missing weak superclasses";
- goto readthem;
- }
- // If there are unresolved future classes then we must read classes.
- if (haveFutureNamedClasses()) {
- reason = "there are unresolved future classes pending";
- goto readthem;
- }
- // readClass() rewrites bits in backward-deploying Swift stable ABI code.
- // The assumption here is there there are no such classes
- // in the dyld shared cache.
- #if DEBUG
- {
- size_t count;
- classref_t const *classlist = _getObjc2ClassList(hi, &count);
- for (size_t i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- ASSERT(!cls->isUnfixedBackwardDeployingStableSwift());
- }
- }
- #endif
- // readClass() does not need to do anything.
- return NO;
- readthem:
- if (PrintPreopt && reason) {
- _objc_inform("PREOPTIMIZATION: reading classes manually from %s "
- "because %s", hi->fname(), reason);
- }
- return YES;
- }
- /***********************************************************************
- * readClass
- * Read a class and metaclass as written by a compiler.
- * Returns the new class pointer. This could be:
- * - cls
- * - nil (cls has a missing weak-linked superclass)
- * - something else (space for this class was reserved by a future class)
- *
- * Note that all work performed by this function is preflighted by
- * mustReadClasses(). Do not change this function without updating that one.
- *
- * Locking: runtimeLock acquired by map_images or objc_readClassPair
- **********************************************************************/
- Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized)
- {
- const char *mangledName = cls->mangledName();
-
- if (missingWeakSuperclass(cls)) {
- // No superclass (probably weak-linked).
- // Disavow any knowledge of this subclass.
- if (PrintConnecting) {
- _objc_inform("CLASS: IGNORING class '%s' with "
- "missing weak-linked superclass",
- cls->nameForLogging());
- }
- addRemappedClass(cls, nil);
- cls->superclass = nil;
- return nil;
- }
-
- cls->fixupBackwardDeployingStableSwift();
- Class replacing = nil;
- if (Class newCls = popFutureNamedClass(mangledName)) {
- // This name was previously allocated as a future class.
- // Copy objc_class to future class's struct.
- // Preserve future's rw data block.
-
- if (newCls->isAnySwift()) {
- _objc_fatal("Can't complete future class request for '%s' "
- "because the real class is too big.",
- cls->nameForLogging());
- }
-
- class_rw_t *rw = newCls->data();
- const class_ro_t *old_ro = rw->ro;
- memcpy(newCls, cls, sizeof(objc_class));
- rw->ro = (class_ro_t *)newCls->data();
- newCls->setData(rw);
- freeIfMutable((char *)old_ro->name);
- free((void *)old_ro);
-
- addRemappedClass(cls, newCls);
-
- replacing = cls;
- cls = newCls;
- }
-
- if (headerIsPreoptimized && !replacing) {
- // class list built in shared cache
- // fixme strict assert doesn't work because of duplicates
- // ASSERT(cls == getClass(name));
- ASSERT(getClassExceptSomeSwift(mangledName));
- } else {
- addNamedClass(cls, mangledName, replacing);
- addClassTableEntry(cls);
- }
- // for future reference: shared cache never contains MH_BUNDLEs
- if (headerIsBundle) {
- cls->data()->flags |= RO_FROM_BUNDLE;
- cls->ISA()->data()->flags |= RO_FROM_BUNDLE;
- }
-
- return cls;
- }
- /***********************************************************************
- * readProtocol
- * Read a protocol as written by a compiler.
- **********************************************************************/
- static void
- readProtocol(protocol_t *newproto, Class protocol_class,
- NXMapTable *protocol_map,
- bool headerIsPreoptimized, bool headerIsBundle)
- {
- // This is not enough to make protocols in unloaded bundles safe,
- // but it does prevent crashes when looking up unrelated protocols.
- auto insertFn = headerIsBundle ? NXMapKeyCopyingInsert : NXMapInsert;
- protocol_t *oldproto = (protocol_t *)getProtocol(newproto->mangledName);
- if (oldproto) {
- if (oldproto != newproto) {
- // Some other definition already won.
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s "
- "(duplicate of %p)",
- newproto, oldproto->nameForLogging(), oldproto);
- }
- // If we are a shared cache binary then we have a definition of this
- // protocol, but if another one was chosen then we need to clear our
- // isCanonical bit so that no-one trusts it.
- // Note, if getProtocol returned a shared cache protocol then the
- // canonical definition is already in the shared cache and we don't
- // need to do anything.
- if (headerIsPreoptimized && !oldproto->isCanonical()) {
- // Note newproto is an entry in our __objc_protolist section which
- // for shared cache binaries points to the original protocol in
- // that binary, not the shared cache uniqued one.
- auto cacheproto = (protocol_t *)
- getSharedCachePreoptimizedProtocol(newproto->mangledName);
- if (cacheproto && cacheproto->isCanonical())
- cacheproto->clearIsCanonical();
- }
- }
- }
- else if (headerIsPreoptimized) {
- // Shared cache initialized the protocol object itself,
- // but in order to allow out-of-cache replacement we need
- // to add it to the protocol table now.
- protocol_t *cacheproto = (protocol_t *)
- getPreoptimizedProtocol(newproto->mangledName);
- protocol_t *installedproto;
- if (cacheproto && cacheproto != newproto) {
- // Another definition in the shared cache wins (because
- // everything in the cache was fixed up to point to it).
- installedproto = cacheproto;
- }
- else {
- // This definition wins.
- installedproto = newproto;
- }
-
- ASSERT(installedproto->getIsa() == protocol_class);
- ASSERT(installedproto->size >= sizeof(protocol_t));
- insertFn(protocol_map, installedproto->mangledName,
- installedproto);
-
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s",
- installedproto, installedproto->nameForLogging());
- if (newproto != installedproto) {
- _objc_inform("PROTOCOLS: protocol at %p is %s "
- "(duplicate of %p)",
- newproto, installedproto->nameForLogging(),
- installedproto);
- }
- }
- }
- else if (newproto->size >= sizeof(protocol_t)) {
- // New protocol from an un-preoptimized image
- // with sufficient storage. Fix it up in place.
- // fixme duplicate protocols from unloadable bundle
- newproto->initIsa(protocol_class); // fixme pinned
- insertFn(protocol_map, newproto->mangledName, newproto);
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s",
- newproto, newproto->nameForLogging());
- }
- }
- else {
- // New protocol from an un-preoptimized image
- // with insufficient storage. Reallocate it.
- // fixme duplicate protocols from unloadable bundle
- size_t size = max(sizeof(protocol_t), (size_t)newproto->size);
- protocol_t *installedproto = (protocol_t *)calloc(size, 1);
- memcpy(installedproto, newproto, newproto->size);
- installedproto->size = (typeof(installedproto->size))size;
-
- installedproto->initIsa(protocol_class); // fixme pinned
- insertFn(protocol_map, installedproto->mangledName, installedproto);
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: protocol at %p is %s ",
- installedproto, installedproto->nameForLogging());
- _objc_inform("PROTOCOLS: protocol at %p is %s "
- "(reallocated to %p)",
- newproto, installedproto->nameForLogging(),
- installedproto);
- }
- }
- }
- /***********************************************************************
- * _read_images
- * Perform initial processing of the headers in the linked
- * list beginning with headerList.
- *
- * Called by: map_images_nolock
- *
- * Locking: runtimeLock acquired by map_images
- **********************************************************************/
- void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses)
- {
- header_info *hi;
- uint32_t hIndex;
- size_t count;
- size_t i;
- Class *resolvedFutureClasses = nil;
- size_t resolvedFutureClassCount = 0;
- static bool doneOnce;
- bool launchTime = NO;
- TimeLogger ts(PrintImageTimes);
- runtimeLock.assertLocked();
- #define EACH_HEADER \
- hIndex = 0; \
- hIndex < hCount && (hi = hList[hIndex]); \
- hIndex++
- if (!doneOnce) {
- doneOnce = YES;
- launchTime = YES;
- #if SUPPORT_NONPOINTER_ISA
- // Disable non-pointer isa under some conditions.
- # if SUPPORT_INDEXED_ISA
- // Disable nonpointer isa if any image contains old Swift code
- for (EACH_HEADER) {
- if (hi->info()->containsSwift() &&
- hi->info()->swiftUnstableVersion() < objc_image_info::SwiftVersion3)
- {
- DisableNonpointerIsa = true;
- if (PrintRawIsa) {
- _objc_inform("RAW ISA: disabling non-pointer isa because "
- "the app or a framework contains Swift code "
- "older than Swift 3.0");
- }
- break;
- }
- }
- # endif
- # if TARGET_OS_OSX
- // Disable non-pointer isa if the app is too old
- // (linked before OS X 10.11)
- if (dyld_get_program_sdk_version() < DYLD_MACOSX_VERSION_10_11) {
- DisableNonpointerIsa = true;
- if (PrintRawIsa) {
- _objc_inform("RAW ISA: disabling non-pointer isa because "
- "the app is too old (SDK version " SDK_FORMAT ")",
- FORMAT_SDK(dyld_get_program_sdk_version()));
- }
- }
- // Disable non-pointer isa if the app has a __DATA,__objc_rawisa section
- // New apps that load old extensions may need this.
- for (EACH_HEADER) {
- if (hi->mhdr()->filetype != MH_EXECUTE) continue;
- unsigned long size;
- if (getsectiondata(hi->mhdr(), "__DATA", "__objc_rawisa", &size)) {
- DisableNonpointerIsa = true;
- if (PrintRawIsa) {
- _objc_inform("RAW ISA: disabling non-pointer isa because "
- "the app has a __DATA,__objc_rawisa section");
- }
- }
- break; // assume only one MH_EXECUTE image
- }
- # endif
- #endif
- if (DisableTaggedPointers) {
- disableTaggedPointers();
- }
-
- initializeTaggedPointerObfuscator();
- if (PrintConnecting) {
- _objc_inform("CLASS: found %d classes during launch", totalClasses);
- }
- // namedClasses
- // Preoptimized classes don't go in this table.
- // 4/3 is NXMapTable's load factor
- int namedClassesSize =
- (isPreoptimized() ? unoptimizedTotalClasses : totalClasses) * 4 / 3;
- gdb_objc_realized_classes =
- NXCreateMapTable(NXStrValueMapPrototype, namedClassesSize);
- ts.log("IMAGE TIMES: first time tasks");
- }
- // Fix up @selector references
- static size_t UnfixedSelectors;
- {
- mutex_locker_t lock(selLock);
- for (EACH_HEADER) {
- if (hi->hasPreoptimizedSelectors()) continue;
- bool isBundle = hi->isBundle();
- SEL *sels = _getObjc2SelectorRefs(hi, &count);
- UnfixedSelectors += count;
- for (i = 0; i < count; i++) {
- const char *name = sel_cname(sels[i]);
- SEL sel = sel_registerNameNoLock(name, isBundle);
- if (sels[i] != sel) {
- sels[i] = sel;
- }
- }
- }
- }
- ts.log("IMAGE TIMES: fix up selector references");
- // Discover classes. Fix up unresolved future classes. Mark bundle classes.
- bool hasDyldRoots = dyld_shared_cache_some_image_overridden();
- for (EACH_HEADER) {
- if (! mustReadClasses(hi, hasDyldRoots)) {
- // Image is sufficiently optimized that we need not call readClass()
- continue;
- }
- classref_t const *classlist = _getObjc2ClassList(hi, &count);
- bool headerIsBundle = hi->isBundle();
- bool headerIsPreoptimized = hi->hasPreoptimizedClasses();
- for (i = 0; i < count; i++) {
- Class cls = (Class)classlist[i];
- Class newCls = readClass(cls, headerIsBundle, headerIsPreoptimized);
- if (newCls != cls && newCls) {
- // Class was moved but not deleted. Currently this occurs
- // only when the new class resolved a future class.
- // Non-lazily realize the class below.
- resolvedFutureClasses = (Class *)
- realloc(resolvedFutureClasses,
- (resolvedFutureClassCount+1) * sizeof(Class));
- resolvedFutureClasses[resolvedFutureClassCount++] = newCls;
- }
- }
- }
- ts.log("IMAGE TIMES: discover classes");
- // Fix up remapped classes
- // Class list and nonlazy class list remain unremapped.
- // Class refs and super refs are remapped for message dispatching.
-
- if (!noClassesRemapped()) {
- for (EACH_HEADER) {
- Class *classrefs = _getObjc2ClassRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapClassRef(&classrefs[i]);
- }
- // fixme why doesn't test future1 catch the absence of this?
- classrefs = _getObjc2SuperRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapClassRef(&classrefs[i]);
- }
- }
- }
- ts.log("IMAGE TIMES: remap classes");
- #if SUPPORT_FIXUP
- // Fix up old objc_msgSend_fixup call sites
- for (EACH_HEADER) {
- message_ref_t *refs = _getObjc2MessageRefs(hi, &count);
- if (count == 0) continue;
- if (PrintVtables) {
- _objc_inform("VTABLES: repairing %zu unsupported vtable dispatch "
- "call sites in %s", count, hi->fname());
- }
- for (i = 0; i < count; i++) {
- fixupMessageRef(refs+i);
- }
- }
- ts.log("IMAGE TIMES: fix up objc_msgSend_fixup");
- #endif
- bool cacheSupportsProtocolRoots = sharedCacheSupportsProtocolRoots();
- // Discover protocols. Fix up protocol refs.
- for (EACH_HEADER) {
- extern objc_class OBJC_CLASS_$_Protocol;
- Class cls = (Class)&OBJC_CLASS_$_Protocol;
- ASSERT(cls);
- NXMapTable *protocol_map = protocols();
- bool isPreoptimized = hi->hasPreoptimizedProtocols();
- // Skip reading protocols if this is an image from the shared cache
- // and we support roots
- // Note, after launch we do need to walk the protocol as the protocol
- // in the shared cache is marked with isCanonical() and that may not
- // be true if some non-shared cache binary was chosen as the canonical
- // definition
- if (launchTime && isPreoptimized && cacheSupportsProtocolRoots) {
- if (PrintProtocols) {
- _objc_inform("PROTOCOLS: Skipping reading protocols in image: %s",
- hi->fname());
- }
- continue;
- }
- bool isBundle = hi->isBundle();
- protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
- for (i = 0; i < count; i++) {
- readProtocol(protolist[i], cls, protocol_map,
- isPreoptimized, isBundle);
- }
- }
- ts.log("IMAGE TIMES: discover protocols");
- // Fix up @protocol references
- // Preoptimized images may have the right
- // answer already but we don't know for sure.
- for (EACH_HEADER) {
- // At launch time, we know preoptimized image refs are pointing at the
- // shared cache definition of a protocol. We can skip the check on
- // launch, but have to visit @protocol refs for shared cache images
- // loaded later.
- if (launchTime && cacheSupportsProtocolRoots && hi->isPreoptimized())
- continue;
- protocol_t **protolist = _getObjc2ProtocolRefs(hi, &count);
- for (i = 0; i < count; i++) {
- remapProtocolRef(&protolist[i]);
- }
- }
- ts.log("IMAGE TIMES: fix up @protocol references");
- // Discover categories.
- for (EACH_HEADER) {
- bool hasClassProperties = hi->info()->hasCategoryClassProperties();
- auto processCatlist = [&](category_t * const *catlist) {
- for (i = 0; i < count; i++) {
- category_t *cat = catlist[i];
- Class cls = remapClass(cat->cls);
- locstamped_category_t lc{cat, hi};
-
- if (!cls) {
- // Category's target class is missing (probably weak-linked).
- // Ignore the category.
- if (PrintConnecting) {
- _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with "
- "missing weak-linked target class",
- cat->name, cat);
- }
- continue;
- }
-
- // Process this category.
- if (cls->isStubClass()) {
- // Stub classes are never realized. Stub classes
- // don't know their metaclass until they're
- // initialized, so we have to add categories with
- // class methods or properties to the stub itself.
- // methodizeClass() will find them and add them to
- // the metaclass as appropriate.
- if (cat->instanceMethods ||
- cat->protocols ||
- cat->instanceProperties ||
- cat->classMethods ||
- cat->protocols ||
- (hasClassProperties && cat->_classProperties))
- {
- objc::unattachedCategories.addForClass(lc, cls);
- }
- } else {
- // First, register the category with its target class.
- // Then, rebuild the class's method lists (etc) if
- // the class is realized.
- if (cat->instanceMethods || cat->protocols
- || cat->instanceProperties)
- {
- if (cls->isRealized()) {
- attachCategories(cls, &lc, 1, ATTACH_EXISTING);
- } else {
- objc::unattachedCategories.addForClass(lc, cls);
- }
- }
-
- if (cat->classMethods || cat->protocols
- || (hasClassProperties && cat->_classProperties))
- {
- if (cls->ISA()->isRealized()) {
- attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS);
- } else {
- objc::unattachedCategories.addForClass(lc, cls->ISA());
- }
- }
- }
- }
- };
- processCatlist(_getObjc2CategoryList(hi, &count));
- processCatlist(_getObjc2CategoryList2(hi, &count));
- }
- ts.log("IMAGE TIMES: discover categories");
- // Category discovery MUST BE Late to avoid potential races
- // when other threads call the new category code before
- // this thread finishes its fixups.
- // +load handled by prepare_load_methods()
- // Realize non-lazy classes (for +load methods and static instances)
- for (EACH_HEADER) {
- classref_t const *classlist =
- _getObjc2NonlazyClassList(hi, &count);
- for (i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (!cls) continue;
- addClassTableEntry(cls);
- if (cls->isSwiftStable()) {
- if (cls->swiftMetadataInitializer()) {
- _objc_fatal("Swift class %s with a metadata initializer "
- "is not allowed to be non-lazy",
- cls->nameForLogging());
- }
- // fixme also disallow relocatable classes
- // We can't disallow all Swift classes because of
- // classes like Swift.__EmptyArrayStorage
- }
- realizeClassWithoutSwift(cls, nil);
- }
- }
- ts.log("IMAGE TIMES: realize non-lazy classes");
- // Realize newly-resolved future classes, in case CF manipulates them
- if (resolvedFutureClasses) {
- for (i = 0; i < resolvedFutureClassCount; i++) {
- Class cls = resolvedFutureClasses[i];
- if (cls->isSwiftStable()) {
- _objc_fatal("Swift class is not allowed to be future");
- }
- realizeClassWithoutSwift(cls, nil);
- cls->setInstancesRequireRawIsaRecursively(false/*inherited*/);
- }
- free(resolvedFutureClasses);
- }
- ts.log("IMAGE TIMES: realize future classes");
- if (DebugNonFragileIvars) {
- realizeAllClasses();
- }
- // Print preoptimization statistics
- if (PrintPreopt) {
- static unsigned int PreoptTotalMethodLists;
- static unsigned int PreoptOptimizedMethodLists;
- static unsigned int PreoptTotalClasses;
- static unsigned int PreoptOptimizedClasses;
- for (EACH_HEADER) {
- if (hi->hasPreoptimizedSelectors()) {
- _objc_inform("PREOPTIMIZATION: honoring preoptimized selectors "
- "in %s", hi->fname());
- }
- else if (hi->info()->optimizedByDyld()) {
- _objc_inform("PREOPTIMIZATION: IGNORING preoptimized selectors "
- "in %s", hi->fname());
- }
- classref_t const *classlist = _getObjc2ClassList(hi, &count);
- for (i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (!cls) continue;
- PreoptTotalClasses++;
- if (hi->hasPreoptimizedClasses()) {
- PreoptOptimizedClasses++;
- }
-
- const method_list_t *mlist;
- if ((mlist = ((class_ro_t *)cls->data())->baseMethods())) {
- PreoptTotalMethodLists++;
- if (mlist->isFixedUp()) {
- PreoptOptimizedMethodLists++;
- }
- }
- if ((mlist=((class_ro_t *)cls->ISA()->data())->baseMethods())) {
- PreoptTotalMethodLists++;
- if (mlist->isFixedUp()) {
- PreoptOptimizedMethodLists++;
- }
- }
- }
- }
- _objc_inform("PREOPTIMIZATION: %zu selector references not "
- "pre-optimized", UnfixedSelectors);
- _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) method lists pre-sorted",
- PreoptOptimizedMethodLists, PreoptTotalMethodLists,
- PreoptTotalMethodLists
- ? 100.0*PreoptOptimizedMethodLists/PreoptTotalMethodLists
- : 0.0);
- _objc_inform("PREOPTIMIZATION: %u/%u (%.3g%%) classes pre-registered",
- PreoptOptimizedClasses, PreoptTotalClasses,
- PreoptTotalClasses
- ? 100.0*PreoptOptimizedClasses/PreoptTotalClasses
- : 0.0);
- _objc_inform("PREOPTIMIZATION: %zu protocol references not "
- "pre-optimized", UnfixedProtocolReferences);
- }
- #undef EACH_HEADER
- }
- /***********************************************************************
- * prepare_load_methods
- * Schedule +load for classes in this image, any un-+load-ed
- * superclasses in other images, and any categories in this image.
- **********************************************************************/
- // Recursively schedule +load for cls and any un-+load-ed superclasses.
- // cls must already be connected.
- static void schedule_class_load(Class cls)
- {
- if (!cls) return;
- ASSERT(cls->isRealized()); // _read_images should realize
- if (cls->data()->flags & RW_LOADED) return;
- // Ensure superclass-first ordering
- schedule_class_load(cls->superclass);
- add_class_to_loadable_list(cls);
- cls->setInfo(RW_LOADED);
- }
- // Quick scan for +load methods that doesn't take a lock.
- bool hasLoadMethods(const headerType *mhdr)
- {
- size_t count;
- if (_getObjc2NonlazyClassList(mhdr, &count) && count > 0) return true;
- if (_getObjc2NonlazyCategoryList(mhdr, &count) && count > 0) return true;
- return false;
- }
- void prepare_load_methods(const headerType *mhdr)
- {
- size_t count, i;
- runtimeLock.assertLocked();
- classref_t const *classlist =
- _getObjc2NonlazyClassList(mhdr, &count);
- for (i = 0; i < count; i++) {
- schedule_class_load(remapClass(classlist[i]));
- }
- category_t * const *categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
- for (i = 0; i < count; i++) {
- category_t *cat = categorylist[i];
- Class cls = remapClass(cat->cls);
- if (!cls) continue; // category for ignored weak-linked class
- if (cls->isSwiftStable()) {
- _objc_fatal("Swift class extensions and categories on Swift "
- "classes are not allowed to have +load methods");
- }
- realizeClassWithoutSwift(cls, nil);
- ASSERT(cls->ISA()->isRealized());
- add_category_to_loadable_list(cat);
- }
- }
- /***********************************************************************
- * _unload_image
- * Only handles MH_BUNDLE for now.
- * Locking: write-lock and loadMethodLock acquired by unmap_image
- **********************************************************************/
- void _unload_image(header_info *hi)
- {
- size_t count, i;
- loadMethodLock.assertLocked();
- runtimeLock.assertLocked();
- // Unload unattached categories and categories waiting for +load.
- // Ignore __objc_catlist2. We don't support unloading Swift
- // and we never will.
- category_t * const *catlist = _getObjc2CategoryList(hi, &count);
- for (i = 0; i < count; i++) {
- category_t *cat = catlist[i];
- Class cls = remapClass(cat->cls);
- if (!cls) continue; // category for ignored weak-linked class
- // fixme for MH_DYLIB cat's class may have been unloaded already
- // unattached list
- objc::unattachedCategories.eraseCategoryForClass(cat, cls);
- // +load queue
- remove_category_from_loadable_list(cat);
- }
- // Unload classes.
- // Gather classes from both __DATA,__objc_clslist
- // and __DATA,__objc_nlclslist. arclite's hack puts a class in the latter
- // only, and we need to unload that class if we unload an arclite image.
- objc::DenseSet<Class> classes{};
- classref_t const *classlist;
- classlist = _getObjc2ClassList(hi, &count);
- for (i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (cls) classes.insert(cls);
- }
- classlist = _getObjc2NonlazyClassList(hi, &count);
- for (i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (cls) classes.insert(cls);
- }
- // First detach classes from each other. Then free each class.
- // This avoid bugs where this loop unloads a subclass before its superclass
- for (Class cls: classes) {
- remove_class_from_loadable_list(cls);
- detach_class(cls->ISA(), YES);
- detach_class(cls, NO);
- }
- for (Class cls: classes) {
- free_class(cls->ISA());
- free_class(cls);
- }
- // XXX FIXME -- Clean up protocols:
- // <rdar://problem/9033191> Support unloading protocols at dylib/image unload time
- // fixme DebugUnload
- }
- /***********************************************************************
- * method_getDescription
- * Returns a pointer to this method's objc_method_description.
- * Locking: none
- **********************************************************************/
- struct objc_method_description *
- method_getDescription(Method m)
- {
- if (!m) return nil;
- return (struct objc_method_description *)m;
- }
- IMP
- method_getImplementation(Method m)
- {
- return m ? m->imp : nil;
- }
- /***********************************************************************
- * method_getName
- * Returns this method's selector.
- * The method must not be nil.
- * The method must already have been fixed-up.
- * Locking: none
- **********************************************************************/
- SEL
- method_getName(Method m)
- {
- if (!m) return nil;
- ASSERT(m->name == sel_registerName(sel_getName(m->name)));
- return m->name;
- }
- /***********************************************************************
- * method_getTypeEncoding
- * Returns this method's old-style type encoding string.
- * The method must not be nil.
- * Locking: none
- **********************************************************************/
- const char *
- method_getTypeEncoding(Method m)
- {
- if (!m) return nil;
- return m->types;
- }
- /***********************************************************************
- * method_setImplementation
- * Sets this method's implementation to imp.
- * The previous implementation is returned.
- **********************************************************************/
- static IMP
- _method_setImplementation(Class cls, method_t *m, IMP imp)
- {
- runtimeLock.assertLocked();
- if (!m) return nil;
- if (!imp) return nil;
- IMP old = m->imp;
- m->imp = imp;
- // Cache updates are slow if cls is nil (i.e. unknown)
- // RR/AWZ updates are slow if cls is nil (i.e. unknown)
- // fixme build list of classes whose Methods are known externally?
- flushCaches(cls);
- adjustCustomFlagsForMethodChange(cls, m);
- return old;
- }
- IMP
- method_setImplementation(Method m, IMP imp)
- {
- // Don't know the class - will be slow if RR/AWZ are affected
- // fixme build list of classes whose Methods are known externally?
- mutex_locker_t lock(runtimeLock);
- return _method_setImplementation(Nil, m, imp);
- }
- void method_exchangeImplementations(Method m1, Method m2)
- {
- if (!m1 || !m2) return;
- mutex_locker_t lock(runtimeLock);
- IMP m1_imp = m1->imp;
- m1->imp = m2->imp;
- m2->imp = m1_imp;
- // RR/AWZ updates are slow because class is unknown
- // Cache updates are slow because class is unknown
- // fixme build list of classes whose Methods are known externally?
- flushCaches(nil);
- adjustCustomFlagsForMethodChange(nil, m1);
- adjustCustomFlagsForMethodChange(nil, m2);
- }
- /***********************************************************************
- * ivar_getOffset
- * fixme
- * Locking: none
- **********************************************************************/
- ptrdiff_t
- ivar_getOffset(Ivar ivar)
- {
- if (!ivar) return 0;
- return *ivar->offset;
- }
- /***********************************************************************
- * ivar_getName
- * fixme
- * Locking: none
- **********************************************************************/
- const char *
- ivar_getName(Ivar ivar)
- {
- if (!ivar) return nil;
- return ivar->name;
- }
- /***********************************************************************
- * ivar_getTypeEncoding
- * fixme
- * Locking: none
- **********************************************************************/
- const char *
- ivar_getTypeEncoding(Ivar ivar)
- {
- if (!ivar) return nil;
- return ivar->type;
- }
- const char *property_getName(objc_property_t prop)
- {
- return prop->name;
- }
- const char *property_getAttributes(objc_property_t prop)
- {
- return prop->attributes;
- }
- objc_property_attribute_t *property_copyAttributeList(objc_property_t prop,
- unsigned int *outCount)
- {
- if (!prop) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- return copyPropertyAttributeList(prop->attributes,outCount);
- }
- char * property_copyAttributeValue(objc_property_t prop, const char *name)
- {
- if (!prop || !name || *name == '\0') return nil;
-
- mutex_locker_t lock(runtimeLock);
- return copyPropertyAttributeValue(prop->attributes, name);
- }
- /***********************************************************************
- * getExtendedTypesIndexesForMethod
- * Returns:
- * a is the count of methods in all method lists before m's method list
- * b is the index of m in m's method list
- * a+b is the index of m's extended types in the extended types array
- **********************************************************************/
- static void getExtendedTypesIndexesForMethod(protocol_t *proto, const method_t *m, bool isRequiredMethod, bool isInstanceMethod, uint32_t& a, uint32_t &b)
- {
- a = 0;
- if (proto->instanceMethods) {
- if (isRequiredMethod && isInstanceMethod) {
- b = proto->instanceMethods->indexOfMethod(m);
- return;
- }
- a += proto->instanceMethods->count;
- }
- if (proto->classMethods) {
- if (isRequiredMethod && !isInstanceMethod) {
- b = proto->classMethods->indexOfMethod(m);
- return;
- }
- a += proto->classMethods->count;
- }
- if (proto->optionalInstanceMethods) {
- if (!isRequiredMethod && isInstanceMethod) {
- b = proto->optionalInstanceMethods->indexOfMethod(m);
- return;
- }
- a += proto->optionalInstanceMethods->count;
- }
- if (proto->optionalClassMethods) {
- if (!isRequiredMethod && !isInstanceMethod) {
- b = proto->optionalClassMethods->indexOfMethod(m);
- return;
- }
- a += proto->optionalClassMethods->count;
- }
- }
- /***********************************************************************
- * getExtendedTypesIndexForMethod
- * Returns the index of m's extended types in proto's extended types array.
- **********************************************************************/
- static uint32_t getExtendedTypesIndexForMethod(protocol_t *proto, const method_t *m, bool isRequiredMethod, bool isInstanceMethod)
- {
- uint32_t a;
- uint32_t b;
- getExtendedTypesIndexesForMethod(proto, m, isRequiredMethod,
- isInstanceMethod, a, b);
- return a + b;
- }
- /***********************************************************************
- * fixupProtocolMethodList
- * Fixes up a single method list in a protocol.
- **********************************************************************/
- static void
- fixupProtocolMethodList(protocol_t *proto, method_list_t *mlist,
- bool required, bool instance)
- {
- runtimeLock.assertLocked();
- if (!mlist) return;
- if (mlist->isFixedUp()) return;
- const char **extTypes = proto->extendedMethodTypes();
- fixupMethodList(mlist, true/*always copy for simplicity*/,
- !extTypes/*sort if no extended method types*/);
-
- if (extTypes) {
- // Sort method list and extended method types together.
- // fixupMethodList() can't do this.
- // fixme COW stomp
- uint32_t count = mlist->count;
- uint32_t prefix;
- uint32_t junk;
- getExtendedTypesIndexesForMethod(proto, &mlist->get(0),
- required, instance, prefix, junk);
- for (uint32_t i = 0; i < count; i++) {
- for (uint32_t j = i+1; j < count; j++) {
- method_t& mi = mlist->get(i);
- method_t& mj = mlist->get(j);
- if (mi.name > mj.name) {
- std::swap(mi, mj);
- std::swap(extTypes[prefix+i], extTypes[prefix+j]);
- }
- }
- }
- }
- }
- /***********************************************************************
- * fixupProtocol
- * Fixes up all of a protocol's method lists.
- **********************************************************************/
- static void
- fixupProtocol(protocol_t *proto)
- {
- runtimeLock.assertLocked();
- if (proto->protocols) {
- for (uintptr_t i = 0; i < proto->protocols->count; i++) {
- protocol_t *sub = remapProtocol(proto->protocols->list[i]);
- if (!sub->isFixedUp()) fixupProtocol(sub);
- }
- }
- fixupProtocolMethodList(proto, proto->instanceMethods, YES, YES);
- fixupProtocolMethodList(proto, proto->classMethods, YES, NO);
- fixupProtocolMethodList(proto, proto->optionalInstanceMethods, NO, YES);
- fixupProtocolMethodList(proto, proto->optionalClassMethods, NO, NO);
- // fixme memory barrier so we can check this with no lock
- proto->setFixedUp();
- }
- /***********************************************************************
- * fixupProtocolIfNeeded
- * Fixes up all of a protocol's method lists if they aren't fixed up already.
- * Locking: write-locks runtimeLock.
- **********************************************************************/
- static void
- fixupProtocolIfNeeded(protocol_t *proto)
- {
- runtimeLock.assertUnlocked();
- ASSERT(proto);
- if (!proto->isFixedUp()) {
- mutex_locker_t lock(runtimeLock);
- fixupProtocol(proto);
- }
- }
- static method_list_t *
- getProtocolMethodList(protocol_t *proto, bool required, bool instance)
- {
- method_list_t **mlistp = nil;
- if (required) {
- if (instance) {
- mlistp = &proto->instanceMethods;
- } else {
- mlistp = &proto->classMethods;
- }
- } else {
- if (instance) {
- mlistp = &proto->optionalInstanceMethods;
- } else {
- mlistp = &proto->optionalClassMethods;
- }
- }
- return *mlistp;
- }
- /***********************************************************************
- * protocol_getMethod_nolock
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static method_t *
- protocol_getMethod_nolock(protocol_t *proto, SEL sel,
- bool isRequiredMethod, bool isInstanceMethod,
- bool recursive)
- {
- runtimeLock.assertLocked();
- if (!proto || !sel) return nil;
- ASSERT(proto->isFixedUp());
- method_list_t *mlist =
- getProtocolMethodList(proto, isRequiredMethod, isInstanceMethod);
- if (mlist) {
- method_t *m = search_method_list(mlist, sel);
- if (m) return m;
- }
- if (recursive && proto->protocols) {
- method_t *m;
- for (uint32_t i = 0; i < proto->protocols->count; i++) {
- protocol_t *realProto = remapProtocol(proto->protocols->list[i]);
- m = protocol_getMethod_nolock(realProto, sel,
- isRequiredMethod, isInstanceMethod,
- true);
- if (m) return m;
- }
- }
- return nil;
- }
- /***********************************************************************
- * protocol_getMethod
- * fixme
- * Locking: acquires runtimeLock
- **********************************************************************/
- Method
- protocol_getMethod(protocol_t *proto, SEL sel, bool isRequiredMethod, bool isInstanceMethod, bool recursive)
- {
- if (!proto) return nil;
- fixupProtocolIfNeeded(proto);
- mutex_locker_t lock(runtimeLock);
- return protocol_getMethod_nolock(proto, sel, isRequiredMethod,
- isInstanceMethod, recursive);
- }
- /***********************************************************************
- * protocol_getMethodTypeEncoding_nolock
- * Return the @encode string for the requested protocol method.
- * Returns nil if the compiler did not emit any extended @encode data.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- const char *
- protocol_getMethodTypeEncoding_nolock(protocol_t *proto, SEL sel,
- bool isRequiredMethod,
- bool isInstanceMethod)
- {
- runtimeLock.assertLocked();
- if (!proto) return nil;
- if (!proto->extendedMethodTypes()) return nil;
- ASSERT(proto->isFixedUp());
- method_t *m =
- protocol_getMethod_nolock(proto, sel,
- isRequiredMethod, isInstanceMethod, false);
- if (m) {
- uint32_t i = getExtendedTypesIndexForMethod(proto, m,
- isRequiredMethod,
- isInstanceMethod);
- return proto->extendedMethodTypes()[i];
- }
- // No method with that name. Search incorporated protocols.
- if (proto->protocols) {
- for (uintptr_t i = 0; i < proto->protocols->count; i++) {
- const char *enc =
- protocol_getMethodTypeEncoding_nolock(remapProtocol(proto->protocols->list[i]), sel, isRequiredMethod, isInstanceMethod);
- if (enc) return enc;
- }
- }
- return nil;
- }
- /***********************************************************************
- * _protocol_getMethodTypeEncoding
- * Return the @encode string for the requested protocol method.
- * Returns nil if the compiler did not emit any extended @encode data.
- * Locking: acquires runtimeLock
- **********************************************************************/
- const char *
- _protocol_getMethodTypeEncoding(Protocol *proto_gen, SEL sel,
- BOOL isRequiredMethod, BOOL isInstanceMethod)
- {
- protocol_t *proto = newprotocol(proto_gen);
- if (!proto) return nil;
- fixupProtocolIfNeeded(proto);
- mutex_locker_t lock(runtimeLock);
- return protocol_getMethodTypeEncoding_nolock(proto, sel,
- isRequiredMethod,
- isInstanceMethod);
- }
- /***********************************************************************
- * protocol_t::demangledName
- * Returns the (Swift-demangled) name of the given protocol.
- * Locking: none
- **********************************************************************/
- const char *
- protocol_t::demangledName()
- {
- ASSERT(hasDemangledNameField());
-
- if (! _demangledName) {
- char *de = copySwiftV1DemangledName(mangledName, true/*isProtocol*/);
- if (! OSAtomicCompareAndSwapPtrBarrier(nil, (void*)(de ?: mangledName),
- (void**)&_demangledName))
- {
- if (de) free(de);
- }
- }
- return _demangledName;
- }
- /***********************************************************************
- * protocol_getName
- * Returns the (Swift-demangled) name of the given protocol.
- * Locking: runtimeLock must not be held by the caller
- **********************************************************************/
- const char *
- protocol_getName(Protocol *proto)
- {
- if (!proto) return "nil";
- else return newprotocol(proto)->demangledName();
- }
- /***********************************************************************
- * protocol_getInstanceMethodDescription
- * Returns the description of a named instance method.
- * Locking: runtimeLock must not be held by the caller
- **********************************************************************/
- struct objc_method_description
- protocol_getMethodDescription(Protocol *p, SEL aSel,
- BOOL isRequiredMethod, BOOL isInstanceMethod)
- {
- Method m =
- protocol_getMethod(newprotocol(p), aSel,
- isRequiredMethod, isInstanceMethod, true);
- if (m) return *method_getDescription(m);
- else return (struct objc_method_description){nil, nil};
- }
- /***********************************************************************
- * protocol_conformsToProtocol_nolock
- * Returns YES if self conforms to other.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static bool
- protocol_conformsToProtocol_nolock(protocol_t *self, protocol_t *other)
- {
- runtimeLock.assertLocked();
- if (!self || !other) {
- return NO;
- }
- // protocols need not be fixed up
- if (0 == strcmp(self->mangledName, other->mangledName)) {
- return YES;
- }
- if (self->protocols) {
- uintptr_t i;
- for (i = 0; i < self->protocols->count; i++) {
- protocol_t *proto = remapProtocol(self->protocols->list[i]);
- if (other == proto) {
- return YES;
- }
- if (0 == strcmp(other->mangledName, proto->mangledName)) {
- return YES;
- }
- if (protocol_conformsToProtocol_nolock(proto, other)) {
- return YES;
- }
- }
- }
- return NO;
- }
- /***********************************************************************
- * protocol_conformsToProtocol
- * Returns YES if self conforms to other.
- * Locking: acquires runtimeLock
- **********************************************************************/
- BOOL protocol_conformsToProtocol(Protocol *self, Protocol *other)
- {
- mutex_locker_t lock(runtimeLock);
- return protocol_conformsToProtocol_nolock(newprotocol(self),
- newprotocol(other));
- }
- /***********************************************************************
- * protocol_isEqual
- * Return YES if two protocols are equal (i.e. conform to each other)
- * Locking: acquires runtimeLock
- **********************************************************************/
- BOOL protocol_isEqual(Protocol *self, Protocol *other)
- {
- if (self == other) return YES;
- if (!self || !other) return NO;
- if (!protocol_conformsToProtocol(self, other)) return NO;
- if (!protocol_conformsToProtocol(other, self)) return NO;
- return YES;
- }
- /***********************************************************************
- * protocol_copyMethodDescriptionList
- * Returns descriptions of a protocol's methods.
- * Locking: acquires runtimeLock
- **********************************************************************/
- struct objc_method_description *
- protocol_copyMethodDescriptionList(Protocol *p,
- BOOL isRequiredMethod,BOOL isInstanceMethod,
- unsigned int *outCount)
- {
- protocol_t *proto = newprotocol(p);
- struct objc_method_description *result = nil;
- unsigned int count = 0;
- if (!proto) {
- if (outCount) *outCount = 0;
- return nil;
- }
- fixupProtocolIfNeeded(proto);
- mutex_locker_t lock(runtimeLock);
- method_list_t *mlist =
- getProtocolMethodList(proto, isRequiredMethod, isInstanceMethod);
- if (mlist) {
- result = (struct objc_method_description *)
- calloc(mlist->count + 1, sizeof(struct objc_method_description));
- for (const auto& meth : *mlist) {
- result[count].name = meth.name;
- result[count].types = (char *)meth.types;
- count++;
- }
- }
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * protocol_getProperty
- * fixme
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static property_t *
- protocol_getProperty_nolock(protocol_t *proto, const char *name,
- bool isRequiredProperty, bool isInstanceProperty)
- {
- runtimeLock.assertLocked();
- if (!isRequiredProperty) {
- // Only required properties are currently supported.
- return nil;
- }
- property_list_t *plist = isInstanceProperty ?
- proto->instanceProperties : proto->classProperties();
- if (plist) {
- for (auto& prop : *plist) {
- if (0 == strcmp(name, prop.name)) {
- return ∝
- }
- }
- }
- if (proto->protocols) {
- uintptr_t i;
- for (i = 0; i < proto->protocols->count; i++) {
- protocol_t *p = remapProtocol(proto->protocols->list[i]);
- property_t *prop =
- protocol_getProperty_nolock(p, name,
- isRequiredProperty,
- isInstanceProperty);
- if (prop) return prop;
- }
- }
- return nil;
- }
- objc_property_t protocol_getProperty(Protocol *p, const char *name,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
- {
- if (!p || !name) return nil;
- mutex_locker_t lock(runtimeLock);
- return (objc_property_t)
- protocol_getProperty_nolock(newprotocol(p), name,
- isRequiredProperty, isInstanceProperty);
- }
- /***********************************************************************
- * protocol_copyPropertyList
- * protocol_copyPropertyList2
- * fixme
- * Locking: acquires runtimeLock
- **********************************************************************/
- static property_t **
- copyPropertyList(property_list_t *plist, unsigned int *outCount)
- {
- property_t **result = nil;
- unsigned int count = 0;
- if (plist) {
- count = plist->count;
- }
- if (count > 0) {
- result = (property_t **)malloc((count+1) * sizeof(property_t *));
- count = 0;
- for (auto& prop : *plist) {
- result[count++] = ∝
- }
- result[count] = nil;
- }
- if (outCount) *outCount = count;
- return result;
- }
- objc_property_t *
- protocol_copyPropertyList2(Protocol *proto, unsigned int *outCount,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
- {
- if (!proto || !isRequiredProperty) {
- // Optional properties are not currently supported.
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- property_list_t *plist = isInstanceProperty
- ? newprotocol(proto)->instanceProperties
- : newprotocol(proto)->classProperties();
- return (objc_property_t *)copyPropertyList(plist, outCount);
- }
- objc_property_t *
- protocol_copyPropertyList(Protocol *proto, unsigned int *outCount)
- {
- return protocol_copyPropertyList2(proto, outCount,
- YES/*required*/, YES/*instance*/);
- }
- /***********************************************************************
- * protocol_copyProtocolList
- * Copies this protocol's incorporated protocols.
- * Does not copy those protocol's incorporated protocols in turn.
- * Locking: acquires runtimeLock
- **********************************************************************/
- Protocol * __unsafe_unretained *
- protocol_copyProtocolList(Protocol *p, unsigned int *outCount)
- {
- unsigned int count = 0;
- Protocol **result = nil;
- protocol_t *proto = newprotocol(p);
-
- if (!proto) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- if (proto->protocols) {
- count = (unsigned int)proto->protocols->count;
- }
- if (count > 0) {
- result = (Protocol **)malloc((count+1) * sizeof(Protocol *));
- unsigned int i;
- for (i = 0; i < count; i++) {
- result[i] = (Protocol *)remapProtocol(proto->protocols->list[i]);
- }
- result[i] = nil;
- }
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * objc_allocateProtocol
- * Creates a new protocol. The protocol may not be used until
- * objc_registerProtocol() is called.
- * Returns nil if a protocol with the same name already exists.
- * Locking: acquires runtimeLock
- **********************************************************************/
- Protocol *
- objc_allocateProtocol(const char *name)
- {
- mutex_locker_t lock(runtimeLock);
- if (getProtocol(name)) {
- return nil;
- }
- protocol_t *result = (protocol_t *)calloc(sizeof(protocol_t), 1);
- extern objc_class OBJC_CLASS_$___IncompleteProtocol;
- Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
- result->initProtocolIsa(cls);
- result->size = sizeof(protocol_t);
- // fixme mangle the name if it looks swift-y?
- result->mangledName = strdupIfMutable(name);
- // fixme reserve name without installing
- return (Protocol *)result;
- }
- /***********************************************************************
- * objc_registerProtocol
- * Registers a newly-constructed protocol. The protocol is now
- * ready for use and immutable.
- * Locking: acquires runtimeLock
- **********************************************************************/
- void objc_registerProtocol(Protocol *proto_gen)
- {
- protocol_t *proto = newprotocol(proto_gen);
- mutex_locker_t lock(runtimeLock);
- extern objc_class OBJC_CLASS_$___IncompleteProtocol;
- Class oldcls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
- extern objc_class OBJC_CLASS_$_Protocol;
- Class cls = (Class)&OBJC_CLASS_$_Protocol;
- if (proto->ISA() == cls) {
- _objc_inform("objc_registerProtocol: protocol '%s' was already "
- "registered!", proto->nameForLogging());
- return;
- }
- if (proto->ISA() != oldcls) {
- _objc_inform("objc_registerProtocol: protocol '%s' was not allocated "
- "with objc_allocateProtocol!", proto->nameForLogging());
- return;
- }
- // NOT initProtocolIsa(). The protocol object may already
- // have been retained and we must preserve that count.
- proto->changeIsa(cls);
- // Don't add this protocol if we already have it.
- // Should we warn on duplicates?
- if (getProtocol(proto->mangledName) == nil) {
- NXMapKeyCopyingInsert(protocols(), proto->mangledName, proto);
- }
- }
- /***********************************************************************
- * protocol_addProtocol
- * Adds an incorporated protocol to another protocol.
- * No method enforcement is performed.
- * `proto` must be under construction. `addition` must not.
- * Locking: acquires runtimeLock
- **********************************************************************/
- void
- protocol_addProtocol(Protocol *proto_gen, Protocol *addition_gen)
- {
- protocol_t *proto = newprotocol(proto_gen);
- protocol_t *addition = newprotocol(addition_gen);
- extern objc_class OBJC_CLASS_$___IncompleteProtocol;
- Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
- if (!proto_gen) return;
- if (!addition_gen) return;
- mutex_locker_t lock(runtimeLock);
- if (proto->ISA() != cls) {
- _objc_inform("protocol_addProtocol: modified protocol '%s' is not "
- "under construction!", proto->nameForLogging());
- return;
- }
- if (addition->ISA() == cls) {
- _objc_inform("protocol_addProtocol: added protocol '%s' is still "
- "under construction!", addition->nameForLogging());
- return;
- }
-
- protocol_list_t *protolist = proto->protocols;
- if (!protolist) {
- protolist = (protocol_list_t *)
- calloc(1, sizeof(protocol_list_t)
- + sizeof(protolist->list[0]));
- } else {
- protolist = (protocol_list_t *)
- realloc(protolist, protocol_list_size(protolist)
- + sizeof(protolist->list[0]));
- }
- protolist->list[protolist->count++] = (protocol_ref_t)addition;
- proto->protocols = protolist;
- }
- /***********************************************************************
- * protocol_addMethodDescription
- * Adds a method to a protocol. The protocol must be under construction.
- * Locking: acquires runtimeLock
- **********************************************************************/
- static void
- protocol_addMethod_nolock(method_list_t*& list, SEL name, const char *types)
- {
- if (!list) {
- list = (method_list_t *)calloc(sizeof(method_list_t), 1);
- list->entsizeAndFlags = sizeof(list->first);
- list->setFixedUp();
- } else {
- size_t size = list->byteSize() + list->entsize();
- list = (method_list_t *)realloc(list, size);
- }
- method_t& meth = list->get(list->count++);
- meth.name = name;
- meth.types = types ? strdupIfMutable(types) : "";
- meth.imp = nil;
- }
- void
- protocol_addMethodDescription(Protocol *proto_gen, SEL name, const char *types,
- BOOL isRequiredMethod, BOOL isInstanceMethod)
- {
- protocol_t *proto = newprotocol(proto_gen);
- extern objc_class OBJC_CLASS_$___IncompleteProtocol;
- Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
- if (!proto_gen) return;
- mutex_locker_t lock(runtimeLock);
- if (proto->ISA() != cls) {
- _objc_inform("protocol_addMethodDescription: protocol '%s' is not "
- "under construction!", proto->nameForLogging());
- return;
- }
- if (isRequiredMethod && isInstanceMethod) {
- protocol_addMethod_nolock(proto->instanceMethods, name, types);
- } else if (isRequiredMethod && !isInstanceMethod) {
- protocol_addMethod_nolock(proto->classMethods, name, types);
- } else if (!isRequiredMethod && isInstanceMethod) {
- protocol_addMethod_nolock(proto->optionalInstanceMethods, name,types);
- } else /* !isRequiredMethod && !isInstanceMethod) */ {
- protocol_addMethod_nolock(proto->optionalClassMethods, name, types);
- }
- }
- /***********************************************************************
- * protocol_addProperty
- * Adds a property to a protocol. The protocol must be under construction.
- * Locking: acquires runtimeLock
- **********************************************************************/
- static void
- protocol_addProperty_nolock(property_list_t *&plist, const char *name,
- const objc_property_attribute_t *attrs,
- unsigned int count)
- {
- if (!plist) {
- plist = (property_list_t *)calloc(sizeof(property_list_t), 1);
- plist->entsizeAndFlags = sizeof(property_t);
- } else {
- plist = (property_list_t *)
- realloc(plist, sizeof(property_list_t)
- + plist->count * plist->entsize());
- }
- property_t& prop = plist->get(plist->count++);
- prop.name = strdupIfMutable(name);
- prop.attributes = copyPropertyAttributeString(attrs, count);
- }
- void
- protocol_addProperty(Protocol *proto_gen, const char *name,
- const objc_property_attribute_t *attrs,
- unsigned int count,
- BOOL isRequiredProperty, BOOL isInstanceProperty)
- {
- protocol_t *proto = newprotocol(proto_gen);
- extern objc_class OBJC_CLASS_$___IncompleteProtocol;
- Class cls = (Class)&OBJC_CLASS_$___IncompleteProtocol;
- if (!proto) return;
- if (!name) return;
- mutex_locker_t lock(runtimeLock);
- if (proto->ISA() != cls) {
- _objc_inform("protocol_addProperty: protocol '%s' is not "
- "under construction!", proto->nameForLogging());
- return;
- }
- if (isRequiredProperty && isInstanceProperty) {
- protocol_addProperty_nolock(proto->instanceProperties, name, attrs, count);
- }
- else if (isRequiredProperty && !isInstanceProperty) {
- protocol_addProperty_nolock(proto->_classProperties, name, attrs, count);
- }
- //else if (!isRequiredProperty && isInstanceProperty) {
- // protocol_addProperty_nolock(proto->optionalInstanceProperties, name, attrs, count);
- //}
- //else /* !isRequiredProperty && !isInstanceProperty) */ {
- // protocol_addProperty_nolock(proto->optionalClassProperties, name, attrs, count);
- //}
- }
- static int
- objc_getRealizedClassList_nolock(Class *buffer, int bufferLen)
- {
- int count = 0;
- if (buffer) {
- int c = 0;
- foreach_realized_class([=, &count, &c](Class cls) {
- count++;
- if (c < bufferLen) {
- buffer[c++] = cls;
- }
- return true;
- });
- } else {
- foreach_realized_class([&count](Class cls) {
- count++;
- return true;
- });
- }
- return count;
- }
- static Class *
- objc_copyRealizedClassList_nolock(unsigned int *outCount)
- {
- Class *result = nil;
- unsigned int count = 0;
- foreach_realized_class([&count](Class cls) {
- count++;
- return true;
- });
- if (count > 0) {
- unsigned int c = 0;
- result = (Class *)malloc((1+count) * sizeof(Class));
- foreach_realized_class([=, &c](Class cls) {
- result[c++] = cls;
- return true;
- });
- result[c] = nil;
- }
- if (outCount) *outCount = count;
- return result;
- }
- static void
- class_getImpCache_nolock(Class cls, cache_t &cache, objc_imp_cache_entry *buffer, int len)
- {
- bucket_t *buckets = cache.buckets();
- uintptr_t count = cache.capacity();
- uintptr_t index;
- int wpos = 0;
- for (index = 0; index < count && wpos < len; index += 1) {
- if (buckets[index].sel()) {
- buffer[wpos].imp = buckets[index].imp(cls);
- buffer[wpos].sel = buckets[index].sel();
- wpos++;
- }
- }
- }
- /***********************************************************************
- * objc_getClassList
- * Returns pointers to all classes.
- * This requires all classes be realized, which is regretfully non-lazy.
- * Locking: acquires runtimeLock
- **********************************************************************/
- int
- objc_getClassList(Class *buffer, int bufferLen)
- {
- mutex_locker_t lock(runtimeLock);
- realizeAllClasses();
- return objc_getRealizedClassList_nolock(buffer, bufferLen);
- }
- /***********************************************************************
- * objc_copyClassList
- * Returns pointers to Realized classes.
- *
- * outCount may be nil. *outCount is the number of classes returned.
- * If the returned array is not nil, it is nil-terminated and must be
- * freed with free().
- * Locking: write-locks runtimeLock
- **********************************************************************/
- Class *
- objc_copyRealizedClassList(unsigned int *outCount)
- {
- mutex_locker_t lock(runtimeLock);
- return objc_copyRealizedClassList_nolock(outCount);
- }
- /***********************************************************************
- * objc_copyClassList
- * Returns pointers to all classes.
- * This requires all classes be realized, which is regretfully non-lazy.
- *
- * outCount may be nil. *outCount is the number of classes returned.
- * If the returned array is not nil, it is nil-terminated and must be
- * freed with free().
- * Locking: write-locks runtimeLock
- **********************************************************************/
- Class *
- objc_copyClassList(unsigned int *outCount)
- {
- mutex_locker_t lock(runtimeLock);
- realizeAllClasses();
- return objc_copyRealizedClassList_nolock(outCount);
- }
- /***********************************************************************
- * class_copyImpCache
- * Returns the current content of the Class IMP Cache
- *
- * outCount may be nil. *outCount is the number of entries returned.
- * If the returned array is not nil, it is nil-terminated and must be
- * freed with free().
- * Locking: write-locks cacheUpdateLock
- **********************************************************************/
- objc_imp_cache_entry *
- class_copyImpCache(Class cls, int *outCount)
- {
- objc_imp_cache_entry *buffer = nullptr;
- #if CONFIG_USE_CACHE_LOCK
- mutex_locker_t lock(cacheUpdateLock);
- #else
- mutex_locker_t lock(runtimeLock);
- #endif
- cache_t &cache = cls->cache;
- int count = (int)cache.occupied();
- if (count) {
- buffer = (objc_imp_cache_entry *)calloc(1+count, sizeof(objc_imp_cache_entry));
- class_getImpCache_nolock(cls, cache, buffer, count);
- }
- if (outCount) *outCount = count;
- return buffer;
- }
- /***********************************************************************
- * objc_copyProtocolList
- * Returns pointers to all protocols.
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Protocol * __unsafe_unretained *
- objc_copyProtocolList(unsigned int *outCount)
- {
- mutex_locker_t lock(runtimeLock);
- NXMapTable *protocol_map = protocols();
- // Find all the protocols from the pre-optimized images. These protocols
- // won't be in the protocol map.
- objc::DenseMap<const char*, Protocol*> preoptimizedProtocols;
- if (sharedCacheSupportsProtocolRoots()) {
- header_info *hi;
- for (hi = FirstHeader; hi; hi = hi->getNext()) {
- if (!hi->hasPreoptimizedProtocols())
- continue;
- size_t count, i;
- const protocol_t * const *protolist = _getObjc2ProtocolList(hi, &count);
- for (i = 0; i < count; i++) {
- const protocol_t* protocol = protolist[i];
- // Skip protocols we have in the run time map. These likely
- // correspond to protocols added dynamically which have the same
- // name as a protocol found later in a dlopen'ed shared cache image.
- if (NXMapGet(protocol_map, protocol->mangledName) != nil)
- continue;
- // The protocols in the shared cache protolist point to their
- // original on-disk object, not the optimized one. We can use the name
- // to find the optimized one.
- Protocol* optimizedProto = getPreoptimizedProtocol(protocol->mangledName);
- preoptimizedProtocols.insert({ protocol->mangledName, optimizedProto });
- }
- }
- }
- unsigned int count = NXCountMapTable(protocol_map) + (unsigned int)preoptimizedProtocols.size();
- if (count == 0) {
- if (outCount) *outCount = 0;
- return nil;
- }
- Protocol **result = (Protocol **)malloc((count+1) * sizeof(Protocol*));
- unsigned int i = 0;
- Protocol *proto;
- const char *name;
- NXMapState state = NXInitMapState(protocol_map);
- while (NXNextMapState(protocol_map, &state,
- (const void **)&name, (const void **)&proto))
- {
- result[i++] = proto;
- }
- // Add any protocols found in the pre-optimized table
- for (auto it : preoptimizedProtocols) {
- result[i++] = it.second;
- }
-
- result[i++] = nil;
- ASSERT(i == count+1);
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * objc_getProtocol
- * Get a protocol by name, or return nil
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Protocol *objc_getProtocol(const char *name)
- {
- mutex_locker_t lock(runtimeLock);
- return getProtocol(name);
- }
- /***********************************************************************
- * class_copyMethodList
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Method *
- class_copyMethodList(Class cls, unsigned int *outCount)
- {
- unsigned int count = 0;
- Method *result = nil;
- if (!cls) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
-
- ASSERT(cls->isRealized());
- count = cls->data()->methods.count();
- if (count > 0) {
- result = (Method *)malloc((count + 1) * sizeof(Method));
-
- count = 0;
- for (auto& meth : cls->data()->methods) {
- result[count++] = &meth;
- }
- result[count] = nil;
- }
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * class_copyIvarList
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Ivar *
- class_copyIvarList(Class cls, unsigned int *outCount)
- {
- const ivar_list_t *ivars;
- Ivar *result = nil;
- unsigned int count = 0;
- if (!cls) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- ASSERT(cls->isRealized());
-
- if ((ivars = cls->data()->ro->ivars) && ivars->count) {
- result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar));
-
- for (auto& ivar : *ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- result[count++] = &ivar;
- }
- result[count] = nil;
- }
-
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * class_copyPropertyList. Returns a heap block containing the
- * properties declared in the class, or nil if the class
- * declares no properties. Caller must free the block.
- * Does not copy any superclass's properties.
- * Locking: read-locks runtimeLock
- **********************************************************************/
- objc_property_t *
- class_copyPropertyList(Class cls, unsigned int *outCount)
- {
- if (!cls) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
- ASSERT(cls->isRealized());
-
- auto rw = cls->data();
- property_t **result = nil;
- unsigned int count = rw->properties.count();
- if (count > 0) {
- result = (property_t **)malloc((count + 1) * sizeof(property_t *));
- count = 0;
- for (auto& prop : rw->properties) {
- result[count++] = ∝
- }
- result[count] = nil;
- }
- if (outCount) *outCount = count;
- return (objc_property_t *)result;
- }
- /***********************************************************************
- * objc_class::getLoadMethod
- * fixme
- * Called only from add_class_to_loadable_list.
- * Locking: runtimeLock must be read- or write-locked by the caller.
- **********************************************************************/
- IMP
- objc_class::getLoadMethod()
- {
- runtimeLock.assertLocked();
- const method_list_t *mlist;
- ASSERT(isRealized());
- ASSERT(ISA()->isRealized());
- ASSERT(!isMetaClass());
- ASSERT(ISA()->isMetaClass());
- mlist = ISA()->data()->ro->baseMethods();
- if (mlist) {
- for (const auto& meth : *mlist) {
- const char *name = sel_cname(meth.name);
- if (0 == strcmp(name, "load")) {
- return meth.imp;
- }
- }
- }
- return nil;
- }
- /***********************************************************************
- * _category_getName
- * Returns a category's name.
- * Locking: none
- **********************************************************************/
- const char *
- _category_getName(Category cat)
- {
- return cat->name;
- }
- /***********************************************************************
- * _category_getClassName
- * Returns a category's class's name
- * Called only from add_category_to_loadable_list and
- * remove_category_from_loadable_list for logging purposes.
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- const char *
- _category_getClassName(Category cat)
- {
- runtimeLock.assertLocked();
- return remapClass(cat->cls)->nameForLogging();
- }
- /***********************************************************************
- * _category_getClass
- * Returns a category's class
- * Called only by call_category_loads.
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Class
- _category_getClass(Category cat)
- {
- mutex_locker_t lock(runtimeLock);
- Class result = remapClass(cat->cls);
- ASSERT(result->isRealized()); // ok for call_category_loads' usage
- return result;
- }
- /***********************************************************************
- * _category_getLoadMethod
- * fixme
- * Called only from add_category_to_loadable_list
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- IMP
- _category_getLoadMethod(Category cat)
- {
- runtimeLock.assertLocked();
- const method_list_t *mlist;
- mlist = cat->classMethods;
- if (mlist) {
- for (const auto& meth : *mlist) {
- const char *name = sel_cname(meth.name);
- if (0 == strcmp(name, "load")) {
- return meth.imp;
- }
- }
- }
- return nil;
- }
- /***********************************************************************
- * category_t::propertiesForMeta
- * Return a category's instance or class properties.
- * hi is the image containing the category.
- **********************************************************************/
- property_list_t *
- category_t::propertiesForMeta(bool isMeta, struct header_info *hi)
- {
- if (!isMeta) return instanceProperties;
- else if (hi->info()->hasCategoryClassProperties()) return _classProperties;
- else return nil;
- }
- /***********************************************************************
- * class_copyProtocolList
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Protocol * __unsafe_unretained *
- class_copyProtocolList(Class cls, unsigned int *outCount)
- {
- unsigned int count = 0;
- Protocol **result = nil;
-
- if (!cls) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
- ASSERT(cls->isRealized());
-
- count = cls->data()->protocols.count();
- if (count > 0) {
- result = (Protocol **)malloc((count+1) * sizeof(Protocol *));
- count = 0;
- for (const auto& proto : cls->data()->protocols) {
- result[count++] = (Protocol *)remapProtocol(proto);
- }
- result[count] = nil;
- }
- if (outCount) *outCount = count;
- return result;
- }
- /***********************************************************************
- * objc_copyImageNames
- * Copies names of loaded images with ObjC contents.
- *
- * Locking: acquires runtimeLock
- **********************************************************************/
- const char **objc_copyImageNames(unsigned int *outCount)
- {
- mutex_locker_t lock(runtimeLock);
- int HeaderCount = 0;
- for (header_info *hi = FirstHeader; hi != nil; hi = hi->getNext()) {
- HeaderCount++;
- }
- #if TARGET_OS_WIN32
- const TCHAR **names = (const TCHAR **)
- malloc((HeaderCount+1) * sizeof(TCHAR *));
- #else
- const char **names = (const char **)
- malloc((HeaderCount+1) * sizeof(char *));
- #endif
- unsigned int count = 0;
- for (header_info *hi = FirstHeader; hi != nil; hi = hi->getNext()) {
- #if TARGET_OS_WIN32
- if (hi->moduleName) {
- names[count++] = hi->moduleName;
- }
- #else
- const char *fname = hi->fname();
- if (fname) {
- names[count++] = fname;
- }
- #endif
- }
- names[count] = nil;
-
- if (count == 0) {
- // Return nil instead of empty list if there are no images
- free((void *)names);
- names = nil;
- }
- if (outCount) *outCount = count;
- return names;
- }
- /***********************************************************************
- * copyClassNamesForImage_nolock
- * Copies class names from the given image.
- * Missing weak-import classes are omitted.
- * Swift class names are demangled.
- *
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- const char **
- copyClassNamesForImage_nolock(header_info *hi, unsigned int *outCount)
- {
- runtimeLock.assertLocked();
- ASSERT(hi);
- size_t count;
- classref_t const *classlist = _getObjc2ClassList(hi, &count);
- const char **names = (const char **)
- malloc((count+1) * sizeof(const char *));
- size_t shift = 0;
- for (size_t i = 0; i < count; i++) {
- Class cls = remapClass(classlist[i]);
- if (cls) {
- names[i-shift] = cls->demangledName();
- } else {
- shift++; // ignored weak-linked class
- }
- }
- count -= shift;
- names[count] = nil;
- if (outCount) *outCount = (unsigned int)count;
- return names;
- }
- /***********************************************************************
- * objc_copyClassNamesForImage
- * Copies class names from the named image.
- * The image name must be identical to dladdr's dli_fname value.
- * Missing weak-import classes are omitted.
- * Swift class names are demangled.
- *
- * Locking: acquires runtimeLock
- **********************************************************************/
- const char **
- objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
- {
- if (!image) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- // Find the image.
- header_info *hi;
- for (hi = FirstHeader; hi != nil; hi = hi->getNext()) {
- #if TARGET_OS_WIN32
- if (0 == wcscmp((TCHAR *)image, hi->moduleName)) break;
- #else
- if (0 == strcmp(image, hi->fname())) break;
- #endif
- }
- if (!hi) {
- if (outCount) *outCount = 0;
- return nil;
- }
- return copyClassNamesForImage_nolock(hi, outCount);
- }
- /***********************************************************************
- * objc_copyClassNamesForImageHeader
- * Copies class names from the given image.
- * Missing weak-import classes are omitted.
- * Swift class names are demangled.
- *
- * Locking: acquires runtimeLock
- **********************************************************************/
- const char **
- objc_copyClassNamesForImageHeader(const struct mach_header *mh, unsigned int *outCount)
- {
- if (!mh) {
- if (outCount) *outCount = 0;
- return nil;
- }
- mutex_locker_t lock(runtimeLock);
- // Find the image.
- header_info *hi;
- for (hi = FirstHeader; hi != nil; hi = hi->getNext()) {
- if (hi->mhdr() == (const headerType *)mh) break;
- }
- if (!hi) {
- if (outCount) *outCount = 0;
- return nil;
- }
- return copyClassNamesForImage_nolock(hi, outCount);
- }
- /***********************************************************************
- * saveTemporaryString
- * Save a string in a thread-local FIFO buffer.
- * This is suitable for temporary strings generated for logging purposes.
- **********************************************************************/
- static void
- saveTemporaryString(char *str)
- {
- // Fixed-size FIFO. We free the first string, shift
- // the rest, and add the new string to the end.
- _objc_pthread_data *data = _objc_fetch_pthread_data(true);
- if (data->printableNames[0]) {
- free(data->printableNames[0]);
- }
- int last = countof(data->printableNames) - 1;
- for (int i = 0; i < last; i++) {
- data->printableNames[i] = data->printableNames[i+1];
- }
- data->printableNames[last] = str;
- }
- /***********************************************************************
- * objc_class::nameForLogging
- * Returns the class's name, suitable for display.
- * The returned memory is TEMPORARY. Print it or copy it immediately.
- * Locking: none
- **********************************************************************/
- const char *
- objc_class::nameForLogging()
- {
- // Handle the easy case directly.
- if (isRealized() || isFuture()) {
- if (data()->demangledName) return data()->demangledName;
- }
- char *result;
- const char *name = mangledName();
- char *de = copySwiftV1DemangledName(name);
- if (de) result = de;
- else result = strdup(name);
- saveTemporaryString(result);
- return result;
- }
- /***********************************************************************
- * objc_class::demangledName
- * If realize=false, the class must already be realized or future.
- * Locking: runtimeLock may or may not be held by the caller.
- **********************************************************************/
- mutex_t DemangleCacheLock;
- static objc::DenseSet<const char *> *DemangleCache;
- const char *
- objc_class::demangledName()
- {
- // Return previously demangled name if available.
- if (isRealized() || isFuture()) {
- if (data()->demangledName) return data()->demangledName;
- }
- // Try demangling the mangled name.
- const char *mangled = mangledName();
- char *de = copySwiftV1DemangledName(mangled);
- if (isRealized() || isFuture()) {
- // Class is already realized or future.
- // Save demangling result in rw data.
- // We may not own runtimeLock so use an atomic operation instead.
- if (! OSAtomicCompareAndSwapPtrBarrier(nil, (void*)(de ?: mangled),
- (void**)&data()->demangledName))
- {
- if (de) free(de);
- }
- return data()->demangledName;
- }
- // Class is not yet realized.
- if (!de) {
- // Name is not mangled. Return it without caching.
- return mangled;
- }
- // Class is not yet realized and name is mangled.
- // Allocate the name but don't save it in the class.
- // Save the name in a side cache instead to prevent leaks.
- // When the class is actually realized we may allocate a second
- // copy of the name, but we don't care.
- // (Previously we would try to realize the class now and save the
- // name there, but realization is more complicated for Swift classes.)
- // Only objc_copyClassNamesForImage() should get here.
- // fixme lldb's calls to class_getName() can also get here when
- // interrogating the dyld shared cache. (rdar://27258517)
- // fixme runtimeLock.assertLocked();
- // fixme ASSERT(realize);
- const char *cached;
- {
- mutex_locker_t lock(DemangleCacheLock);
- if (!DemangleCache) {
- DemangleCache = new objc::DenseSet<const char *>{};
- }
- cached = *DemangleCache->insert(de).first;
- }
- if (cached != de) free(de);
- return cached;
- }
- /***********************************************************************
- * class_getName
- * fixme
- * Locking: may acquire DemangleCacheLock
- **********************************************************************/
- const char *class_getName(Class cls)
- {
- if (!cls) return "nil";
- // fixme lldb calls class_getName() on unrealized classes (rdar://27258517)
- // ASSERT(cls->isRealized() || cls->isFuture());
- return cls->demangledName();
- }
- /***********************************************************************
- * objc_debug_class_getNameRaw
- * fixme
- * Locking: none
- **********************************************************************/
- const char *objc_debug_class_getNameRaw(Class cls)
- {
- if (!cls) return "nil";
- return cls->mangledName();
- }
- /***********************************************************************
- * class_getVersion
- * fixme
- * Locking: none
- **********************************************************************/
- int
- class_getVersion(Class cls)
- {
- if (!cls) return 0;
- ASSERT(cls->isRealized());
- return cls->data()->version;
- }
- /***********************************************************************
- * class_setVersion
- * fixme
- * Locking: none
- **********************************************************************/
- void
- class_setVersion(Class cls, int version)
- {
- if (!cls) return;
- ASSERT(cls->isRealized());
- cls->data()->version = version;
- }
- /***********************************************************************
- * search_method_list_inline
- **********************************************************************/
- ALWAYS_INLINE static method_t *
- findMethodInSortedMethodList(SEL key, const method_list_t *list)
- {
- ASSERT(list);
- const method_t * const first = &list->first;
- const method_t *base = first;
- const method_t *probe;
- uintptr_t keyValue = (uintptr_t)key;
- uint32_t count;
-
- for (count = list->count; count != 0; count >>= 1) {
- probe = base + (count >> 1);
-
- uintptr_t probeValue = (uintptr_t)probe->name;
-
- if (keyValue == probeValue) {
- // `probe` is a match.
- // Rewind looking for the *first* occurrence of this value.
- // This is required for correct category overrides.
- while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
- probe--;
- }
- return (method_t *)probe;
- }
-
- if (keyValue > probeValue) {
- base = probe + 1;
- count--;
- }
- }
-
- return nil;
- }
- ALWAYS_INLINE static method_t *
- search_method_list_inline(const method_list_t *mlist, SEL sel)
- {
- int methodListIsFixedUp = mlist->isFixedUp();
- int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
-
- if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
- return findMethodInSortedMethodList(sel, mlist);
- } else {
- // Linear search of unsorted method list
- for (auto& meth : *mlist) {
- if (meth.name == sel) return &meth;
- }
- }
- #if DEBUG
- // sanity-check negative results
- if (mlist->isFixedUp()) {
- for (auto& meth : *mlist) {
- if (meth.name == sel) {
- _objc_fatal("linear search worked when binary search did not");
- }
- }
- }
- #endif
- return nil;
- }
- NEVER_INLINE static method_t *
- search_method_list(const method_list_t *mlist, SEL sel)
- {
- return search_method_list_inline(mlist, sel);
- }
- /***********************************************************************
- * method_lists_contains_any
- **********************************************************************/
- static NEVER_INLINE bool
- method_lists_contains_any(method_list_t **mlists, method_list_t **end,
- SEL sels[], size_t selcount)
- {
- while (mlists < end) {
- const method_list_t *mlist = *mlists++;
- int methodListIsFixedUp = mlist->isFixedUp();
- int methodListHasExpectedSize = mlist->entsize() == sizeof(method_t);
- if (fastpath(methodListIsFixedUp && methodListHasExpectedSize)) {
- for (size_t i = 0; i < selcount; i++) {
- if (findMethodInSortedMethodList(sels[i], mlist)) {
- return true;
- }
- }
- } else {
- for (auto& meth : *mlist) {
- for (size_t i = 0; i < selcount; i++) {
- if (meth.name == sels[i]) {
- return true;
- }
- }
- }
- }
- }
- return false;
- }
- /***********************************************************************
- * getMethodNoSuper_nolock
- * fixme
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static method_t *
- getMethodNoSuper_nolock(Class cls, SEL sel)
- {
- runtimeLock.assertLocked();
- ASSERT(cls->isRealized());
- // fixme nil cls?
- // fixme nil sel?
- for (auto mlists = cls->data()->methods.beginLists(),
- end = cls->data()->methods.endLists();
- mlists != end;
- ++mlists)
- {
- // <rdar://problem/46904873> getMethodNoSuper_nolock is the hottest
- // caller of search_method_list, inlining it turns
- // getMethodNoSuper_nolock into a frame-less function and eliminates
- // any store from this codepath.
- method_t *m = search_method_list_inline(*mlists, sel);
- if (m) return m;
- }
- return nil;
- }
- /***********************************************************************
- * getMethod_nolock
- * fixme
- * Locking: runtimeLock must be read- or write-locked by the caller
- **********************************************************************/
- static method_t *
- getMethod_nolock(Class cls, SEL sel)
- {
- method_t *m = nil;
- runtimeLock.assertLocked();
- // fixme nil cls?
- // fixme nil sel?
- ASSERT(cls->isRealized());
- while (cls && ((m = getMethodNoSuper_nolock(cls, sel))) == nil) {
- cls = cls->superclass;
- }
- return m;
- }
- /***********************************************************************
- * _class_getMethod
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- static Method _class_getMethod(Class cls, SEL sel)
- {
- mutex_locker_t lock(runtimeLock);
- return getMethod_nolock(cls, sel);
- }
- /***********************************************************************
- * class_getInstanceMethod. Return the instance method for the
- * specified class and selector.
- **********************************************************************/
- Method class_getInstanceMethod(Class cls, SEL sel)
- {
- if (!cls || !sel) return nil;
- // This deliberately avoids +initialize because it historically did so.
- // This implementation is a bit weird because it's the only place that
- // wants a Method instead of an IMP.
- #warning fixme build and search caches
-
- // Search method lists, try method resolver, etc.
- lookUpImpOrForward(nil, sel, cls, LOOKUP_RESOLVER);
- #warning fixme build and search caches
- return _class_getMethod(cls, sel);
- }
- /***********************************************************************
- * resolveClassMethod
- * Call +resolveClassMethod, looking for a method to be added to class cls.
- * cls should be a metaclass.
- * Does not check if the method already exists.
- **********************************************************************/
- static void resolveClassMethod(id inst, SEL sel, Class cls)
- {
- runtimeLock.assertUnlocked();
- ASSERT(cls->isRealized());
- ASSERT(cls->isMetaClass());
- if (!lookUpImpOrNil(inst, @selector(resolveClassMethod:), cls)) {
- // Resolver not implemented.
- return;
- }
- Class nonmeta;
- {
- mutex_locker_t lock(runtimeLock);
- nonmeta = getMaybeUnrealizedNonMetaClass(cls, inst);
- // +initialize path should have realized nonmeta already
- if (!nonmeta->isRealized()) {
- _objc_fatal("nonmeta class %s (%p) unexpectedly not realized",
- nonmeta->nameForLogging(), nonmeta);
- }
- }
- BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
- bool resolved = msg(nonmeta, @selector(resolveClassMethod:), sel);
- // Cache the result (good or bad) so the resolver doesn't fire next time.
- // +resolveClassMethod adds to self->ISA() a.k.a. cls
- IMP imp = lookUpImpOrNil(inst, sel, cls);
- if (resolved && PrintResolving) {
- if (imp) {
- _objc_inform("RESOLVE: method %c[%s %s] "
- "dynamically resolved to %p",
- cls->isMetaClass() ? '+' : '-',
- cls->nameForLogging(), sel_getName(sel), imp);
- }
- else {
- // Method resolver didn't add anything?
- _objc_inform("RESOLVE: +[%s resolveClassMethod:%s] returned YES"
- ", but no new implementation of %c[%s %s] was found",
- cls->nameForLogging(), sel_getName(sel),
- cls->isMetaClass() ? '+' : '-',
- cls->nameForLogging(), sel_getName(sel));
- }
- }
- }
- /***********************************************************************
- * resolveInstanceMethod
- * Call +resolveInstanceMethod, looking for a method to be added to class cls.
- * cls may be a metaclass or a non-meta class.
- * Does not check if the method already exists.
- **********************************************************************/
- static void resolveInstanceMethod(id inst, SEL sel, Class cls)
- {
- runtimeLock.assertUnlocked();
- ASSERT(cls->isRealized());
- SEL resolve_sel = @selector(resolveInstanceMethod:);
- if (!lookUpImpOrNil(cls, resolve_sel, cls->ISA())) {
- // Resolver not implemented.
- return;
- }
- BOOL (*msg)(Class, SEL, SEL) = (typeof(msg))objc_msgSend;
- bool resolved = msg(cls, resolve_sel, sel);
- // Cache the result (good or bad) so the resolver doesn't fire next time.
- // +resolveInstanceMethod adds to self a.k.a. cls
- IMP imp = lookUpImpOrNil(inst, sel, cls);
- if (resolved && PrintResolving) {
- if (imp) {
- _objc_inform("RESOLVE: method %c[%s %s] "
- "dynamically resolved to %p",
- cls->isMetaClass() ? '+' : '-',
- cls->nameForLogging(), sel_getName(sel), imp);
- }
- else {
- // Method resolver didn't add anything?
- _objc_inform("RESOLVE: +[%s resolveInstanceMethod:%s] returned YES"
- ", but no new implementation of %c[%s %s] was found",
- cls->nameForLogging(), sel_getName(sel),
- cls->isMetaClass() ? '+' : '-',
- cls->nameForLogging(), sel_getName(sel));
- }
- }
- }
- /***********************************************************************
- * resolveMethod_locked
- * Call +resolveClassMethod or +resolveInstanceMethod.
- *
- * Called with the runtimeLock held to avoid pressure in the caller
- * Tail calls into lookUpImpOrForward, also to avoid pressure in the callerb
- **********************************************************************/
- static NEVER_INLINE IMP
- resolveMethod_locked(id inst, SEL sel, Class cls, int behavior)
- {
- runtimeLock.assertLocked();
- ASSERT(cls->isRealized());
- runtimeLock.unlock();
- if (! cls->isMetaClass()) {
- // try [cls resolveInstanceMethod:sel]
- resolveInstanceMethod(inst, sel, cls);
- }
- else {
- // try [nonMetaClass resolveClassMethod:sel]
- // and [cls resolveInstanceMethod:sel]
- resolveClassMethod(inst, sel, cls);
- if (!lookUpImpOrNil(inst, sel, cls)) {
- resolveInstanceMethod(inst, sel, cls);
- }
- }
- // chances are that calling the resolver have populated the cache
- // so attempt using it
- return lookUpImpOrForward(inst, sel, cls, behavior | LOOKUP_CACHE);
- }
- /***********************************************************************
- * log_and_fill_cache
- * Log this method call. If the logger permits it, fill the method cache.
- * cls is the method whose cache should be filled.
- * implementer is the class that owns the implementation in question.
- **********************************************************************/
- static void
- log_and_fill_cache(Class cls, IMP imp, SEL sel, id receiver, Class implementer)
- {
- #if SUPPORT_MESSAGE_LOGGING
- if (slowpath(objcMsgLogEnabled && implementer)) {
- bool cacheIt = logMessageSend(implementer->isMetaClass(),
- cls->nameForLogging(),
- implementer->nameForLogging(),
- sel);
- if (!cacheIt) return;
- }
- #endif
- cache_fill(cls, sel, imp, receiver);
- }
- /***********************************************************************
- * lookUpImpOrForward.
- * The standard IMP lookup.
- * Without LOOKUP_INITIALIZE: tries to avoid +initialize (but sometimes fails)
- * Without LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
- * Most callers should use LOOKUP_INITIALIZE and LOOKUP_CACHE
- * inst is an instance of cls or a subclass thereof, or nil if none is known.
- * If cls is an un-initialized metaclass then a non-nil inst is faster.
- * May return _objc_msgForward_impcache. IMPs destined for external use
- * must be converted to _objc_msgForward or _objc_msgForward_stret.
- * If you don't want forwarding at all, use LOOKUP_NIL.
- **********************************************************************/
- IMP lookUpImpOrForward(id inst, SEL sel, Class cls, int behavior)
- {
- const IMP forward_imp = (IMP)_objc_msgForward_impcache;
- IMP imp = nil;
- Class curClass;
- runtimeLock.assertUnlocked();
- // Optimistic cache lookup
- if (fastpath(behavior & LOOKUP_CACHE)) {
- imp = cache_getImp(cls, sel);
- if (imp) goto done_nolock;
- }
- // 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.
- //
- // TODO: this check is quite costly during process startup.
- checkIsKnownClass(cls);
- if (slowpath(!cls->isRealized())) {
- cls = realizeClassMaybeSwiftAndLeaveLocked(cls, runtimeLock);
- // runtimeLock may have been dropped but is now locked again
- }
- if (slowpath((behavior & LOOKUP_INITIALIZE) && !cls->isInitialized())) {
- cls = initializeAndLeaveLocked(cls, inst, runtimeLock);
- // runtimeLock may have been dropped but is now locked again
- // If sel == initialize, class_initialize will send +initialize and
- // then the messenger will send +initialize again after this
- // procedure finishes. Of course, if this is not being called
- // from the messenger then it won't happen. 2778172
- }
- runtimeLock.assertLocked();
- curClass = cls;
- // The code used to lookpu 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().
- for (unsigned attempts = unreasonableClassCount();;) {
- // curClass method list.
- Method meth = getMethodNoSuper_nolock(curClass, sel);
- if (meth) {
- imp = meth->imp;
- goto done;
- }
- if (slowpath((curClass = curClass->superclass) == 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.
- if (slowpath(behavior & LOOKUP_RESOLVER)) {
- behavior ^= LOOKUP_RESOLVER;
- return resolveMethod_locked(inst, sel, cls, behavior);
- }
- done:
- log_and_fill_cache(cls, imp, sel, inst, curClass);
- runtimeLock.unlock();
- done_nolock:
- if (slowpath((behavior & LOOKUP_NIL) && imp == forward_imp)) {
- return nil;
- }
- return imp;
- }
- /***********************************************************************
- * lookupMethodInClassAndLoadCache.
- * Like lookUpImpOrForward, but does not search superclasses.
- * Caches and returns objc_msgForward if the method is not found in the class.
- **********************************************************************/
- IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel)
- {
- Method meth;
- IMP imp;
- // fixme this is incomplete - no resolver, +initialize -
- // but it's only used for .cxx_construct/destruct so we don't care
- ASSERT(sel == SEL_cxx_construct || sel == SEL_cxx_destruct);
- // Search cache first.
- imp = cache_getImp(cls, sel);
- if (imp) return imp;
- // Cache miss. Search method list.
- mutex_locker_t lock(runtimeLock);
- meth = getMethodNoSuper_nolock(cls, sel);
- if (meth) {
- // Hit in method list. Cache it.
- cache_fill(cls, sel, meth->imp, nil);
- return meth->imp;
- } else {
- // Miss in method list. Cache objc_msgForward.
- cache_fill(cls, sel, _objc_msgForward_impcache, nil);
- return _objc_msgForward_impcache;
- }
- }
- /***********************************************************************
- * class_getProperty
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- objc_property_t class_getProperty(Class cls, const char *name)
- {
- if (!cls || !name) return nil;
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
-
- ASSERT(cls->isRealized());
- for ( ; cls; cls = cls->superclass) {
- for (auto& prop : cls->data()->properties) {
- if (0 == strcmp(name, prop.name)) {
- return (objc_property_t)∝
- }
- }
- }
-
- return nil;
- }
- /***********************************************************************
- * Locking: fixme
- **********************************************************************/
- Class gdb_class_getClass(Class cls)
- {
- const char *className = cls->mangledName();
- if(!className || !strlen(className)) return Nil;
- Class rCls = look_up_class(className, NO, NO);
- return rCls;
- }
- Class gdb_object_getClass(id obj)
- {
- if (!obj) return nil;
- return gdb_class_getClass(obj->getIsa());
- }
- /***********************************************************************
- * Locking: write-locks runtimeLock
- **********************************************************************/
- void
- objc_class::setInitialized()
- {
- Class metacls;
- Class cls;
- ASSERT(!isMetaClass());
- cls = (Class)this;
- metacls = cls->ISA();
- mutex_locker_t lock(runtimeLock);
- // Special cases:
- // - NSObject AWZ class methods are default.
- // - NSObject RR class and instance methods are default.
- // - NSObject Core class and instance methods are default.
- // adjustCustomFlagsForMethodChange() also knows these special cases.
- // attachMethodLists() also knows these special cases.
- objc::AWZScanner::scanInitializedClass(cls, metacls);
- objc::RRScanner::scanInitializedClass(cls, metacls);
- objc::CoreScanner::scanInitializedClass(cls, metacls);
- // Update the +initialize flags.
- // Do this last.
- metacls->changeInfo(RW_INITIALIZED, RW_INITIALIZING);
- }
- void
- objc_class::printInstancesRequireRawIsa(bool inherited)
- {
- ASSERT(PrintRawIsa);
- ASSERT(instancesRequireRawIsa());
- _objc_inform("RAW ISA: %s%s%s", nameForLogging(),
- isMetaClass() ? " (meta)" : "",
- inherited ? " (inherited)" : "");
- }
- /***********************************************************************
- * Mark this class and all of its subclasses as requiring raw isa pointers
- **********************************************************************/
- void objc_class::setInstancesRequireRawIsaRecursively(bool inherited)
- {
- Class cls = (Class)this;
- runtimeLock.assertLocked();
- if (instancesRequireRawIsa()) return;
-
- foreach_realized_class_and_subclass(cls, [=](Class c){
- if (c->instancesRequireRawIsa()) {
- return false;
- }
- c->setInstancesRequireRawIsa();
- if (PrintRawIsa) c->printInstancesRequireRawIsa(inherited || c != cls);
- return true;
- });
- }
- /***********************************************************************
- * Choose a class index.
- * Set instancesRequireRawIsa if no more class indexes are available.
- **********************************************************************/
- void objc_class::chooseClassArrayIndex()
- {
- #if SUPPORT_INDEXED_ISA
- Class cls = (Class)this;
- runtimeLock.assertLocked();
- if (objc_indexed_classes_count >= ISA_INDEX_COUNT) {
- // No more indexes available.
- ASSERT(cls->classArrayIndex() == 0);
- cls->setInstancesRequireRawIsaRecursively(false/*not inherited*/);
- return;
- }
- unsigned index = objc_indexed_classes_count++;
- if (index == 0) index = objc_indexed_classes_count++; // index 0 is unused
- classForIndex(index) = cls;
- cls->setClassArrayIndex(index);
- #endif
- }
- /***********************************************************************
- * Update custom RR and AWZ when a method changes its IMP
- **********************************************************************/
- static void
- adjustCustomFlagsForMethodChange(Class cls, method_t *meth)
- {
- objc::AWZScanner::scanChangedMethod(cls, meth);
- objc::RRScanner::scanChangedMethod(cls, meth);
- objc::CoreScanner::scanChangedMethod(cls, meth);
- }
- /***********************************************************************
- * class_getIvarLayout
- * Called by the garbage collector.
- * The class must be nil or already realized.
- * Locking: none
- **********************************************************************/
- const uint8_t *
- class_getIvarLayout(Class cls)
- {
- if (cls) return cls->data()->ro->ivarLayout;
- else return nil;
- }
- /***********************************************************************
- * class_getWeakIvarLayout
- * Called by the garbage collector.
- * The class must be nil or already realized.
- * Locking: none
- **********************************************************************/
- const uint8_t *
- class_getWeakIvarLayout(Class cls)
- {
- if (cls) return cls->data()->ro->weakIvarLayout;
- else return nil;
- }
- /***********************************************************************
- * class_setIvarLayout
- * Changes the class's ivar layout.
- * nil layout means no unscanned ivars
- * The class must be under construction.
- * fixme: sanity-check layout vs instance size?
- * fixme: sanity-check layout vs superclass?
- * Locking: acquires runtimeLock
- **********************************************************************/
- void
- class_setIvarLayout(Class cls, const uint8_t *layout)
- {
- if (!cls) return;
- mutex_locker_t lock(runtimeLock);
-
- checkIsKnownClass(cls);
- // Can only change layout of in-construction classes.
- // note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent object_setIvar)
- if (!(cls->data()->flags & RW_CONSTRUCTING)) {
- _objc_inform("*** Can't set ivar layout for already-registered "
- "class '%s'", cls->nameForLogging());
- return;
- }
- class_ro_t *ro_w = make_ro_writeable(cls->data());
- try_free(ro_w->ivarLayout);
- ro_w->ivarLayout = ustrdupMaybeNil(layout);
- }
- /***********************************************************************
- * class_setWeakIvarLayout
- * Changes the class's weak ivar layout.
- * nil layout means no weak ivars
- * The class must be under construction.
- * fixme: sanity-check layout vs instance size?
- * fixme: sanity-check layout vs superclass?
- * Locking: acquires runtimeLock
- **********************************************************************/
- void
- class_setWeakIvarLayout(Class cls, const uint8_t *layout)
- {
- if (!cls) return;
- mutex_locker_t lock(runtimeLock);
-
- checkIsKnownClass(cls);
- // Can only change layout of in-construction classes.
- // note: if modifications to post-construction classes were
- // allowed, there would be a race below (us vs. concurrent object_setIvar)
- if (!(cls->data()->flags & RW_CONSTRUCTING)) {
- _objc_inform("*** Can't set weak ivar layout for already-registered "
- "class '%s'", cls->nameForLogging());
- return;
- }
- class_ro_t *ro_w = make_ro_writeable(cls->data());
- try_free(ro_w->weakIvarLayout);
- ro_w->weakIvarLayout = ustrdupMaybeNil(layout);
- }
- /***********************************************************************
- * getIvar
- * Look up an ivar by name.
- * Locking: runtimeLock must be read- or write-locked by the caller.
- **********************************************************************/
- static ivar_t *getIvar(Class cls, const char *name)
- {
- runtimeLock.assertLocked();
- const ivar_list_t *ivars;
- ASSERT(cls->isRealized());
- if ((ivars = cls->data()->ro->ivars)) {
- for (auto& ivar : *ivars) {
- if (!ivar.offset) continue; // anonymous bitfield
- // ivar.name may be nil for anonymous bitfields etc.
- if (ivar.name && 0 == strcmp(name, ivar.name)) {
- return &ivar;
- }
- }
- }
- return nil;
- }
- /***********************************************************************
- * _class_getClassForIvar
- * Given a class and an ivar that is in it or one of its superclasses,
- * find the actual class that defined the ivar.
- **********************************************************************/
- Class _class_getClassForIvar(Class cls, Ivar ivar)
- {
- mutex_locker_t lock(runtimeLock);
- for ( ; cls; cls = cls->superclass) {
- if (auto ivars = cls->data()->ro->ivars) {
- if (ivars->containsIvar(ivar)) {
- return cls;
- }
- }
- }
- return nil;
- }
- /***********************************************************************
- * _class_getVariable
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- Ivar
- _class_getVariable(Class cls, const char *name)
- {
- mutex_locker_t lock(runtimeLock);
- for ( ; cls; cls = cls->superclass) {
- ivar_t *ivar = getIvar(cls, name);
- if (ivar) {
- return ivar;
- }
- }
- return nil;
- }
- /***********************************************************************
- * class_conformsToProtocol
- * fixme
- * Locking: read-locks runtimeLock
- **********************************************************************/
- BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen)
- {
- protocol_t *proto = newprotocol(proto_gen);
-
- if (!cls) return NO;
- if (!proto_gen) return NO;
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
-
- ASSERT(cls->isRealized());
-
- for (const auto& proto_ref : cls->data()->protocols) {
- protocol_t *p = remapProtocol(proto_ref);
- if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) {
- return YES;
- }
- }
- return NO;
- }
- /**********************************************************************
- * addMethod
- * fixme
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static IMP
- addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace)
- {
- IMP result = nil;
- runtimeLock.assertLocked();
- checkIsKnownClass(cls);
-
- ASSERT(types);
- ASSERT(cls->isRealized());
- method_t *m;
- if ((m = getMethodNoSuper_nolock(cls, name))) {
- // already exists
- if (!replace) {
- result = m->imp;
- } else {
- result = _method_setImplementation(cls, m, imp);
- }
- } else {
- // fixme optimize
- method_list_t *newlist;
- newlist = (method_list_t *)calloc(sizeof(*newlist), 1);
- newlist->entsizeAndFlags =
- (uint32_t)sizeof(method_t) | fixed_up_method_list;
- newlist->count = 1;
- newlist->first.name = name;
- newlist->first.types = strdupIfMutable(types);
- newlist->first.imp = imp;
- prepareMethodLists(cls, &newlist, 1, NO, NO);
- cls->data()->methods.attachLists(&newlist, 1);
- flushCaches(cls);
- result = nil;
- }
- return result;
- }
- /**********************************************************************
- * addMethods
- * Add the given methods to a class in bulk.
- * Returns the selectors which could not be added, when replace == NO and a
- * method already exists. The returned selectors are NULL terminated and must be
- * freed by the caller. They are NULL if no failures occurred.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static SEL *
- addMethods(Class cls, const SEL *names, const IMP *imps, const char **types,
- uint32_t count, bool replace, uint32_t *outFailedCount)
- {
- runtimeLock.assertLocked();
-
- ASSERT(names);
- ASSERT(imps);
- ASSERT(types);
- ASSERT(cls->isRealized());
-
- method_list_t *newlist;
- size_t newlistSize = method_list_t::byteSize(sizeof(method_t), count);
- newlist = (method_list_t *)calloc(newlistSize, 1);
- newlist->entsizeAndFlags =
- (uint32_t)sizeof(method_t) | fixed_up_method_list;
- newlist->count = 0;
-
- method_t *newlistMethods = &newlist->first;
-
- SEL *failedNames = nil;
- uint32_t failedCount = 0;
-
- for (uint32_t i = 0; i < count; i++) {
- method_t *m;
- if ((m = getMethodNoSuper_nolock(cls, names[i]))) {
- // already exists
- if (!replace) {
- // report failure
- if (failedNames == nil) {
- // allocate an extra entry for a trailing NULL in case
- // every method fails
- failedNames = (SEL *)calloc(sizeof(*failedNames),
- count + 1);
- }
- failedNames[failedCount] = m->name;
- failedCount++;
- } else {
- _method_setImplementation(cls, m, imps[i]);
- }
- } else {
- method_t *newmethod = &newlistMethods[newlist->count];
- newmethod->name = names[i];
- newmethod->types = strdupIfMutable(types[i]);
- newmethod->imp = imps[i];
- newlist->count++;
- }
- }
-
- if (newlist->count > 0) {
- // fixme resize newlist because it may have been over-allocated above.
- // Note that realloc() alone doesn't work due to ptrauth.
-
- method_t::SortBySELAddress sorter;
- std::stable_sort(newlist->begin(), newlist->end(), sorter);
-
- prepareMethodLists(cls, &newlist, 1, NO, NO);
- cls->data()->methods.attachLists(&newlist, 1);
- flushCaches(cls);
- } else {
- // Attaching the method list to the class consumes it. If we don't
- // do that, we have to free the memory ourselves.
- free(newlist);
- }
-
- if (outFailedCount) *outFailedCount = failedCount;
-
- return failedNames;
- }
- BOOL
- class_addMethod(Class cls, SEL name, IMP imp, const char *types)
- {
- if (!cls) return NO;
- mutex_locker_t lock(runtimeLock);
- return ! addMethod(cls, name, imp, types ?: "", NO);
- }
- IMP
- class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
- {
- if (!cls) return nil;
- mutex_locker_t lock(runtimeLock);
- return addMethod(cls, name, imp, types ?: "", YES);
- }
- SEL *
- class_addMethodsBulk(Class cls, const SEL *names, const IMP *imps,
- const char **types, uint32_t count,
- uint32_t *outFailedCount)
- {
- if (!cls) {
- if (outFailedCount) *outFailedCount = count;
- return (SEL *)memdup(names, count * sizeof(*names));
- }
-
- mutex_locker_t lock(runtimeLock);
- return addMethods(cls, names, imps, types, count, NO, outFailedCount);
- }
- void
- class_replaceMethodsBulk(Class cls, const SEL *names, const IMP *imps,
- const char **types, uint32_t count)
- {
- if (!cls) return;
-
- mutex_locker_t lock(runtimeLock);
- addMethods(cls, names, imps, types, count, YES, nil);
- }
- /***********************************************************************
- * class_addIvar
- * Adds an ivar to a class.
- * Locking: acquires runtimeLock
- **********************************************************************/
- BOOL
- class_addIvar(Class cls, const char *name, size_t size,
- uint8_t alignment, const char *type)
- {
- if (!cls) return NO;
- if (!type) type = "";
- if (name && 0 == strcmp(name, "")) name = nil;
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
- ASSERT(cls->isRealized());
- // No class variables
- if (cls->isMetaClass()) {
- return NO;
- }
- // Can only add ivars to in-construction classes.
- if (!(cls->data()->flags & RW_CONSTRUCTING)) {
- return NO;
- }
- // Check for existing ivar with this name, unless it's anonymous.
- // Check for too-big ivar.
- // fixme check for superclass ivar too?
- if ((name && getIvar(cls, name)) || size > UINT32_MAX) {
- return NO;
- }
- class_ro_t *ro_w = make_ro_writeable(cls->data());
- // fixme allocate less memory here
-
- ivar_list_t *oldlist, *newlist;
- if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) {
- size_t oldsize = oldlist->byteSize();
- newlist = (ivar_list_t *)calloc(oldsize + oldlist->entsize(), 1);
- memcpy(newlist, oldlist, oldsize);
- free(oldlist);
- } else {
- newlist = (ivar_list_t *)calloc(sizeof(ivar_list_t), 1);
- newlist->entsizeAndFlags = (uint32_t)sizeof(ivar_t);
- }
- uint32_t offset = cls->unalignedInstanceSize();
- uint32_t alignMask = (1<<alignment)-1;
- offset = (offset + alignMask) & ~alignMask;
- ivar_t& ivar = newlist->get(newlist->count++);
- #if __x86_64__
- // Deliberately over-allocate the ivar offset variable.
- // Use calloc() to clear all 64 bits. See the note in struct ivar_t.
- ivar.offset = (int32_t *)(int64_t *)calloc(sizeof(int64_t), 1);
- #else
- ivar.offset = (int32_t *)malloc(sizeof(int32_t));
- #endif
- *ivar.offset = offset;
- ivar.name = name ? strdupIfMutable(name) : nil;
- ivar.type = strdupIfMutable(type);
- ivar.alignment_raw = alignment;
- ivar.size = (uint32_t)size;
- ro_w->ivars = newlist;
- cls->setInstanceSize((uint32_t)(offset + size));
- // Ivar layout updated in registerClass.
- return YES;
- }
- /***********************************************************************
- * class_addProtocol
- * Adds a protocol to a class.
- * Locking: acquires runtimeLock
- **********************************************************************/
- BOOL class_addProtocol(Class cls, Protocol *protocol_gen)
- {
- protocol_t *protocol = newprotocol(protocol_gen);
- if (!cls) return NO;
- if (class_conformsToProtocol(cls, protocol_gen)) return NO;
- mutex_locker_t lock(runtimeLock);
- ASSERT(cls->isRealized());
-
- // fixme optimize
- protocol_list_t *protolist = (protocol_list_t *)
- malloc(sizeof(protocol_list_t) + sizeof(protocol_t *));
- protolist->count = 1;
- protolist->list[0] = (protocol_ref_t)protocol;
- cls->data()->protocols.attachLists(&protolist, 1);
- // fixme metaclass?
- return YES;
- }
- /***********************************************************************
- * class_addProperty
- * Adds a property to a class.
- * Locking: acquires runtimeLock
- **********************************************************************/
- static bool
- _class_addProperty(Class cls, const char *name,
- const objc_property_attribute_t *attrs, unsigned int count,
- bool replace)
- {
- if (!cls) return NO;
- if (!name) return NO;
- property_t *prop = class_getProperty(cls, name);
- if (prop && !replace) {
- // already exists, refuse to replace
- return NO;
- }
- else if (prop) {
- // replace existing
- mutex_locker_t lock(runtimeLock);
- try_free(prop->attributes);
- prop->attributes = copyPropertyAttributeString(attrs, count);
- return YES;
- }
- else {
- mutex_locker_t lock(runtimeLock);
-
- ASSERT(cls->isRealized());
-
- property_list_t *proplist = (property_list_t *)
- malloc(sizeof(*proplist));
- proplist->count = 1;
- proplist->entsizeAndFlags = sizeof(proplist->first);
- proplist->first.name = strdupIfMutable(name);
- proplist->first.attributes = copyPropertyAttributeString(attrs, count);
-
- cls->data()->properties.attachLists(&proplist, 1);
-
- return YES;
- }
- }
- BOOL
- class_addProperty(Class cls, const char *name,
- const objc_property_attribute_t *attrs, unsigned int n)
- {
- return _class_addProperty(cls, name, attrs, n, NO);
- }
- void
- class_replaceProperty(Class cls, const char *name,
- const objc_property_attribute_t *attrs, unsigned int n)
- {
- _class_addProperty(cls, name, attrs, n, YES);
- }
- /***********************************************************************
- * look_up_class
- * Look up a class by name, and realize it.
- * Locking: acquires runtimeLock
- **********************************************************************/
- static BOOL empty_getClass(const char *name, Class *outClass)
- {
- *outClass = nil;
- return NO;
- }
- static ChainedHookFunction<objc_hook_getClass> GetClassHook{empty_getClass};
- void objc_setHook_getClass(objc_hook_getClass newValue,
- objc_hook_getClass *outOldValue)
- {
- GetClassHook.set(newValue, outOldValue);
- }
- Class
- look_up_class(const char *name,
- bool includeUnconnected __attribute__((unused)),
- bool includeClassHandler __attribute__((unused)))
- {
- if (!name) return nil;
- 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])) {
- return nil;
- }
- }
- // 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 = (const char **)
- 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;
- }
- return result;
- }
- /***********************************************************************
- * objc_duplicateClass
- * fixme
- * Locking: acquires runtimeLock
- **********************************************************************/
- Class
- objc_duplicateClass(Class original, const char *name,
- size_t extraBytes)
- {
- Class duplicate;
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(original);
- ASSERT(original->isRealized());
- ASSERT(!original->isMetaClass());
- duplicate = alloc_class_for_subclass(original, extraBytes);
- duplicate->initClassIsa(original->ISA());
- duplicate->superclass = original->superclass;
- duplicate->cache.initializeToEmpty();
- class_rw_t *rw = (class_rw_t *)calloc(sizeof(*original->data()), 1);
- rw->flags = (original->data()->flags | RW_COPIED_RO | RW_REALIZING);
- rw->version = original->data()->version;
- rw->firstSubclass = nil;
- rw->nextSiblingClass = nil;
- duplicate->bits = original->bits;
- duplicate->setData(rw);
- rw->ro = original->data()->ro->duplicate();
- *(char **)&rw->ro->name = strdupIfMutable(name);
- rw->methods = original->data()->methods.duplicate();
- // fixme dies when categories are added to the base
- rw->properties = original->data()->properties;
- rw->protocols = original->data()->protocols;
- duplicate->chooseClassArrayIndex();
- if (duplicate->superclass) {
- addSubclass(duplicate->superclass, duplicate);
- // duplicate->isa == original->isa so don't addSubclass() for it
- } else {
- addRootClass(duplicate);
- }
- // Don't methodize class - construction above is correct
- addNamedClass(duplicate, duplicate->data()->ro->name);
- addClassTableEntry(duplicate, /*addMeta=*/false);
-
- if (PrintConnecting) {
- _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p",
- name, original->nameForLogging(),
- (void*)duplicate, duplicate->data()->ro);
- }
- duplicate->clearInfo(RW_REALIZING);
- return duplicate;
- }
- /***********************************************************************
- * objc_initializeClassPair
- * Locking: runtimeLock must be write-locked by the caller
- **********************************************************************/
- // &UnsetLayout is the default ivar layout during class construction
- static const uint8_t UnsetLayout = 0;
- static void objc_initializeClassPair_internal(Class superclass, const char *name, Class cls, Class meta)
- {
- runtimeLock.assertLocked();
- class_ro_t *cls_ro_w, *meta_ro_w;
-
- cls->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
- meta->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1));
- cls_ro_w = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
- meta_ro_w = (class_ro_t *)calloc(sizeof(class_ro_t), 1);
- cls->data()->ro = cls_ro_w;
- meta->data()->ro = meta_ro_w;
- // Set basic info
- cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
- meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING;
- cls->data()->version = 0;
- meta->data()->version = 7;
- cls_ro_w->flags = 0;
- meta_ro_w->flags = RO_META;
- if (!superclass) {
- cls_ro_w->flags |= RO_ROOT;
- meta_ro_w->flags |= RO_ROOT;
- }
- if (superclass) {
- uint32_t flagsToCopy = RW_FORBIDS_ASSOCIATED_OBJECTS;
- cls->data()->flags |= superclass->data()->flags & flagsToCopy;
- cls_ro_w->instanceStart = superclass->unalignedInstanceSize();
- meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize();
- cls->setInstanceSize(cls_ro_w->instanceStart);
- meta->setInstanceSize(meta_ro_w->instanceStart);
- } else {
- cls_ro_w->instanceStart = 0;
- meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class);
- cls->setInstanceSize((uint32_t)sizeof(id)); // just an isa
- meta->setInstanceSize(meta_ro_w->instanceStart);
- }
- cls_ro_w->name = strdupIfMutable(name);
- meta_ro_w->name = strdupIfMutable(name);
- cls_ro_w->ivarLayout = &UnsetLayout;
- cls_ro_w->weakIvarLayout = &UnsetLayout;
- meta->chooseClassArrayIndex();
- cls->chooseClassArrayIndex();
- // This absolutely needs to be done before addSubclass
- // as initializeToEmpty() clobbers the FAST_CACHE bits
- cls->cache.initializeToEmpty();
- meta->cache.initializeToEmpty();
- #if FAST_CACHE_META
- meta->cache.setBit(FAST_CACHE_META);
- #endif
- meta->setInstancesRequireRawIsa();
- // Connect to superclasses and metaclasses
- cls->initClassIsa(meta);
- if (superclass) {
- meta->initClassIsa(superclass->ISA()->ISA());
- cls->superclass = superclass;
- meta->superclass = superclass->ISA();
- addSubclass(superclass, cls);
- addSubclass(superclass->ISA(), meta);
- } else {
- meta->initClassIsa(meta);
- cls->superclass = Nil;
- meta->superclass = cls;
- addRootClass(cls);
- addSubclass(cls, meta);
- }
- addClassTableEntry(cls);
- }
- /***********************************************************************
- * verifySuperclass
- * Sanity-check the superclass provided to
- * objc_allocateClassPair, objc_initializeClassPair, or objc_readClassPair.
- **********************************************************************/
- bool
- verifySuperclass(Class superclass, bool rootOK)
- {
- if (!superclass) {
- // Superclass does not exist.
- // If subclass may be a root class, this is OK.
- // If subclass must not be a root class, this is bad.
- return rootOK;
- }
- // Superclass must be realized.
- if (! superclass->isRealized()) return false;
- // Superclass must not be under construction.
- if (superclass->data()->flags & RW_CONSTRUCTING) return false;
- return true;
- }
- /***********************************************************************
- * objc_initializeClassPair
- **********************************************************************/
- Class objc_initializeClassPair(Class superclass, const char *name, Class cls, Class meta)
- {
- // Fail if the class name is in use.
- if (look_up_class(name, NO, NO)) return nil;
- mutex_locker_t lock(runtimeLock);
- // Fail if the class name is in use.
- // Fail if the superclass isn't kosher.
- if (getClassExceptSomeSwift(name) ||
- !verifySuperclass(superclass, true/*rootOK*/))
- {
- return nil;
- }
- objc_initializeClassPair_internal(superclass, name, cls, meta);
- return cls;
- }
- /***********************************************************************
- * objc_allocateClassPair
- * fixme
- * Locking: acquires runtimeLock
- **********************************************************************/
- Class objc_allocateClassPair(Class superclass, const char *name,
- size_t extraBytes)
- {
- Class cls, meta;
- // Fail if the class name is in use.
- if (look_up_class(name, NO, NO)) return nil;
- mutex_locker_t lock(runtimeLock);
- // Fail if the class name is in use.
- // Fail if the superclass isn't kosher.
- if (getClassExceptSomeSwift(name) ||
- !verifySuperclass(superclass, true/*rootOK*/))
- {
- return nil;
- }
- // Allocate new classes.
- cls = alloc_class_for_subclass(superclass, extraBytes);
- meta = alloc_class_for_subclass(superclass, extraBytes);
- // fixme mangle the name if it looks swift-y?
- objc_initializeClassPair_internal(superclass, name, cls, meta);
- return cls;
- }
- /***********************************************************************
- * objc_registerClassPair
- * fixme
- * Locking: acquires runtimeLock
- **********************************************************************/
- void objc_registerClassPair(Class cls)
- {
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
- if ((cls->data()->flags & RW_CONSTRUCTED) ||
- (cls->ISA()->data()->flags & RW_CONSTRUCTED))
- {
- _objc_inform("objc_registerClassPair: class '%s' was already "
- "registered!", cls->data()->ro->name);
- return;
- }
- if (!(cls->data()->flags & RW_CONSTRUCTING) ||
- !(cls->ISA()->data()->flags & RW_CONSTRUCTING))
- {
- _objc_inform("objc_registerClassPair: class '%s' was not "
- "allocated with objc_allocateClassPair!",
- cls->data()->ro->name);
- return;
- }
- // Clear "under construction" bit, set "done constructing" bit
- cls->ISA()->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
- cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING);
- // Add to named class table.
- addNamedClass(cls, cls->data()->ro->name);
- }
- /***********************************************************************
- * objc_readClassPair()
- * Read a class and metaclass as written by a compiler.
- * Assumes the class and metaclass are not referenced by other things
- * that might need to be fixed up (such as categories and subclasses).
- * Does not call +load.
- * Returns the class pointer, or nil.
- *
- * Locking: runtimeLock acquired by map_images
- **********************************************************************/
- Class objc_readClassPair(Class bits, const struct objc_image_info *info)
- {
- mutex_locker_t lock(runtimeLock);
- // No info bits are significant yet.
- (void)info;
- // Fail if the superclass isn't kosher.
- bool rootOK = bits->data()->flags & RO_ROOT;
- if (!verifySuperclass(bits->superclass, rootOK)){
- return nil;
- }
- // Duplicate classes are allowed, just like they are for image loading.
- // readClass will complain about the duplicate.
- Class cls = readClass(bits, false/*bundle*/, false/*shared cache*/);
- if (cls != bits) {
- // This function isn't allowed to remap anything.
- _objc_fatal("objc_readClassPair for class %s changed %p to %p",
- cls->nameForLogging(), bits, cls);
- }
- // The only client of this function is old Swift.
- // Stable Swift won't use it.
- // fixme once Swift in the OS settles we can assert(!cls->isSwiftStable()).
- cls = realizeClassWithoutSwift(cls, nil);
- return cls;
- }
- /***********************************************************************
- * detach_class
- * Disconnect a class from other data structures.
- * Exception: does not remove the class from the +load list
- * Call this before free_class.
- * Locking: runtimeLock must be held by the caller.
- **********************************************************************/
- static void detach_class(Class cls, bool isMeta)
- {
- runtimeLock.assertLocked();
- // categories not yet attached to this class
- objc::unattachedCategories.eraseClass(cls);
- // superclass's subclass list
- if (cls->isRealized()) {
- Class supercls = cls->superclass;
- if (supercls) {
- removeSubclass(supercls, cls);
- } else {
- removeRootClass(cls);
- }
- }
- // class tables and +load queue
- if (!isMeta) {
- removeNamedClass(cls, cls->mangledName());
- }
- objc::allocatedClasses.get().erase(cls);
- }
- /***********************************************************************
- * free_class
- * Frees a class's data structures.
- * Call this after detach_class.
- * Locking: runtimeLock must be held by the caller
- **********************************************************************/
- static void free_class(Class cls)
- {
- runtimeLock.assertLocked();
- if (! cls->isRealized()) return;
- auto rw = cls->data();
- auto ro = rw->ro;
- cache_delete(cls);
-
- for (auto& meth : rw->methods) {
- try_free(meth.types);
- }
- rw->methods.tryFree();
-
- const ivar_list_t *ivars = ro->ivars;
- if (ivars) {
- for (auto& ivar : *ivars) {
- try_free(ivar.offset);
- try_free(ivar.name);
- try_free(ivar.type);
- }
- try_free(ivars);
- }
- for (auto& prop : rw->properties) {
- try_free(prop.name);
- try_free(prop.attributes);
- }
- rw->properties.tryFree();
- rw->protocols.tryFree();
-
- try_free(ro->ivarLayout);
- try_free(ro->weakIvarLayout);
- try_free(ro->name);
- try_free(ro);
- try_free(rw);
- try_free(cls);
- }
- void objc_disposeClassPair(Class cls)
- {
- mutex_locker_t lock(runtimeLock);
- checkIsKnownClass(cls);
- if (!(cls->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)) ||
- !(cls->ISA()->data()->flags & (RW_CONSTRUCTED|RW_CONSTRUCTING)))
- {
- // class not allocated with objc_allocateClassPair
- // disposing still-unregistered class is OK!
- _objc_inform("objc_disposeClassPair: class '%s' was not "
- "allocated with objc_allocateClassPair!",
- cls->data()->ro->name);
- return;
- }
- if (cls->isMetaClass()) {
- _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, "
- "not a class!", cls->data()->ro->name);
- return;
- }
- // Shouldn't have any live subclasses.
- if (cls->data()->firstSubclass) {
- _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
- "including '%s'!", cls->data()->ro->name,
- cls->data()->firstSubclass->nameForLogging());
- }
- if (cls->ISA()->data()->firstSubclass) {
- _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, "
- "including '%s'!", cls->data()->ro->name,
- cls->ISA()->data()->firstSubclass->nameForLogging());
- }
- // don't remove_class_from_loadable_list()
- // - it's not there and we don't have the lock
- detach_class(cls->ISA(), YES);
- detach_class(cls, NO);
- free_class(cls->ISA());
- free_class(cls);
- }
- /***********************************************************************
- * objc_constructInstance
- * Creates an instance of `cls` at the location pointed to by `bytes`.
- * `bytes` must point to at least class_getInstanceSize(cls) bytes of
- * well-aligned zero-filled memory.
- * The new object's isa is set. Any C++ constructors are called.
- * Returns `bytes` if successful. Returns nil if `cls` or `bytes` is
- * nil, or if C++ constructors fail.
- * Note: class_createInstance() and class_createInstances() preflight this.
- **********************************************************************/
- id
- objc_constructInstance(Class cls, void *bytes)
- {
- if (!cls || !bytes) return nil;
- id obj = (id)bytes;
- // Read class's info bits all at once for performance
- bool hasCxxCtor = cls->hasCxxCtor();
- bool hasCxxDtor = cls->hasCxxDtor();
- bool fast = cls->canAllocNonpointer();
-
- if (fast) {
- obj->initInstanceIsa(cls, hasCxxDtor);
- } else {
- obj->initIsa(cls);
- }
- if (hasCxxCtor) {
- return object_cxxConstructFromClass(obj, cls, OBJECT_CONSTRUCT_NONE);
- } else {
- return obj;
- }
- }
- /***********************************************************************
- * class_createInstance
- * fixme
- * Locking: none
- *
- * Note: this function has been carefully written so that the fastpath
- * takes no branch.
- **********************************************************************/
- static ALWAYS_INLINE id
- _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
- int construct_flags = OBJECT_CONSTRUCT_NONE,
- bool cxxConstruct = true,
- size_t *outAllocatedSize = nil)
- {
- ASSERT(cls->isRealized());
- // Read class's info bits all at once for performance
- bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
- bool hasCxxDtor = cls->hasCxxDtor();
- bool fast = cls->canAllocNonpointer();
- size_t size;
- size = cls->instanceSize(extraBytes);
- if (outAllocatedSize) *outAllocatedSize = size;
- id obj;
- if (zone) {
- obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
- } else {
- obj = (id)calloc(1, size);
- }
- if (slowpath(!obj)) {
- if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
- return _objc_callBadAllocHandler(cls);
- }
- return nil;
- }
- if (!zone && fast) {
- obj->initInstanceIsa(cls, hasCxxDtor);
- } else {
- // Use raw pointer isa on the assumption that they might be
- // doing something weird with the zone or RR.
- obj->initIsa(cls);
- }
- if (fastpath(!hasCxxCtor)) {
- return obj;
- }
- construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
- return object_cxxConstructFromClass(obj, cls, construct_flags);
- }
- id
- class_createInstance(Class cls, size_t extraBytes)
- {
- if (!cls) return nil;
- return _class_createInstanceFromZone(cls, extraBytes, nil);
- }
- NEVER_INLINE
- id
- _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
- {
- // allocWithZone under __OBJC2__ ignores the zone parameter
- return _class_createInstanceFromZone(cls, 0, nil,
- OBJECT_CONSTRUCT_CALL_BADALLOC);
- }
- /***********************************************************************
- * class_createInstances
- * fixme
- * Locking: none
- **********************************************************************/
- #if SUPPORT_NONPOINTER_ISA
- #warning fixme optimize class_createInstances
- #endif
- unsigned
- class_createInstances(Class cls, size_t extraBytes,
- id *results, unsigned num_requested)
- {
- return _class_createInstancesFromZone(cls, extraBytes, nil,
- results, num_requested);
- }
- /***********************************************************************
- * object_copyFromZone
- * fixme
- * Locking: none
- **********************************************************************/
- static id
- _object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
- {
- if (!oldObj) return nil;
- if (oldObj->isTaggedPointer()) return oldObj;
- // fixme this doesn't handle C++ ivars correctly (#4619414)
- Class cls = oldObj->ISA();
- size_t size;
- id obj = _class_createInstanceFromZone(cls, extraBytes, zone,
- OBJECT_CONSTRUCT_NONE, false, &size);
- if (!obj) return nil;
- // Copy everything except the isa, which was already set above.
- uint8_t *copyDst = (uint8_t *)obj + sizeof(Class);
- uint8_t *copySrc = (uint8_t *)oldObj + sizeof(Class);
- size_t copySize = size - sizeof(Class);
- memmove(copyDst, copySrc, copySize);
- fixupCopiedIvars(obj, oldObj);
- return obj;
- }
- /***********************************************************************
- * object_copy
- * fixme
- * Locking: none
- **********************************************************************/
- id
- object_copy(id oldObj, size_t extraBytes)
- {
- return _object_copyFromZone(oldObj, extraBytes, malloc_default_zone());
- }
- #if SUPPORT_ZONES
- /***********************************************************************
- * class_createInstanceFromZone
- * fixme
- * Locking: none
- **********************************************************************/
- id
- class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone)
- {
- if (!cls) return nil;
- return _class_createInstanceFromZone(cls, extraBytes, zone);
- }
- /***********************************************************************
- * object_copyFromZone
- * fixme
- * Locking: none
- **********************************************************************/
- id
- object_copyFromZone(id oldObj, size_t extraBytes, void *zone)
- {
- return _object_copyFromZone(oldObj, extraBytes, zone);
- }
- #endif
- /***********************************************************************
- * objc_destructInstance
- * Destroys an instance without freeing memory.
- * Calls C++ destructors.
- * Calls ARC ivar cleanup.
- * Removes associative references.
- * Returns `obj`. Does nothing if `obj` is nil.
- **********************************************************************/
- void *objc_destructInstance(id obj)
- {
- if (obj) {
- // Read all of the flags at once for performance.
- bool cxx = obj->hasCxxDtor();
- bool assoc = obj->hasAssociatedObjects();
- // This order is important.
- if (cxx) object_cxxDestruct(obj);
- if (assoc) _object_remove_assocations(obj);
- obj->clearDeallocating();
- }
- return obj;
- }
- /***********************************************************************
- * object_dispose
- * fixme
- * Locking: none
- **********************************************************************/
- id
- object_dispose(id obj)
- {
- if (!obj) return nil;
- objc_destructInstance(obj);
- free(obj);
- return nil;
- }
- /***********************************************************************
- * _objc_getFreedObjectClass
- * fixme
- * Locking: none
- **********************************************************************/
- Class _objc_getFreedObjectClass (void)
- {
- return nil;
- }
- /***********************************************************************
- * Tagged pointer objects.
- *
- * Tagged pointer objects store the class and the object value in the
- * object pointer; the "pointer" does not actually point to anything.
- *
- * Tagged pointer objects currently use this representation:
- * (LSB)
- * 1 bit set if tagged, clear if ordinary object pointer
- * 3 bits tag index
- * 60 bits payload
- * (MSB)
- * The tag index defines the object's class.
- * The payload format is defined by the object's class.
- *
- * If the tag index is 0b111, the tagged pointer object uses an
- * "extended" representation, allowing more classes but with smaller payloads:
- * (LSB)
- * 1 bit set if tagged, clear if ordinary object pointer
- * 3 bits 0b111
- * 8 bits extended tag index
- * 52 bits payload
- * (MSB)
- *
- * Some architectures reverse the MSB and LSB in these representations.
- *
- * This representation is subject to change. Representation-agnostic SPI is:
- * objc-internal.h for class implementers.
- * objc-gdb.h for debuggers.
- **********************************************************************/
- #if !SUPPORT_TAGGED_POINTERS
- // These variables are always provided for debuggers.
- uintptr_t objc_debug_taggedpointer_obfuscator = 0;
- uintptr_t objc_debug_taggedpointer_mask = 0;
- unsigned objc_debug_taggedpointer_slot_shift = 0;
- uintptr_t objc_debug_taggedpointer_slot_mask = 0;
- unsigned objc_debug_taggedpointer_payload_lshift = 0;
- unsigned objc_debug_taggedpointer_payload_rshift = 0;
- Class objc_debug_taggedpointer_classes[1] = { nil };
- uintptr_t objc_debug_taggedpointer_ext_mask = 0;
- unsigned objc_debug_taggedpointer_ext_slot_shift = 0;
- uintptr_t objc_debug_taggedpointer_ext_slot_mask = 0;
- unsigned objc_debug_taggedpointer_ext_payload_lshift = 0;
- unsigned objc_debug_taggedpointer_ext_payload_rshift = 0;
- Class objc_debug_taggedpointer_ext_classes[1] = { nil };
- static void
- disableTaggedPointers() { }
- static void
- initializeTaggedPointerObfuscator(void) { }
- #else
- // The "slot" used in the class table and given to the debugger
- // includes the is-tagged bit. This makes objc_msgSend faster.
- // The "ext" representation doesn't do that.
- uintptr_t objc_debug_taggedpointer_obfuscator;
- uintptr_t objc_debug_taggedpointer_mask = _OBJC_TAG_MASK;
- unsigned objc_debug_taggedpointer_slot_shift = _OBJC_TAG_SLOT_SHIFT;
- uintptr_t objc_debug_taggedpointer_slot_mask = _OBJC_TAG_SLOT_MASK;
- unsigned objc_debug_taggedpointer_payload_lshift = _OBJC_TAG_PAYLOAD_LSHIFT;
- unsigned objc_debug_taggedpointer_payload_rshift = _OBJC_TAG_PAYLOAD_RSHIFT;
- // objc_debug_taggedpointer_classes is defined in objc-msg-*.s
- uintptr_t objc_debug_taggedpointer_ext_mask = _OBJC_TAG_EXT_MASK;
- unsigned objc_debug_taggedpointer_ext_slot_shift = _OBJC_TAG_EXT_SLOT_SHIFT;
- uintptr_t objc_debug_taggedpointer_ext_slot_mask = _OBJC_TAG_EXT_SLOT_MASK;
- unsigned objc_debug_taggedpointer_ext_payload_lshift = _OBJC_TAG_EXT_PAYLOAD_LSHIFT;
- unsigned objc_debug_taggedpointer_ext_payload_rshift = _OBJC_TAG_EXT_PAYLOAD_RSHIFT;
- // objc_debug_taggedpointer_ext_classes is defined in objc-msg-*.s
- static void
- disableTaggedPointers()
- {
- objc_debug_taggedpointer_mask = 0;
- objc_debug_taggedpointer_slot_shift = 0;
- objc_debug_taggedpointer_slot_mask = 0;
- objc_debug_taggedpointer_payload_lshift = 0;
- objc_debug_taggedpointer_payload_rshift = 0;
- objc_debug_taggedpointer_ext_mask = 0;
- objc_debug_taggedpointer_ext_slot_shift = 0;
- objc_debug_taggedpointer_ext_slot_mask = 0;
- objc_debug_taggedpointer_ext_payload_lshift = 0;
- objc_debug_taggedpointer_ext_payload_rshift = 0;
- }
- // Returns a pointer to the class's storage in the tagged class arrays.
- // Assumes the tag is a valid basic tag.
- static Class *
- classSlotForBasicTagIndex(objc_tag_index_t tag)
- {
- uintptr_t tagObfuscator = ((objc_debug_taggedpointer_obfuscator
- >> _OBJC_TAG_INDEX_SHIFT)
- & _OBJC_TAG_INDEX_MASK);
- uintptr_t obfuscatedTag = tag ^ tagObfuscator;
- // Array index in objc_tag_classes includes the tagged bit itself
- #if SUPPORT_MSB_TAGGED_POINTERS
- return &objc_tag_classes[0x8 | obfuscatedTag];
- #else
- return &objc_tag_classes[(obfuscatedTag << 1) | 1];
- #endif
- }
- // Returns a pointer to the class's storage in the tagged class arrays,
- // or nil if the tag is out of range.
- static Class *
- classSlotForTagIndex(objc_tag_index_t tag)
- {
- if (tag >= OBJC_TAG_First60BitPayload && tag <= OBJC_TAG_Last60BitPayload) {
- return classSlotForBasicTagIndex(tag);
- }
- if (tag >= OBJC_TAG_First52BitPayload && tag <= OBJC_TAG_Last52BitPayload) {
- int index = tag - OBJC_TAG_First52BitPayload;
- uintptr_t tagObfuscator = ((objc_debug_taggedpointer_obfuscator
- >> _OBJC_TAG_EXT_INDEX_SHIFT)
- & _OBJC_TAG_EXT_INDEX_MASK);
- return &objc_tag_ext_classes[index ^ tagObfuscator];
- }
- return nil;
- }
- /***********************************************************************
- * initializeTaggedPointerObfuscator
- * Initialize objc_debug_taggedpointer_obfuscator with randomness.
- *
- * The tagged pointer obfuscator is intended to make it more difficult
- * for an attacker to construct a particular object as a tagged pointer,
- * in the presence of a buffer overflow or other write control over some
- * memory. The obfuscator is XORed with the tagged pointers when setting
- * or retrieving payload values. They are filled with randomness on first
- * use.
- **********************************************************************/
- static void
- initializeTaggedPointerObfuscator(void)
- {
- if (sdkIsOlderThan(10_14, 12_0, 12_0, 5_0, 3_0) ||
- // Set the obfuscator to zero for apps linked against older SDKs,
- // in case they're relying on the tagged pointer representation.
- DisableTaggedPointerObfuscation) {
- objc_debug_taggedpointer_obfuscator = 0;
- } else {
- // Pull random data into the variable, then shift away all non-payload bits.
- arc4random_buf(&objc_debug_taggedpointer_obfuscator,
- sizeof(objc_debug_taggedpointer_obfuscator));
- objc_debug_taggedpointer_obfuscator &= ~_OBJC_TAG_MASK;
- }
- }
- /***********************************************************************
- * _objc_registerTaggedPointerClass
- * Set the class to use for the given tagged pointer index.
- * Aborts if the tag is out of range, or if the tag is already
- * used by some other class.
- **********************************************************************/
- void
- _objc_registerTaggedPointerClass(objc_tag_index_t tag, Class cls)
- {
- if (objc_debug_taggedpointer_mask == 0) {
- _objc_fatal("tagged pointers are disabled");
- }
- Class *slot = classSlotForTagIndex(tag);
- if (!slot) {
- _objc_fatal("tag index %u is invalid", (unsigned int)tag);
- }
- Class oldCls = *slot;
-
- if (cls && oldCls && cls != oldCls) {
- _objc_fatal("tag index %u used for two different classes "
- "(was %p %s, now %p %s)", tag,
- oldCls, oldCls->nameForLogging(),
- cls, cls->nameForLogging());
- }
- *slot = cls;
- // Store a placeholder class in the basic tag slot that is
- // reserved for the extended tag space, if it isn't set already.
- // Do this lazily when the first extended tag is registered so
- // that old debuggers characterize bogus pointers correctly more often.
- if (tag < OBJC_TAG_First60BitPayload || tag > OBJC_TAG_Last60BitPayload) {
- Class *extSlot = classSlotForBasicTagIndex(OBJC_TAG_RESERVED_7);
- if (*extSlot == nil) {
- extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
- *extSlot = (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
- }
- }
- }
- /***********************************************************************
- * _objc_getClassForTag
- * Returns the class that is using the given tagged pointer tag.
- * Returns nil if no class is using that tag or the tag is out of range.
- **********************************************************************/
- Class
- _objc_getClassForTag(objc_tag_index_t tag)
- {
- Class *slot = classSlotForTagIndex(tag);
- if (slot) return *slot;
- else return nil;
- }
- #endif
- #if SUPPORT_FIXUP
- OBJC_EXTERN void objc_msgSend_fixup(void);
- OBJC_EXTERN void objc_msgSendSuper2_fixup(void);
- OBJC_EXTERN void objc_msgSend_stret_fixup(void);
- OBJC_EXTERN void objc_msgSendSuper2_stret_fixup(void);
- #if defined(__i386__) || defined(__x86_64__)
- OBJC_EXTERN void objc_msgSend_fpret_fixup(void);
- #endif
- #if defined(__x86_64__)
- OBJC_EXTERN void objc_msgSend_fp2ret_fixup(void);
- #endif
- OBJC_EXTERN void objc_msgSend_fixedup(void);
- OBJC_EXTERN void objc_msgSendSuper2_fixedup(void);
- OBJC_EXTERN void objc_msgSend_stret_fixedup(void);
- OBJC_EXTERN void objc_msgSendSuper2_stret_fixedup(void);
- #if defined(__i386__) || defined(__x86_64__)
- OBJC_EXTERN void objc_msgSend_fpret_fixedup(void);
- #endif
- #if defined(__x86_64__)
- OBJC_EXTERN void objc_msgSend_fp2ret_fixedup(void);
- #endif
- /***********************************************************************
- * fixupMessageRef
- * Repairs an old vtable dispatch call site.
- * vtable dispatch itself is not supported.
- **********************************************************************/
- static void
- fixupMessageRef(message_ref_t *msg)
- {
- msg->sel = sel_registerName((const char *)msg->sel);
- if (msg->imp == &objc_msgSend_fixup) {
- if (msg->sel == @selector(alloc)) {
- msg->imp = (IMP)&objc_alloc;
- } else if (msg->sel == @selector(allocWithZone:)) {
- msg->imp = (IMP)&objc_allocWithZone;
- } else if (msg->sel == @selector(retain)) {
- msg->imp = (IMP)&objc_retain;
- } else if (msg->sel == @selector(release)) {
- msg->imp = (IMP)&objc_release;
- } else if (msg->sel == @selector(autorelease)) {
- msg->imp = (IMP)&objc_autorelease;
- } else {
- msg->imp = &objc_msgSend_fixedup;
- }
- }
- else if (msg->imp == &objc_msgSendSuper2_fixup) {
- msg->imp = &objc_msgSendSuper2_fixedup;
- }
- else if (msg->imp == &objc_msgSend_stret_fixup) {
- msg->imp = &objc_msgSend_stret_fixedup;
- }
- else if (msg->imp == &objc_msgSendSuper2_stret_fixup) {
- msg->imp = &objc_msgSendSuper2_stret_fixedup;
- }
- #if defined(__i386__) || defined(__x86_64__)
- else if (msg->imp == &objc_msgSend_fpret_fixup) {
- msg->imp = &objc_msgSend_fpret_fixedup;
- }
- #endif
- #if defined(__x86_64__)
- else if (msg->imp == &objc_msgSend_fp2ret_fixup) {
- msg->imp = &objc_msgSend_fp2ret_fixedup;
- }
- #endif
- }
- // SUPPORT_FIXUP
- #endif
- // ProKit SPI
- static Class setSuperclass(Class cls, Class newSuper)
- {
- Class oldSuper;
- runtimeLock.assertLocked();
- ASSERT(cls->isRealized());
- ASSERT(newSuper->isRealized());
- oldSuper = cls->superclass;
- removeSubclass(oldSuper, cls);
- removeSubclass(oldSuper->ISA(), cls->ISA());
- cls->superclass = newSuper;
- cls->ISA()->superclass = newSuper->ISA();
- addSubclass(newSuper, cls);
- addSubclass(newSuper->ISA(), cls->ISA());
- // Flush subclass's method caches.
- flushCaches(cls);
- flushCaches(cls->ISA());
-
- return oldSuper;
- }
- Class class_setSuperclass(Class cls, Class newSuper)
- {
- mutex_locker_t lock(runtimeLock);
- return setSuperclass(cls, newSuper);
- }
- void runtime_init(void)
- {
- objc::unattachedCategories.init(32);
- objc::allocatedClasses.init();
- }
- // __OBJC2__
- #endif
|