ThreadContext.cpp 149 KB

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