ThreadContext.cpp 138 KB

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