ThreadContext.cpp 159 KB

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