ThreadContext.cpp 156 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeBasePch.h"
  6. #include "ThreadServiceWrapper.h"
  7. #include "Types/TypePropertyCache.h"
  8. #ifdef ENABLE_SCRIPT_DEBUGGING
  9. #include "Debug/DebuggingFlags.h"
  10. #include "Debug/DiagProbe.h"
  11. #include "Debug/DebugManager.h"
  12. #endif
  13. #include "Chars.h"
  14. #include "CaseInsensitive.h"
  15. #include "CharSet.h"
  16. #include "CharMap.h"
  17. #include "StandardChars.h"
  18. #include "Base/ThreadContextTlsEntry.h"
  19. #include "Base/ThreadBoundThreadContextManager.h"
  20. #include "Language/SourceDynamicProfileManager.h"
  21. #include "Language/CodeGenRecyclableData.h"
  22. #include "Language/InterpreterStackFrame.h"
  23. #include "Language/JavascriptStackWalker.h"
  24. #include "Base/ScriptMemoryDumper.h"
  25. #if DBG
  26. #include "Memory/StressTest.h"
  27. #endif
  28. #ifdef DYNAMIC_PROFILE_MUTATOR
  29. #include "Language/DynamicProfileMutator.h"
  30. #endif
  31. #ifdef ENABLE_BASIC_TELEMETRY
  32. #include "Telemetry.h"
  33. #include "Recycler/RecyclerTelemetryTransmitter.h"
  34. #endif // ENABLE_BASIC_TELEMETRY
  35. const int TotalNumberOfBuiltInProperties = Js::PropertyIds::_countJSOnlyProperty;
  36. /*
  37. * When we aren't adding any additional properties
  38. */
  39. void DefaultInitializeAdditionalProperties(ThreadContext *threadContext)
  40. {
  41. }
  42. /*
  43. *
  44. */
  45. void (*InitializeAdditionalProperties)(ThreadContext *threadContext) = DefaultInitializeAdditionalProperties;
  46. CriticalSection ThreadContext::s_csThreadContext;
  47. size_t ThreadContext::processNativeCodeSize = 0;
  48. ThreadContext * ThreadContext::globalListFirst = nullptr;
  49. ThreadContext * ThreadContext::globalListLast = nullptr;
  50. THREAD_LOCAL uint ThreadContext::activeScriptSiteCount = 0;
  51. const Js::PropertyRecord * const ThreadContext::builtInPropertyRecords[] =
  52. {
  53. Js::BuiltInPropertyRecords::EMPTY,
  54. #define ENTRY_INTERNAL_SYMBOL(n) Js::BuiltInPropertyRecords::n,
  55. #define ENTRY_SYMBOL(n, d) Js::BuiltInPropertyRecords::n,
  56. #define ENTRY(n) Js::BuiltInPropertyRecords::n,
  57. #define ENTRY2(n, s) ENTRY(n)
  58. #include "Base/JnDirectFields.h"
  59. };
  60. ThreadContext::RecyclableData::RecyclableData(Recycler *const recycler) :
  61. pendingFinallyException(nullptr),
  62. soErrorObject(nullptr, nullptr, nullptr, true),
  63. oomErrorObject(nullptr, nullptr, nullptr, true),
  64. terminatedErrorObject(nullptr, nullptr, nullptr),
  65. typesWithProtoPropertyCache(recycler),
  66. #if ENABLE_NATIVE_CODEGEN
  67. propertyGuards(recycler, 128),
  68. #endif
  69. oldEntryPointInfo(nullptr),
  70. #ifdef ENABLE_SCRIPT_DEBUGGING
  71. returnedValueList(nullptr),
  72. #endif
  73. constructorCacheInvalidationCount(0)
  74. {
  75. }
  76. ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager, JsUtil::ThreadService::ThreadServiceCallback threadServiceCallback, bool enableExperimentalFeatures) :
  77. currentThreadId(::GetCurrentThreadId()),
  78. stackLimitForCurrentThread(0),
  79. stackProber(nullptr),
  80. isThreadBound(false),
  81. hasThrownPendingException(false),
  82. hasBailedOutBitPtr(nullptr),
  83. noScriptScope(false),
  84. heapEnum(nullptr),
  85. threadContextFlags(ThreadContextFlagNoFlag),
  86. JsUtil::DoublyLinkedListElement<ThreadContext>(),
  87. allocationPolicyManager(allocationPolicyManager),
  88. threadService(threadServiceCallback),
  89. isOptimizedForManyInstances(Js::Configuration::Global.flags.OptimizeForManyInstances),
  90. bgJit(Js::Configuration::Global.flags.BgJit),
  91. pageAllocator(allocationPolicyManager, PageAllocatorType_Thread, Js::Configuration::Global.flags, 0, RecyclerHeuristic::Instance.DefaultMaxFreePageCount,
  92. false
  93. #if ENABLE_BACKGROUND_PAGE_FREEING
  94. , &backgroundPageQueue
  95. #endif
  96. ),
  97. recycler(nullptr),
  98. hasCollectionCallBack(false),
  99. callDispose(true),
  100. #if ENABLE_NATIVE_CODEGEN
  101. jobProcessor(nullptr),
  102. #endif
  103. interruptPoller(nullptr),
  104. expirableCollectModeGcCount(-1),
  105. expirableObjectList(nullptr),
  106. expirableObjectDisposeList(nullptr),
  107. numExpirableObjects(0),
  108. disableExpiration(false),
  109. callRootLevel(0),
  110. nextTypeId((Js::TypeId)Js::Constants::ReservedTypeIds),
  111. entryExitRecord(nullptr),
  112. leafInterpreterFrame(nullptr),
  113. threadServiceWrapper(nullptr),
  114. tryHandlerAddrOfReturnAddr(nullptr),
  115. temporaryArenaAllocatorCount(0),
  116. temporaryGuestArenaAllocatorCount(0),
  117. crefSContextForDiag(0),
  118. m_prereservedRegionAddr(0),
  119. scriptContextList(nullptr),
  120. scriptContextEverRegistered(false),
  121. #if DBG_DUMP || defined(PROFILE_EXEC)
  122. topLevelScriptSite(nullptr),
  123. #endif
  124. polymorphicCacheState(0),
  125. stackProbeCount(0),
  126. #ifdef BAILOUT_INJECTION
  127. bailOutByteCodeLocationCount(0),
  128. #endif
  129. sourceCodeSize(0),
  130. nativeCodeSize(0),
  131. threadAlloc(_u("TC"), GetPageAllocator(), Js::Throw::OutOfMemory),
  132. inlineCacheThreadInfoAllocator(_u("TC-InlineCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
  133. isInstInlineCacheThreadInfoAllocator(_u("TC-IsInstInlineCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
  134. equivalentTypeCacheInfoAllocator(_u("TC-EquivalentTypeCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
  135. protoInlineCacheByPropId(&inlineCacheThreadInfoAllocator, 521),
  136. storeFieldInlineCacheByPropId(&inlineCacheThreadInfoAllocator, 293),
  137. isInstInlineCacheByFunction(&isInstInlineCacheThreadInfoAllocator, 131),
  138. registeredInlineCacheCount(0),
  139. unregisteredInlineCacheCount(0),
  140. noSpecialPropertyRegistry(this->GetPageAllocator()),
  141. onlyWritablePropertyRegistry(this->GetPageAllocator()),
  142. standardUTF8Chars(0),
  143. standardUnicodeChars(0),
  144. hasUnhandledException(FALSE),
  145. hasCatchHandler(FALSE),
  146. disableImplicitFlags(DisableImplicitNoFlag),
  147. hasCatchHandlerToUserCode(false),
  148. caseInvariantPropertySet(nullptr),
  149. entryPointToBuiltInOperationIdCache(&threadAlloc, 0),
  150. #if ENABLE_NATIVE_CODEGEN
  151. preReservedVirtualAllocator(),
  152. #if !FLOATVAR
  153. codeGenNumberThreadAllocator(nullptr),
  154. xProcNumberPageSegmentManager(nullptr),
  155. #endif
  156. m_jitNumericProperties(nullptr),
  157. m_jitNeedsPropertyUpdate(false),
  158. #if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
  159. thunkPageAllocators(allocationPolicyManager, /* allocXData */ false, /* virtualAllocator */ nullptr, GetCurrentProcess()),
  160. #endif
  161. codePageAllocators(allocationPolicyManager, ALLOC_XDATA, GetPreReservedVirtualAllocator(), GetCurrentProcess()),
  162. #if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
  163. jitThunkEmitter(this, &VirtualAllocWrapper::Instance , GetCurrentProcess()),
  164. #endif
  165. #endif
  166. dynamicObjectEnumeratorCacheMap(&HeapAllocator::Instance, 16),
  167. //threadContextFlags(ThreadContextFlagNoFlag),
  168. #ifdef NTBUILD
  169. telemetryBlock(&localTelemetryBlock),
  170. #endif
  171. configuration(enableExperimentalFeatures),
  172. jsrtRuntime(nullptr),
  173. propertyMap(nullptr),
  174. rootPendingClose(nullptr),
  175. exceptionCode(0),
  176. isProfilingUserCode(true),
  177. loopDepth(0),
  178. redeferralState(InitialRedeferralState),
  179. gcSinceLastRedeferral(0),
  180. gcSinceCallCountsCollected(0),
  181. tridentLoadAddress(nullptr),
  182. m_remoteThreadContextInfo(nullptr)
  183. #ifdef ENABLE_SCRIPT_DEBUGGING
  184. , debugManager(nullptr)
  185. #endif
  186. #if ENABLE_TTD
  187. , TTDContext(nullptr)
  188. , TTDExecutionInfo(nullptr)
  189. , TTDLog(nullptr)
  190. , TTDRootNestingCount(0)
  191. #endif
  192. #ifdef ENABLE_DIRECTCALL_TELEMETRY
  193. , directCallTelemetry(this)
  194. #endif
  195. #if ENABLE_JS_REENTRANCY_CHECK
  196. , noJsReentrancy(false)
  197. #endif
  198. , emptyStringPropertyRecord(nullptr)
  199. , recyclerTelemetryHostInterface(this)
  200. , reentrancySafeOrHandled(false)
  201. , isInReentrancySafeRegion(false)
  202. , closedScriptContextCount(0)
  203. , visibilityState(VisibilityState::Undefined)
  204. {
  205. hostScriptContextStack = Anew(GetThreadAlloc(), JsUtil::Stack<HostScriptContext*>, GetThreadAlloc());
  206. functionCount = 0;
  207. sourceInfoCount = 0;
  208. #if DBG || defined(RUNTIME_DATA_COLLECTION)
  209. scriptContextCount = 0;
  210. #endif
  211. isScriptActive = false;
  212. #ifdef ENABLE_CUSTOM_ENTROPY
  213. entropy.Initialize();
  214. #endif
  215. #if ENABLE_NATIVE_CODEGEN
  216. this->bailOutRegisterSaveSpace = AnewArrayZ(this->GetThreadAlloc(), Js::Var, GetBailOutRegisterSaveSlotCount());
  217. #endif
  218. #if DBG_DUMP
  219. scriptSiteCount = 0;
  220. pageAllocator.debugName = _u("Thread");
  221. #endif
  222. #ifdef DYNAMIC_PROFILE_MUTATOR
  223. this->dynamicProfileMutator = DynamicProfileMutator::GetMutator();
  224. #endif
  225. PERF_COUNTER_INC(Basic, ThreadContext);
  226. #ifdef LEAK_REPORT
  227. this->rootTrackerScriptContext = nullptr;
  228. this->threadId = ::GetCurrentThreadId();
  229. #endif
  230. #ifdef NTBUILD
  231. memset(&localTelemetryBlock, 0, sizeof(localTelemetryBlock));
  232. #endif
  233. AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
  234. ThreadContext::LinkToBeginning(this, &ThreadContext::globalListFirst, &ThreadContext::globalListLast);
  235. #if DBG
  236. // Since we created our page allocator while we were constructing this thread context
  237. // it will pick up the thread context id that is current on the thread. We need to update
  238. // that now.
  239. pageAllocator.UpdateThreadContextHandle((ThreadContextId)this);
  240. #endif
  241. #if DBG
  242. arrayMutationSeed = (Js::Configuration::Global.flags.ArrayMutationTestSeed != 0) ? (uint)Js::Configuration::Global.flags.ArrayMutationTestSeed : (uint)time(NULL);
  243. srand(arrayMutationSeed);
  244. #endif
  245. this->InitAvailableCommit();
  246. }
  247. void ThreadContext::InitAvailableCommit()
  248. {
  249. // Once per process: get the available commit for the process from the OS and push it to the AutoSystemInfo.
  250. // (This must be done lazily, outside DllMain. And it must be done from the Runtime, since the common lib
  251. // doesn't have access to the DelayLoadLibrary stuff.)
  252. ULONG64 commit;
  253. BOOL success = AutoSystemInfo::Data.GetAvailableCommit(&commit);
  254. if (!success)
  255. {
  256. commit = (ULONG64)-1;
  257. #ifdef NTBUILD
  258. APP_MEMORY_INFORMATION AppMemInfo;
  259. success = GetWinCoreProcessThreads()->GetProcessInformation(
  260. GetCurrentProcess(),
  261. ProcessAppMemoryInfo,
  262. &AppMemInfo,
  263. sizeof(AppMemInfo));
  264. if (success)
  265. {
  266. commit = AppMemInfo.AvailableCommit;
  267. }
  268. #endif
  269. AutoSystemInfo::Data.SetAvailableCommit(commit);
  270. }
  271. }
  272. void ThreadContext::SetStackProber(StackProber * stackProber)
  273. {
  274. this->stackProber = stackProber;
  275. if (stackProber != NULL && this->stackLimitForCurrentThread != Js::Constants::StackLimitForScriptInterrupt)
  276. {
  277. this->stackLimitForCurrentThread = stackProber->GetScriptStackLimit();
  278. }
  279. }
  280. size_t ThreadContext::GetScriptStackLimit() const
  281. {
  282. return stackProber->GetScriptStackLimit();
  283. }
  284. HANDLE
  285. ThreadContext::GetProcessHandle() const
  286. {
  287. return GetCurrentProcess();
  288. }
  289. intptr_t
  290. ThreadContext::GetThreadStackLimitAddr() const
  291. {
  292. return (intptr_t)GetAddressOfStackLimitForCurrentThread();
  293. }
  294. #if ENABLE_NATIVE_CODEGEN && defined(ENABLE_WASM_SIMD)
  295. intptr_t
  296. ThreadContext::GetSimdTempAreaAddr(uint8 tempIndex) const
  297. {
  298. return (intptr_t)&X86_TEMP_SIMD[tempIndex];
  299. }
  300. #endif
  301. intptr_t
  302. ThreadContext::GetDisableImplicitFlagsAddr() const
  303. {
  304. return (intptr_t)&disableImplicitFlags;
  305. }
  306. intptr_t
  307. ThreadContext::GetImplicitCallFlagsAddr() const
  308. {
  309. return (intptr_t)&implicitCallFlags;
  310. }
  311. ptrdiff_t
  312. ThreadContext::GetChakraBaseAddressDifference() const
  313. {
  314. return 0;
  315. }
  316. ptrdiff_t
  317. ThreadContext::GetCRTBaseAddressDifference() const
  318. {
  319. return 0;
  320. }
  321. IActiveScriptProfilerHeapEnum* ThreadContext::GetHeapEnum()
  322. {
  323. return heapEnum;
  324. }
  325. void ThreadContext::SetHeapEnum(IActiveScriptProfilerHeapEnum* newHeapEnum)
  326. {
  327. Assert((newHeapEnum != nullptr && heapEnum == nullptr) || (newHeapEnum == nullptr && heapEnum != nullptr));
  328. heapEnum = newHeapEnum;
  329. }
  330. void ThreadContext::ClearHeapEnum()
  331. {
  332. Assert(heapEnum != nullptr);
  333. heapEnum = nullptr;
  334. }
  335. void ThreadContext::GlobalInitialize()
  336. {
  337. for (int i = 0; i < _countof(builtInPropertyRecords); i++)
  338. {
  339. builtInPropertyRecords[i]->SetHash(JsUtil::CharacterBuffer<WCHAR>::StaticGetHashCode(builtInPropertyRecords[i]->GetBuffer(), builtInPropertyRecords[i]->GetLength()));
  340. }
  341. }
  342. ThreadContext::~ThreadContext()
  343. {
  344. {
  345. AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
  346. ThreadContext::Unlink(this, &ThreadContext::globalListFirst, &ThreadContext::globalListLast);
  347. }
  348. #if ENABLE_TTD
  349. if(this->TTDContext != nullptr)
  350. {
  351. TT_HEAP_DELETE(TTD::ThreadContextTTD, this->TTDContext);
  352. this->TTDContext = nullptr;
  353. }
  354. if(this->TTDExecutionInfo != nullptr)
  355. {
  356. TT_HEAP_DELETE(TTD::ThreadContextTTD, this->TTDExecutionInfo);
  357. this->TTDExecutionInfo = nullptr;
  358. }
  359. if(this->TTDLog != nullptr)
  360. {
  361. TT_HEAP_DELETE(TTD::EventLog, this->TTDLog);
  362. this->TTDLog = nullptr;
  363. }
  364. #endif
  365. #ifdef LEAK_REPORT
  366. if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
  367. {
  368. AUTO_LEAK_REPORT_SECTION(Js::Configuration::Global.flags, _u("Thread Context (%p): %s (TID: %d)"), this,
  369. this->GetRecycler()->IsInDllCanUnloadNow()? _u("DllCanUnloadNow") :
  370. this->GetRecycler()->IsInDetachProcess()? _u("DetachProcess") : _u("Destructor"), this->threadId);
  371. LeakReport::DumpUrl(this->threadId);
  372. }
  373. #endif
  374. if (interruptPoller)
  375. {
  376. HeapDelete(interruptPoller);
  377. interruptPoller = nullptr;
  378. }
  379. #if DBG
  380. // ThreadContext dtor may be running on a different thread.
  381. // Recycler may call finalizer that free temp Arenas, which will free pages back to
  382. // the page Allocator, which will try to suspend idle on a different thread.
  383. // So we need to disable idle decommit asserts.
  384. pageAllocator.ShutdownIdleDecommit();
  385. #endif
  386. // Allocating memory during the shutdown codepath is not preferred
  387. // so we'll close the page allocator before we release the GC
  388. // If any dispose is allocating memory during shutdown, that is a bug
  389. pageAllocator.Close();
  390. // The recycler need to delete before the background code gen thread
  391. // because that might run finalizer which need access to the background code gen thread.
  392. if (recycler != nullptr)
  393. {
  394. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
  395. {
  396. if (!scriptContext->IsActuallyClosed())
  397. {
  398. // We close ScriptContext here because anyhow HeapDelete(recycler) when disposing the
  399. // JavaScriptLibrary will close ScriptContext. Explicit close gives us chance to clear
  400. // other things to which ScriptContext holds reference to
  401. AssertMsg(!IsInScript(), "Can we be in script here?");
  402. scriptContext->MarkForClose();
  403. }
  404. }
  405. // If all scriptContext's have been closed, then the sourceProfileManagersByUrl
  406. // should have been released
  407. AssertMsg(this->recyclableData->sourceProfileManagersByUrl == nullptr ||
  408. this->recyclableData->sourceProfileManagersByUrl->Count() == 0, "There seems to have been a refcounting imbalance.");
  409. this->recyclableData->sourceProfileManagersByUrl = nullptr;
  410. this->recyclableData->oldEntryPointInfo = nullptr;
  411. if (this->recyclableData->symbolRegistrationMap != nullptr)
  412. {
  413. this->recyclableData->symbolRegistrationMap->Clear();
  414. this->recyclableData->symbolRegistrationMap = nullptr;
  415. }
  416. #ifdef ENABLE_SCRIPT_DEBUGGING
  417. if (this->recyclableData->returnedValueList != nullptr)
  418. {
  419. this->recyclableData->returnedValueList->Clear();
  420. this->recyclableData->returnedValueList = nullptr;
  421. }
  422. #endif
  423. if (this->propertyMap != nullptr)
  424. {
  425. HeapDelete(this->propertyMap);
  426. this->propertyMap = nullptr;
  427. }
  428. #if ENABLE_NATIVE_CODEGEN
  429. if (this->m_jitNumericProperties != nullptr)
  430. {
  431. HeapDelete(this->m_jitNumericProperties);
  432. this->m_jitNumericProperties = nullptr;
  433. }
  434. #endif
  435. // Unpin the memory for leak report so we don't report this as a leak.
  436. recyclableData.Unroot(recycler);
  437. #if defined(LEAK_REPORT) || defined(CHECK_MEMORY_LEAK)
  438. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
  439. {
  440. scriptContext->ClearSourceContextInfoMaps();
  441. scriptContext->ShutdownClearSourceLists();
  442. }
  443. #ifdef LEAK_REPORT
  444. // heuristically figure out which one is the root tracker script engine
  445. // and force close on it
  446. if (this->rootTrackerScriptContext != nullptr)
  447. {
  448. this->rootTrackerScriptContext->Close(false);
  449. }
  450. #endif
  451. #endif
  452. #if ENABLE_NATIVE_CODEGEN
  453. #if !FLOATVAR
  454. if (this->codeGenNumberThreadAllocator)
  455. {
  456. HeapDelete(this->codeGenNumberThreadAllocator);
  457. this->codeGenNumberThreadAllocator = nullptr;
  458. }
  459. if (this->xProcNumberPageSegmentManager)
  460. {
  461. HeapDelete(this->xProcNumberPageSegmentManager);
  462. this->xProcNumberPageSegmentManager = nullptr;
  463. }
  464. #endif
  465. #endif
  466. #ifdef ENABLE_SCRIPT_DEBUGGING
  467. Assert(this->debugManager == nullptr);
  468. #endif
  469. #if ENABLE_CONCURRENT_GC && defined(_WIN32)
  470. AssertOrFailFastMsg(recycler->concurrentThread == NULL, "Recycler background thread should have been shutdown before destroying Recycler.");
  471. AssertOrFailFastMsg((recycler->parallelThread1.concurrentThread == NULL) && (recycler->parallelThread2.concurrentThread == NULL), "Recycler parallelThread(s) should have been shutdown before destroying Recycler.");
  472. #endif
  473. HeapDelete(recycler);
  474. }
  475. #if ENABLE_NATIVE_CODEGEN
  476. if(jobProcessor)
  477. {
  478. if(this->bgJit)
  479. {
  480. HeapDelete(static_cast<JsUtil::BackgroundJobProcessor *>(jobProcessor));
  481. }
  482. else
  483. {
  484. HeapDelete(static_cast<JsUtil::ForegroundJobProcessor *>(jobProcessor));
  485. }
  486. jobProcessor = nullptr;
  487. }
  488. #endif
  489. // Do not require all GC callbacks to be revoked, because Trident may not revoke if there
  490. // is a leak, and we don't want the leak to be masked by an assert
  491. this->collectCallBackList.Clear(&HeapAllocator::Instance);
  492. this->protoInlineCacheByPropId.Reset();
  493. this->storeFieldInlineCacheByPropId.Reset();
  494. this->isInstInlineCacheByFunction.Reset();
  495. this->equivalentTypeCacheEntryPoints.Reset();
  496. this->noSpecialPropertyRegistry.Reset();
  497. this->onlyWritablePropertyRegistry.Reset();
  498. this->registeredInlineCacheCount = 0;
  499. this->unregisteredInlineCacheCount = 0;
  500. AssertMsg(this->GetHeapEnum() == nullptr, "Heap enumeration should have been cleared/closed by the ScriptSite.");
  501. if (this->GetHeapEnum() != nullptr)
  502. {
  503. this->ClearHeapEnum();
  504. }
  505. #ifdef BAILOUT_INJECTION
  506. if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag)
  507. && Js::Configuration::Global.flags.BailOutByteCode.Empty())
  508. {
  509. Output::Print(_u("Bail out byte code location count: %d"), this->bailOutByteCodeLocationCount);
  510. }
  511. #endif
  512. Assert(processNativeCodeSize >= nativeCodeSize);
  513. ::InterlockedExchangeSubtract(&processNativeCodeSize, nativeCodeSize);
  514. PERF_COUNTER_DEC(Basic, ThreadContext);
  515. #ifdef DYNAMIC_PROFILE_MUTATOR
  516. if (this->dynamicProfileMutator != nullptr)
  517. {
  518. this->dynamicProfileMutator->Delete();
  519. }
  520. #endif
  521. }
  522. void
  523. ThreadContext::SetJSRTRuntime(void* runtime)
  524. {
  525. Assert(jsrtRuntime == nullptr);
  526. jsrtRuntime = runtime;
  527. #ifdef ENABLE_BASIC_TELEMETRY
  528. Telemetry::EnsureInitializeForJSRT();
  529. #endif
  530. }
  531. void ThreadContext::CloseForJSRT()
  532. {
  533. // This is used for JSRT APIs only.
  534. Assert(this->jsrtRuntime);
  535. #ifdef ENABLE_BASIC_TELEMETRY
  536. // log any relevant telemetry before disposing the current thread for cases which are properly shutdown
  537. Telemetry::OnJSRTThreadContextClose();
  538. #endif
  539. ShutdownThreads();
  540. }
  541. ThreadContext* ThreadContext::GetContextForCurrentThread()
  542. {
  543. ThreadContextTLSEntry * tlsEntry = ThreadContextTLSEntry::GetEntryForCurrentThread();
  544. if (tlsEntry != nullptr)
  545. {
  546. return static_cast<ThreadContext *>(tlsEntry->GetThreadContext());
  547. }
  548. return nullptr;
  549. }
  550. void ThreadContext::ValidateThreadContext()
  551. {
  552. #if DBG
  553. // verify the runtime pointer is valid.
  554. {
  555. BOOL found = FALSE;
  556. AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
  557. ThreadContext* currentThreadContext = GetThreadContextList();
  558. while (currentThreadContext)
  559. {
  560. if (currentThreadContext == this)
  561. {
  562. return;
  563. }
  564. currentThreadContext = currentThreadContext->Next();
  565. }
  566. AssertMsg(found, "invalid thread context");
  567. }
  568. #endif
  569. }
  570. class AutoRecyclerPtr : public AutoPtr<Recycler>
  571. {
  572. public:
  573. AutoRecyclerPtr(Recycler * ptr) : AutoPtr<Recycler>(ptr) {}
  574. ~AutoRecyclerPtr()
  575. {
  576. #if ENABLE_CONCURRENT_GC
  577. if (ptr != nullptr)
  578. {
  579. ptr->ShutdownThread();
  580. }
  581. #endif
  582. }
  583. };
  584. LPFILETIME ThreadContext::ThreadContextRecyclerTelemetryHostInterface::GetLastScriptExecutionEndTime() const
  585. {
  586. #if defined(ENABLE_BASIC_TELEMETRY) && defined(NTBUILD)
  587. return &(tc->telemetryBlock->lastScriptEndTime);
  588. #else
  589. return nullptr;
  590. #endif
  591. }
  592. bool ThreadContext::ThreadContextRecyclerTelemetryHostInterface::TransmitGCTelemetryStats(RecyclerTelemetryInfo& rti)
  593. {
  594. #if defined(ENABLE_BASIC_TELEMETRY) && defined(NTBUILD)
  595. return Js::TransmitRecyclerTelemetryStats(rti);
  596. #else
  597. return false;
  598. #endif
  599. }
  600. bool ThreadContext::ThreadContextRecyclerTelemetryHostInterface::TransmitHeapUsage(size_t totalHeapBytes, size_t usedHeapBytes, double heapUsedRatio)
  601. {
  602. #if defined(ENABLE_BASIC_TELEMETRY) && defined(NTBUILD)
  603. return Js::TransmitRecyclerHeapUsage(totalHeapBytes, usedHeapBytes, heapUsedRatio);
  604. #else
  605. return false;
  606. #endif
  607. }
  608. bool ThreadContext::ThreadContextRecyclerTelemetryHostInterface::IsTelemetryProviderEnabled() const
  609. {
  610. #if defined(ENABLE_BASIC_TELEMETRY) && defined(NTBUILD)
  611. return Js::IsTelemetryProviderEnabled();
  612. #else
  613. return false;
  614. #endif
  615. }
  616. bool ThreadContext::ThreadContextRecyclerTelemetryHostInterface::TransmitTelemetryError(const RecyclerTelemetryInfo& rti, const char * msg)
  617. {
  618. #if defined(ENABLE_BASIC_TELEMETRY) && defined(NTBUILD)
  619. return Js::TransmitRecyclerTelemetryError(rti, msg);
  620. #else
  621. return false;
  622. #endif
  623. }
  624. bool ThreadContext::ThreadContextRecyclerTelemetryHostInterface::IsThreadBound() const
  625. {
  626. return this->tc->IsThreadBound();
  627. }
  628. DWORD ThreadContext::ThreadContextRecyclerTelemetryHostInterface::GetCurrentScriptThreadID() const
  629. {
  630. return this->tc->GetCurrentThreadId();
  631. }
  632. uint ThreadContext::ThreadContextRecyclerTelemetryHostInterface::GetClosedContextCount() const
  633. {
  634. return this->tc->closedScriptContextCount;
  635. }
  636. Recycler* ThreadContext::EnsureRecycler()
  637. {
  638. if (recycler == NULL)
  639. {
  640. AutoRecyclerPtr newRecycler(HeapNew(Recycler, GetAllocationPolicyManager(), &pageAllocator, Js::Throw::OutOfMemory, Js::Configuration::Global.flags, &recyclerTelemetryHostInterface));
  641. newRecycler->Initialize(isOptimizedForManyInstances, &threadService); // use in-thread GC when optimizing for many instances
  642. newRecycler->SetCollectionWrapper(this);
  643. #if ENABLE_NATIVE_CODEGEN
  644. // This may throw, so it needs to be after the recycler is initialized,
  645. // otherwise, the recycler dtor may encounter problems
  646. #if !FLOATVAR
  647. // TODO: we only need one of the following, one for OOP jit and one for in-proc BG JIT
  648. AutoPtr<CodeGenNumberThreadAllocator> localCodeGenNumberThreadAllocator(
  649. HeapNew(CodeGenNumberThreadAllocator, newRecycler));
  650. AutoPtr<XProcNumberPageSegmentManager> localXProcNumberPageSegmentManager(
  651. HeapNew(XProcNumberPageSegmentManager, newRecycler));
  652. #endif
  653. #endif
  654. this->recyclableData.Root(RecyclerNewZ(newRecycler, RecyclableData, newRecycler), newRecycler);
  655. if (this->IsThreadBound())
  656. {
  657. newRecycler->SetIsThreadBound();
  658. }
  659. // Assign the recycler to the ThreadContext after everything is initialized, because an OOM during initialization would
  660. // result in only partial initialization, so the 'recycler' member variable should remain null to cause full
  661. // reinitialization when requested later. Anything that happens after the Detach must have special cleanup code.
  662. this->recycler = newRecycler.Detach();
  663. try
  664. {
  665. #ifdef RECYCLER_WRITE_BARRIER
  666. #ifdef TARGET_64
  667. if (!RecyclerWriteBarrierManager::OnThreadInit())
  668. {
  669. Js::Throw::OutOfMemory();
  670. }
  671. #endif
  672. #endif
  673. this->expirableObjectList = Anew(&this->threadAlloc, ExpirableObjectList, &this->threadAlloc);
  674. this->expirableObjectDisposeList = Anew(&this->threadAlloc, ExpirableObjectList, &this->threadAlloc);
  675. InitializePropertyMaps(); // has many dependencies on the recycler and other members of the thread context
  676. #if ENABLE_NATIVE_CODEGEN
  677. #if !FLOATVAR
  678. this->codeGenNumberThreadAllocator = localCodeGenNumberThreadAllocator.Detach();
  679. this->xProcNumberPageSegmentManager = localXProcNumberPageSegmentManager.Detach();
  680. #endif
  681. #endif
  682. }
  683. catch(...)
  684. {
  685. // Initialization failed, undo what was done above. Callees that throw must clean up after themselves.
  686. if (this->recyclableData != nullptr)
  687. {
  688. this->recyclableData.Unroot(this->recycler);
  689. }
  690. {
  691. // AutoRecyclerPtr's destructor takes care of shutting down the background thread and deleting the recycler
  692. AutoRecyclerPtr recyclerToDelete(this->recycler);
  693. this->recycler = nullptr;
  694. }
  695. throw;
  696. }
  697. JS_ETW(EventWriteJSCRIPT_GC_INIT(this->recycler, this->GetHiResTimer()->Now()));
  698. }
  699. #if DBG
  700. if (CONFIG_FLAG(RecyclerTest))
  701. {
  702. StressTester test(recycler);
  703. test.Run();
  704. }
  705. #endif
  706. return recycler;
  707. }
  708. Js::PropertyRecord const *
  709. ThreadContext::GetPropertyName(Js::PropertyId propertyId)
  710. {
  711. // This API should only be use on the main thread
  712. Assert(GetCurrentThreadContextId() == (ThreadContextId)this);
  713. return this->GetPropertyNameImpl<false>(propertyId);
  714. }
  715. Js::PropertyRecord const *
  716. ThreadContext::GetPropertyNameLocked(Js::PropertyId propertyId)
  717. {
  718. return GetPropertyNameImpl<true>(propertyId);
  719. }
  720. template <bool locked>
  721. Js::PropertyRecord const *
  722. ThreadContext::GetPropertyNameImpl(Js::PropertyId propertyId)
  723. {
  724. //TODO: Remove this when completely transformed to use PropertyRecord*. Currently this is only partially done,
  725. // and there are calls to GetPropertyName with InternalPropertyId.
  726. if (propertyId >= 0 && Js::IsInternalPropertyId(propertyId))
  727. {
  728. return Js::InternalPropertyRecords::GetInternalPropertyName(propertyId);
  729. }
  730. int propertyIndex = propertyId - Js::PropertyIds::_none;
  731. if (propertyIndex < 0 || propertyIndex > propertyMap->GetLastIndex())
  732. {
  733. propertyIndex = 0;
  734. }
  735. const Js::PropertyRecord * propertyRecord = nullptr;
  736. if (locked) { propertyMap->LockResize(); }
  737. bool found = propertyMap->TryGetValueAt(propertyIndex, &propertyRecord);
  738. if (locked) { propertyMap->UnlockResize(); }
  739. AssertMsg(found && propertyRecord != nullptr, "using invalid propertyid");
  740. return propertyRecord;
  741. }
  742. void
  743. ThreadContext::FindPropertyRecord(Js::JavascriptString *pstName, Js::PropertyRecord const ** propertyRecord)
  744. {
  745. pstName->GetPropertyRecord(propertyRecord, true);
  746. if (*propertyRecord != nullptr)
  747. {
  748. return;
  749. }
  750. // GetString is not guaranteed to be null-terminated, but we explicitly pass length to the next step
  751. LPCWCH propertyName = pstName->GetString();
  752. FindPropertyRecord(propertyName, pstName->GetLength(), propertyRecord);
  753. if (*propertyRecord)
  754. {
  755. pstName->CachePropertyRecord(*propertyRecord);
  756. }
  757. }
  758. void
  759. ThreadContext::FindPropertyRecord(__in LPCWCH propertyName, __in int propertyNameLength, Js::PropertyRecord const ** propertyRecord)
  760. {
  761. EnterPinnedScope((volatile void **)propertyRecord);
  762. *propertyRecord = FindPropertyRecord(propertyName, propertyNameLength);
  763. LeavePinnedScope();
  764. }
  765. Js::PropertyRecord const *
  766. ThreadContext::GetPropertyRecord(Js::PropertyId propertyId)
  767. {
  768. return GetPropertyNameLocked(propertyId);
  769. }
  770. bool
  771. ThreadContext::IsNumericProperty(Js::PropertyId propertyId)
  772. {
  773. return GetPropertyRecord(propertyId)->IsNumeric();
  774. }
  775. const Js::PropertyRecord *
  776. ThreadContext::FindPropertyRecord(const char16 * propertyName, int propertyNameLength)
  777. {
  778. // IsDirectPropertyName == 1 char properties && GetEmptyStringPropertyRecord == 0 length
  779. if (propertyNameLength < 2)
  780. {
  781. if (propertyNameLength == 0)
  782. {
  783. return this->GetEmptyStringPropertyRecord();
  784. }
  785. if (IsDirectPropertyName(propertyName, propertyNameLength))
  786. {
  787. Js::PropertyRecord const * propertyRecord = propertyNamesDirect[propertyName[0]];
  788. Assert(propertyRecord == propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength)));
  789. return propertyRecord;
  790. }
  791. }
  792. return propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength));
  793. }
  794. Js::PropertyRecord const *
  795. ThreadContext::UncheckedAddPropertyId(__in LPCWSTR propertyName, __in int propertyNameLength, bool bind, bool isSymbol)
  796. {
  797. return UncheckedAddPropertyId(JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength), bind, isSymbol);
  798. }
  799. void ThreadContext::InitializePropertyMaps()
  800. {
  801. Assert(this->recycler != nullptr);
  802. Assert(this->recyclableData != nullptr);
  803. Assert(this->propertyMap == nullptr);
  804. Assert(this->caseInvariantPropertySet == nullptr);
  805. try
  806. {
  807. this->propertyMap = HeapNew(PropertyMap, &HeapAllocator::Instance, TotalNumberOfBuiltInProperties + 700);
  808. this->recyclableData->boundPropertyStrings = RecyclerNew(this->recycler, JsUtil::List<Js::PropertyRecord const*>, this->recycler);
  809. memset(propertyNamesDirect, 0, 128*sizeof(Js::PropertyRecord *));
  810. Js::JavascriptLibrary::InitializeProperties(this);
  811. InitializeAdditionalProperties(this);
  812. //Js::JavascriptLibrary::InitializeDOMProperties(this);
  813. }
  814. catch(...)
  815. {
  816. // Initialization failed, undo what was done above. Callees that throw must clean up after themselves. The recycler will
  817. // be trashed, so clear members that point to recyclable memory. Stuff in 'recyclableData' will be taken care of by the
  818. // recycler, and the 'recyclableData' instance will be trashed as well.
  819. if (this->propertyMap != nullptr)
  820. {
  821. HeapDelete(this->propertyMap);
  822. }
  823. this->propertyMap = nullptr;
  824. this->caseInvariantPropertySet = nullptr;
  825. memset(propertyNamesDirect, 0, 128*sizeof(Js::PropertyRecord *));
  826. throw;
  827. }
  828. }
  829. void ThreadContext::UncheckedAddBuiltInPropertyId()
  830. {
  831. for (int i = 0; i < _countof(builtInPropertyRecords); i++)
  832. {
  833. AddPropertyRecordInternal(builtInPropertyRecords[i]);
  834. }
  835. }
  836. bool
  837. ThreadContext::IsDirectPropertyName(const char16 * propertyName, int propertyNameLength)
  838. {
  839. return ((propertyNameLength == 1) && ((propertyName[0] & 0xFF80) == 0));
  840. }
  841. RecyclerWeakReference<const Js::PropertyRecord> *
  842. ThreadContext::CreatePropertyRecordWeakRef(const Js::PropertyRecord * propertyRecord)
  843. {
  844. RecyclerWeakReference<const Js::PropertyRecord> * propertyRecordWeakRef;
  845. if (propertyRecord->IsBound())
  846. {
  847. // Create a fake weak ref
  848. propertyRecordWeakRef = RecyclerNewLeaf(this->recycler, StaticPropertyRecordReference, propertyRecord);
  849. }
  850. else
  851. {
  852. propertyRecordWeakRef = recycler->CreateWeakReferenceHandle(propertyRecord);
  853. }
  854. return propertyRecordWeakRef;
  855. }
  856. Js::PropertyRecord const *
  857. ThreadContext::UncheckedAddPropertyId(JsUtil::CharacterBuffer<WCHAR> const& propertyName, bool bind, bool isSymbol)
  858. {
  859. #if ENABLE_TTD
  860. if(isSymbol & this->IsRuntimeInTTDMode())
  861. {
  862. if(this->TTDContext->GetActiveScriptContext() != nullptr && this->TTDContext->GetActiveScriptContext()->ShouldPerformReplayAction())
  863. {
  864. //We reload all properties that occur in the trace so they only way we get here in TTD mode is:
  865. //(1) if the program is creating a new symbol (which always gets a fresh id) and we should recreate it or
  866. //(2) if it is forcing arguments in debug parse mode (instead of regular which we recorded in)
  867. Js::PropertyId propertyId = Js::Constants::NoProperty;
  868. this->TTDLog->ReplaySymbolCreationEvent(&propertyId);
  869. //Don't recreate the symbol below, instead return the known symbol by looking up on the pid
  870. const Js::PropertyRecord* res = this->GetPropertyName(propertyId);
  871. AssertMsg(res != nullptr, "This should never happen!!!");
  872. return res;
  873. }
  874. }
  875. #endif
  876. this->propertyMap->EnsureCapacity();
  877. // Automatically bind direct (single-character) property names, so that they can be
  878. // stored in the direct property table
  879. if (IsDirectPropertyName(propertyName.GetBuffer(), propertyName.GetLength()))
  880. {
  881. bind = true;
  882. }
  883. // Create the PropertyRecord
  884. int length = propertyName.GetLength();
  885. uint bytelength = sizeof(char16) * length;
  886. size_t allocLength = bytelength + sizeof(char16) + ( (!isSymbol && length <= 10 && length > 0) ? sizeof(uint32) : 0);
  887. // If it's bound, create it in the thread arena, along with a fake weak ref
  888. Js::PropertyRecord * propertyRecord;
  889. if (bind)
  890. {
  891. propertyRecord = AnewPlus(GetThreadAlloc(), allocLength, Js::PropertyRecord, propertyName.GetBuffer(), length, bytelength, isSymbol);
  892. propertyRecord->isBound = true;
  893. }
  894. else
  895. {
  896. propertyRecord = RecyclerNewFinalizedLeafPlus(recycler, allocLength, Js::PropertyRecord, propertyName.GetBuffer(), length, bytelength, isSymbol);
  897. }
  898. Js::PropertyId propertyId = this->GetNextPropertyId();
  899. #if ENABLE_TTD
  900. if(isSymbol & this->IsRuntimeInTTDMode())
  901. {
  902. if(this->TTDContext->GetActiveScriptContext() != nullptr && this->TTDContext->GetActiveScriptContext()->ShouldPerformRecordAction())
  903. {
  904. this->TTDLog->RecordSymbolCreationEvent(propertyId);
  905. }
  906. }
  907. #endif
  908. propertyRecord->pid = propertyId;
  909. AddPropertyRecordInternal(propertyRecord);
  910. return propertyRecord;
  911. }
  912. void
  913. ThreadContext::AddPropertyRecordInternal(const Js::PropertyRecord * propertyRecord)
  914. {
  915. // At this point the PropertyRecord is constructed but not added to the map.
  916. const char16 * propertyName = propertyRecord->GetBuffer();
  917. int propertyNameLength = propertyRecord->GetLength();
  918. Js::PropertyId propertyId = propertyRecord->GetPropertyId();
  919. Assert(propertyId == GetNextPropertyId());
  920. Assert(!IsActivePropertyId(propertyId));
  921. #if DBG
  922. // Only Assert we can't find the property if we are not adding a symbol.
  923. // For a symbol, the propertyName is not used and may collide with something in the map already.
  924. if (propertyNameLength > 0 && !propertyRecord->IsSymbol())
  925. {
  926. Assert(FindPropertyRecord(propertyName, propertyNameLength) == nullptr);
  927. }
  928. #endif
  929. #if ENABLE_TTD
  930. if(this->IsRuntimeInTTDMode())
  931. {
  932. this->TTDLog->AddPropertyRecord(propertyRecord);
  933. }
  934. #endif
  935. // Add to the map
  936. propertyMap->Add(propertyRecord);
  937. #if ENABLE_NATIVE_CODEGEN
  938. if (m_jitNumericProperties)
  939. {
  940. if (propertyRecord->IsNumeric())
  941. {
  942. m_jitNumericProperties->Set(propertyRecord->GetPropertyId());
  943. m_jitNeedsPropertyUpdate = true;
  944. }
  945. }
  946. #endif
  947. PropertyRecordTrace(_u("Added property '%s' at 0x%08x, pid = %d\n"), propertyName, propertyRecord, propertyId);
  948. // Do not store the pid for symbols in the direct property name table.
  949. // We don't want property ids for symbols to be searchable anyway.
  950. if (!propertyRecord->IsSymbol() && IsDirectPropertyName(propertyName, propertyNameLength))
  951. {
  952. // Store the pids for single character properties in the propertyNamesDirect array.
  953. // This property record should have been created as bound by the caller.
  954. Assert(propertyRecord->IsBound());
  955. Assert(propertyNamesDirect[propertyName[0]] == nullptr);
  956. propertyNamesDirect[propertyName[0]] = propertyRecord;
  957. }
  958. if (caseInvariantPropertySet)
  959. {
  960. AddCaseInvariantPropertyRecord(propertyRecord);
  961. }
  962. // Check that everything was added correctly
  963. #if DBG
  964. // Only Assert we can find the property if we are not adding a symbol.
  965. // For a symbol, the propertyName is not used and we won't be able to look the pid up via name.
  966. if (propertyNameLength && !propertyRecord->IsSymbol())
  967. {
  968. Assert(FindPropertyRecord(propertyName, propertyNameLength) == propertyRecord);
  969. }
  970. // We will still be able to lookup the symbol property by the property id, so go ahead and check that.
  971. Assert(GetPropertyName(propertyRecord->GetPropertyId()) == propertyRecord);
  972. #endif
  973. JS_ETW_INTERNAL(EventWriteJSCRIPT_HOSTING_PROPERTYID_LIST(propertyRecord, propertyRecord->GetBuffer()));
  974. }
  975. void
  976. ThreadContext::AddCaseInvariantPropertyRecord(const Js::PropertyRecord * propertyRecord)
  977. {
  978. Assert(this->caseInvariantPropertySet != nullptr);
  979. // Create a weak reference to the property record here (since we no longer use weak refs in the property map)
  980. RecyclerWeakReference<const Js::PropertyRecord> * propertyRecordWeakRef = CreatePropertyRecordWeakRef(propertyRecord);
  981. JsUtil::CharacterBuffer<WCHAR> newPropertyName(propertyRecord->GetBuffer(), propertyRecord->GetLength());
  982. Js::CaseInvariantPropertyListWithHashCode* list;
  983. if (!FindExistingPropertyRecord(newPropertyName, &list))
  984. {
  985. // This binds all the property string that is key in this map with no hope of reclaiming them
  986. // TODO: do better
  987. list = RecyclerNew(recycler, Js::CaseInvariantPropertyListWithHashCode, recycler, 1);
  988. // Do the add first so that the list is non-empty and we can calculate its hashcode correctly
  989. list->Add(propertyRecordWeakRef);
  990. // This will calculate the hashcode
  991. caseInvariantPropertySet->Add(list);
  992. }
  993. else
  994. {
  995. list->Add(propertyRecordWeakRef);
  996. }
  997. }
  998. void
  999. ThreadContext::BindPropertyRecord(const Js::PropertyRecord * propertyRecord)
  1000. {
  1001. if (!propertyRecord->IsBound())
  1002. {
  1003. Assert(!this->recyclableData->boundPropertyStrings->Contains(propertyRecord));
  1004. this->recyclableData->boundPropertyStrings->Add(propertyRecord);
  1005. // Cast around constness to set propertyRecord as bound
  1006. const_cast<Js::PropertyRecord *>(propertyRecord)->isBound = true;
  1007. }
  1008. }
  1009. void ThreadContext::GetOrAddPropertyId(_In_ LPCWSTR propertyName, _In_ int propertyNameLength, _Out_ Js::PropertyRecord const ** propertyRecord)
  1010. {
  1011. GetOrAddPropertyId(JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength), propertyRecord);
  1012. }
  1013. void ThreadContext::GetOrAddPropertyId(_In_ JsUtil::CharacterBuffer<WCHAR> const& propertyName, _Out_ Js::PropertyRecord const ** propRecord)
  1014. {
  1015. EnterPinnedScope((volatile void **)propRecord);
  1016. *propRecord = GetOrAddPropertyRecord(propertyName);
  1017. LeavePinnedScope();
  1018. }
  1019. const Js::PropertyRecord *
  1020. ThreadContext::GetOrAddPropertyRecordImpl(JsUtil::CharacterBuffer<char16> propertyName, bool bind)
  1021. {
  1022. // Make sure the recycler is around so that we can take weak references to the property strings
  1023. EnsureRecycler();
  1024. const Js::PropertyRecord * propertyRecord;
  1025. FindPropertyRecord(propertyName.GetBuffer(), propertyName.GetLength(), &propertyRecord);
  1026. if (propertyRecord == nullptr)
  1027. {
  1028. propertyRecord = UncheckedAddPropertyId(propertyName, bind);
  1029. }
  1030. else
  1031. {
  1032. // PropertyRecord exists, but may not be bound. Bind now if requested.
  1033. if (bind)
  1034. {
  1035. BindPropertyRecord(propertyRecord);
  1036. }
  1037. }
  1038. Assert(propertyRecord != nullptr);
  1039. Assert(!bind || propertyRecord->IsBound());
  1040. return propertyRecord;
  1041. }
  1042. void ThreadContext::AddBuiltInPropertyRecord(const Js::PropertyRecord *propertyRecord)
  1043. {
  1044. this->AddPropertyRecordInternal(propertyRecord);
  1045. }
  1046. BOOL ThreadContext::IsNumericPropertyId(Js::PropertyId propertyId, uint32* value)
  1047. {
  1048. if (Js::IsInternalPropertyId(propertyId))
  1049. {
  1050. return false;
  1051. }
  1052. Js::PropertyRecord const * propertyRecord = this->GetPropertyName(propertyId);
  1053. Assert(propertyRecord != nullptr);
  1054. if (propertyRecord == nullptr || !propertyRecord->IsNumeric())
  1055. {
  1056. return false;
  1057. }
  1058. *value = propertyRecord->GetNumericValue();
  1059. return true;
  1060. }
  1061. bool ThreadContext::IsActivePropertyId(Js::PropertyId pid)
  1062. {
  1063. Assert(pid != Js::Constants::NoProperty);
  1064. if (Js::IsInternalPropertyId(pid))
  1065. {
  1066. return true;
  1067. }
  1068. int propertyIndex = pid - Js::PropertyIds::_none;
  1069. const Js::PropertyRecord * propertyRecord;
  1070. if (propertyMap->TryGetValueAt(propertyIndex, &propertyRecord) && propertyRecord != nullptr)
  1071. {
  1072. return true;
  1073. }
  1074. return false;
  1075. }
  1076. void ThreadContext::InvalidatePropertyRecord(const Js::PropertyRecord * propertyRecord)
  1077. {
  1078. InternalInvalidateProtoTypePropertyCaches(propertyRecord->GetPropertyId()); // use the internal version so we don't check for active property id
  1079. #if ENABLE_NATIVE_CODEGEN
  1080. if (propertyRecord->IsNumeric() && m_jitNumericProperties)
  1081. {
  1082. m_jitNumericProperties->Clear(propertyRecord->GetPropertyId());
  1083. m_jitNeedsPropertyUpdate = true;
  1084. }
  1085. #endif
  1086. this->propertyMap->Remove(propertyRecord);
  1087. PropertyRecordTrace(_u("Reclaimed property '%s' at 0x%08x, pid = %d\n"),
  1088. propertyRecord->GetBuffer(), propertyRecord, propertyRecord->GetPropertyId());
  1089. }
  1090. Js::PropertyId ThreadContext::GetNextPropertyId()
  1091. {
  1092. return this->propertyMap->GetNextIndex() + Js::PropertyIds::_none;
  1093. }
  1094. Js::PropertyId ThreadContext::GetMaxPropertyId()
  1095. {
  1096. auto maxPropertyId = this->propertyMap->Count() + Js::InternalPropertyIds::Count;
  1097. return maxPropertyId;
  1098. }
  1099. void ThreadContext::CreateNoCasePropertyMap()
  1100. {
  1101. Assert(caseInvariantPropertySet == nullptr);
  1102. caseInvariantPropertySet = RecyclerNew(recycler, PropertyNoCaseSetType, recycler, 173);
  1103. // Prevent the set from being reclaimed
  1104. // Individual items in the set can be reclaimed though since they're lists of weak references
  1105. // The lists themselves can be reclaimed when all the weak references in them are cleared
  1106. this->recyclableData->caseInvariantPropertySet = caseInvariantPropertySet;
  1107. // Note that we are allocating from the recycler below, so we may cause a GC at any time, which
  1108. // could cause PropertyRecords to be collected and removed from the propertyMap.
  1109. // Thus, don't use BaseDictionary::Map here, as it cannot tolerate changes while mapping.
  1110. // Instead, walk the PropertyRecord entries in index order. This will work even if a GC occurs.
  1111. for (int propertyIndex = 0; propertyIndex <= this->propertyMap->GetLastIndex(); propertyIndex++)
  1112. {
  1113. const Js::PropertyRecord * propertyRecord;
  1114. if (this->propertyMap->TryGetValueAt(propertyIndex, &propertyRecord) && propertyRecord != nullptr)
  1115. {
  1116. AddCaseInvariantPropertyRecord(propertyRecord);
  1117. }
  1118. }
  1119. }
  1120. JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>*
  1121. ThreadContext::FindPropertyIdNoCase(Js::ScriptContext * scriptContext, LPCWSTR propertyName, int propertyNameLength)
  1122. {
  1123. return ThreadContext::FindPropertyIdNoCase(scriptContext, JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength));
  1124. }
  1125. JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>*
  1126. ThreadContext::FindPropertyIdNoCase(Js::ScriptContext * scriptContext, JsUtil::CharacterBuffer<WCHAR> const& propertyName)
  1127. {
  1128. if (caseInvariantPropertySet == nullptr)
  1129. {
  1130. this->CreateNoCasePropertyMap();
  1131. }
  1132. Js::CaseInvariantPropertyListWithHashCode* list;
  1133. if (FindExistingPropertyRecord(propertyName, &list))
  1134. {
  1135. return list;
  1136. }
  1137. return nullptr;
  1138. }
  1139. bool
  1140. ThreadContext::FindExistingPropertyRecord(_In_ JsUtil::CharacterBuffer<WCHAR> const& propertyName, Js::CaseInvariantPropertyListWithHashCode** list)
  1141. {
  1142. Js::CaseInvariantPropertyListWithHashCode* l = this->caseInvariantPropertySet->LookupWithKey(propertyName);
  1143. (*list) = l;
  1144. return (l != nullptr);
  1145. }
  1146. void ThreadContext::CleanNoCasePropertyMap()
  1147. {
  1148. if (this->caseInvariantPropertySet != nullptr)
  1149. {
  1150. this->caseInvariantPropertySet->MapAndRemoveIf([](Js::CaseInvariantPropertyListWithHashCode* value) -> bool {
  1151. if (value && value->Count() == 0)
  1152. {
  1153. // Remove entry
  1154. return true;
  1155. }
  1156. // Keep entry
  1157. return false;
  1158. });
  1159. }
  1160. }
  1161. void
  1162. ThreadContext::ForceCleanPropertyMap()
  1163. {
  1164. // No-op now that we no longer use weak refs
  1165. }
  1166. #if ENABLE_NATIVE_CODEGEN
  1167. JsUtil::JobProcessor *
  1168. ThreadContext::GetJobProcessor()
  1169. {
  1170. if(bgJit && isOptimizedForManyInstances)
  1171. {
  1172. return ThreadBoundThreadContextManager::GetSharedJobProcessor();
  1173. }
  1174. if (!jobProcessor)
  1175. {
  1176. if(bgJit && !isOptimizedForManyInstances)
  1177. {
  1178. jobProcessor = HeapNew(JsUtil::BackgroundJobProcessor, GetAllocationPolicyManager(), &threadService, false /*disableParallelThreads*/);
  1179. }
  1180. else
  1181. {
  1182. jobProcessor = HeapNew(JsUtil::ForegroundJobProcessor);
  1183. }
  1184. }
  1185. return jobProcessor;
  1186. }
  1187. #endif
  1188. void
  1189. ThreadContext::RegisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData)
  1190. {
  1191. Assert(codeGenRecyclableData);
  1192. Assert(recyclableData);
  1193. // Linking must not be done concurrently with unlinking (caller must use lock)
  1194. recyclableData->codeGenRecyclableDatas.LinkToEnd(codeGenRecyclableData);
  1195. }
  1196. void
  1197. ThreadContext::UnregisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData)
  1198. {
  1199. Assert(codeGenRecyclableData);
  1200. if(!recyclableData)
  1201. {
  1202. // The thread context's recyclable data may have already been released to the recycler if we're shutting down
  1203. return;
  1204. }
  1205. // Unlinking may be done from a background thread, but not concurrently with linking (caller must use lock). Partial unlink
  1206. // does not zero the previous and next links for the unlinked node so that the recycler can scan through the node from the
  1207. // main thread.
  1208. recyclableData->codeGenRecyclableDatas.UnlinkPartial(codeGenRecyclableData);
  1209. }
  1210. uint
  1211. ThreadContext::EnterScriptStart(Js::ScriptEntryExitRecord * record, bool doCleanup)
  1212. {
  1213. Recycler * recycler = this->GetRecycler();
  1214. Assert(recycler->IsReentrantState());
  1215. JS_ETW_INTERNAL(EventWriteJSCRIPT_RUN_START(this,0));
  1216. // Increment the callRootLevel early so that Dispose ran during FinishConcurrent will not close the current scriptContext
  1217. uint oldCallRootLevel = this->callRootLevel++;
  1218. if (oldCallRootLevel == 0)
  1219. {
  1220. Assert(!this->hasThrownPendingException);
  1221. RECORD_TIMESTAMP(lastScriptStartTime);
  1222. InterruptPoller *poller = this->interruptPoller;
  1223. if (poller)
  1224. {
  1225. poller->StartScript();
  1226. }
  1227. recycler->SetIsInScript(true);
  1228. if (doCleanup)
  1229. {
  1230. recycler->EnterIdleDecommit();
  1231. #if ENABLE_CONCURRENT_GC
  1232. recycler->FinishConcurrent<FinishConcurrentOnEnterScript>();
  1233. #endif
  1234. if (threadServiceWrapper == NULL)
  1235. {
  1236. // Reschedule the next collection at the start of the script.
  1237. recycler->ScheduleNextCollection();
  1238. }
  1239. }
  1240. }
  1241. this->PushEntryExitRecord(record);
  1242. AssertMsg(!this->IsScriptActive(),
  1243. "Missing EnterScriptEnd or LeaveScriptStart");
  1244. this->isScriptActive = true;
  1245. recycler->SetIsScriptActive(true);
  1246. #if DBG_DUMP
  1247. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
  1248. {
  1249. Output::Trace(Js::RunPhase, _u("%p> EnterScriptStart(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
  1250. Output::Flush();
  1251. }
  1252. #endif
  1253. return oldCallRootLevel;
  1254. }
  1255. void
  1256. ThreadContext::EnterScriptEnd(Js::ScriptEntryExitRecord * record, bool doCleanup)
  1257. {
  1258. #if DBG_DUMP
  1259. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
  1260. {
  1261. Output::Trace(Js::RunPhase, _u("%p> EnterScriptEnd (%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
  1262. Output::Flush();
  1263. }
  1264. #endif
  1265. this->PopEntryExitRecord(record);
  1266. AssertMsg(this->IsScriptActive(),
  1267. "Missing EnterScriptStart or LeaveScriptEnd");
  1268. this->isScriptActive = false;
  1269. this->GetRecycler()->SetIsScriptActive(false);
  1270. this->callRootLevel--;
  1271. #ifdef EXCEPTION_CHECK
  1272. ExceptionCheck::SetHandledExceptionType(record->handledExceptionType);
  1273. #endif
  1274. #ifdef RECYCLER_MEMORY_VERIFY
  1275. recycler->Verify(Js::RunPhase);
  1276. #endif
  1277. if (this->callRootLevel == 0)
  1278. {
  1279. RECORD_TIMESTAMP(lastScriptEndTime);
  1280. this->GetRecycler()->SetIsInScript(false);
  1281. InterruptPoller *poller = this->interruptPoller;
  1282. if (poller)
  1283. {
  1284. poller->EndScript();
  1285. }
  1286. ClosePendingScriptContexts();
  1287. Assert(rootPendingClose == nullptr);
  1288. if (this->hasThrownPendingException)
  1289. {
  1290. // We have some cases where the thread instant of JavascriptExceptionObject
  1291. // are ignored and not clear. To avoid leaks, clear it here when
  1292. // we are not in script, where no one should be using these JavascriptExceptionObject
  1293. this->ClearPendingOOMError();
  1294. this->ClearPendingSOError();
  1295. this->hasThrownPendingException = false;
  1296. }
  1297. delayFreeCallback.ClearAll();
  1298. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  1299. if (Js::Configuration::Global.flags.FreeRejittedCode)
  1300. #endif
  1301. {
  1302. // Since we're no longer in script, old entry points can now be collected
  1303. Js::FunctionEntryPointInfo* current = this->recyclableData->oldEntryPointInfo;
  1304. this->recyclableData->oldEntryPointInfo = nullptr;
  1305. // Clear out the next pointers so older entry points wont be held on
  1306. // as a false positive
  1307. while (current != nullptr)
  1308. {
  1309. Js::FunctionEntryPointInfo* next = current->nextEntryPoint;
  1310. current->nextEntryPoint = nullptr;
  1311. current = next;
  1312. }
  1313. }
  1314. if (doCleanup)
  1315. {
  1316. ThreadServiceWrapper* threadServiceWrapper = GetThreadServiceWrapper();
  1317. if (!threadServiceWrapper || !threadServiceWrapper->ScheduleNextCollectOnExit())
  1318. {
  1319. // Do the idle GC now if we fail schedule one.
  1320. recycler->CollectNow<CollectOnScriptExit>();
  1321. }
  1322. recycler->LeaveIdleDecommit();
  1323. }
  1324. }
  1325. if (doCleanup)
  1326. {
  1327. PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in EnterScriptEnd: %d\n"), this->recycler->NeedDispose());
  1328. this->recycler->FinishDisposeObjectsNow<FinishDispose>();
  1329. }
  1330. JS_ETW_INTERNAL(EventWriteJSCRIPT_RUN_STOP(this,0));
  1331. }
  1332. void
  1333. ThreadContext::SetForceOneIdleCollection()
  1334. {
  1335. ThreadServiceWrapper* threadServiceWrapper = GetThreadServiceWrapper();
  1336. if (threadServiceWrapper)
  1337. {
  1338. threadServiceWrapper->SetForceOneIdleCollection();
  1339. }
  1340. }
  1341. bool
  1342. ThreadContext::IsOnStack(void const *ptr)
  1343. {
  1344. if (IS_ASAN_FAKE_STACK_ADDR(ptr))
  1345. {
  1346. return true;
  1347. }
  1348. #if defined(_M_IX86) && defined(_MSC_VER)
  1349. return ptr < (void*)__readfsdword(0x4) && ptr >= (void*)__readfsdword(0xE0C);
  1350. #elif defined(_M_AMD64) && defined(_MSC_VER)
  1351. return ptr < (void*)__readgsqword(0x8) && ptr >= (void*)__readgsqword(0x1478);
  1352. #elif defined(_M_ARM)
  1353. ULONG lowLimit, highLimit;
  1354. ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
  1355. bool isOnStack = (void*)lowLimit <= ptr && ptr < (void*)highLimit;
  1356. return isOnStack;
  1357. #elif defined(_M_ARM64)
  1358. ULONG64 lowLimit, highLimit;
  1359. ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
  1360. bool isOnStack = (void*)lowLimit <= ptr && ptr < (void*)highLimit;
  1361. return isOnStack;
  1362. #elif !defined(_MSC_VER)
  1363. return ::IsAddressOnStack((ULONG_PTR) ptr);
  1364. #else
  1365. AssertMsg(FALSE, "IsOnStack -- not implemented yet case");
  1366. Js::Throw::NotImplemented();
  1367. return false;
  1368. #endif
  1369. }
  1370. size_t
  1371. ThreadContext::GetStackLimitForCurrentThread() const
  1372. {
  1373. FAULTINJECT_SCRIPT_TERMINATION;
  1374. size_t limit = this->stackLimitForCurrentThread;
  1375. Assert(limit == Js::Constants::StackLimitForScriptInterrupt
  1376. || !this->GetStackProber()
  1377. || limit == this->GetStackProber()->GetScriptStackLimit());
  1378. return limit;
  1379. }
  1380. void
  1381. ThreadContext::SetStackLimitForCurrentThread(size_t limit)
  1382. {
  1383. this->stackLimitForCurrentThread = limit;
  1384. }
  1385. _NOINLINE //Win8 947081: might use wrong _AddressOfReturnAddress() if this and caller are inlined
  1386. bool
  1387. ThreadContext::IsStackAvailable(size_t size, bool* isInterrupt)
  1388. {
  1389. size_t sp = (size_t)_AddressOfReturnAddress();
  1390. size_t stackLimit = this->GetStackLimitForCurrentThread();
  1391. bool stackAvailable = (sp > size && (sp - size) > stackLimit);
  1392. // Verify that JIT'd frames didn't mess up the ABI stack alignment
  1393. Assert(((uintptr_t)sp & (AutoSystemInfo::StackAlign - 1)) == (sizeof(void*) & (AutoSystemInfo::StackAlign - 1)));
  1394. #if DBG
  1395. this->GetStackProber()->AdjustKnownStackLimit(sp, size);
  1396. #endif
  1397. FAULTINJECT_STACK_PROBE
  1398. if (stackAvailable)
  1399. {
  1400. return true;
  1401. }
  1402. if (sp <= stackLimit)
  1403. {
  1404. if (stackLimit == Js::Constants::StackLimitForScriptInterrupt)
  1405. {
  1406. if (sp <= this->GetStackProber()->GetScriptStackLimit())
  1407. {
  1408. // Take down the process if we cant recover from the stack overflow
  1409. Js::Throw::FatalInternalError();
  1410. }
  1411. if (isInterrupt)
  1412. {
  1413. *isInterrupt = true; // when stack not available, indicate if due to script interrupt
  1414. }
  1415. }
  1416. }
  1417. return false;
  1418. }
  1419. _NOINLINE //Win8 947081: might use wrong _AddressOfReturnAddress() if this and caller are inlined
  1420. bool
  1421. ThreadContext::IsStackAvailableNoThrow(size_t size)
  1422. {
  1423. size_t sp = (size_t)_AddressOfReturnAddress();
  1424. size_t stackLimit = this->GetStackLimitForCurrentThread();
  1425. bool stackAvailable = (sp > stackLimit) && (sp > size) && ((sp - size) > stackLimit);
  1426. FAULTINJECT_STACK_PROBE
  1427. return stackAvailable;
  1428. }
  1429. /* static */ bool
  1430. ThreadContext::IsCurrentStackAvailable(size_t size)
  1431. {
  1432. ThreadContext *currentContext = GetContextForCurrentThread();
  1433. Assert(currentContext);
  1434. return currentContext->IsStackAvailable(size);
  1435. }
  1436. /*
  1437. returnAddress will be passed in the stackprobe call at the beginning of interpreter frame.
  1438. We need to probe the stack before we link up the InterpreterFrame structure in threadcontext,
  1439. and if we throw there, the stack walker might get confused when trying to identify a frame
  1440. is interpreter frame by comparing the current ebp in ebp chain with return address specified
  1441. in the last InterpreterFrame linked in threadcontext. We need to pass in the return address
  1442. of the probing frame to skip the right one (we need to skip first match in a->a->a recursion,
  1443. but not in a->b->a recursion).
  1444. */
  1445. void
  1446. ThreadContext::ProbeStackNoDispose(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress)
  1447. {
  1448. AssertCanHandleStackOverflow();
  1449. if (!this->IsStackAvailable(size))
  1450. {
  1451. if (this->IsExecutionDisabled())
  1452. {
  1453. // The probe failed because we hammered the stack limit to trigger script interrupt.
  1454. Assert(this->DoInterruptProbe());
  1455. throw Js::ScriptAbortException();
  1456. }
  1457. Js::Throw::StackOverflow(scriptContext, returnAddress);
  1458. }
  1459. #if defined(NTBUILD) || defined(__IOS__) || defined(__ANDROID__)
  1460. // Use every Nth stack probe as a QC trigger.
  1461. if (AutoSystemInfo::ShouldQCMoreFrequently() && this->HasInterruptPoller() && this->IsScriptActive())
  1462. {
  1463. ++(this->stackProbeCount);
  1464. if (this->stackProbeCount > ThreadContext::StackProbePollThreshold)
  1465. {
  1466. this->stackProbeCount = 0;
  1467. this->CheckInterruptPoll();
  1468. }
  1469. }
  1470. #endif
  1471. }
  1472. void
  1473. ThreadContext::ProbeStack(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress)
  1474. {
  1475. this->ProbeStackNoDispose(size, scriptContext, returnAddress);
  1476. #if PERFMAP_TRACE_ENABLED
  1477. if (PlatformAgnostic::PerfTrace::mapsRequested)
  1478. {
  1479. PlatformAgnostic::PerfTrace::WritePerfMap();
  1480. }
  1481. #endif
  1482. // BACKGROUND-GC TODO: If we're stuck purely in JITted code, we should have the
  1483. // background GC thread modify the threads stack limit to trigger the runtime stack probe
  1484. if (this->callDispose)
  1485. {
  1486. PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in ProbeStack: %d\n"), this->recycler->NeedDispose());
  1487. this->recycler->FinishDisposeObjectsNow<FinishDisposeTimed>();
  1488. }
  1489. }
  1490. void
  1491. ThreadContext::ProbeStack(size_t size, Js::RecyclableObject * obj, Js::ScriptContext *scriptContext)
  1492. {
  1493. AssertCanHandleStackOverflowCall(obj->IsExternal() ||
  1494. (Js::JavascriptOperators::GetTypeId(obj) == Js::TypeIds_Function &&
  1495. Js::VarTo<Js::JavascriptFunction>(obj)->IsExternalFunction()));
  1496. if (!this->IsStackAvailable(size))
  1497. {
  1498. if (this->IsExecutionDisabled())
  1499. {
  1500. // The probe failed because we hammered the stack limit to trigger script interrupt.
  1501. Assert(this->DoInterruptProbe());
  1502. throw Js::ScriptAbortException();
  1503. }
  1504. if (obj->IsExternal() ||
  1505. (Js::JavascriptOperators::GetTypeId(obj) == Js::TypeIds_Function &&
  1506. Js::VarTo<Js::JavascriptFunction>(obj)->IsExternalFunction()))
  1507. {
  1508. Js::JavascriptError::ThrowStackOverflowError(scriptContext);
  1509. }
  1510. Js::Throw::StackOverflow(scriptContext, NULL);
  1511. }
  1512. }
  1513. void
  1514. ThreadContext::ProbeStack(size_t size)
  1515. {
  1516. Assert(this->IsScriptActive());
  1517. Js::ScriptEntryExitRecord *entryExitRecord = this->GetScriptEntryExit();
  1518. Assert(entryExitRecord);
  1519. Js::ScriptContext *scriptContext = entryExitRecord->scriptContext;
  1520. Assert(scriptContext);
  1521. this->ProbeStack(size, scriptContext);
  1522. }
  1523. /* static */ void
  1524. ThreadContext::ProbeCurrentStack(size_t size, Js::ScriptContext *scriptContext)
  1525. {
  1526. Assert(scriptContext != nullptr);
  1527. Assert(scriptContext->GetThreadContext() == GetContextForCurrentThread());
  1528. scriptContext->GetThreadContext()->ProbeStack(size, scriptContext);
  1529. }
  1530. /* static */ void
  1531. ThreadContext::ProbeCurrentStackNoDispose(size_t size, Js::ScriptContext *scriptContext)
  1532. {
  1533. Assert(scriptContext != nullptr);
  1534. Assert(scriptContext->GetThreadContext() == GetContextForCurrentThread());
  1535. scriptContext->GetThreadContext()->ProbeStackNoDispose(size, scriptContext);
  1536. }
  1537. template <bool leaveForHost>
  1538. void
  1539. ThreadContext::LeaveScriptStart(void * frameAddress)
  1540. {
  1541. Assert(this->IsScriptActive());
  1542. #if DBG_DUMP
  1543. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
  1544. {
  1545. Output::Trace(Js::RunPhase, _u("%p> LeaveScriptStart(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
  1546. Output::Flush();
  1547. }
  1548. #endif
  1549. Js::ScriptEntryExitRecord * entryExitRecord = this->GetScriptEntryExit();
  1550. AssertMsg(entryExitRecord && entryExitRecord->frameIdOfScriptExitFunction == nullptr,
  1551. "Missing LeaveScriptEnd or EnterScriptStart");
  1552. entryExitRecord->frameIdOfScriptExitFunction = frameAddress;
  1553. this->isScriptActive = false;
  1554. this->GetRecycler()->SetIsScriptActive(false);
  1555. AssertMsg(!(leaveForHost && this->IsDisableImplicitCall()),
  1556. "Disable implicit call should have been caught before leaving script for host");
  1557. // Save the implicit call flags
  1558. entryExitRecord->savedImplicitCallFlags = this->GetImplicitCallFlags();
  1559. // clear the hasReentered to detect if we have reentered into script
  1560. entryExitRecord->hasReentered = false;
  1561. #if DBG || defined(PROFILE_EXEC)
  1562. entryExitRecord->leaveForHost = leaveForHost;
  1563. #endif
  1564. #if DBG
  1565. entryExitRecord->leaveForAsyncHostOperation = false;
  1566. #endif
  1567. #ifdef PROFILE_EXEC
  1568. if (leaveForHost)
  1569. {
  1570. entryExitRecord->scriptContext->ProfileEnd(Js::RunPhase);
  1571. }
  1572. #endif
  1573. }
  1574. void ThreadContext::DisposeOnLeaveScript()
  1575. {
  1576. PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in LeaveScriptStart: %d\n"), this->recycler->NeedDispose());
  1577. if (this->callDispose && !recycler->IsCollectionDisabled())
  1578. {
  1579. this->recycler->FinishDisposeObjectsNow<FinishDispose>();
  1580. }
  1581. }
  1582. template <bool leaveForHost>
  1583. void
  1584. ThreadContext::LeaveScriptEnd(void * frameAddress)
  1585. {
  1586. Assert(!this->IsScriptActive());
  1587. #if DBG_DUMP
  1588. if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
  1589. {
  1590. Output::Trace(Js::RunPhase, _u("%p> LeaveScriptEnd(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
  1591. Output::Flush();
  1592. }
  1593. #endif
  1594. Js::ScriptEntryExitRecord * entryExitRecord = this->GetScriptEntryExit();
  1595. AssertMsg(entryExitRecord && entryExitRecord->frameIdOfScriptExitFunction,
  1596. "LeaveScriptEnd without LeaveScriptStart");
  1597. AssertMsg(frameAddress == nullptr || frameAddress == entryExitRecord->frameIdOfScriptExitFunction,
  1598. "Mismatched script exit frames");
  1599. Assert(!!entryExitRecord->leaveForHost == leaveForHost);
  1600. entryExitRecord->frameIdOfScriptExitFunction = nullptr;
  1601. AssertMsg(!this->IsScriptActive(), "Missing LeaveScriptStart or LeaveScriptStart");
  1602. this->isScriptActive = true;
  1603. this->GetRecycler()->SetIsScriptActive(true);
  1604. Js::ImplicitCallFlags savedImplicitCallFlags = entryExitRecord->savedImplicitCallFlags;
  1605. if (leaveForHost)
  1606. {
  1607. savedImplicitCallFlags = (Js::ImplicitCallFlags)(savedImplicitCallFlags | Js::ImplicitCall_External);
  1608. }
  1609. else if (entryExitRecord->hasReentered)
  1610. {
  1611. savedImplicitCallFlags = (Js::ImplicitCallFlags)(savedImplicitCallFlags | Js::ImplicitCall_AsyncHostOperation);
  1612. }
  1613. // Restore the implicit call flags
  1614. this->SetImplicitCallFlags(savedImplicitCallFlags);
  1615. #ifdef PROFILE_EXEC
  1616. if (leaveForHost)
  1617. {
  1618. entryExitRecord->scriptContext->ProfileBegin(Js::RunPhase);
  1619. }
  1620. #endif
  1621. }
  1622. // explicit instantiations
  1623. template void ThreadContext::LeaveScriptStart<true>(void * frameAddress);
  1624. template void ThreadContext::LeaveScriptStart<false>(void * frameAddress);
  1625. template void ThreadContext::LeaveScriptEnd<true>(void * frameAddress);
  1626. template void ThreadContext::LeaveScriptEnd<false>(void * frameAddress);
  1627. void
  1628. ThreadContext::PushInterpreterFrame(Js::InterpreterStackFrame *interpreterFrame)
  1629. {
  1630. interpreterFrame->SetPreviousFrame(this->leafInterpreterFrame);
  1631. this->leafInterpreterFrame = interpreterFrame;
  1632. }
  1633. Js::InterpreterStackFrame *
  1634. ThreadContext::PopInterpreterFrame()
  1635. {
  1636. Js::InterpreterStackFrame *interpreterFrame = this->leafInterpreterFrame;
  1637. Assert(interpreterFrame);
  1638. this->leafInterpreterFrame = interpreterFrame->GetPreviousFrame();
  1639. return interpreterFrame;
  1640. }
  1641. BOOL
  1642. ThreadContext::ExecuteRecyclerCollectionFunctionCommon(Recycler * recycler, CollectionFunction function, CollectionFlags flags)
  1643. {
  1644. return __super::ExecuteRecyclerCollectionFunction(recycler, function, flags);
  1645. }
  1646. #if DBG
  1647. bool
  1648. ThreadContext::IsInAsyncHostOperation() const
  1649. {
  1650. if (!this->IsScriptActive())
  1651. {
  1652. Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
  1653. if (lastRecord != NULL)
  1654. {
  1655. return !!lastRecord->leaveForAsyncHostOperation;
  1656. }
  1657. }
  1658. return false;
  1659. }
  1660. #endif
  1661. #if ENABLE_NATIVE_CODEGEN
  1662. void
  1663. ThreadContext::SetJITConnectionInfo(HANDLE processHandle, void* serverSecurityDescriptor, UUID connectionId)
  1664. {
  1665. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  1666. if (!JITManager::GetJITManager()->IsConnected())
  1667. {
  1668. // TODO: return HRESULT
  1669. JITManager::GetJITManager()->ConnectRpcServer(processHandle, serverSecurityDescriptor, connectionId);
  1670. }
  1671. }
  1672. bool
  1673. ThreadContext::EnsureJITThreadContext(bool allowPrereserveAlloc)
  1674. {
  1675. #if ENABLE_OOP_NATIVE_CODEGEN
  1676. Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
  1677. if (!JITManager::GetJITManager()->IsConnected())
  1678. {
  1679. return false;
  1680. }
  1681. if (m_remoteThreadContextInfo)
  1682. {
  1683. return true;
  1684. }
  1685. ThreadContextDataIDL contextData;
  1686. contextData.threadStackLimitAddr = reinterpret_cast<intptr_t>(GetAddressOfStackLimitForCurrentThread());
  1687. contextData.bailOutRegisterSaveSpaceAddr = (intptr_t)bailOutRegisterSaveSpace;
  1688. contextData.disableImplicitFlagsAddr = (intptr_t)GetAddressOfDisableImplicitFlags();
  1689. contextData.implicitCallFlagsAddr = (intptr_t)GetAddressOfImplicitCallFlags();
  1690. contextData.scriptStackLimit = GetScriptStackLimit();
  1691. contextData.isThreadBound = IsThreadBound();
  1692. contextData.allowPrereserveAlloc = allowPrereserveAlloc;
  1693. #if defined(ENABLE_WASM_SIMD) && (_M_IX86 || _M_AMD64)
  1694. contextData.simdTempAreaBaseAddr = (intptr_t)GetSimdTempArea();
  1695. #endif
  1696. m_jitNumericProperties = HeapNew(BVSparse<HeapAllocator>, &HeapAllocator::Instance);
  1697. for (auto iter = propertyMap->GetIterator(); iter.IsValid(); iter.MoveNext())
  1698. {
  1699. if (iter.CurrentKey()->IsNumeric())
  1700. {
  1701. m_jitNumericProperties->Set(iter.CurrentKey()->GetPropertyId());
  1702. m_jitNeedsPropertyUpdate = true;
  1703. }
  1704. }
  1705. HRESULT hr = JITManager::GetJITManager()->InitializeThreadContext(
  1706. &contextData,
  1707. &m_remoteThreadContextInfo,
  1708. &m_prereservedRegionAddr,
  1709. &m_jitThunkStartAddr);
  1710. JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate);
  1711. // Initialize mutable ThreadContext state if needed
  1712. Js::TypeId wellKnownType = this->wellKnownHostTypeIds[WellKnownHostType_HTMLAllCollection];
  1713. if (m_remoteThreadContextInfo && wellKnownType != Js::TypeIds_Undefined)
  1714. {
  1715. hr = JITManager::GetJITManager()->SetWellKnownHostTypeId(m_remoteThreadContextInfo, wellKnownType);
  1716. JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate);
  1717. }
  1718. return m_remoteThreadContextInfo != nullptr;
  1719. #endif
  1720. }
  1721. #endif
  1722. #if ENABLE_TTD
  1723. void ThreadContext::InitTimeTravel(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength)
  1724. {
  1725. TTDAssert(!this->IsRuntimeInTTDMode(), "We should only init once.");
  1726. this->TTDContext = HeapNew(TTD::ThreadContextTTD, this, runtimeHandle, snapInterval, snapHistoryLength);
  1727. this->TTDLog = HeapNew(TTD::EventLog, this);
  1728. }
  1729. void ThreadContext::InitHostFunctionsAndTTData(bool record, bool replay, bool debug, size_t optTTUriLength, const char* optTTUri,
  1730. TTD::TTDOpenResourceStreamCallback openResourceStreamfp, TTD::TTDReadBytesFromStreamCallback readBytesFromStreamfp,
  1731. TTD::TTDWriteBytesToStreamCallback writeBytesToStreamfp, TTD::TTDFlushAndCloseStreamCallback flushAndCloseStreamfp,
  1732. TTD::TTDCreateExternalObjectCallback createExternalObjectfp,
  1733. TTD::TTDCreateJsRTContextCallback createJsRTContextCallbackfp, TTD::TTDReleaseJsRTContextCallback releaseJsRTContextCallbackfp, TTD::TTDSetActiveJsRTContext setActiveJsRTContextfp)
  1734. {
  1735. AssertMsg(this->IsRuntimeInTTDMode(), "Need to call init first.");
  1736. this->TTDContext->TTDataIOInfo = { openResourceStreamfp, readBytesFromStreamfp, writeBytesToStreamfp, flushAndCloseStreamfp, 0, nullptr };
  1737. this->TTDContext->TTDExternalObjectFunctions = { createExternalObjectfp, createJsRTContextCallbackfp, releaseJsRTContextCallbackfp, setActiveJsRTContextfp };
  1738. if(record)
  1739. {
  1740. TTDAssert(optTTUri == nullptr, "No URI is needed in record mode (the host explicitly provides it when writing.");
  1741. this->TTDLog->InitForTTDRecord(debug);
  1742. }
  1743. else
  1744. {
  1745. TTDAssert(optTTUri != nullptr, "We need a URI in replay mode so we can initialize the log from it");
  1746. this->TTDLog->InitForTTDReplay(this->TTDContext->TTDataIOInfo, optTTUri, optTTUriLength, debug);
  1747. this->sourceInfoCount = this->TTDLog->GetSourceInfoCount();
  1748. }
  1749. #if !ENABLE_TTD_DIAGNOSTICS_TRACING
  1750. if(debug)
  1751. {
  1752. #endif
  1753. TTD::TTInnerLoopLastStatementInfo lsi;
  1754. TTD::TTDebuggerSourceLocation dsl;
  1755. this->TTDLog->LoadLastSourceLineInfo(lsi, dsl);
  1756. this->TTDExecutionInfo = HeapNew(TTD::ExecutionInfoManager, lsi);
  1757. if(dsl.HasValue())
  1758. {
  1759. this->TTDExecutionInfo->SetPendingTTDToTarget(dsl);
  1760. }
  1761. #if !ENABLE_TTD_DIAGNOSTICS_TRACING
  1762. }
  1763. #endif
  1764. }
  1765. #endif
  1766. BOOL
  1767. ThreadContext::ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags)
  1768. {
  1769. // If the thread context doesn't have an associated Recycler set, don't do anything
  1770. if (this->recycler == nullptr)
  1771. {
  1772. return FALSE;
  1773. }
  1774. // Take etw rundown lock on this thread context. We can't collect entryPoints if we are in etw rundown.
  1775. AutoCriticalSection autocs(this->GetFunctionBodyLock());
  1776. // Disable calling dispose from leave script or the stack probe
  1777. // while we're executing the recycler wrapper
  1778. AutoRestoreValue<bool> callDispose(&this->callDispose, false);
  1779. BOOL ret = FALSE;
  1780. #if ENABLE_TTD
  1781. //
  1782. //TODO: We lose any events that happen in the callbacks (such as JsRelease) which may be a problem in the future.
  1783. // It may be possible to defer the collection of these objects to an explicit collection at the yield loop (same for weak set/map).
  1784. // We already indirectly do this for ScriptContext collection (but that is buggy so needs to be fixed too).
  1785. //
  1786. if(this->IsRuntimeInTTDMode())
  1787. {
  1788. this->TTDLog->PushMode(TTD::TTDMode::ExcludedExecutionTTAction);
  1789. }
  1790. #endif
  1791. if (!this->IsScriptActive())
  1792. {
  1793. Assert(!this->IsDisableImplicitCall() || this->IsInAsyncHostOperation());
  1794. ret = this->ExecuteRecyclerCollectionFunctionCommon(recycler, function, flags);
  1795. // Make sure that we finish a collect that is activated outside of script, since
  1796. // we won't have exit script to schedule it
  1797. if (!this->IsInScript() && recycler->CollectionInProgress()
  1798. && ((flags & CollectOverride_DisableIdleFinish) == 0) && threadServiceWrapper)
  1799. {
  1800. threadServiceWrapper->ScheduleFinishConcurrent();
  1801. }
  1802. }
  1803. else
  1804. {
  1805. void * frameAddr = nullptr;
  1806. GET_CURRENT_FRAME_ID(frameAddr);
  1807. // We may need stack to call out from Dispose or QC
  1808. if (!this->IsDisableImplicitCall()) // otherwise Dispose/QC disabled
  1809. {
  1810. // If we don't have stack space to call out from Dispose or QC,
  1811. // don't throw, simply return false. This gives SnailAlloc a better
  1812. // chance of allocating in low stack-space situations (like allocating
  1813. // a StackOverflowException object)
  1814. if (!this->IsStackAvailableNoThrow(Js::Constants::MinStackCallout))
  1815. {
  1816. return false;
  1817. }
  1818. }
  1819. this->LeaveScriptStart<false>(frameAddr);
  1820. ret = this->ExecuteRecyclerCollectionFunctionCommon(recycler, function, flags);
  1821. this->LeaveScriptEnd<false>(frameAddr);
  1822. // After OOM changed to fatal error, this throw still exists on allocation path
  1823. if (this->callRootLevel != 0)
  1824. {
  1825. this->CheckScriptInterrupt();
  1826. }
  1827. }
  1828. #if ENABLE_TTD
  1829. if(this->IsRuntimeInTTDMode())
  1830. {
  1831. this->TTDLog->PopMode(TTD::TTDMode::ExcludedExecutionTTAction);
  1832. }
  1833. #endif
  1834. return ret;
  1835. }
  1836. void
  1837. ThreadContext::DisposeObjects(Recycler * recycler)
  1838. {
  1839. if (this->IsDisableImplicitCall())
  1840. {
  1841. // Don't dispose objects when implicit calls are disabled, since disposing may cause implicit calls. Objects will remain
  1842. // in the dispose queue and will be disposed later when implicit calls are not disabled.
  1843. return;
  1844. }
  1845. // We shouldn't DisposeObjects in NoScriptScope as this might lead to script execution.
  1846. // Callers of DisposeObjects should ensure !IsNoScriptScope() before calling DisposeObjects.
  1847. if (this->IsNoScriptScope())
  1848. {
  1849. FromDOM_NoScriptScope_unrecoverable_error();
  1850. }
  1851. if (!this->IsScriptActive())
  1852. {
  1853. __super::DisposeObjects(recycler);
  1854. }
  1855. else
  1856. {
  1857. void * frameAddr = nullptr;
  1858. GET_CURRENT_FRAME_ID(frameAddr);
  1859. // We may need stack to call out from Dispose
  1860. // This code path is not in GC on allocation code path any more, it's OK to throw here
  1861. this->ProbeStack(Js::Constants::MinStackCallout);
  1862. this->LeaveScriptStart<false>(frameAddr);
  1863. __super::DisposeObjects(recycler);
  1864. this->LeaveScriptEnd<false>(frameAddr);
  1865. }
  1866. }
  1867. void
  1868. ThreadContext::PushEntryExitRecord(Js::ScriptEntryExitRecord * record)
  1869. {
  1870. AssertMsg(record, "Didn't provide a script entry record to push");
  1871. Assert(record->next == nullptr);
  1872. Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
  1873. if (lastRecord != nullptr)
  1874. {
  1875. // If we enter script again, we should have leave with leaveForHost or leave for dispose.
  1876. Assert(lastRecord->leaveForHost || lastRecord->leaveForAsyncHostOperation);
  1877. lastRecord->hasReentered = true;
  1878. record->next = lastRecord;
  1879. // these are on stack, which grows down. if this condition doesn't hold,
  1880. // then the list somehow got messed up
  1881. if (
  1882. #if defined(JSRT_VERIFY_RUNTIME_STATE) || defined(DEBUG)
  1883. !IsOnStack(lastRecord) ||
  1884. #endif
  1885. ((uintptr_t)record >= (uintptr_t)lastRecord
  1886. && !IS_ASAN_FAKE_STACK_ADDR(record)
  1887. && !IS_ASAN_FAKE_STACK_ADDR(lastRecord)))
  1888. {
  1889. EntryExitRecord_Corrupted_unrecoverable_error();
  1890. }
  1891. }
  1892. this->entryExitRecord = record;
  1893. }
  1894. void ThreadContext::PopEntryExitRecord(Js::ScriptEntryExitRecord * record)
  1895. {
  1896. AssertMsg(record && record == this->entryExitRecord, "Mismatch script entry/exit");
  1897. // these are on stack, which grows down. if this condition doesn't hold,
  1898. // then the list somehow got messed up
  1899. Js::ScriptEntryExitRecord * next = this->entryExitRecord->next;
  1900. if (next && (
  1901. #if defined(JSRT_VERIFY_RUNTIME_STATE) || defined(DEBUG)
  1902. !IsOnStack(next) ||
  1903. #endif
  1904. ((uintptr_t)this->entryExitRecord >= (uintptr_t)next
  1905. && !IS_ASAN_FAKE_STACK_ADDR(this->entryExitRecord)
  1906. && !IS_ASAN_FAKE_STACK_ADDR(next))))
  1907. {
  1908. EntryExitRecord_Corrupted_unrecoverable_error();
  1909. }
  1910. this->entryExitRecord = next;
  1911. }
  1912. BOOL ThreadContext::ReserveStaticTypeIds(__in int first, __in int last)
  1913. {
  1914. if ( nextTypeId <= first )
  1915. {
  1916. nextTypeId = (Js::TypeId) last;
  1917. return TRUE;
  1918. }
  1919. else
  1920. {
  1921. return FALSE;
  1922. }
  1923. }
  1924. Js::TypeId ThreadContext::ReserveTypeIds(int count)
  1925. {
  1926. Js::TypeId firstTypeId = nextTypeId;
  1927. nextTypeId = (Js::TypeId)(nextTypeId + count);
  1928. return firstTypeId;
  1929. }
  1930. Js::TypeId ThreadContext::CreateTypeId()
  1931. {
  1932. return nextTypeId = (Js::TypeId)(nextTypeId + 1);
  1933. }
  1934. void ThreadContext::SetWellKnownHostTypeId(WellKnownHostType wellKnownType, Js::TypeId typeId)
  1935. {
  1936. AssertMsg(wellKnownType <= WellKnownHostType_Last, "ThreadContext::SetWellKnownHostTypeId called on unknown type");
  1937. if (wellKnownType >= 0 && wellKnownType <= WellKnownHostType_Last)
  1938. {
  1939. this->wellKnownHostTypeIds[wellKnownType] = typeId;
  1940. #if ENABLE_NATIVE_CODEGEN
  1941. // The jit server really only needs to know about WellKnownHostType_HTMLAllCollection
  1942. if (this->m_remoteThreadContextInfo && wellKnownType == WellKnownHostType_HTMLAllCollection)
  1943. {
  1944. HRESULT hr = JITManager::GetJITManager()->SetWellKnownHostTypeId(this->m_remoteThreadContextInfo, (int)typeId);
  1945. JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate);
  1946. }
  1947. #endif
  1948. }
  1949. }
  1950. #ifdef ENABLE_SCRIPT_DEBUGGING
  1951. void ThreadContext::EnsureDebugManager()
  1952. {
  1953. if (this->debugManager == nullptr)
  1954. {
  1955. this->debugManager = HeapNew(Js::DebugManager, this, this->GetAllocationPolicyManager());
  1956. }
  1957. InterlockedIncrement(&crefSContextForDiag);
  1958. Assert(this->debugManager != nullptr);
  1959. }
  1960. void ThreadContext::ReleaseDebugManager()
  1961. {
  1962. Assert(crefSContextForDiag > 0);
  1963. Assert(this->debugManager != nullptr);
  1964. LONG lref = InterlockedDecrement(&crefSContextForDiag);
  1965. if (lref == 0)
  1966. {
  1967. if (this->recyclableData != nullptr)
  1968. {
  1969. this->recyclableData->returnedValueList = nullptr;
  1970. }
  1971. if (this->debugManager != nullptr)
  1972. {
  1973. this->debugManager->Close();
  1974. HeapDelete(this->debugManager);
  1975. this->debugManager = nullptr;
  1976. }
  1977. }
  1978. }
  1979. #endif
  1980. Js::TempArenaAllocatorObject *
  1981. ThreadContext::GetTemporaryAllocator(LPCWSTR name)
  1982. {
  1983. AssertCanHandleOutOfMemory();
  1984. if (temporaryArenaAllocatorCount != 0)
  1985. {
  1986. temporaryArenaAllocatorCount--;
  1987. Js::TempArenaAllocatorObject * allocator = recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount];
  1988. recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount] = nullptr;
  1989. return allocator;
  1990. }
  1991. return Js::TempArenaAllocatorObject::Create(this);
  1992. }
  1993. void
  1994. ThreadContext::ReleaseTemporaryAllocator(Js::TempArenaAllocatorObject * tempAllocator)
  1995. {
  1996. if (temporaryArenaAllocatorCount < MaxTemporaryArenaAllocators)
  1997. {
  1998. tempAllocator->GetAllocator()->Reset();
  1999. recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount] = tempAllocator;
  2000. temporaryArenaAllocatorCount++;
  2001. return;
  2002. }
  2003. tempAllocator->Dispose(false);
  2004. }
  2005. Js::TempGuestArenaAllocatorObject *
  2006. ThreadContext::GetTemporaryGuestAllocator(LPCWSTR name)
  2007. {
  2008. AssertCanHandleOutOfMemory();
  2009. if (temporaryGuestArenaAllocatorCount != 0)
  2010. {
  2011. temporaryGuestArenaAllocatorCount--;
  2012. Js::TempGuestArenaAllocatorObject * allocator = recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount];
  2013. allocator->AdviseInUse();
  2014. recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount] = nullptr;
  2015. return allocator;
  2016. }
  2017. return Js::TempGuestArenaAllocatorObject::Create(this);
  2018. }
  2019. void
  2020. ThreadContext::ReleaseTemporaryGuestAllocator(Js::TempGuestArenaAllocatorObject * tempGuestAllocator)
  2021. {
  2022. if (temporaryGuestArenaAllocatorCount < MaxTemporaryArenaAllocators)
  2023. {
  2024. tempGuestAllocator->AdviseNotInUse();
  2025. recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount] = tempGuestAllocator;
  2026. temporaryGuestArenaAllocatorCount++;
  2027. return;
  2028. }
  2029. tempGuestAllocator->Dispose(false);
  2030. }
  2031. void
  2032. ThreadContext::AddToPendingScriptContextCloseList(Js::ScriptContext * scriptContext)
  2033. {
  2034. Assert(scriptContext != nullptr);
  2035. if (rootPendingClose == nullptr)
  2036. {
  2037. rootPendingClose = scriptContext;
  2038. return;
  2039. }
  2040. // Prepend to the list.
  2041. scriptContext->SetNextPendingClose(rootPendingClose);
  2042. rootPendingClose = scriptContext;
  2043. }
  2044. void
  2045. ThreadContext::RemoveFromPendingClose(Js::ScriptContext * scriptContext)
  2046. {
  2047. Assert(scriptContext != nullptr);
  2048. if (rootPendingClose == nullptr)
  2049. {
  2050. // We already sent a close message, ignore the notification.
  2051. return;
  2052. }
  2053. // Base case: The root is being removed. Move the root along.
  2054. if (scriptContext == rootPendingClose)
  2055. {
  2056. rootPendingClose = rootPendingClose->GetNextPendingClose();
  2057. return;
  2058. }
  2059. Js::ScriptContext * currScriptContext = rootPendingClose;
  2060. Js::ScriptContext * nextScriptContext = nullptr;
  2061. while (currScriptContext)
  2062. {
  2063. nextScriptContext = currScriptContext->GetNextPendingClose();
  2064. if (!nextScriptContext)
  2065. {
  2066. break;
  2067. }
  2068. if (nextScriptContext == scriptContext) {
  2069. // The next pending close ScriptContext is the one to be removed - set prev->next to next->next
  2070. currScriptContext->SetNextPendingClose(nextScriptContext->GetNextPendingClose());
  2071. return;
  2072. }
  2073. currScriptContext = nextScriptContext;
  2074. }
  2075. // We expect to find scriptContext in the pending close list.
  2076. Assert(false);
  2077. }
  2078. void ThreadContext::ClosePendingScriptContexts()
  2079. {
  2080. Js::ScriptContext * scriptContext = rootPendingClose;
  2081. if (scriptContext == nullptr)
  2082. {
  2083. return;
  2084. }
  2085. Js::ScriptContext * nextScriptContext;
  2086. do
  2087. {
  2088. nextScriptContext = scriptContext->GetNextPendingClose();
  2089. scriptContext->Close(false);
  2090. scriptContext = nextScriptContext;
  2091. }
  2092. while (scriptContext);
  2093. rootPendingClose = nullptr;
  2094. }
  2095. void
  2096. ThreadContext::RegisterScriptContext(Js::ScriptContext *scriptContext)
  2097. {
  2098. // NOTE: ETW rundown thread may be reading the scriptContextList concurrently. We don't need to
  2099. // lock access because we only insert to the front here.
  2100. scriptContext->next = this->scriptContextList;
  2101. if (this->scriptContextList)
  2102. {
  2103. Assert(this->scriptContextList->prev == NULL);
  2104. this->scriptContextList->prev = scriptContext;
  2105. }
  2106. scriptContext->prev = NULL;
  2107. this->scriptContextList = scriptContext;
  2108. if(NoJIT())
  2109. {
  2110. scriptContext->ForceNoNative();
  2111. }
  2112. if (NoDynamicThunks())
  2113. {
  2114. scriptContext->ForceNoDynamicThunks();
  2115. }
  2116. #if DBG || defined(RUNTIME_DATA_COLLECTION)
  2117. scriptContextCount++;
  2118. #endif
  2119. scriptContextEverRegistered = true;
  2120. }
  2121. void
  2122. ThreadContext::UnregisterScriptContext(Js::ScriptContext *scriptContext)
  2123. {
  2124. // NOTE: ETW rundown thread may be reading the scriptContextList concurrently. Since this function
  2125. // is only called by ~ScriptContext() which already synchronized to ETW rundown, we are safe here.
  2126. if (scriptContext == this->scriptContextList)
  2127. {
  2128. Assert(scriptContext->prev == NULL);
  2129. this->scriptContextList = scriptContext->next;
  2130. }
  2131. else
  2132. {
  2133. scriptContext->prev->next = scriptContext->next;
  2134. }
  2135. if (scriptContext->next)
  2136. {
  2137. scriptContext->next->prev = scriptContext->prev;
  2138. }
  2139. scriptContext->prev = nullptr;
  2140. scriptContext->next = nullptr;
  2141. #if DBG || defined(RUNTIME_DATA_COLLECTION)
  2142. scriptContextCount--;
  2143. #endif
  2144. }
  2145. ThreadContext::CollectCallBack *
  2146. ThreadContext::AddRecyclerCollectCallBack(RecyclerCollectCallBackFunction callback, void * context)
  2147. {
  2148. AutoCriticalSection autocs(&csCollectionCallBack);
  2149. CollectCallBack * collectCallBack = this->collectCallBackList.PrependNode(&HeapAllocator::Instance);
  2150. collectCallBack->callback = callback;
  2151. collectCallBack->context = context;
  2152. this->hasCollectionCallBack = true;
  2153. return collectCallBack;
  2154. }
  2155. void
  2156. ThreadContext::RemoveRecyclerCollectCallBack(ThreadContext::CollectCallBack * collectCallBack)
  2157. {
  2158. AutoCriticalSection autocs(&csCollectionCallBack);
  2159. this->collectCallBackList.RemoveElement(&HeapAllocator::Instance, collectCallBack);
  2160. this->hasCollectionCallBack = !this->collectCallBackList.Empty();
  2161. }
  2162. void
  2163. ThreadContext::PreCollectionCallBack(CollectionFlags flags)
  2164. {
  2165. #ifdef PERF_COUNTERS
  2166. PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: deferparse - # of func: %d # deferparsed: %d\n"), PerfCounter::CodeCounterSet::GetTotalFunctionCounter().GetValue(), PerfCounter::CodeCounterSet::GetDeferredFunctionCounter().GetValue());
  2167. #endif
  2168. // This needs to be done before ClearInlineCaches since that method can empty the list of
  2169. // script contexts with inline caches
  2170. this->ClearScriptContextCaches();
  2171. // Clear up references to types to avoid keeping them alive
  2172. this->noSpecialPropertyRegistry.Clear();
  2173. this->onlyWritablePropertyRegistry.Clear();
  2174. // Clean up unused memory before we start collecting
  2175. this->CleanNoCasePropertyMap();
  2176. this->TryEnterExpirableCollectMode();
  2177. const BOOL concurrent = flags & CollectMode_Concurrent;
  2178. const BOOL partial = flags & CollectMode_Partial;
  2179. if (!partial)
  2180. {
  2181. // Integrate allocated pages from background JIT threads
  2182. #if ENABLE_NATIVE_CODEGEN
  2183. #if !FLOATVAR
  2184. if (codeGenNumberThreadAllocator)
  2185. {
  2186. codeGenNumberThreadAllocator->Integrate();
  2187. }
  2188. if (this->xProcNumberPageSegmentManager)
  2189. {
  2190. this->xProcNumberPageSegmentManager->Integrate();
  2191. }
  2192. #endif
  2193. #endif
  2194. }
  2195. RecyclerCollectCallBackFlags callBackFlags = (RecyclerCollectCallBackFlags)
  2196. ((concurrent ? Collect_Begin_Concurrent : Collect_Begin) | (partial? Collect_Begin_Partial : Collect_Begin));
  2197. CollectionCallBack(callBackFlags);
  2198. }
  2199. void
  2200. ThreadContext::PreSweepCallback()
  2201. {
  2202. CollectionCallBack(Collect_Begin_Sweep);
  2203. #ifdef PERSISTENT_INLINE_CACHES
  2204. ClearInlineCachesWithDeadWeakRefs();
  2205. #else
  2206. ClearInlineCaches();
  2207. #endif
  2208. ClearIsInstInlineCaches();
  2209. ClearEquivalentTypeCaches();
  2210. ClearEnumeratorCaches();
  2211. this->dynamicObjectEnumeratorCacheMap.Clear();
  2212. }
  2213. void
  2214. ThreadContext::PreRescanMarkCallback()
  2215. {
  2216. // If this feature is turned off or if we're already in profile collection mode, do nothing
  2217. // We also do nothing if expiration is explicitly disabled by someone lower down the stack
  2218. if (!PHASE_OFF1(Js::ExpirableCollectPhase) && InExpirableCollectMode() && !this->disableExpiration)
  2219. {
  2220. this->DoExpirableCollectModeStackWalk();
  2221. }
  2222. }
  2223. void
  2224. ThreadContext::DoExpirableCollectModeStackWalk()
  2225. {
  2226. if (this->entryExitRecord != nullptr)
  2227. {
  2228. // If we're in script, we will do a stack walk, find the JavascriptFunction's on the stack
  2229. // and mark their entry points as being used so that we don't prematurely expire them
  2230. Js::ScriptContext* topScriptContext = this->entryExitRecord->scriptContext;
  2231. Js::JavascriptStackWalker walker(topScriptContext, TRUE);
  2232. Js::JavascriptFunction* javascriptFunction = nullptr;
  2233. while (walker.GetCallerWithoutInlinedFrames(&javascriptFunction))
  2234. {
  2235. if (javascriptFunction != nullptr && Js::ScriptFunction::Test(javascriptFunction))
  2236. {
  2237. Js::ScriptFunction* scriptFunction = (Js::ScriptFunction*) javascriptFunction;
  2238. scriptFunction->GetFunctionBody()->MapEntryPoints([](int index, Js::FunctionEntryPointInfo* entryPoint){
  2239. entryPoint->SetIsObjectUsed();
  2240. });
  2241. // Make sure we marked the current one when iterating all entry points
  2242. Js::ProxyEntryPointInfo* entryPointInfo = scriptFunction->GetEntryPointInfo();
  2243. Assert(entryPointInfo == nullptr
  2244. || !entryPointInfo->IsFunctionEntryPointInfo()
  2245. || ((Js::FunctionEntryPointInfo*)entryPointInfo)->IsObjectUsed());
  2246. }
  2247. }
  2248. }
  2249. }
  2250. void
  2251. ThreadContext::CollectionCallBack(RecyclerCollectCallBackFlags flags)
  2252. {
  2253. DListBase<CollectCallBack>::Iterator i(&this->collectCallBackList);
  2254. while (i.Next())
  2255. {
  2256. i.Data().callback(i.Data().context, flags);
  2257. }
  2258. }
  2259. void
  2260. ThreadContext::WaitCollectionCallBack()
  2261. {
  2262. // Avoid taking the lock if there are no call back
  2263. if (hasCollectionCallBack)
  2264. {
  2265. AutoCriticalSection autocs(&csCollectionCallBack);
  2266. CollectionCallBack(Collect_Wait);
  2267. }
  2268. }
  2269. void
  2270. ThreadContext::PostCollectionCallBack()
  2271. {
  2272. CollectionCallBack(Collect_End);
  2273. TryExitExpirableCollectMode();
  2274. // Recycler is null in the case where the ThreadContext is in the process of creating the recycler and
  2275. // we have a GC triggered (say because the -recyclerStress flag is passed in)
  2276. if (this->recycler != NULL)
  2277. {
  2278. if (this->recycler->InCacheCleanupCollection())
  2279. {
  2280. this->recycler->ClearCacheCleanupCollection();
  2281. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
  2282. {
  2283. scriptContext->CleanupWeakReferenceDictionaries();
  2284. }
  2285. }
  2286. }
  2287. }
  2288. void
  2289. ThreadContext::PostSweepRedeferralCallBack()
  2290. {
  2291. if (this->DoTryRedeferral())
  2292. {
  2293. HRESULT hr = S_OK;
  2294. BEGIN_TRANSLATE_OOM_TO_HRESULT
  2295. {
  2296. this->TryRedeferral();
  2297. }
  2298. END_TRANSLATE_OOM_TO_HRESULT(hr);
  2299. }
  2300. this->UpdateRedeferralState();
  2301. }
  2302. bool
  2303. ThreadContext::DoTryRedeferral() const
  2304. {
  2305. if (PHASE_FORCE1(Js::RedeferralPhase) || PHASE_STRESS1(Js::RedeferralPhase))
  2306. {
  2307. return true;
  2308. }
  2309. if (PHASE_OFF1(Js::RedeferralPhase))
  2310. {
  2311. return false;
  2312. }
  2313. switch (this->redeferralState)
  2314. {
  2315. case InitialRedeferralState:
  2316. return false;
  2317. case StartupRedeferralState:
  2318. return gcSinceCallCountsCollected >= StartupRedeferralInactiveThreshold;
  2319. case MainRedeferralState:
  2320. return gcSinceCallCountsCollected >= MainRedeferralInactiveThreshold;
  2321. default:
  2322. Assert(0);
  2323. return false;
  2324. };
  2325. }
  2326. void
  2327. ThreadContext::OnScanStackCallback(void ** stackTop, size_t byteCount, void ** registers, size_t registersByteCount)
  2328. {
  2329. // Scan the stack to match with current list of delayed free buffer. For those which are not found on the stack
  2330. // will be released (ref-count decremented)
  2331. if (!this->delayFreeCallback.HasAnyItem())
  2332. {
  2333. return;
  2334. }
  2335. this->delayFreeCallback.ScanStack(stackTop, byteCount, registers, registersByteCount);
  2336. }
  2337. bool
  2338. ThreadContext::DoRedeferFunctionBodies() const
  2339. {
  2340. #if ENABLE_TTD
  2341. if (this->IsRuntimeInTTDMode())
  2342. {
  2343. return false;
  2344. }
  2345. #endif
  2346. if (PHASE_FORCE1(Js::RedeferralPhase) || PHASE_STRESS1(Js::RedeferralPhase))
  2347. {
  2348. return true;
  2349. }
  2350. if (PHASE_OFF1(Js::RedeferralPhase))
  2351. {
  2352. return false;
  2353. }
  2354. switch (this->redeferralState)
  2355. {
  2356. case InitialRedeferralState:
  2357. return false;
  2358. case StartupRedeferralState:
  2359. return gcSinceLastRedeferral >= StartupRedeferralCheckInterval;
  2360. case MainRedeferralState:
  2361. return gcSinceLastRedeferral >= MainRedeferralCheckInterval;
  2362. default:
  2363. Assert(0);
  2364. return false;
  2365. };
  2366. }
  2367. uint
  2368. ThreadContext::GetRedeferralCollectionInterval() const
  2369. {
  2370. switch(this->redeferralState)
  2371. {
  2372. case InitialRedeferralState:
  2373. return InitialRedeferralDelay;
  2374. case StartupRedeferralState:
  2375. return StartupRedeferralCheckInterval;
  2376. case MainRedeferralState:
  2377. return MainRedeferralCheckInterval;
  2378. default:
  2379. Assert(0);
  2380. return (uint)-1;
  2381. }
  2382. }
  2383. uint
  2384. ThreadContext::GetRedeferralInactiveThreshold() const
  2385. {
  2386. switch(this->redeferralState)
  2387. {
  2388. case InitialRedeferralState:
  2389. return InitialRedeferralDelay;
  2390. case StartupRedeferralState:
  2391. return StartupRedeferralInactiveThreshold;
  2392. case MainRedeferralState:
  2393. return MainRedeferralInactiveThreshold;
  2394. default:
  2395. Assert(0);
  2396. return (uint)-1;
  2397. }
  2398. }
  2399. void
  2400. ThreadContext::TryRedeferral()
  2401. {
  2402. bool doRedefer = this->DoRedeferFunctionBodies();
  2403. // Collect the set of active functions.
  2404. ActiveFunctionSet *pActiveFuncs = nullptr;
  2405. if (doRedefer)
  2406. {
  2407. pActiveFuncs = Anew(this->GetThreadAlloc(), ActiveFunctionSet, this->GetThreadAlloc());
  2408. this->GetActiveFunctions(pActiveFuncs);
  2409. #if DBG
  2410. this->redeferredFunctions = 0;
  2411. this->recoveredBytes = 0;
  2412. #endif
  2413. }
  2414. uint inactiveThreshold = this->GetRedeferralInactiveThreshold();
  2415. Js::ScriptContext *scriptContext;
  2416. for (scriptContext = GetScriptContextList(); scriptContext; scriptContext = scriptContext->next)
  2417. {
  2418. if (scriptContext->IsClosed())
  2419. {
  2420. continue;
  2421. }
  2422. scriptContext->RedeferFunctionBodies(pActiveFuncs, inactiveThreshold);
  2423. }
  2424. if (pActiveFuncs)
  2425. {
  2426. Adelete(this->GetThreadAlloc(), pActiveFuncs);
  2427. #if DBG
  2428. if (PHASE_STATS1(Js::RedeferralPhase) && this->redeferredFunctions)
  2429. {
  2430. Output::Print(_u("Redeferred: %d, Bytes: 0x%x\n"), this->redeferredFunctions, this->recoveredBytes);
  2431. }
  2432. #endif
  2433. }
  2434. }
  2435. void
  2436. ThreadContext::GetActiveFunctions(ActiveFunctionSet * pActiveFuncs)
  2437. {
  2438. if (!this->IsInScript() || this->entryExitRecord == nullptr)
  2439. {
  2440. return;
  2441. }
  2442. Js::JavascriptStackWalker walker(GetScriptContextList(), TRUE, NULL, true);
  2443. Js::JavascriptFunction *function = nullptr;
  2444. while (walker.GetCallerWithoutInlinedFrames(&function))
  2445. {
  2446. if (function->GetFunctionInfo()->HasBody())
  2447. {
  2448. Js::FunctionBody *body = function->GetFunctionInfo()->GetFunctionBody();
  2449. body->UpdateActiveFunctionSet(pActiveFuncs, nullptr);
  2450. }
  2451. }
  2452. }
  2453. void
  2454. ThreadContext::UpdateRedeferralState()
  2455. {
  2456. uint inactiveThreshold = this->GetRedeferralInactiveThreshold();
  2457. uint collectInterval = this->GetRedeferralCollectionInterval();
  2458. if (this->gcSinceCallCountsCollected >= inactiveThreshold)
  2459. {
  2460. this->gcSinceCallCountsCollected = 0;
  2461. if (this->gcSinceLastRedeferral >= collectInterval)
  2462. {
  2463. // Advance state
  2464. switch (this->redeferralState)
  2465. {
  2466. case InitialRedeferralState:
  2467. this->redeferralState = StartupRedeferralState;
  2468. break;
  2469. case StartupRedeferralState:
  2470. this->redeferralState = MainRedeferralState;
  2471. break;
  2472. case MainRedeferralState:
  2473. break;
  2474. default:
  2475. Assert(0);
  2476. break;
  2477. }
  2478. this->gcSinceLastRedeferral = 0;
  2479. }
  2480. }
  2481. else
  2482. {
  2483. this->gcSinceCallCountsCollected++;
  2484. this->gcSinceLastRedeferral++;
  2485. }
  2486. }
  2487. void
  2488. ThreadContext::PreDisposeObjectsCallBack()
  2489. {
  2490. this->expirableObjectDisposeList->Clear();
  2491. }
  2492. #ifdef FAULT_INJECTION
  2493. void
  2494. ThreadContext::DisposeScriptContextByFaultInjectionCallBack()
  2495. {
  2496. if (FAULTINJECT_SCRIPT_TERMINATION_ON_DISPOSE) {
  2497. int scriptContextToClose = -1;
  2498. /* inject only if we have more than 1 script context*/
  2499. uint totalScriptCount = GetScriptContextCount();
  2500. if (totalScriptCount > 1) {
  2501. if (Js::Configuration::Global.flags.FaultInjectionScriptContextToTerminateCount > 0)
  2502. {
  2503. scriptContextToClose = Js::Configuration::Global.flags.FaultInjectionScriptContextToTerminateCount % totalScriptCount;
  2504. for (Js::ScriptContext *scriptContext = GetScriptContextList(); scriptContext; scriptContext = scriptContext->next)
  2505. {
  2506. if (scriptContextToClose-- == 0)
  2507. {
  2508. scriptContext->DisposeScriptContextByFaultInjection();
  2509. break;
  2510. }
  2511. }
  2512. }
  2513. else
  2514. {
  2515. fwprintf(stderr, _u("***FI: FaultInjectionScriptContextToTerminateCount Failed, Value should be > 0. \n"));
  2516. }
  2517. }
  2518. }
  2519. }
  2520. #endif
  2521. #pragma region "Expirable Object Methods"
  2522. void
  2523. ThreadContext::TryExitExpirableCollectMode()
  2524. {
  2525. // If this feature is turned off or if we're already in profile collection mode, do nothing
  2526. // We also do nothing if expiration is explicitly disabled by someone lower down the stack
  2527. if (PHASE_OFF1(Js::ExpirableCollectPhase) || !InExpirableCollectMode() || this->disableExpiration)
  2528. {
  2529. return;
  2530. }
  2531. if (InExpirableCollectMode())
  2532. {
  2533. OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Checking to see whether to complete Expirable Object Collection: GC Count is %d\n"), this->expirableCollectModeGcCount);
  2534. if (this->expirableCollectModeGcCount > 0)
  2535. {
  2536. this->expirableCollectModeGcCount--;
  2537. }
  2538. if (this->expirableCollectModeGcCount == 0 &&
  2539. (this->recycler->InCacheCleanupCollection() || CONFIG_FLAG(ForceExpireOnNonCacheCollect)))
  2540. {
  2541. OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Completing Expirable Object Collection\n"));
  2542. ExpirableObjectList::Iterator expirableObjectIterator(this->expirableObjectList);
  2543. while (expirableObjectIterator.Next())
  2544. {
  2545. ExpirableObject* object = expirableObjectIterator.Data();
  2546. Assert(object);
  2547. if (!object->IsObjectUsed())
  2548. {
  2549. object->Expire();
  2550. }
  2551. }
  2552. // Leave expirable collection mode
  2553. expirableCollectModeGcCount = -1;
  2554. }
  2555. }
  2556. }
  2557. bool
  2558. ThreadContext::InExpirableCollectMode()
  2559. {
  2560. // We're in expirable collect if we have expirable objects registered,
  2561. // and expirableCollectModeGcCount is not negative
  2562. // and when debugger is attaching, it might have set the function to deferredParse.
  2563. return (expirableObjectList != nullptr &&
  2564. numExpirableObjects > 0 &&
  2565. expirableCollectModeGcCount >= 0
  2566. #ifdef ENABLE_SCRIPT_DEBUGGING
  2567. &&
  2568. (this->GetDebugManager() != nullptr &&
  2569. !this->GetDebugManager()->IsDebuggerAttaching())
  2570. #endif
  2571. );
  2572. }
  2573. void
  2574. ThreadContext::TryEnterExpirableCollectMode()
  2575. {
  2576. // If this feature is turned off or if we're already in profile collection mode, do nothing
  2577. if (PHASE_OFF1(Js::ExpirableCollectPhase) || InExpirableCollectMode())
  2578. {
  2579. OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Not running Expirable Object Collection\n"));
  2580. return;
  2581. }
  2582. double entryPointCollectionThreshold = Js::Configuration::Global.flags.ExpirableCollectionTriggerThreshold / 100.0;
  2583. double currentThreadNativeCodeRatio = ((double) GetCodeSize()) / Js::Constants::MaxThreadJITCodeHeapSize;
  2584. OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Current native code ratio: %f\n"), currentThreadNativeCodeRatio);
  2585. if (currentThreadNativeCodeRatio > entryPointCollectionThreshold)
  2586. {
  2587. OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Setting up Expirable Object Collection\n"));
  2588. this->expirableCollectModeGcCount = Js::Configuration::Global.flags.ExpirableCollectionGCCount;
  2589. ExpirableObjectList::Iterator expirableObjectIterator(this->expirableObjectList);
  2590. while (expirableObjectIterator.Next())
  2591. {
  2592. ExpirableObject* object = expirableObjectIterator.Data();
  2593. Assert(object);
  2594. object->EnterExpirableCollectMode();
  2595. }
  2596. }
  2597. }
  2598. void
  2599. ThreadContext::RegisterExpirableObject(ExpirableObject* object)
  2600. {
  2601. Assert(this->expirableObjectList);
  2602. Assert(object->GetRegistrationHandle() == nullptr);
  2603. ExpirableObject** registrationData = this->expirableObjectList->PrependNode();
  2604. (*registrationData) = object;
  2605. object->SetRegistrationHandle((void*) registrationData);
  2606. OUTPUT_VERBOSE_TRACE(Js::ExpirableCollectPhase, _u("Registered 0x%p\n"), object);
  2607. numExpirableObjects++;
  2608. }
  2609. void
  2610. ThreadContext::UnregisterExpirableObject(ExpirableObject* object)
  2611. {
  2612. Assert(this->expirableObjectList);
  2613. Assert(object->GetRegistrationHandle() != nullptr);
  2614. ExpirableObject** registrationData = (ExpirableObject**)PointerValue(object->GetRegistrationHandle());
  2615. Assert(*registrationData == object);
  2616. this->expirableObjectList->MoveElementTo(registrationData, this->expirableObjectDisposeList);
  2617. object->ClearRegistrationHandle();
  2618. OUTPUT_VERBOSE_TRACE(Js::ExpirableCollectPhase, _u("Unregistered 0x%p\n"), object);
  2619. numExpirableObjects--;
  2620. }
  2621. #pragma endregion
  2622. void
  2623. ThreadContext::ClearScriptContextCaches()
  2624. {
  2625. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext != nullptr; scriptContext = scriptContext->next)
  2626. {
  2627. scriptContext->ClearScriptContextCaches();
  2628. }
  2629. }
  2630. #ifdef PERSISTENT_INLINE_CACHES
  2631. void
  2632. ThreadContext::ClearInlineCachesWithDeadWeakRefs()
  2633. {
  2634. #if ENABLE_DEBUG_CONFIG_OPTIONS || defined(ENABLE_JS_ETW)
  2635. size_t allocatedSize = 0;
  2636. size_t preClearFreeListSize = 0;
  2637. size_t freeListSize = 0;
  2638. size_t polyInlineCacheSize = 0;
  2639. uint scriptContextCount = 0;
  2640. // Note: this event is not meaningful for MemGC, only Chakra
  2641. JS_ETW(EventWriteJSCRIPT_GC_CLEAR_INLINECACHE_START());
  2642. #endif
  2643. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext != nullptr; scriptContext = scriptContext->next)
  2644. {
  2645. #if ENABLE_DEBUG_CONFIG_OPTIONS || defined(ENABLE_JS_ETW)
  2646. scriptContextCount++;
  2647. allocatedSize += scriptContext->GetInlineCacheAllocator()->AllocatedSize();
  2648. preClearFreeListSize += scriptContext->GetInlineCacheAllocator()->FreeListSize();
  2649. #endif
  2650. scriptContext->ClearInlineCachesWithDeadWeakRefs();
  2651. #if ENABLE_DEBUG_CONFIG_OPTIONS || defined(ENABLE_JS_ETW)
  2652. freeListSize += scriptContext->GetInlineCacheAllocator()->FreeListSize();;
  2653. polyInlineCacheSize += scriptContext->GetInlineCacheAllocator()->GetPolyInlineCacheSize();
  2654. #endif
  2655. }
  2656. JS_ETW(EventWriteJSCRIPT_GC_CLEAR_INLINECACHE_STOP(this, scriptContextCount, (uint) allocatedSize, (uint) preClearFreeListSize, (uint) freeListSize, (uint) polyInlineCacheSize));
  2657. #if ENABLE_DEBUG_CONFIG_OPTIONS
  2658. if (PHASE_TRACE1(Js::InlineCachePhase))
  2659. {
  2660. Output::Print(_u("Inline cache arena: total = %5I64u KB, free list = %5I64u KB, poly caches = %5I64u KB, script contexts = %u\n"),
  2661. static_cast<uint64>(allocatedSize / 1024), static_cast<uint64>(freeListSize / 1024), static_cast<uint64>(polyInlineCacheSize / 1024), scriptContextCount);
  2662. }
  2663. #endif
  2664. }
  2665. #if ENABLE_NATIVE_CODEGEN
  2666. void
  2667. ThreadContext::ClearInvalidatedUniqueGuards()
  2668. {
  2669. // If a propertyGuard was invalidated, make sure to remove it's entry from unique property guard table of other property records.
  2670. PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
  2671. guards.Map([this](Js::PropertyRecord const * propertyRecord, PropertyGuardEntry* entry, const RecyclerWeakReference<const Js::PropertyRecord>* weakRef)
  2672. {
  2673. entry->uniqueGuards.MapAndRemoveIf([=](RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
  2674. {
  2675. Js::PropertyGuard* guard = guardWeakRef->Get();
  2676. bool shouldRemove = guard != nullptr && !guard->IsValid();
  2677. if (shouldRemove)
  2678. {
  2679. if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
  2680. {
  2681. Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
  2682. propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
  2683. Output::Flush();
  2684. }
  2685. if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
  2686. {
  2687. Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"),
  2688. propertyRecord->GetBuffer(), guard->GetValue());
  2689. Output::Flush();
  2690. }
  2691. }
  2692. return shouldRemove;
  2693. });
  2694. });
  2695. }
  2696. #endif
  2697. void
  2698. ThreadContext::ClearInlineCaches()
  2699. {
  2700. if (PHASE_TRACE1(Js::InlineCachePhase))
  2701. {
  2702. size_t size = 0;
  2703. size_t freeListSize = 0;
  2704. size_t polyInlineCacheSize = 0;
  2705. uint scriptContextCount = 0;
  2706. for (Js::ScriptContext *scriptContext = scriptContextList;
  2707. scriptContext;
  2708. scriptContext = scriptContext->next)
  2709. {
  2710. scriptContextCount++;
  2711. size += scriptContext->GetInlineCacheAllocator()->AllocatedSize();
  2712. freeListSize += scriptContext->GetInlineCacheAllocator()->FreeListSize();
  2713. #ifdef POLY_INLINE_CACHE_SIZE_STATS
  2714. polyInlineCacheSize += scriptContext->GetInlineCacheAllocator()->GetPolyInlineCacheSize();
  2715. #endif
  2716. };
  2717. Output::Print(_u("Inline cache arena: total = %5I64u KB, free list = %5I64u KB, poly caches = %5I64u KB, script contexts = %u\n"),
  2718. static_cast<uint64>(size / 1024), static_cast<uint64>(freeListSize / 1024), static_cast<uint64>(polyInlineCacheSize / 1024), scriptContextCount);
  2719. }
  2720. Js::ScriptContext *scriptContext = this->scriptContextList;
  2721. while (scriptContext != nullptr)
  2722. {
  2723. scriptContext->ClearInlineCaches();
  2724. scriptContext = scriptContext->next;
  2725. }
  2726. inlineCacheThreadInfoAllocator.Reset();
  2727. protoInlineCacheByPropId.ResetNoDelete();
  2728. storeFieldInlineCacheByPropId.ResetNoDelete();
  2729. registeredInlineCacheCount = 0;
  2730. unregisteredInlineCacheCount = 0;
  2731. }
  2732. #endif //PERSISTENT_INLINE_CACHES
  2733. void
  2734. ThreadContext::ClearIsInstInlineCaches()
  2735. {
  2736. Js::ScriptContext *scriptContext = this->scriptContextList;
  2737. while (scriptContext != nullptr)
  2738. {
  2739. scriptContext->ClearIsInstInlineCaches();
  2740. scriptContext = scriptContext->next;
  2741. }
  2742. isInstInlineCacheThreadInfoAllocator.Reset();
  2743. isInstInlineCacheByFunction.ResetNoDelete();
  2744. }
  2745. void
  2746. ThreadContext::ClearEnumeratorCaches()
  2747. {
  2748. Js::ScriptContext *scriptContext = this->scriptContextList;
  2749. while (scriptContext != nullptr)
  2750. {
  2751. scriptContext->ClearEnumeratorCaches();
  2752. scriptContext = scriptContext->next;
  2753. }
  2754. }
  2755. void
  2756. ThreadContext::ClearEquivalentTypeCaches()
  2757. {
  2758. #if ENABLE_NATIVE_CODEGEN
  2759. // Called from PreSweepCallback to clear pointers to types that have no live object references left.
  2760. // The EquivalentTypeCache used to keep these types alive, but this caused memory growth in cases where
  2761. // entry points stayed around for a long time.
  2762. // In future we may want to pin the reference/guard type to the entry point, but that choice will depend
  2763. // on a use case where pinning the type helps us optimize. Lacking that, clearing the guard type is a
  2764. // simpler short-term solution.
  2765. // Note that clearing unmarked types from the cache and guard is needed for correctness if the cache doesn't keep
  2766. // the types alive.
  2767. FOREACH_DLISTBASE_ENTRY_EDITING(Js::EntryPointInfo *, entryPoint, &equivalentTypeCacheEntryPoints, iter)
  2768. {
  2769. bool isLive = entryPoint->ClearEquivalentTypeCaches();
  2770. if (!isLive)
  2771. {
  2772. iter.RemoveCurrent(&equivalentTypeCacheInfoAllocator);
  2773. }
  2774. }
  2775. NEXT_DLISTBASE_ENTRY_EDITING;
  2776. // Note: Don't reset the list, because we're only clearing the dead types from these caches.
  2777. // There may still be type references we need to keep an eye on.
  2778. #endif
  2779. }
  2780. Js::EntryPointInfo **
  2781. ThreadContext::RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint)
  2782. {
  2783. return equivalentTypeCacheEntryPoints.PrependNode(&equivalentTypeCacheInfoAllocator, entryPoint);
  2784. }
  2785. void
  2786. ThreadContext::UnregisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo ** entryPoint)
  2787. {
  2788. equivalentTypeCacheEntryPoints.RemoveElement(&equivalentTypeCacheInfoAllocator, entryPoint);
  2789. }
  2790. void
  2791. ThreadContext::RegisterProtoInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId)
  2792. {
  2793. if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  2794. {
  2795. Output::Print(_u("InlineCacheInvalidation: registering proto cache 0x%p for property %s(%u)\n"),
  2796. inlineCache, GetPropertyName(propertyId)->GetBuffer(), propertyId);
  2797. Output::Flush();
  2798. }
  2799. RegisterInlineCache(protoInlineCacheByPropId, inlineCache, propertyId);
  2800. }
  2801. void
  2802. ThreadContext::RegisterStoreFieldInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId)
  2803. {
  2804. if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  2805. {
  2806. Output::Print(_u("InlineCacheInvalidation: registering store field cache 0x%p for property %s(%u)\n"),
  2807. inlineCache, GetPropertyName(propertyId)->GetBuffer(), propertyId);
  2808. Output::Flush();
  2809. }
  2810. RegisterInlineCache(storeFieldInlineCacheByPropId, inlineCache, propertyId);
  2811. }
  2812. void
  2813. ThreadContext::RegisterInlineCache(InlineCacheListMapByPropertyId& inlineCacheMap, Js::InlineCache * inlineCache, Js::PropertyId propertyId)
  2814. {
  2815. InlineCacheList* inlineCacheList;
  2816. if (!inlineCacheMap.TryGetValue(propertyId, &inlineCacheList))
  2817. {
  2818. inlineCacheList = Anew(&this->inlineCacheThreadInfoAllocator, InlineCacheList, &this->inlineCacheThreadInfoAllocator);
  2819. inlineCacheMap.AddNew(propertyId, inlineCacheList);
  2820. }
  2821. Js::InlineCache** inlineCacheRef = inlineCacheList->PrependNode();
  2822. Assert(inlineCacheRef != nullptr);
  2823. *inlineCacheRef = inlineCache;
  2824. inlineCache->invalidationListSlotPtr = inlineCacheRef;
  2825. this->registeredInlineCacheCount++;
  2826. }
  2827. void ThreadContext::NotifyInlineCacheBatchUnregistered(uint count)
  2828. {
  2829. this->unregisteredInlineCacheCount += count;
  2830. // Negative or 0 InlineCacheInvalidationListCompactionThreshold forces compaction all the time.
  2831. if (CONFIG_FLAG(InlineCacheInvalidationListCompactionThreshold) <= 0 ||
  2832. this->registeredInlineCacheCount / this->unregisteredInlineCacheCount < (uint)CONFIG_FLAG(InlineCacheInvalidationListCompactionThreshold))
  2833. {
  2834. CompactInlineCacheInvalidationLists();
  2835. }
  2836. }
  2837. void
  2838. ThreadContext::InvalidateProtoInlineCaches(Js::PropertyId propertyId)
  2839. {
  2840. InlineCacheList* inlineCacheList;
  2841. if (protoInlineCacheByPropId.TryGetValueAndRemove(propertyId, &inlineCacheList))
  2842. {
  2843. if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  2844. {
  2845. Output::Print(_u("InlineCacheInvalidation: invalidating proto caches for property %s(%u)\n"),
  2846. GetPropertyName(propertyId)->GetBuffer(), propertyId);
  2847. Output::Flush();
  2848. }
  2849. InvalidateAndDeleteInlineCacheList(inlineCacheList);
  2850. }
  2851. }
  2852. void
  2853. ThreadContext::InvalidateStoreFieldInlineCaches(Js::PropertyId propertyId)
  2854. {
  2855. InlineCacheList* inlineCacheList;
  2856. if (storeFieldInlineCacheByPropId.TryGetValueAndRemove(propertyId, &inlineCacheList))
  2857. {
  2858. if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  2859. {
  2860. Output::Print(_u("InlineCacheInvalidation: invalidating store field caches for property %s(%u)\n"),
  2861. GetPropertyName(propertyId)->GetBuffer(), propertyId);
  2862. Output::Flush();
  2863. }
  2864. InvalidateAndDeleteInlineCacheList(inlineCacheList);
  2865. }
  2866. }
  2867. void
  2868. ThreadContext::InvalidateAndDeleteInlineCacheList(InlineCacheList* inlineCacheList)
  2869. {
  2870. Assert(inlineCacheList != nullptr);
  2871. uint cacheCount = 0;
  2872. uint nullCacheCount = 0;
  2873. FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
  2874. {
  2875. cacheCount++;
  2876. if (inlineCache != nullptr)
  2877. {
  2878. if (PHASE_VERBOSE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  2879. {
  2880. Output::Print(_u("InlineCacheInvalidation: invalidating cache 0x%p\n"), inlineCache);
  2881. Output::Flush();
  2882. }
  2883. memset(inlineCache, 0, sizeof(Js::InlineCache));
  2884. }
  2885. else
  2886. {
  2887. nullCacheCount++;
  2888. }
  2889. }
  2890. NEXT_SLISTBASE_ENTRY;
  2891. Adelete(&this->inlineCacheThreadInfoAllocator, inlineCacheList);
  2892. this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ? this->registeredInlineCacheCount - cacheCount : 0;
  2893. this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > nullCacheCount ? this->unregisteredInlineCacheCount - nullCacheCount : 0;
  2894. }
  2895. void
  2896. ThreadContext::CompactInlineCacheInvalidationLists()
  2897. {
  2898. #if DBG
  2899. uint countOfNodesToCompact = this->unregisteredInlineCacheCount;
  2900. this->totalUnregisteredCacheCount = 0;
  2901. #endif
  2902. Assert(this->unregisteredInlineCacheCount > 0);
  2903. CompactProtoInlineCaches();
  2904. if (this->unregisteredInlineCacheCount > 0)
  2905. {
  2906. CompactStoreFieldInlineCaches();
  2907. }
  2908. Assert(countOfNodesToCompact == this->totalUnregisteredCacheCount);
  2909. }
  2910. void
  2911. ThreadContext::CompactProtoInlineCaches()
  2912. {
  2913. protoInlineCacheByPropId.MapUntil([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  2914. {
  2915. CompactInlineCacheList(inlineCacheList);
  2916. return this->unregisteredInlineCacheCount == 0;
  2917. });
  2918. }
  2919. void
  2920. ThreadContext::CompactStoreFieldInlineCaches()
  2921. {
  2922. storeFieldInlineCacheByPropId.MapUntil([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  2923. {
  2924. CompactInlineCacheList(inlineCacheList);
  2925. return this->unregisteredInlineCacheCount == 0;
  2926. });
  2927. }
  2928. void
  2929. ThreadContext::CompactInlineCacheList(InlineCacheList* inlineCacheList)
  2930. {
  2931. Assert(inlineCacheList != nullptr);
  2932. uint cacheCount = 0;
  2933. FOREACH_SLISTBASE_ENTRY_EDITING(Js::InlineCache*, inlineCache, inlineCacheList, iterator)
  2934. {
  2935. if (inlineCache == nullptr)
  2936. {
  2937. iterator.RemoveCurrent();
  2938. cacheCount++;
  2939. }
  2940. }
  2941. NEXT_SLISTBASE_ENTRY_EDITING;
  2942. #if DBG
  2943. this->totalUnregisteredCacheCount += cacheCount;
  2944. #endif
  2945. if (cacheCount > 0)
  2946. {
  2947. AssertMsg(this->unregisteredInlineCacheCount >= cacheCount, "Some codepaths didn't unregistered the inlineCaches which might leak memory.");
  2948. this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > cacheCount ?
  2949. this->unregisteredInlineCacheCount - cacheCount : 0;
  2950. AssertMsg(this->registeredInlineCacheCount >= cacheCount, "Some codepaths didn't registered the inlineCaches which might leak memory.");
  2951. this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ?
  2952. this->registeredInlineCacheCount - cacheCount : 0;
  2953. }
  2954. }
  2955. #if DBG
  2956. bool
  2957. ThreadContext::IsProtoInlineCacheRegistered(const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
  2958. {
  2959. return IsInlineCacheRegistered(protoInlineCacheByPropId, inlineCache, propertyId);
  2960. }
  2961. bool
  2962. ThreadContext::IsStoreFieldInlineCacheRegistered(const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
  2963. {
  2964. return IsInlineCacheRegistered(storeFieldInlineCacheByPropId, inlineCache, propertyId);
  2965. }
  2966. bool
  2967. ThreadContext::IsInlineCacheRegistered(InlineCacheListMapByPropertyId& inlineCacheMap, const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
  2968. {
  2969. InlineCacheList* inlineCacheList;
  2970. if (inlineCacheMap.TryGetValue(propertyId, &inlineCacheList))
  2971. {
  2972. return IsInlineCacheInList(inlineCache, inlineCacheList);
  2973. }
  2974. else
  2975. {
  2976. return false;
  2977. }
  2978. }
  2979. bool
  2980. ThreadContext::IsInlineCacheInList(const Js::InlineCache* inlineCache, const InlineCacheList* inlineCacheList)
  2981. {
  2982. Assert(inlineCache != nullptr);
  2983. Assert(inlineCacheList != nullptr);
  2984. FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, curInlineCache, inlineCacheList)
  2985. {
  2986. if (curInlineCache == inlineCache)
  2987. {
  2988. return true;
  2989. }
  2990. }
  2991. NEXT_SLISTBASE_ENTRY;
  2992. return false;
  2993. }
  2994. #endif
  2995. #if ENABLE_NATIVE_CODEGEN
  2996. ThreadContext::PropertyGuardEntry*
  2997. ThreadContext::EnsurePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, bool& foundExistingEntry)
  2998. {
  2999. PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
  3000. PropertyGuardEntry* entry = nullptr;
  3001. foundExistingEntry = guards.TryGetValue(propertyRecord, &entry);
  3002. if (!foundExistingEntry)
  3003. {
  3004. entry = RecyclerNew(GetRecycler(), PropertyGuardEntry, GetRecycler());
  3005. guards.UncheckedAdd(CreatePropertyRecordWeakRef(propertyRecord), entry);
  3006. }
  3007. return entry;
  3008. }
  3009. Js::PropertyGuard*
  3010. ThreadContext::RegisterSharedPropertyGuard(Js::PropertyId propertyId)
  3011. {
  3012. Assert(IsActivePropertyId(propertyId));
  3013. const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
  3014. bool foundExistingGuard;
  3015. PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
  3016. if (entry->sharedGuard == nullptr)
  3017. {
  3018. entry->sharedGuard = Js::PropertyGuard::New(GetRecycler());
  3019. }
  3020. Js::PropertyGuard* guard = entry->sharedGuard;
  3021. PHASE_PRINT_VERBOSE_TRACE1(Js::FixedMethodsPhase, _u("FixedFields: registered shared guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p, %s\n"),
  3022. propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue(), foundExistingGuard ? _u("existing") : _u("new"));
  3023. PHASE_PRINT_TESTTRACE1(Js::FixedMethodsPhase, _u("FixedFields: registered shared guard: name: %s, value: 0x%p, %s\n"),
  3024. propertyRecord->GetBuffer(), guard->GetValue(), foundExistingGuard ? _u("existing") : _u("new"));
  3025. return guard;
  3026. }
  3027. void
  3028. ThreadContext::RegisterLazyBailout(Js::PropertyId propertyId, Js::EntryPointInfo* entryPoint)
  3029. {
  3030. const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
  3031. bool foundExistingGuard;
  3032. PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
  3033. if (!entry->entryPoints)
  3034. {
  3035. entry->entryPoints = RecyclerNew(recycler, PropertyGuardEntry::EntryPointDictionary, recycler, /*capacity*/ 3);
  3036. }
  3037. entry->entryPoints->UncheckedAdd(entryPoint, NULL);
  3038. }
  3039. void
  3040. ThreadContext::RegisterUniquePropertyGuard(Js::PropertyId propertyId, Js::PropertyGuard* guard)
  3041. {
  3042. Assert(IsActivePropertyId(propertyId));
  3043. Assert(guard != nullptr);
  3044. RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef = this->recycler->CreateWeakReferenceHandle(guard);
  3045. RegisterUniquePropertyGuard(propertyId, guardWeakRef);
  3046. }
  3047. void
  3048. ThreadContext::RegisterUniquePropertyGuard(Js::PropertyId propertyId, RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
  3049. {
  3050. Assert(IsActivePropertyId(propertyId));
  3051. Assert(guardWeakRef != nullptr);
  3052. Js::PropertyGuard* guard = guardWeakRef->Get();
  3053. Assert(guard != nullptr);
  3054. const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
  3055. bool foundExistingGuard;
  3056. PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
  3057. entry->uniqueGuards.Item(guardWeakRef);
  3058. if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
  3059. {
  3060. Output::Print(_u("FixedFields: registered unique guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p, %s entry\n"),
  3061. propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue(), foundExistingGuard ? _u("existing") : _u("new"));
  3062. Output::Flush();
  3063. }
  3064. if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
  3065. {
  3066. Output::Print(_u("FixedFields: registered unique guard: name: %s, value: 0x%p, %s entry\n"),
  3067. propertyRecord->GetBuffer(), guard->GetValue(), foundExistingGuard ? _u("existing") : _u("new"));
  3068. Output::Flush();
  3069. }
  3070. }
  3071. void
  3072. ThreadContext::RegisterConstructorCache(Js::PropertyId propertyId, Js::ConstructorCache* cache)
  3073. {
  3074. Assert(Js::ConstructorCache::GetOffsetOfGuardValue() == Js::PropertyGuard::GetOffsetOfValue());
  3075. Assert(Js::ConstructorCache::GetSizeOfGuardValue() == Js::PropertyGuard::GetSizeOfValue());
  3076. RegisterUniquePropertyGuard(propertyId, reinterpret_cast<Js::PropertyGuard*>(cache));
  3077. }
  3078. void
  3079. ThreadContext::InvalidatePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, PropertyGuardEntry* entry, bool isAllPropertyGuardsInvalidation)
  3080. {
  3081. Assert(entry != nullptr);
  3082. if (entry->sharedGuard != nullptr)
  3083. {
  3084. Js::PropertyGuard* guard = entry->sharedGuard;
  3085. if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
  3086. {
  3087. Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
  3088. propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
  3089. Output::Flush();
  3090. }
  3091. if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
  3092. {
  3093. Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"), propertyRecord->GetBuffer(), guard->GetValue());
  3094. Output::Flush();
  3095. }
  3096. guard->Invalidate();
  3097. }
  3098. uint count = 0;
  3099. entry->uniqueGuards.Map([&count, propertyRecord](RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
  3100. {
  3101. Js::PropertyGuard* guard = guardWeakRef->Get();
  3102. if (guard != nullptr)
  3103. {
  3104. if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
  3105. {
  3106. Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
  3107. propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
  3108. Output::Flush();
  3109. }
  3110. if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
  3111. {
  3112. Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"),
  3113. propertyRecord->GetBuffer(), guard->GetValue());
  3114. Output::Flush();
  3115. }
  3116. guard->Invalidate();
  3117. count++;
  3118. }
  3119. });
  3120. entry->uniqueGuards.Clear();
  3121. // Count no. of invalidations done so far. Exclude if this is all property guards invalidation in which case
  3122. // the unique Guards will be cleared anyway.
  3123. if (!isAllPropertyGuardsInvalidation)
  3124. {
  3125. this->recyclableData->constructorCacheInvalidationCount += count;
  3126. if (this->recyclableData->constructorCacheInvalidationCount > (uint)CONFIG_FLAG(ConstructorCacheInvalidationThreshold))
  3127. {
  3128. // TODO: In future, we should compact the uniqueGuards dictionary so this function can be called from PreCollectionCallback
  3129. // instead
  3130. this->ClearInvalidatedUniqueGuards();
  3131. this->recyclableData->constructorCacheInvalidationCount = 0;
  3132. }
  3133. }
  3134. if (entry->entryPoints && entry->entryPoints->Count() > 0)
  3135. {
  3136. Js::JavascriptStackWalker stackWalker(this->GetScriptContextList());
  3137. Js::JavascriptFunction* caller = nullptr;
  3138. while (stackWalker.GetCaller(&caller, /*includeInlineFrames*/ false))
  3139. {
  3140. // If the current frame is already from a bailout - we do not need to do on stack invalidation
  3141. if (caller != nullptr && Js::ScriptFunction::Test(caller) && !stackWalker.GetCurrentFrameFromBailout())
  3142. {
  3143. BYTE dummy;
  3144. Js::FunctionEntryPointInfo* functionEntryPoint = caller->GetFunctionBody()->GetDefaultFunctionEntryPointInfo();
  3145. if (functionEntryPoint->IsInNativeAddressRange((DWORD_PTR)stackWalker.GetInstructionPointer()))
  3146. {
  3147. if (entry->entryPoints->TryGetValue(functionEntryPoint, &dummy))
  3148. {
  3149. functionEntryPoint->DoLazyBailout(
  3150. stackWalker.GetCurrentAddressOfInstructionPointer(),
  3151. static_cast<BYTE*>(stackWalker.GetFramePointer())
  3152. #if DBG
  3153. , caller->GetFunctionBody()
  3154. , propertyRecord
  3155. #endif
  3156. );
  3157. }
  3158. }
  3159. }
  3160. }
  3161. entry->entryPoints->Map([=](Js::EntryPointInfo* info, BYTE& dummy, const RecyclerWeakReference<Js::EntryPointInfo>* infoWeakRef)
  3162. {
  3163. OUTPUT_TRACE2(Js::LazyBailoutPhase, info->GetFunctionBody(), _u("Lazy bailout - Invalidation due to property: %s \n"), propertyRecord->GetBuffer());
  3164. info->Invalidate(true);
  3165. });
  3166. entry->entryPoints->Clear();
  3167. }
  3168. }
  3169. void
  3170. ThreadContext::InvalidatePropertyGuards(Js::PropertyId propertyId)
  3171. {
  3172. const Js::PropertyRecord* propertyRecord = GetPropertyName(propertyId);
  3173. PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
  3174. PropertyGuardEntry* entry = nullptr;
  3175. if (guards.TryGetValueAndRemove(propertyRecord, &entry))
  3176. {
  3177. InvalidatePropertyGuardEntry(propertyRecord, entry, false);
  3178. }
  3179. }
  3180. void
  3181. ThreadContext::InvalidateAllPropertyGuards()
  3182. {
  3183. PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
  3184. if (guards.Count() > 0)
  3185. {
  3186. guards.Map([this](Js::PropertyRecord const * propertyRecord, PropertyGuardEntry* entry, const RecyclerWeakReference<const Js::PropertyRecord>* weakRef)
  3187. {
  3188. InvalidatePropertyGuardEntry(propertyRecord, entry, true);
  3189. });
  3190. guards.Clear();
  3191. }
  3192. }
  3193. #endif
  3194. void
  3195. ThreadContext::InvalidateAllProtoInlineCaches()
  3196. {
  3197. protoInlineCacheByPropId.Map([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  3198. {
  3199. InvalidateAndDeleteInlineCacheList(inlineCacheList);
  3200. });
  3201. protoInlineCacheByPropId.Reset();
  3202. }
  3203. #if DBG
  3204. // Verifies if object is registered in any proto InlineCache
  3205. bool
  3206. ThreadContext::IsObjectRegisteredInProtoInlineCaches(Js::DynamicObject * object)
  3207. {
  3208. return protoInlineCacheByPropId.MapUntil([object](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  3209. {
  3210. FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
  3211. {
  3212. if (inlineCache != nullptr && !inlineCache->IsEmpty())
  3213. {
  3214. // Verify this object is not present in prototype chain of inlineCache's type
  3215. bool isObjectPresentOnPrototypeChain =
  3216. Js::JavascriptOperators::MapObjectAndPrototypesUntil<true>(inlineCache->GetType()->GetPrototype(), [=](Js::RecyclableObject* prototype)
  3217. {
  3218. return prototype == object;
  3219. });
  3220. if (isObjectPresentOnPrototypeChain) {
  3221. return true;
  3222. }
  3223. }
  3224. }
  3225. NEXT_SLISTBASE_ENTRY;
  3226. return false;
  3227. });
  3228. }
  3229. // Verifies if object is registered in any storeField InlineCache
  3230. bool
  3231. ThreadContext::IsObjectRegisteredInStoreFieldInlineCaches(Js::DynamicObject * object)
  3232. {
  3233. return storeFieldInlineCacheByPropId.MapUntil([object](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  3234. {
  3235. FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
  3236. {
  3237. if (inlineCache != nullptr && !inlineCache->IsEmpty())
  3238. {
  3239. // Verify this object is not present in prototype chain of inlineCache's type
  3240. bool isObjectPresentOnPrototypeChain =
  3241. Js::JavascriptOperators::MapObjectAndPrototypesUntil<true>(inlineCache->GetType()->GetPrototype(), [=](Js::RecyclableObject* prototype)
  3242. {
  3243. return prototype == object;
  3244. });
  3245. if (isObjectPresentOnPrototypeChain) {
  3246. return true;
  3247. }
  3248. }
  3249. }
  3250. NEXT_SLISTBASE_ENTRY;
  3251. return false;
  3252. });
  3253. }
  3254. #endif
  3255. bool
  3256. ThreadContext::AreAllProtoInlineCachesInvalidated()
  3257. {
  3258. return protoInlineCacheByPropId.Count() == 0;
  3259. }
  3260. void
  3261. ThreadContext::InvalidateAllStoreFieldInlineCaches()
  3262. {
  3263. storeFieldInlineCacheByPropId.Map([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
  3264. {
  3265. InvalidateAndDeleteInlineCacheList(inlineCacheList);
  3266. });
  3267. storeFieldInlineCacheByPropId.Reset();
  3268. }
  3269. bool
  3270. ThreadContext::AreAllStoreFieldInlineCachesInvalidated()
  3271. {
  3272. return storeFieldInlineCacheByPropId.Count() == 0;
  3273. }
  3274. #if DBG
  3275. bool
  3276. ThreadContext::IsIsInstInlineCacheRegistered(Js::IsInstInlineCache * inlineCache, Js::Var function)
  3277. {
  3278. Assert(inlineCache != nullptr);
  3279. Assert(function != nullptr);
  3280. Js::IsInstInlineCache* firstInlineCache;
  3281. if (this->isInstInlineCacheByFunction.TryGetValue(function, &firstInlineCache))
  3282. {
  3283. return IsIsInstInlineCacheInList(inlineCache, firstInlineCache);
  3284. }
  3285. else
  3286. {
  3287. return false;
  3288. }
  3289. }
  3290. #endif
  3291. void
  3292. ThreadContext::RegisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function)
  3293. {
  3294. Assert(function != nullptr);
  3295. Assert(inlineCache != nullptr);
  3296. // We should never cross-register or re-register a cache that is already on some invalidation list (for its function or some other function).
  3297. // Every cache must be first cleared and unregistered before being registered again.
  3298. AssertMsg(inlineCache->function == nullptr, "We should only register instance-of caches that have not yet been populated.");
  3299. Js::IsInstInlineCache** inlineCacheRef = nullptr;
  3300. if (this->isInstInlineCacheByFunction.TryGetReference(function, &inlineCacheRef))
  3301. {
  3302. AssertMsg(!IsIsInstInlineCacheInList(inlineCache, *inlineCacheRef), "Why are we registering a cache that is already registered?");
  3303. inlineCache->next = *inlineCacheRef;
  3304. *inlineCacheRef = inlineCache;
  3305. }
  3306. else
  3307. {
  3308. inlineCache->next = nullptr;
  3309. this->isInstInlineCacheByFunction.Add(function, inlineCache);
  3310. }
  3311. }
  3312. void
  3313. ThreadContext::UnregisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function)
  3314. {
  3315. Assert(inlineCache != nullptr);
  3316. Js::IsInstInlineCache** inlineCacheRef = nullptr;
  3317. if (this->isInstInlineCacheByFunction.TryGetReference(function, &inlineCacheRef))
  3318. {
  3319. Assert(*inlineCacheRef != nullptr);
  3320. if (inlineCache == *inlineCacheRef)
  3321. {
  3322. *inlineCacheRef = (*inlineCacheRef)->next;
  3323. if (*inlineCacheRef == nullptr)
  3324. {
  3325. this->isInstInlineCacheByFunction.Remove(function);
  3326. }
  3327. }
  3328. else
  3329. {
  3330. Js::IsInstInlineCache * prevInlineCache;
  3331. Js::IsInstInlineCache * curInlineCache;
  3332. for (prevInlineCache = *inlineCacheRef, curInlineCache = (*inlineCacheRef)->next; curInlineCache != nullptr;
  3333. prevInlineCache = curInlineCache, curInlineCache = curInlineCache->next)
  3334. {
  3335. if (curInlineCache == inlineCache)
  3336. {
  3337. prevInlineCache->next = curInlineCache->next;
  3338. return;
  3339. }
  3340. }
  3341. AssertMsg(false, "Why are we unregistering a cache that is not registered?");
  3342. }
  3343. }
  3344. }
  3345. void
  3346. ThreadContext::InvalidateIsInstInlineCacheList(Js::IsInstInlineCache* inlineCacheList)
  3347. {
  3348. Assert(inlineCacheList != nullptr);
  3349. Js::IsInstInlineCache* curInlineCache;
  3350. Js::IsInstInlineCache* nextInlineCache;
  3351. for (curInlineCache = inlineCacheList; curInlineCache != nullptr; curInlineCache = nextInlineCache)
  3352. {
  3353. if (PHASE_VERBOSE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
  3354. {
  3355. Output::Print(_u("InlineCacheInvalidation: invalidating instanceof cache 0x%p\n"), curInlineCache);
  3356. Output::Flush();
  3357. }
  3358. // Stash away the next cache before we zero out the current one (including its next pointer).
  3359. nextInlineCache = curInlineCache->next;
  3360. // Clear the current cache to invalidate it.
  3361. memset(curInlineCache, 0, sizeof(Js::IsInstInlineCache));
  3362. }
  3363. }
  3364. void
  3365. ThreadContext::InvalidateIsInstInlineCachesForFunction(Js::Var function)
  3366. {
  3367. Js::IsInstInlineCache* inlineCacheList;
  3368. if (this->isInstInlineCacheByFunction.TryGetValueAndRemove(function, &inlineCacheList))
  3369. {
  3370. InvalidateIsInstInlineCacheList(inlineCacheList);
  3371. }
  3372. }
  3373. void
  3374. ThreadContext::InvalidateAllIsInstInlineCaches()
  3375. {
  3376. isInstInlineCacheByFunction.Map([this](const Js::Var function, Js::IsInstInlineCache* inlineCacheList)
  3377. {
  3378. InvalidateIsInstInlineCacheList(inlineCacheList);
  3379. });
  3380. isInstInlineCacheByFunction.Clear();
  3381. }
  3382. bool
  3383. ThreadContext::AreAllIsInstInlineCachesInvalidated() const
  3384. {
  3385. return isInstInlineCacheByFunction.Count() == 0;
  3386. }
  3387. #if DBG
  3388. bool
  3389. ThreadContext::IsIsInstInlineCacheInList(const Js::IsInstInlineCache* inlineCache, const Js::IsInstInlineCache* inlineCacheList)
  3390. {
  3391. Assert(inlineCache != nullptr);
  3392. Assert(inlineCacheList != nullptr);
  3393. for (const Js::IsInstInlineCache* curInlineCache = inlineCacheList; curInlineCache != nullptr; curInlineCache = curInlineCache->next)
  3394. {
  3395. if (curInlineCache == inlineCache)
  3396. {
  3397. return true;
  3398. }
  3399. }
  3400. return false;
  3401. }
  3402. #endif
  3403. void ThreadContext::RegisterTypeWithProtoPropertyCache(const Js::PropertyId propertyId, Js::Type *const type)
  3404. {
  3405. Assert(propertyId != Js::Constants::NoProperty);
  3406. Assert(IsActivePropertyId(propertyId));
  3407. Assert(type);
  3408. PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
  3409. TypeHashSet *typeHashSet = nullptr;
  3410. if(!typesWithProtoPropertyCache.TryGetValue(propertyId, &typeHashSet))
  3411. {
  3412. typeHashSet = RecyclerNew(recycler, TypeHashSet, recycler);
  3413. typesWithProtoPropertyCache.Add(propertyId, typeHashSet);
  3414. }
  3415. typeHashSet->Item(type, false);
  3416. }
  3417. void ThreadContext::InvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId)
  3418. {
  3419. Assert(propertyId != Js::Constants::NoProperty);
  3420. Assert(IsActivePropertyId(propertyId));
  3421. InternalInvalidateProtoTypePropertyCaches(propertyId);
  3422. }
  3423. void ThreadContext::InternalInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId)
  3424. {
  3425. // Get the hash set of registered types associated with the property ID, invalidate each type in the hash set, and
  3426. // remove the property ID and its hash set from the map
  3427. PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
  3428. TypeHashSet *typeHashSet = nullptr;
  3429. if(typesWithProtoPropertyCache.Count() != 0 && typesWithProtoPropertyCache.TryGetValueAndRemove(propertyId, &typeHashSet))
  3430. {
  3431. DoInvalidateProtoTypePropertyCaches(propertyId, typeHashSet);
  3432. }
  3433. }
  3434. void ThreadContext::InvalidateAllProtoTypePropertyCaches()
  3435. {
  3436. PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
  3437. if (typesWithProtoPropertyCache.Count() > 0)
  3438. {
  3439. typesWithProtoPropertyCache.Map([this](Js::PropertyId propertyId, TypeHashSet * typeHashSet)
  3440. {
  3441. DoInvalidateProtoTypePropertyCaches(propertyId, typeHashSet);
  3442. });
  3443. typesWithProtoPropertyCache.Clear();
  3444. }
  3445. }
  3446. void ThreadContext::DoInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId, TypeHashSet *const typeHashSet)
  3447. {
  3448. Assert(propertyId != Js::Constants::NoProperty);
  3449. Assert(typeHashSet);
  3450. typeHashSet->Map(
  3451. [propertyId](Js::Type *const type, const bool unused, const RecyclerWeakReference<Js::Type>*)
  3452. {
  3453. type->GetPropertyCache()->ClearIfPropertyIsOnAPrototype(propertyId);
  3454. });
  3455. }
  3456. BOOL ThreadContext::HasPreviousHostScriptContext()
  3457. {
  3458. return hostScriptContextStack->Count() != 0;
  3459. }
  3460. HostScriptContext* ThreadContext::GetPreviousHostScriptContext()
  3461. {
  3462. return hostScriptContextStack->Peek();
  3463. }
  3464. void ThreadContext::PushHostScriptContext(HostScriptContext* topProvider)
  3465. {
  3466. // script engine can be created coming from GetDispID, so push/pop can be
  3467. // happening after the first round of enterscript as well. we might need to
  3468. // revisit the whole callRootLevel but probably not now.
  3469. // Assert(HasPreviousHostScriptContext() || callRootLevel == 0);
  3470. hostScriptContextStack->Push(topProvider);
  3471. }
  3472. HostScriptContext* ThreadContext::PopHostScriptContext()
  3473. {
  3474. return hostScriptContextStack->Pop();
  3475. // script engine can be created coming from GetDispID, so push/pop can be
  3476. // happening after the first round of enterscript as well. we might need to
  3477. // revisit the whole callRootLevel but probably not now.
  3478. // Assert(HasPreviousHostScriptContext() || callRootLevel == 0);
  3479. }
  3480. #if DBG || defined(PROFILE_EXEC)
  3481. bool
  3482. ThreadContext::AsyncHostOperationStart(void * suspendRecord)
  3483. {
  3484. bool wasInAsync = false;
  3485. Assert(!this->IsScriptActive());
  3486. Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
  3487. if (lastRecord != NULL)
  3488. {
  3489. if (!lastRecord->leaveForHost)
  3490. {
  3491. #if DBG
  3492. wasInAsync = !!lastRecord->leaveForAsyncHostOperation;
  3493. lastRecord->leaveForAsyncHostOperation = true;
  3494. #endif
  3495. #ifdef PROFILE_EXEC
  3496. lastRecord->scriptContext->ProfileSuspend(Js::RunPhase, (Js::Profiler::SuspendRecord *)suspendRecord);
  3497. #endif
  3498. }
  3499. else
  3500. {
  3501. Assert(!lastRecord->leaveForAsyncHostOperation);
  3502. }
  3503. }
  3504. return wasInAsync;
  3505. }
  3506. void
  3507. ThreadContext::AsyncHostOperationEnd(bool wasInAsync, void * suspendRecord)
  3508. {
  3509. Assert(!this->IsScriptActive());
  3510. Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
  3511. if (lastRecord != NULL)
  3512. {
  3513. if (!lastRecord->leaveForHost)
  3514. {
  3515. Assert(lastRecord->leaveForAsyncHostOperation);
  3516. #if DBG
  3517. lastRecord->leaveForAsyncHostOperation = wasInAsync;
  3518. #endif
  3519. #ifdef PROFILE_EXEC
  3520. lastRecord->scriptContext->ProfileResume((Js::Profiler::SuspendRecord *)suspendRecord);
  3521. #endif
  3522. }
  3523. else
  3524. {
  3525. Assert(!lastRecord->leaveForAsyncHostOperation);
  3526. Assert(!wasInAsync);
  3527. }
  3528. }
  3529. }
  3530. #endif
  3531. #if DBG
  3532. void ThreadContext::CheckJsReentrancyOnDispose()
  3533. {
  3534. if (!this->IsDisableImplicitCall())
  3535. {
  3536. AssertJsReentrancy();
  3537. }
  3538. }
  3539. #endif
  3540. #ifdef RECYCLER_DUMP_OBJECT_GRAPH
  3541. void DumpRecyclerObjectGraph()
  3542. {
  3543. ThreadContext * threadContext = ThreadContext::GetContextForCurrentThread();
  3544. if (threadContext == nullptr)
  3545. {
  3546. Output::Print(_u("No thread context"));
  3547. }
  3548. threadContext->GetRecycler()->DumpObjectGraph();
  3549. }
  3550. #endif
  3551. #if ENABLE_NATIVE_CODEGEN
  3552. bool ThreadContext::IsNativeAddressHelper(void * pCodeAddr, Js::ScriptContext* currentScriptContext)
  3553. {
  3554. bool isNativeAddr = false;
  3555. if (currentScriptContext && currentScriptContext->GetJitFuncRangeCache() != nullptr)
  3556. {
  3557. isNativeAddr = currentScriptContext->GetJitFuncRangeCache()->IsNativeAddr(pCodeAddr);
  3558. }
  3559. for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext && !isNativeAddr; scriptContext = scriptContext->next)
  3560. {
  3561. if (scriptContext == currentScriptContext || scriptContext->GetJitFuncRangeCache() == nullptr)
  3562. {
  3563. continue;
  3564. }
  3565. isNativeAddr = scriptContext->GetJitFuncRangeCache()->IsNativeAddr(pCodeAddr);
  3566. }
  3567. return isNativeAddr;
  3568. }
  3569. BOOL ThreadContext::IsNativeAddress(void * pCodeAddr, Js::ScriptContext* currentScriptContext)
  3570. {
  3571. #if ENABLE_OOP_NATIVE_CODEGEN
  3572. if (JITManager::GetJITManager()->IsOOPJITEnabled())
  3573. {
  3574. if (PreReservedVirtualAllocWrapper::IsInRange((void*)m_prereservedRegionAddr, pCodeAddr))
  3575. {
  3576. return true;
  3577. }
  3578. if (IsAllJITCodeInPreReservedRegion())
  3579. {
  3580. return false;
  3581. }
  3582. if (AutoSystemInfo::IsJscriptModulePointer(pCodeAddr))
  3583. {
  3584. return false;
  3585. }
  3586. #if DBG
  3587. boolean result;
  3588. HRESULT hr = JITManager::GetJITManager()->IsNativeAddr(this->m_remoteThreadContextInfo, (intptr_t)pCodeAddr, &result);
  3589. #endif
  3590. bool isNativeAddr = IsNativeAddressHelper(pCodeAddr, currentScriptContext);
  3591. #if DBG
  3592. Assert(FAILED(hr) || result == (isNativeAddr? TRUE:FALSE));
  3593. #endif
  3594. return isNativeAddr;
  3595. }
  3596. else
  3597. #endif
  3598. {
  3599. PreReservedVirtualAllocWrapper *preReservedVirtualAllocWrapper = this->GetPreReservedVirtualAllocator();
  3600. if (preReservedVirtualAllocWrapper->IsInRange(pCodeAddr))
  3601. {
  3602. return TRUE;
  3603. }
  3604. if (!this->IsAllJITCodeInPreReservedRegion())
  3605. {
  3606. #if DBG
  3607. AutoCriticalSection autoLock(&this->codePageAllocators.cs);
  3608. #endif
  3609. bool isNativeAddr = IsNativeAddressHelper(pCodeAddr, currentScriptContext);
  3610. #if DBG
  3611. Assert(this->codePageAllocators.IsInNonPreReservedPageAllocator(pCodeAddr) == isNativeAddr);
  3612. #endif
  3613. return isNativeAddr;
  3614. }
  3615. return FALSE;
  3616. }
  3617. }
  3618. #endif
  3619. #if ENABLE_PROFILE_INFO
  3620. void ThreadContext::EnsureSourceProfileManagersByUrlMap()
  3621. {
  3622. if(this->recyclableData->sourceProfileManagersByUrl == nullptr)
  3623. {
  3624. this->EnsureRecycler();
  3625. this->recyclableData->sourceProfileManagersByUrl = RecyclerNew(GetRecycler(), SourceProfileManagersByUrlMap, GetRecycler());
  3626. }
  3627. }
  3628. //
  3629. // Returns the cache profile manager for the URL and hash combination for a particular dynamic script. There is a ref count added for every script context
  3630. // that references the shared profile manager info.
  3631. //
  3632. Js::SourceDynamicProfileManager* ThreadContext::GetSourceDynamicProfileManager(_In_z_ const WCHAR* url, _In_ uint hash, _Inout_ bool* addRef)
  3633. {
  3634. EnsureSourceProfileManagersByUrlMap();
  3635. Js::SourceDynamicProfileManager* profileManager = nullptr;
  3636. SourceDynamicProfileManagerCache* managerCache = nullptr;
  3637. bool newCache = false;
  3638. if(!this->recyclableData->sourceProfileManagersByUrl->TryGetValue(url, &managerCache))
  3639. {
  3640. if(this->recyclableData->sourceProfileManagersByUrl->Count() >= INMEMORY_CACHE_MAX_URL)
  3641. {
  3642. return nullptr;
  3643. }
  3644. managerCache = RecyclerNewZ(this->GetRecycler(), SourceDynamicProfileManagerCache);
  3645. newCache = true;
  3646. }
  3647. bool createProfileManager = false;
  3648. if(!managerCache->sourceProfileManagerMap)
  3649. {
  3650. managerCache->sourceProfileManagerMap = RecyclerNew(this->GetRecycler(), SourceDynamicProfileManagerMap, this->GetRecycler());
  3651. createProfileManager = true;
  3652. }
  3653. else
  3654. {
  3655. createProfileManager = !managerCache->sourceProfileManagerMap->TryGetValue(hash, &profileManager);
  3656. }
  3657. if(createProfileManager)
  3658. {
  3659. if(managerCache->sourceProfileManagerMap->Count() < INMEMORY_CACHE_MAX_PROFILE_MANAGER)
  3660. {
  3661. profileManager = RecyclerNewZ(this->GetRecycler(), Js::SourceDynamicProfileManager, this->GetRecycler());
  3662. managerCache->sourceProfileManagerMap->Add(hash, profileManager);
  3663. }
  3664. }
  3665. else
  3666. {
  3667. profileManager->Reuse();
  3668. }
  3669. if(!*addRef)
  3670. {
  3671. managerCache->AddRef();
  3672. *addRef = true;
  3673. OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, _u("Addref dynamic source profile manger - Url: %s\n"), url);
  3674. }
  3675. if (newCache)
  3676. {
  3677. // Let's make a copy of the URL because there is no guarantee this URL will remain alive in the future.
  3678. size_t lengthInChars = wcslen(url) + 1;
  3679. WCHAR* urlCopy = RecyclerNewArrayLeaf(GetRecycler(), WCHAR, lengthInChars);
  3680. js_memcpy_s(urlCopy, lengthInChars * sizeof(WCHAR), url, lengthInChars * sizeof(WCHAR));
  3681. this->recyclableData->sourceProfileManagersByUrl->Add(urlCopy, managerCache);
  3682. }
  3683. return profileManager;
  3684. }
  3685. //
  3686. // Decrement the ref count for this URL and cleanup the corresponding record if there are no other references to it.
  3687. //
  3688. uint ThreadContext::ReleaseSourceDynamicProfileManagers(const WCHAR* url)
  3689. {
  3690. // If we've already freed the recyclable data, we're shutting down the thread context so skip clean up
  3691. if (this->recyclableData == nullptr) return 0;
  3692. SourceDynamicProfileManagerCache* managerCache = this->recyclableData->sourceProfileManagersByUrl->Lookup(url, nullptr);
  3693. uint refCount = 0;
  3694. if(managerCache) // manager cache may be null we exceeded -INMEMORY_CACHE_MAX_URL
  3695. {
  3696. refCount = managerCache->Release();
  3697. OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, _u("Release dynamic source profile manger %d Url: %s\n"), refCount, url);
  3698. Output::Flush();
  3699. if(refCount == 0)
  3700. {
  3701. this->recyclableData->sourceProfileManagersByUrl->Remove(url);
  3702. }
  3703. }
  3704. return refCount;
  3705. }
  3706. #endif
  3707. void ThreadContext::EnsureSymbolRegistrationMap()
  3708. {
  3709. if (this->recyclableData->symbolRegistrationMap == nullptr)
  3710. {
  3711. this->EnsureRecycler();
  3712. this->recyclableData->symbolRegistrationMap = RecyclerNew(GetRecycler(), SymbolRegistrationMap, GetRecycler());
  3713. }
  3714. }
  3715. const Js::PropertyRecord* ThreadContext::GetSymbolFromRegistrationMap(const char16* stringKey, charcount_t stringLength)
  3716. {
  3717. this->EnsureSymbolRegistrationMap();
  3718. Js::HashedCharacterBuffer<char16> propertyName = Js::HashedCharacterBuffer<char16>(stringKey, stringLength);
  3719. return this->recyclableData->symbolRegistrationMap->LookupWithKey(&propertyName, nullptr);
  3720. }
  3721. const Js::PropertyRecord* ThreadContext::AddSymbolToRegistrationMap(const char16* stringKey, charcount_t stringLength)
  3722. {
  3723. this->EnsureSymbolRegistrationMap();
  3724. const Js::PropertyRecord* propertyRecord = this->UncheckedAddPropertyId(stringKey, stringLength, /*bind*/false, /*isSymbol*/true);
  3725. Assert(propertyRecord);
  3726. // We need to support null characters in the Symbol names. For e.g. "A\0Z" is a valid symbol name and is different than "A\0Y".
  3727. // However, as the key contains a null character we need to hash the symbol name past the null character. The default implementation terminates
  3728. // at the null character, so we use the Js::HashedCharacterBuffer as key. We allocate the key in the recycler memory as it needs to be around
  3729. // for the lifetime of the map.
  3730. Js::HashedCharacterBuffer<char16> * propertyName = RecyclerNew(GetRecycler(), Js::HashedCharacterBuffer<char16>, propertyRecord->GetBuffer(), propertyRecord->GetLength());
  3731. this->recyclableData->symbolRegistrationMap->Add(propertyName, propertyRecord);
  3732. return propertyRecord;
  3733. }
  3734. #if ENABLE_TTD
  3735. JsUtil::BaseDictionary<Js::HashedCharacterBuffer<char16>*, const Js::PropertyRecord*, Recycler, PowerOf2SizePolicy, Js::PropertyRecordStringHashComparer>* ThreadContext::GetSymbolRegistrationMap_TTD()
  3736. {
  3737. //This adds a little memory but makes simplifies other logic -- maybe revise later
  3738. this->EnsureSymbolRegistrationMap();
  3739. return this->recyclableData->symbolRegistrationMap;
  3740. }
  3741. #endif
  3742. void ThreadContext::ClearImplicitCallFlags()
  3743. {
  3744. SetImplicitCallFlags(Js::ImplicitCall_None);
  3745. }
  3746. void ThreadContext::ClearImplicitCallFlags(Js::ImplicitCallFlags flags)
  3747. {
  3748. Assert((flags & Js::ImplicitCall_None) == 0);
  3749. SetImplicitCallFlags((Js::ImplicitCallFlags)(implicitCallFlags & ~flags));
  3750. }
  3751. void ThreadContext::CheckAndResetImplicitCallAccessorFlag()
  3752. {
  3753. Js::ImplicitCallFlags accessorCallFlag = (Js::ImplicitCallFlags)(Js::ImplicitCall_Accessor & ~Js::ImplicitCall_None);
  3754. if ((GetImplicitCallFlags() & accessorCallFlag) != 0)
  3755. {
  3756. ClearImplicitCallFlags(accessorCallFlag);
  3757. AddImplicitCallFlags(Js::ImplicitCall_NonProfiledAccessor);
  3758. }
  3759. }
  3760. bool ThreadContext::HasNoSideEffect(Js::RecyclableObject * function) const
  3761. {
  3762. Js::FunctionInfo::Attributes attributes = Js::FunctionInfo::GetAttributes(function);
  3763. return this->HasNoSideEffect(function, attributes);
  3764. }
  3765. bool ThreadContext::HasNoSideEffect(Js::RecyclableObject * function, Js::FunctionInfo::Attributes attributes) const
  3766. {
  3767. if (((attributes & Js::FunctionInfo::CanBeHoisted) != 0)
  3768. || ((attributes & Js::FunctionInfo::HasNoSideEffect) != 0 && !IsDisableImplicitException()))
  3769. {
  3770. Assert((attributes & Js::FunctionInfo::HasNoSideEffect) != 0);
  3771. return true;
  3772. }
  3773. return false;
  3774. }
  3775. bool
  3776. ThreadContext::RecordImplicitException()
  3777. {
  3778. // Record the exception in the implicit call flag
  3779. AddImplicitCallFlags(Js::ImplicitCall_Exception);
  3780. if (IsDisableImplicitException())
  3781. {
  3782. // Indicate that we shouldn't throw if ImplicitExceptions have been disabled
  3783. return false;
  3784. }
  3785. // Disabling implicit exception when disabling implicit calls can result in valid exceptions not being thrown.
  3786. // Instead we tell not to throw only if an implicit call happened and they are disabled. This is to cover the case
  3787. // of an exception being thrown because an implicit call not executed left the execution in a bad state.
  3788. // Since there is an implicit call, we expect to bailout and handle this operation in the interpreter instead.
  3789. bool hasImplicitCallHappened = IsDisableImplicitCall() && (GetImplicitCallFlags() & ~Js::ImplicitCall_Exception);
  3790. return !hasImplicitCallHappened;
  3791. }
  3792. void ThreadContext::SetThreadServiceWrapper(ThreadServiceWrapper* inThreadServiceWrapper)
  3793. {
  3794. AssertMsg(threadServiceWrapper == NULL || inThreadServiceWrapper == NULL, "double set ThreadServiceWrapper");
  3795. threadServiceWrapper = inThreadServiceWrapper;
  3796. }
  3797. ThreadServiceWrapper* ThreadContext::GetThreadServiceWrapper()
  3798. {
  3799. return threadServiceWrapper;
  3800. }
  3801. uint ThreadContext::GetRandomNumber()
  3802. {
  3803. #ifdef ENABLE_CUSTOM_ENTROPY
  3804. return (uint)GetEntropy().GetRand();
  3805. #else
  3806. uint randomNumber = 0;
  3807. errno_t e = rand_s(&randomNumber);
  3808. Assert(e == 0);
  3809. return randomNumber;
  3810. #endif
  3811. }
  3812. #if defined(ENABLE_JS_ETW) && defined(NTBUILD)
  3813. void ThreadContext::EtwLogPropertyIdList()
  3814. {
  3815. propertyMap->Map([&](const Js::PropertyRecord* propertyRecord){
  3816. EventWriteJSCRIPT_HOSTING_PROPERTYID_LIST(propertyRecord, propertyRecord->GetBuffer());
  3817. });
  3818. }
  3819. #endif
  3820. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  3821. Js::Var ThreadContext::GetMemoryStat(Js::ScriptContext* scriptContext)
  3822. {
  3823. ScriptMemoryDumper dumper(scriptContext);
  3824. return dumper.Dump();
  3825. }
  3826. void ThreadContext::SetAutoProxyName(LPCWSTR objectName)
  3827. {
  3828. recyclableData->autoProxyName = objectName;
  3829. }
  3830. #endif
  3831. //
  3832. // Regex helpers
  3833. //
  3834. UnifiedRegex::StandardChars<uint8>* ThreadContext::GetStandardChars(__inout_opt uint8* dummy)
  3835. {
  3836. if (standardUTF8Chars == 0)
  3837. {
  3838. ArenaAllocator* allocator = GetThreadAlloc();
  3839. standardUTF8Chars = Anew(allocator, UnifiedRegex::UTF8StandardChars, allocator);
  3840. }
  3841. return standardUTF8Chars;
  3842. }
  3843. UnifiedRegex::StandardChars<char16>* ThreadContext::GetStandardChars(__inout_opt char16* dummy)
  3844. {
  3845. if (standardUnicodeChars == 0)
  3846. {
  3847. ArenaAllocator* allocator = GetThreadAlloc();
  3848. standardUnicodeChars = Anew(allocator, UnifiedRegex::UnicodeStandardChars, allocator);
  3849. }
  3850. return standardUnicodeChars;
  3851. }
  3852. void ThreadContext::CheckScriptInterrupt()
  3853. {
  3854. if (TestThreadContextFlag(ThreadContextFlagCanDisableExecution))
  3855. {
  3856. if (this->IsExecutionDisabled())
  3857. {
  3858. Assert(this->DoInterruptProbe());
  3859. throw Js::ScriptAbortException();
  3860. }
  3861. }
  3862. else
  3863. {
  3864. this->CheckInterruptPoll();
  3865. }
  3866. }
  3867. void ThreadContext::CheckInterruptPoll()
  3868. {
  3869. // Disable QC when implicit calls are disabled since the host can do anything before returning back, like servicing the
  3870. // message loop, which may in turn cause script code to be executed and implicit calls to be made
  3871. if (!IsDisableImplicitCall())
  3872. {
  3873. InterruptPoller *poller = this->interruptPoller;
  3874. if (poller)
  3875. {
  3876. poller->CheckInterruptPoll();
  3877. }
  3878. }
  3879. }
  3880. void *
  3881. ThreadContext::GetDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType)
  3882. {
  3883. void * data = nullptr;
  3884. return this->dynamicObjectEnumeratorCacheMap.TryGetValue(dynamicType, &data)? data : nullptr;
  3885. }
  3886. void
  3887. ThreadContext::AddDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType, void * cache)
  3888. {
  3889. this->dynamicObjectEnumeratorCacheMap.Item(dynamicType, cache);
  3890. }
  3891. InterruptPoller::InterruptPoller(ThreadContext *tc) :
  3892. threadContext(tc),
  3893. lastPollTick(0),
  3894. lastResetTick(0),
  3895. isDisabled(FALSE)
  3896. {
  3897. tc->SetInterruptPoller(this);
  3898. }
  3899. void InterruptPoller::CheckInterruptPoll()
  3900. {
  3901. if (!isDisabled)
  3902. {
  3903. Js::ScriptEntryExitRecord *entryExitRecord = this->threadContext->GetScriptEntryExit();
  3904. if (entryExitRecord)
  3905. {
  3906. Js::ScriptContext *scriptContext = entryExitRecord->scriptContext;
  3907. if (scriptContext)
  3908. {
  3909. this->TryInterruptPoll(scriptContext);
  3910. }
  3911. }
  3912. }
  3913. }
  3914. void InterruptPoller::GetStatementCount(ULONG *pluHi, ULONG *pluLo)
  3915. {
  3916. DWORD resetTick = this->lastResetTick;
  3917. DWORD pollTick = this->lastPollTick;
  3918. DWORD elapsed;
  3919. elapsed = pollTick - resetTick;
  3920. ULONGLONG statements = (ULONGLONG)elapsed * InterruptPoller::TicksToStatements;
  3921. *pluLo = (ULONG)statements;
  3922. *pluHi = (ULONG)(statements >> 32);
  3923. }
  3924. void ThreadContext::DisableExecution()
  3925. {
  3926. Assert(TestThreadContextFlag(ThreadContextFlagCanDisableExecution));
  3927. // Hammer the stack limit with a value that will cause script abort on the next stack probe.
  3928. this->SetStackLimitForCurrentThread(Js::Constants::StackLimitForScriptInterrupt);
  3929. return;
  3930. }
  3931. void ThreadContext::EnableExecution()
  3932. {
  3933. Assert(this->GetStackProber());
  3934. // Restore the normal stack limit.
  3935. this->SetStackLimitForCurrentThread(this->GetStackProber()->GetScriptStackLimit());
  3936. // It's possible that the host disabled execution after the script threw an exception
  3937. // of it's own, so we shouldn't clear that. Only exceptions for script termination
  3938. // should be cleared.
  3939. if (GetRecordedException() == GetPendingTerminatedErrorObject())
  3940. {
  3941. SetRecordedException(NULL);
  3942. }
  3943. }
  3944. bool ThreadContext::TestThreadContextFlag(ThreadContextFlags contextFlag) const
  3945. {
  3946. return (this->threadContextFlags & contextFlag) != 0;
  3947. }
  3948. void ThreadContext::SetThreadContextFlag(ThreadContextFlags contextFlag)
  3949. {
  3950. this->threadContextFlags = (ThreadContextFlags)(this->threadContextFlags | contextFlag);
  3951. }
  3952. void ThreadContext::ClearThreadContextFlag(ThreadContextFlags contextFlag)
  3953. {
  3954. this->threadContextFlags = (ThreadContextFlags)(this->threadContextFlags & ~contextFlag);
  3955. }
  3956. #ifdef ENABLE_GLOBALIZATION
  3957. Js::DelayLoadWinRtString * ThreadContext::GetWinRTStringLibrary()
  3958. {
  3959. delayLoadWinRtString.EnsureFromSystemDirOnly();
  3960. return &delayLoadWinRtString;
  3961. }
  3962. #if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
  3963. #ifdef INTL_WINGLOB
  3964. Js::WindowsGlobalizationAdapter* ThreadContext::GetWindowsGlobalizationAdapter()
  3965. {
  3966. return &windowsGlobalizationAdapter;
  3967. }
  3968. Js::DelayLoadWindowsGlobalization* ThreadContext::GetWindowsGlobalizationLibrary()
  3969. {
  3970. delayLoadWindowsGlobalizationLibrary.Ensure(this->GetWinRTStringLibrary());
  3971. return &delayLoadWindowsGlobalizationLibrary;
  3972. }
  3973. #endif // INTL_WINGLOB
  3974. #endif
  3975. #ifdef ENABLE_FOUNDATION_OBJECT
  3976. Js::WindowsFoundationAdapter* ThreadContext::GetWindowsFoundationAdapter()
  3977. {
  3978. return &windowsFoundationAdapter;
  3979. }
  3980. Js::DelayLoadWinRtFoundation* ThreadContext::GetWinRtFoundationLibrary()
  3981. {
  3982. delayLoadWinRtFoundationLibrary.EnsureFromSystemDirOnly();
  3983. return &delayLoadWinRtFoundationLibrary;
  3984. }
  3985. #endif
  3986. #endif // ENABLE_GLOBALIZATION
  3987. // Despite the name, callers expect this to return the highest propid + 1.
  3988. uint ThreadContext::GetHighestPropertyNameIndex() const
  3989. {
  3990. return propertyMap->GetLastIndex() + 1 + Js::InternalPropertyIds::Count;
  3991. }
  3992. #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
  3993. void ThreadContext::ReportAndCheckLeaksOnProcessDetach()
  3994. {
  3995. bool needReportOrCheck = false;
  3996. #ifdef LEAK_REPORT
  3997. needReportOrCheck = needReportOrCheck || Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag);
  3998. #endif
  3999. #ifdef CHECK_MEMORY_LEAK
  4000. needReportOrCheck = needReportOrCheck ||
  4001. (Js::Configuration::Global.flags.CheckMemoryLeak && MemoryLeakCheck::IsEnableOutput());
  4002. #endif
  4003. if (!needReportOrCheck)
  4004. {
  4005. return;
  4006. }
  4007. // Report leaks even if this is a force termination and we have not clean up the thread
  4008. // This is call during process detach. No one should be creating new thread context.
  4009. // So don't need to take the lock
  4010. ThreadContext * current = GetThreadContextList();
  4011. while (current)
  4012. {
  4013. #if DBG
  4014. current->pageAllocator.ClearConcurrentThreadId();
  4015. #endif
  4016. Recycler * recycler = current->GetRecycler();
  4017. #ifdef LEAK_REPORT
  4018. if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
  4019. {
  4020. AUTO_LEAK_REPORT_SECTION(Js::Configuration::Global.flags, _u("Thread Context (%p): Process Termination (TID: %d)"), current, current->threadId);
  4021. LeakReport::DumpUrl(current->threadId);
  4022. // Heuristically figure out which one is the root tracker script engine
  4023. // and force close on it
  4024. if (current->rootTrackerScriptContext != nullptr)
  4025. {
  4026. current->rootTrackerScriptContext->Close(false);
  4027. }
  4028. recycler->ReportLeaksOnProcessDetach();
  4029. }
  4030. #endif
  4031. #ifdef CHECK_MEMORY_LEAK
  4032. recycler->CheckLeaksOnProcessDetach(_u("Process Termination"));
  4033. #endif
  4034. current = current->Next();
  4035. }
  4036. }
  4037. #endif
  4038. #ifdef LEAK_REPORT
  4039. void
  4040. ThreadContext::SetRootTrackerScriptContext(Js::ScriptContext * scriptContext)
  4041. {
  4042. Assert(this->rootTrackerScriptContext == nullptr);
  4043. this->rootTrackerScriptContext = scriptContext;
  4044. scriptContext->isRootTrackerScriptContext = true;
  4045. }
  4046. void
  4047. ThreadContext::ClearRootTrackerScriptContext(Js::ScriptContext * scriptContext)
  4048. {
  4049. Assert(this->rootTrackerScriptContext == scriptContext);
  4050. this->rootTrackerScriptContext->isRootTrackerScriptContext = false;
  4051. this->rootTrackerScriptContext = nullptr;
  4052. }
  4053. #endif
  4054. AutoTagNativeLibraryEntry::AutoTagNativeLibraryEntry(Js::RecyclableObject* function, Js::CallInfo callInfo, PCWSTR name, void* addr)
  4055. {
  4056. // Save function/callInfo values (for StackWalker). Compiler may stackpack/optimize them for built-in native functions.
  4057. entry.function = function;
  4058. entry.callInfo = callInfo;
  4059. entry.name = name;
  4060. entry.addr = addr;
  4061. ThreadContext* threadContext = function->GetScriptContext()->GetThreadContext();
  4062. threadContext->PushNativeLibraryEntry(&entry);
  4063. }
  4064. AutoTagNativeLibraryEntry::~AutoTagNativeLibraryEntry()
  4065. {
  4066. ThreadContext* threadContext = entry.function->GetScriptContext()->GetThreadContext();
  4067. Assert(threadContext->PeekNativeLibraryEntry() == &entry);
  4068. threadContext->PopNativeLibraryEntry();
  4069. }
  4070. #if ENABLE_JS_REENTRANCY_CHECK
  4071. #if DBG
  4072. void JsReentLock::setObjectForMutation(Js::Var object)
  4073. {
  4074. m_arrayObject = nullptr;
  4075. if (object != nullptr && Js::DynamicObject::IsAnyArray(object))
  4076. {
  4077. m_arrayObject = object;
  4078. }
  4079. // Don't care about any other objects for now
  4080. }
  4081. void JsReentLock::setSecondObjectForMutation(Js::Var object)
  4082. {
  4083. m_arrayObject2 = nullptr;
  4084. if (object != nullptr && Js::DynamicObject::IsAnyArray(object))
  4085. {
  4086. m_arrayObject2 = object;
  4087. }
  4088. // Don't care about any other objects for now
  4089. }
  4090. void JsReentLock::MutateArrayObject(Js::Var arrayObject)
  4091. {
  4092. if (arrayObject)
  4093. {
  4094. Js::JavascriptArray *arr = Js::JavascriptArray::FromAnyArray(arrayObject);
  4095. uint32 random = static_cast<unsigned int>(rand());
  4096. if (random % 20 == 0)
  4097. {
  4098. arr->DoTypeMutation();
  4099. }
  4100. else if (random % 20 == 1)
  4101. {
  4102. // TODO : modify the length of the current array
  4103. // Or other opportunities
  4104. }
  4105. }
  4106. }
  4107. void JsReentLock::MutateArrayObject()
  4108. {
  4109. if (CONFIG_FLAG(EnableArrayTypeMutation))
  4110. {
  4111. JsReentLock::MutateArrayObject(m_arrayObject);
  4112. JsReentLock::MutateArrayObject(m_arrayObject2);
  4113. }
  4114. }
  4115. #endif
  4116. #endif