| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeBasePch.h"
- #include "BackendApi.h"
- #include "ThreadServiceWrapper.h"
- #include "Types/TypePropertyCache.h"
- #include "Debug/DebuggingFlags.h"
- #include "Debug/DiagProbe.h"
- #include "Debug/DebugManager.h"
- #include "Chars.h"
- #include "CaseInsensitive.h"
- #include "CharSet.h"
- #include "CharMap.h"
- #include "StandardChars.h"
- #include "Base/ThreadContextTlsEntry.h"
- #include "Base/ThreadBoundThreadContextManager.h"
- #include "Language/SourceDynamicProfileManager.h"
- #include "Language/CodeGenRecyclableData.h"
- #include "Language/InterpreterStackFrame.h"
- #include "Language/JavascriptStackWalker.h"
- #include "Base/ScriptMemoryDumper.h"
- // SIMD_JS
- #include "Library/SimdLib.h"
- #if DBG
- #include "Memory/StressTest.h"
- #endif
- #ifdef DYNAMIC_PROFILE_MUTATOR
- #include "Language/DynamicProfileMutator.h"
- #endif
- #ifdef ENABLE_BASIC_TELEMETRY
- #include "Telemetry.h"
- #endif
- const int TotalNumberOfBuiltInProperties = Js::PropertyIds::_countJSOnlyProperty;
- /*
- * When we aren't adding any additional properties
- */
- void DefaultInitializeAdditionalProperties(ThreadContext *threadContext)
- {
- }
- /*
- *
- */
- void (*InitializeAdditionalProperties)(ThreadContext *threadContext) = DefaultInitializeAdditionalProperties;
- // To make sure the marker function doesn't get inlined, optimized away, or merged with other functions we disable optimization.
- // If this method ends up causing a perf problem in the future, we should replace it with asm versions which should be lighter.
- #pragma optimize("g", off)
- _NOINLINE extern "C" void* MarkerForExternalDebugStep()
- {
- // We need to return something here to prevent this function from being merged with other empty functions by the linker.
- static int __dummy;
- return &__dummy;
- }
- #pragma optimize("", on)
- CriticalSection ThreadContext::s_csThreadContext;
- size_t ThreadContext::processNativeCodeSize = 0;
- ThreadContext * ThreadContext::globalListFirst = nullptr;
- ThreadContext * ThreadContext::globalListLast = nullptr;
- THREAD_LOCAL uint ThreadContext::activeScriptSiteCount = 0;
- const Js::PropertyRecord * const ThreadContext::builtInPropertyRecords[] =
- {
- Js::BuiltInPropertyRecords::EMPTY,
- #define ENTRY_INTERNAL_SYMBOL(n) Js::BuiltInPropertyRecords::n,
- #define ENTRY_SYMBOL(n, d) Js::BuiltInPropertyRecords::n,
- #define ENTRY(n) Js::BuiltInPropertyRecords::n,
- #define ENTRY2(n, s) ENTRY(n)
- #include "Base/JnDirectFields.h"
- };
- ThreadContext::RecyclableData::RecyclableData(Recycler *const recycler) :
- soErrorObject(nullptr, nullptr, nullptr, true),
- oomErrorObject(nullptr, nullptr, nullptr, true),
- terminatedErrorObject(nullptr, nullptr, nullptr),
- typesWithProtoPropertyCache(recycler),
- propertyGuards(recycler, 128),
- oldEntryPointInfo(nullptr),
- returnedValueList(nullptr),
- constructorCacheInvalidationCount(0)
- {
- }
- ThreadContext::ThreadContext(AllocationPolicyManager * allocationPolicyManager, JsUtil::ThreadService::ThreadServiceCallback threadServiceCallback, bool enableExperimentalFeatures) :
- currentThreadId(::GetCurrentThreadId()),
- stackLimitForCurrentThread(0),
- stackProber(nullptr),
- isThreadBound(false),
- hasThrownPendingException(false),
- noScriptScope(false),
- heapEnum(nullptr),
- threadContextFlags(ThreadContextFlagNoFlag),
- JsUtil::DoublyLinkedListElement<ThreadContext>(),
- allocationPolicyManager(allocationPolicyManager),
- threadService(threadServiceCallback),
- isOptimizedForManyInstances(Js::Configuration::Global.flags.OptimizeForManyInstances),
- bgJit(Js::Configuration::Global.flags.BgJit),
- pageAllocator(allocationPolicyManager, PageAllocatorType_Thread, Js::Configuration::Global.flags, 0, PageAllocator::DefaultMaxFreePageCount,
- false
- #if ENABLE_BACKGROUND_PAGE_FREEING
- , &backgroundPageQueue
- #endif
- ),
- recycler(nullptr),
- hasCollectionCallBack(false),
- callDispose(true),
- #if ENABLE_NATIVE_CODEGEN
- jobProcessor(nullptr),
- #endif
- interruptPoller(nullptr),
- expirableCollectModeGcCount(-1),
- expirableObjectList(nullptr),
- expirableObjectDisposeList(nullptr),
- numExpirableObjects(0),
- disableExpiration(false),
- callRootLevel(0),
- nextTypeId((Js::TypeId)Js::Constants::ReservedTypeIds),
- entryExitRecord(nullptr),
- leafInterpreterFrame(nullptr),
- threadServiceWrapper(nullptr),
- temporaryArenaAllocatorCount(0),
- temporaryGuestArenaAllocatorCount(0),
- crefSContextForDiag(0),
- m_prereservedRegionAddr(0),
- scriptContextList(nullptr),
- scriptContextEverRegistered(false),
- #if DBG_DUMP || defined(PROFILE_EXEC)
- topLevelScriptSite(nullptr),
- #endif
- polymorphicCacheState(0),
- stackProbeCount(0),
- #ifdef BAILOUT_INJECTION
- bailOutByteCodeLocationCount(0),
- #endif
- sourceCodeSize(0),
- nativeCodeSize(0),
- threadAlloc(_u("TC"), GetPageAllocator(), Js::Throw::OutOfMemory),
- inlineCacheThreadInfoAllocator(_u("TC-InlineCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
- isInstInlineCacheThreadInfoAllocator(_u("TC-IsInstInlineCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
- equivalentTypeCacheInfoAllocator(_u("TC-EquivalentTypeCacheInfo"), GetPageAllocator(), Js::Throw::OutOfMemory),
- preReservedVirtualAllocator(),
- protoInlineCacheByPropId(&inlineCacheThreadInfoAllocator, 512),
- storeFieldInlineCacheByPropId(&inlineCacheThreadInfoAllocator, 256),
- isInstInlineCacheByFunction(&isInstInlineCacheThreadInfoAllocator, 128),
- registeredInlineCacheCount(0),
- unregisteredInlineCacheCount(0),
- prototypeChainEnsuredToHaveOnlyWritableDataPropertiesAllocator(_u("TC-ProtoWritableProp"), GetPageAllocator(), Js::Throw::OutOfMemory),
- standardUTF8Chars(0),
- standardUnicodeChars(0),
- hasUnhandledException(FALSE),
- hasCatchHandler(FALSE),
- disableImplicitFlags(DisableImplicitNoFlag),
- hasCatchHandlerToUserCode(false),
- caseInvariantPropertySet(nullptr),
- entryPointToBuiltInOperationIdCache(&threadAlloc, 0),
- #if ENABLE_NATIVE_CODEGEN
- #if !FLOATVAR
- codeGenNumberThreadAllocator(nullptr),
- xProcNumberPageSegmentManager(nullptr),
- #endif
- m_jitNumericProperties(nullptr),
- m_jitNeedsPropertyUpdate(false),
- #if DYNAMIC_INTERPRETER_THUNK || defined(ASMJS_PLAT)
- thunkPageAllocators(allocationPolicyManager, /* allocXData */ false, /* virtualAllocator */ nullptr, GetCurrentProcess()),
- #endif
- codePageAllocators(allocationPolicyManager, ALLOC_XDATA, GetPreReservedVirtualAllocator(), GetCurrentProcess()),
- #endif
- dynamicObjectEnumeratorCacheMap(&HeapAllocator::Instance, 16),
- //threadContextFlags(ThreadContextFlagNoFlag),
- #ifdef NTBUILD
- telemetryBlock(&localTelemetryBlock),
- #endif
- configuration(enableExperimentalFeatures),
- jsrtRuntime(nullptr),
- propertyMap(nullptr),
- rootPendingClose(nullptr),
- exceptionCode(0),
- isProfilingUserCode(true),
- loopDepth(0),
- redeferralState(InitialRedeferralState),
- gcSinceLastRedeferral(0),
- gcSinceCallCountsCollected(0),
- tridentLoadAddress(nullptr),
- m_remoteThreadContextInfo(nullptr),
- debugManager(nullptr)
- #if ENABLE_TTD
- , TTDContext(nullptr)
- , TTDExecutionInfo(nullptr)
- , TTDLog(nullptr)
- , TTDRootNestingCount(0)
- #endif
- #ifdef ENABLE_DIRECTCALL_TELEMETRY
- , directCallTelemetry(this)
- #endif
- #if ENABLE_JS_REENTRANCY_CHECK
- , noJsReentrancy(false)
- #endif
- {
- pendingProjectionContextCloseList = JsUtil::List<IProjectionContext*, ArenaAllocator>::New(GetThreadAlloc());
- hostScriptContextStack = Anew(GetThreadAlloc(), JsUtil::Stack<HostScriptContext*>, GetThreadAlloc());
- functionCount = 0;
- sourceInfoCount = 0;
- #if DBG || defined(RUNTIME_DATA_COLLECTION)
- scriptContextCount = 0;
- #endif
- isScriptActive = false;
- #ifdef ENABLE_CUSTOM_ENTROPY
- entropy.Initialize();
- #endif
- #if ENABLE_NATIVE_CODEGEN
- this->bailOutRegisterSaveSpace = AnewArrayZ(this->GetThreadAlloc(), Js::Var, GetBailOutRegisterSaveSlotCount());
- #endif
- #if defined(ENABLE_SIMDJS) && ENABLE_NATIVE_CODEGEN
- simdFuncInfoToOpcodeMap = Anew(this->GetThreadAlloc(), FuncInfoToOpcodeMap, this->GetThreadAlloc());
- simdOpcodeToSignatureMap = AnewArrayZ(this->GetThreadAlloc(), SimdFuncSignature, Js::Simd128OpcodeCount());
- {
- #define MACRO_SIMD_WMS(op, LayoutAsmJs, OpCodeAttrAsmJs, OpCodeAttr, ...) \
- AddSimdFuncToMaps(Js::OpCode::##op, __VA_ARGS__);
- #define MACRO_SIMD_EXTEND_WMS(op, LayoutAsmJs, OpCodeAttrAsmJs, OpCodeAttr, ...) MACRO_SIMD_WMS(op, LayoutAsmJs, OpCodeAttrAsmJs, OpCodeAttr, __VA_ARGS__)
- #include "ByteCode/OpCodesSimd.h"
- }
- #endif // defined(ENABLE_SIMDJS) && ENABLE_NATIVE_CODEGEN
- #if DBG_DUMP
- scriptSiteCount = 0;
- pageAllocator.debugName = _u("Thread");
- #endif
- #ifdef DYNAMIC_PROFILE_MUTATOR
- this->dynamicProfileMutator = DynamicProfileMutator::GetMutator();
- #endif
- PERF_COUNTER_INC(Basic, ThreadContext);
- #ifdef LEAK_REPORT
- this->rootTrackerScriptContext = nullptr;
- this->threadId = ::GetCurrentThreadId();
- #endif
- #ifdef NTBUILD
- memset(&localTelemetryBlock, 0, sizeof(localTelemetryBlock));
- #endif
- AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
- ThreadContext::LinkToBeginning(this, &ThreadContext::globalListFirst, &ThreadContext::globalListLast);
- #if DBG
- // Since we created our page allocator while we were constructing this thread context
- // it will pick up the thread context id that is current on the thread. We need to update
- // that now.
- pageAllocator.UpdateThreadContextHandle((ThreadContextId)this);
- #endif
- #ifdef ENABLE_PROJECTION
- #if DBG_DUMP
- this->projectionMemoryInformation = nullptr;
- #endif
- #endif
- this->InitAvailableCommit();
- }
- void ThreadContext::InitAvailableCommit()
- {
- // Once per process: get the available commit for the process from the OS and push it to the AutoSystemInfo.
- // (This must be done lazily, outside DllMain. And it must be done from the Runtime, since the common lib
- // doesn't have access to the DelayLoadLibrary stuff.)
- ULONG64 commit;
- BOOL success = AutoSystemInfo::Data.GetAvailableCommit(&commit);
- if (!success)
- {
- commit = (ULONG64)-1;
- #ifdef NTBUILD
- APP_MEMORY_INFORMATION AppMemInfo;
- success = GetWinCoreProcessThreads()->GetProcessInformation(
- GetCurrentProcess(),
- ProcessAppMemoryInfo,
- &AppMemInfo,
- sizeof(AppMemInfo));
- if (success)
- {
- commit = AppMemInfo.AvailableCommit;
- }
- #endif
- AutoSystemInfo::Data.SetAvailableCommit(commit);
- }
- }
- void ThreadContext::SetStackProber(StackProber * stackProber)
- {
- this->stackProber = stackProber;
- if (stackProber != NULL && this->stackLimitForCurrentThread != Js::Constants::StackLimitForScriptInterrupt)
- {
- this->stackLimitForCurrentThread = stackProber->GetScriptStackLimit();
- }
- }
- size_t ThreadContext::GetScriptStackLimit() const
- {
- return stackProber->GetScriptStackLimit();
- }
- HANDLE
- ThreadContext::GetProcessHandle() const
- {
- return GetCurrentProcess();
- }
- intptr_t
- ThreadContext::GetThreadStackLimitAddr() const
- {
- return (intptr_t)GetAddressOfStackLimitForCurrentThread();
- }
- #if ENABLE_NATIVE_CODEGEN && defined(ENABLE_SIMDJS) && (defined(_M_IX86) || defined(_M_X64))
- intptr_t
- ThreadContext::GetSimdTempAreaAddr(uint8 tempIndex) const
- {
- return (intptr_t)&X86_TEMP_SIMD[tempIndex];
- }
- #endif
- intptr_t
- ThreadContext::GetDisableImplicitFlagsAddr() const
- {
- return (intptr_t)&disableImplicitFlags;
- }
- intptr_t
- ThreadContext::GetImplicitCallFlagsAddr() const
- {
- return (intptr_t)&implicitCallFlags;
- }
- ptrdiff_t
- ThreadContext::GetChakraBaseAddressDifference() const
- {
- return 0;
- }
- ptrdiff_t
- ThreadContext::GetCRTBaseAddressDifference() const
- {
- return 0;
- }
- IActiveScriptProfilerHeapEnum* ThreadContext::GetHeapEnum()
- {
- return heapEnum;
- }
- void ThreadContext::SetHeapEnum(IActiveScriptProfilerHeapEnum* newHeapEnum)
- {
- Assert((newHeapEnum != nullptr && heapEnum == nullptr) || (newHeapEnum == nullptr && heapEnum != nullptr));
- heapEnum = newHeapEnum;
- }
- void ThreadContext::ClearHeapEnum()
- {
- Assert(heapEnum != nullptr);
- heapEnum = nullptr;
- }
- void ThreadContext::GlobalInitialize()
- {
- for (int i = 0; i < _countof(builtInPropertyRecords); i++)
- {
- builtInPropertyRecords[i]->SetHash(JsUtil::CharacterBuffer<WCHAR>::StaticGetHashCode(builtInPropertyRecords[i]->GetBuffer(), builtInPropertyRecords[i]->GetLength()));
- }
- }
- ThreadContext::~ThreadContext()
- {
- {
- AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
- ThreadContext::Unlink(this, &ThreadContext::globalListFirst, &ThreadContext::globalListLast);
- }
- #if ENABLE_TTD
- if(this->TTDContext != nullptr)
- {
- TT_HEAP_DELETE(TTD::ThreadContextTTD, this->TTDContext);
- this->TTDContext = nullptr;
- }
- if(this->TTDExecutionInfo != nullptr)
- {
- TT_HEAP_DELETE(TTD::ThreadContextTTD, this->TTDExecutionInfo);
- this->TTDExecutionInfo = nullptr;
- }
- if(this->TTDLog != nullptr)
- {
- TT_HEAP_DELETE(TTD::EventLog, this->TTDLog);
- this->TTDLog = nullptr;
- }
- #endif
- #ifdef LEAK_REPORT
- if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
- {
- AUTO_LEAK_REPORT_SECTION(Js::Configuration::Global.flags, _u("Thread Context (%p): %s (TID: %d)"), this,
- this->GetRecycler()->IsInDllCanUnloadNow()? _u("DllCanUnloadNow") :
- this->GetRecycler()->IsInDetachProcess()? _u("DetachProcess") : _u("Destructor"), this->threadId);
- LeakReport::DumpUrl(this->threadId);
- }
- #endif
- if (interruptPoller)
- {
- HeapDelete(interruptPoller);
- interruptPoller = nullptr;
- }
- #if DBG
- // ThreadContext dtor may be running on a different thread.
- // Recycler may call finalizer that free temp Arenas, which will free pages back to
- // the page Allocator, which will try to suspend idle on a different thread.
- // So we need to disable idle decommit asserts.
- pageAllocator.ShutdownIdleDecommit();
- #endif
- // Allocating memory during the shutdown codepath is not preferred
- // so we'll close the page allocator before we release the GC
- // If any dispose is allocating memory during shutdown, that is a bug
- pageAllocator.Close();
- // The recycler need to delete before the background code gen thread
- // because that might run finalizer which need access to the background code gen thread.
- if (recycler != nullptr)
- {
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
- {
- if (!scriptContext->IsActuallyClosed())
- {
- // We close ScriptContext here because anyhow HeapDelete(recycler) when disposing the
- // JavaScriptLibrary will close ScriptContext. Explicit close gives us chance to clear
- // other things to which ScriptContext holds reference to
- AssertMsg(!IsInScript(), "Can we be in script here?");
- scriptContext->MarkForClose();
- }
- }
- // If all scriptContext's have been closed, then the sourceProfileManagersByUrl
- // should have been released
- AssertMsg(this->recyclableData->sourceProfileManagersByUrl == nullptr ||
- this->recyclableData->sourceProfileManagersByUrl->Count() == 0, "There seems to have been a refcounting imbalance.");
- this->recyclableData->sourceProfileManagersByUrl = nullptr;
- this->recyclableData->oldEntryPointInfo = nullptr;
- if (this->recyclableData->symbolRegistrationMap != nullptr)
- {
- this->recyclableData->symbolRegistrationMap->Clear();
- this->recyclableData->symbolRegistrationMap = nullptr;
- }
- if (this->recyclableData->returnedValueList != nullptr)
- {
- this->recyclableData->returnedValueList->Clear();
- this->recyclableData->returnedValueList = nullptr;
- }
- if (this->propertyMap != nullptr)
- {
- HeapDelete(this->propertyMap);
- this->propertyMap = nullptr;
- }
- #if ENABLE_NATIVE_CODEGEN
- if (this->m_jitNumericProperties != nullptr)
- {
- HeapDelete(this->m_jitNumericProperties);
- this->m_jitNumericProperties = nullptr;
- }
- #endif
- // Unpin the memory for leak report so we don't report this as a leak.
- recyclableData.Unroot(recycler);
- #if defined(LEAK_REPORT) || defined(CHECK_MEMORY_LEAK)
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
- {
- scriptContext->ClearSourceContextInfoMaps();
- scriptContext->ShutdownClearSourceLists();
- }
- #ifdef LEAK_REPORT
- // heuristically figure out which one is the root tracker script engine
- // and force close on it
- if (this->rootTrackerScriptContext != nullptr)
- {
- this->rootTrackerScriptContext->Close(false);
- }
- #endif
- #endif
- #if ENABLE_NATIVE_CODEGEN
- #if !FLOATVAR
- if (this->codeGenNumberThreadAllocator)
- {
- HeapDelete(this->codeGenNumberThreadAllocator);
- this->codeGenNumberThreadAllocator = nullptr;
- }
- if (this->xProcNumberPageSegmentManager)
- {
- HeapDelete(this->xProcNumberPageSegmentManager);
- this->xProcNumberPageSegmentManager = nullptr;
- }
- #endif
- #endif
- Assert(this->debugManager == nullptr);
- HeapDelete(recycler);
- }
- #if ENABLE_NATIVE_CODEGEN
- if(jobProcessor)
- {
- if(this->bgJit)
- {
- HeapDelete(static_cast<JsUtil::BackgroundJobProcessor *>(jobProcessor));
- }
- else
- {
- HeapDelete(static_cast<JsUtil::ForegroundJobProcessor *>(jobProcessor));
- }
- jobProcessor = nullptr;
- }
- #endif
- // Do not require all GC callbacks to be revoked, because Trident may not revoke if there
- // is a leak, and we don't want the leak to be masked by an assert
- #ifdef ENABLE_PROJECTION
- externalWeakReferenceCacheList.Clear(&HeapAllocator::Instance);
- #endif
- this->collectCallBackList.Clear(&HeapAllocator::Instance);
- this->protoInlineCacheByPropId.Reset();
- this->storeFieldInlineCacheByPropId.Reset();
- this->isInstInlineCacheByFunction.Reset();
- this->equivalentTypeCacheEntryPoints.Reset();
- this->prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext.Reset();
- this->registeredInlineCacheCount = 0;
- this->unregisteredInlineCacheCount = 0;
- AssertMsg(this->GetHeapEnum() == nullptr, "Heap enumeration should have been cleared/closed by the ScriptSite.");
- if (this->GetHeapEnum() != nullptr)
- {
- this->ClearHeapEnum();
- }
- #ifdef BAILOUT_INJECTION
- if (Js::Configuration::Global.flags.IsEnabled(Js::BailOutByteCodeFlag)
- && Js::Configuration::Global.flags.BailOutByteCode.Empty())
- {
- Output::Print(_u("Bail out byte code location count: %d"), this->bailOutByteCodeLocationCount);
- }
- #endif
- Assert(processNativeCodeSize >= nativeCodeSize);
- ::InterlockedExchangeSubtract(&processNativeCodeSize, nativeCodeSize);
- PERF_COUNTER_DEC(Basic, ThreadContext);
- #ifdef DYNAMIC_PROFILE_MUTATOR
- if (this->dynamicProfileMutator != nullptr)
- {
- this->dynamicProfileMutator->Delete();
- }
- #endif
- #ifdef ENABLE_PROJECTION
- #if DBG_DUMP
- if (this->projectionMemoryInformation)
- {
- this->projectionMemoryInformation->Release();
- this->projectionMemoryInformation = nullptr;
- }
- #endif
- #endif
- }
- void
- ThreadContext::SetJSRTRuntime(void* runtime)
- {
- Assert(jsrtRuntime == nullptr);
- jsrtRuntime = runtime;
- #ifdef ENABLE_BASIC_TELEMETRY
- Telemetry::EnsureInitializeForJSRT();
- #endif
- }
- void ThreadContext::CloseForJSRT()
- {
- // This is used for JSRT APIs only.
- Assert(this->jsrtRuntime);
- #ifdef ENABLE_BASIC_TELEMETRY
- // log any relevant telemetry before disposing the current thread for cases which are properly shutdown
- Telemetry::OnJSRTThreadContextClose();
- #endif
- ShutdownThreads();
- }
- ThreadContext* ThreadContext::GetContextForCurrentThread()
- {
- ThreadContextTLSEntry * tlsEntry = ThreadContextTLSEntry::GetEntryForCurrentThread();
- if (tlsEntry != nullptr)
- {
- return static_cast<ThreadContext *>(tlsEntry->GetThreadContext());
- }
- return nullptr;
- }
- void ThreadContext::ValidateThreadContext()
- {
- #if DBG
- // verify the runtime pointer is valid.
- {
- BOOL found = FALSE;
- AutoCriticalSection autocs(ThreadContext::GetCriticalSection());
- ThreadContext* currentThreadContext = GetThreadContextList();
- while (currentThreadContext)
- {
- if (currentThreadContext == this)
- {
- return;
- }
- currentThreadContext = currentThreadContext->Next();
- }
- AssertMsg(found, "invalid thread context");
- }
- #endif
- }
- #if ENABLE_NATIVE_CODEGEN && defined(ENABLE_SIMDJS)
- void ThreadContext::AddSimdFuncToMaps(Js::OpCode op, ...)
- {
- Assert(simdFuncInfoToOpcodeMap != nullptr);
- Assert(simdOpcodeToSignatureMap != nullptr);
- va_list arguments;
- va_start(arguments, op);
- int argumentsCount = va_arg(arguments, int);
- AssertMsg(argumentsCount >= 0 && argumentsCount <= 20, "Invalid arguments count for SIMD opcode");
- if (argumentsCount == 0)
- {
- // no info to add
- return;
- }
- Js::FunctionInfo *funcInfo = va_arg(arguments, Js::FunctionInfo*);
- AddSimdFuncInfo(op, funcInfo);
- SimdFuncSignature simdFuncSignature;
- simdFuncSignature.valid = true;
- simdFuncSignature.argCount = argumentsCount - 2; // arg count to Simd func = argumentsCount - FuncInfo and return Type fields.
- simdFuncSignature.returnType = va_arg(arguments, ValueType);
- simdFuncSignature.args = AnewArrayZ(this->GetThreadAlloc(), ValueType, simdFuncSignature.argCount);
- for (uint iArg = 0; iArg < simdFuncSignature.argCount; iArg++)
- {
- simdFuncSignature.args[iArg] = va_arg(arguments, ValueType);
- }
- simdOpcodeToSignatureMap[Js::SIMDUtils::SimdOpcodeAsIndex(op)] = simdFuncSignature;
- va_end(arguments);
- }
- void ThreadContext::AddSimdFuncInfo(Js::OpCode op, Js::FunctionInfo *funcInfo)
- {
- // primary funcInfo
- simdFuncInfoToOpcodeMap->AddNew(funcInfo, op);
- // Entry points of SIMD loads/stores of non-full width all map to the same opcode. This is not captured in the opcode table, so add additional entry points here.
- switch (op)
- {
- case Js::OpCode::Simd128_LdArr_F4:
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Load1, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Load2, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Load3, op);
- break;
- case Js::OpCode::Simd128_StArr_F4:
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Store1, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Store2, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDFloat32x4Lib::EntryInfo::Store3, op);
- break;
- case Js::OpCode::Simd128_LdArr_I4:
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Load1, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Load2, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Load3, op);
- break;
- case Js::OpCode::Simd128_StArr_I4:
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Store1, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Store2, op);
- simdFuncInfoToOpcodeMap->AddNew(&Js::SIMDInt32x4Lib::EntryInfo::Store3, op);
- break;
- }
- }
- Js::OpCode ThreadContext::GetSimdOpcodeFromFuncInfo(Js::FunctionInfo * funcInfo)
- {
- Assert(simdFuncInfoToOpcodeMap != nullptr);
- if (simdFuncInfoToOpcodeMap->ContainsKey(funcInfo))
- {
- return simdFuncInfoToOpcodeMap->Item(funcInfo);
- }
- return (Js::OpCode) 0;
- }
- void ThreadContext::GetSimdFuncSignatureFromOpcode(Js::OpCode op, SimdFuncSignature &funcSignature)
- {
- Assert(simdOpcodeToSignatureMap != nullptr);
- funcSignature = simdOpcodeToSignatureMap[Js::SIMDUtils::SimdOpcodeAsIndex(op)];
- }
- #endif
- class AutoRecyclerPtr : public AutoPtr<Recycler>
- {
- public:
- AutoRecyclerPtr(Recycler * ptr) : AutoPtr<Recycler>(ptr) {}
- ~AutoRecyclerPtr()
- {
- #if ENABLE_CONCURRENT_GC
- if (ptr != nullptr)
- {
- ptr->ShutdownThread();
- }
- #endif
- }
- };
- Recycler* ThreadContext::EnsureRecycler()
- {
- if (recycler == NULL)
- {
- AutoRecyclerPtr newRecycler(HeapNew(Recycler, GetAllocationPolicyManager(), &pageAllocator, Js::Throw::OutOfMemory, Js::Configuration::Global.flags));
- newRecycler->Initialize(isOptimizedForManyInstances, &threadService); // use in-thread GC when optimizing for many instances
- newRecycler->SetCollectionWrapper(this);
- #if ENABLE_NATIVE_CODEGEN
- // This may throw, so it needs to be after the recycler is initialized,
- // otherwise, the recycler dtor may encounter problems
- #if !FLOATVAR
- // TODO: we only need one of the following, one for OOP jit and one for in-proc BG JIT
- AutoPtr<CodeGenNumberThreadAllocator> localCodeGenNumberThreadAllocator(
- HeapNew(CodeGenNumberThreadAllocator, newRecycler));
- AutoPtr<XProcNumberPageSegmentManager> localXProcNumberPageSegmentManager(
- HeapNew(XProcNumberPageSegmentManager, newRecycler));
- #endif
- #endif
- this->recyclableData.Root(RecyclerNewZ(newRecycler, RecyclableData, newRecycler), newRecycler);
- if (this->IsThreadBound())
- {
- newRecycler->SetIsThreadBound();
- }
- // Assign the recycler to the ThreadContext after everything is initialized, because an OOM during initialization would
- // result in only partial initialization, so the 'recycler' member variable should remain null to cause full
- // reinitialization when requested later. Anything that happens after the Detach must have special cleanup code.
- this->recycler = newRecycler.Detach();
- try
- {
- #ifdef RECYCLER_WRITE_BARRIER
- #ifdef _M_X64_OR_ARM64
- if (!RecyclerWriteBarrierManager::OnThreadInit())
- {
- Js::Throw::OutOfMemory();
- }
- #endif
- #endif
- this->expirableObjectList = Anew(&this->threadAlloc, ExpirableObjectList, &this->threadAlloc);
- this->expirableObjectDisposeList = Anew(&this->threadAlloc, ExpirableObjectList, &this->threadAlloc);
- InitializePropertyMaps(); // has many dependencies on the recycler and other members of the thread context
- #if ENABLE_NATIVE_CODEGEN
- #if !FLOATVAR
- this->codeGenNumberThreadAllocator = localCodeGenNumberThreadAllocator.Detach();
- this->xProcNumberPageSegmentManager = localXProcNumberPageSegmentManager.Detach();
- #endif
- #endif
- }
- catch(...)
- {
- // Initialization failed, undo what was done above. Callees that throw must clean up after themselves.
- if (this->recyclableData != nullptr)
- {
- this->recyclableData.Unroot(this->recycler);
- }
- {
- // AutoRecyclerPtr's destructor takes care of shutting down the background thread and deleting the recycler
- AutoRecyclerPtr recyclerToDelete(this->recycler);
- this->recycler = nullptr;
- }
- throw;
- }
- JS_ETW(EventWriteJSCRIPT_GC_INIT(this->recycler, this->GetHiResTimer()->Now()));
- }
- #if DBG
- if (CONFIG_FLAG(RecyclerTest))
- {
- StressTester test(recycler);
- test.Run();
- }
- #endif
- return recycler;
- }
- Js::PropertyRecord const *
- ThreadContext::GetPropertyName(Js::PropertyId propertyId)
- {
- // This API should only be use on the main thread
- Assert(GetCurrentThreadContextId() == (ThreadContextId)this);
- return this->GetPropertyNameImpl<false>(propertyId);
- }
- Js::PropertyRecord const *
- ThreadContext::GetPropertyNameLocked(Js::PropertyId propertyId)
- {
- return GetPropertyNameImpl<true>(propertyId);
- }
- template <bool locked>
- Js::PropertyRecord const *
- ThreadContext::GetPropertyNameImpl(Js::PropertyId propertyId)
- {
- //TODO: Remove this when completely transformed to use PropertyRecord*. Currently this is only partially done,
- // and there are calls to GetPropertyName with InternalPropertyId.
- if (propertyId >= 0 && Js::IsInternalPropertyId(propertyId))
- {
- return Js::InternalPropertyRecords::GetInternalPropertyName(propertyId);
- }
- int propertyIndex = propertyId - Js::PropertyIds::_none;
- if (propertyIndex < 0 || propertyIndex > propertyMap->GetLastIndex())
- {
- propertyIndex = 0;
- }
- const Js::PropertyRecord * propertyRecord = nullptr;
- if (locked) { propertyMap->LockResize(); }
- bool found = propertyMap->TryGetValueAt(propertyIndex, &propertyRecord);
- if (locked) { propertyMap->UnlockResize(); }
- AssertMsg(found && propertyRecord != nullptr, "using invalid propertyid");
- return propertyRecord;
- }
- void
- ThreadContext::FindPropertyRecord(Js::JavascriptString *pstName, Js::PropertyRecord const ** propertyRecord)
- {
- LPCWSTR psz = pstName->GetSz();
- FindPropertyRecord(psz, pstName->GetLength(), propertyRecord);
- }
- void
- ThreadContext::FindPropertyRecord(__in LPCWSTR propertyName, __in int propertyNameLength, Js::PropertyRecord const ** propertyRecord)
- {
- EnterPinnedScope((volatile void **)propertyRecord);
- *propertyRecord = FindPropertyRecord(propertyName, propertyNameLength);
- LeavePinnedScope();
- }
- Js::PropertyRecord const *
- ThreadContext::GetPropertyRecord(Js::PropertyId propertyId)
- {
- return GetPropertyNameLocked(propertyId);
- }
- bool
- ThreadContext::IsNumericProperty(Js::PropertyId propertyId)
- {
- return GetPropertyRecord(propertyId)->IsNumeric();
- }
- const Js::PropertyRecord *
- ThreadContext::FindPropertyRecord(const char16 * propertyName, int propertyNameLength)
- {
- Js::PropertyRecord const * propertyRecord = nullptr;
- if (IsDirectPropertyName(propertyName, propertyNameLength))
- {
- propertyRecord = propertyNamesDirect[propertyName[0]];
- Assert(propertyRecord == propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength)));
- }
- else
- {
- propertyRecord = propertyMap->LookupWithKey(Js::HashedCharacterBuffer<char16>(propertyName, propertyNameLength));
- }
- return propertyRecord;
- }
- Js::PropertyRecord const *
- ThreadContext::UncheckedAddPropertyId(__in LPCWSTR propertyName, __in int propertyNameLength, bool bind, bool isSymbol)
- {
- return UncheckedAddPropertyId(JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength), bind, isSymbol);
- }
- void ThreadContext::InitializePropertyMaps()
- {
- Assert(this->recycler != nullptr);
- Assert(this->recyclableData != nullptr);
- Assert(this->propertyMap == nullptr);
- Assert(this->caseInvariantPropertySet == nullptr);
- try
- {
- this->propertyMap = HeapNew(PropertyMap, &HeapAllocator::Instance, TotalNumberOfBuiltInProperties + 700);
- this->recyclableData->boundPropertyStrings = RecyclerNew(this->recycler, JsUtil::List<Js::PropertyRecord const*>, this->recycler);
- memset(propertyNamesDirect, 0, 128*sizeof(Js::PropertyRecord *));
- Js::JavascriptLibrary::InitializeProperties(this);
- InitializeAdditionalProperties(this);
- //Js::JavascriptLibrary::InitializeDOMProperties(this);
- }
- catch(...)
- {
- // Initialization failed, undo what was done above. Callees that throw must clean up after themselves. The recycler will
- // be trashed, so clear members that point to recyclable memory. Stuff in 'recyclableData' will be taken care of by the
- // recycler, and the 'recyclableData' instance will be trashed as well.
- if (this->propertyMap != nullptr)
- {
- HeapDelete(this->propertyMap);
- }
- this->propertyMap = nullptr;
- this->caseInvariantPropertySet = nullptr;
- memset(propertyNamesDirect, 0, 128*sizeof(Js::PropertyRecord *));
- throw;
- }
- }
- void ThreadContext::UncheckedAddBuiltInPropertyId()
- {
- for (int i = 0; i < _countof(builtInPropertyRecords); i++)
- {
- AddPropertyRecordInternal(builtInPropertyRecords[i]);
- }
- }
- bool
- ThreadContext::IsDirectPropertyName(const char16 * propertyName, int propertyNameLength)
- {
- return ((propertyNameLength == 1) && ((propertyName[0] & 0xFF80) == 0));
- }
- RecyclerWeakReference<const Js::PropertyRecord> *
- ThreadContext::CreatePropertyRecordWeakRef(const Js::PropertyRecord * propertyRecord)
- {
- RecyclerWeakReference<const Js::PropertyRecord> * propertyRecordWeakRef;
- if (propertyRecord->IsBound())
- {
- // Create a fake weak ref
- propertyRecordWeakRef = RecyclerNewLeaf(this->recycler, StaticPropertyRecordReference, propertyRecord);
- }
- else
- {
- propertyRecordWeakRef = recycler->CreateWeakReferenceHandle(propertyRecord);
- }
- return propertyRecordWeakRef;
- }
- Js::PropertyRecord const *
- ThreadContext::UncheckedAddPropertyId(JsUtil::CharacterBuffer<WCHAR> const& propertyName, bool bind, bool isSymbol)
- {
- #if ENABLE_TTD
- if(isSymbol & this->IsRuntimeInTTDMode())
- {
- if(this->TTDContext->GetActiveScriptContext() != nullptr && this->TTDContext->GetActiveScriptContext()->ShouldPerformReplayAction())
- {
- //We reload all properties that occour in the trace so they only way we get here in TTD mode is:
- //(1) if the program is creating a new symbol (which always gets a fresh id) and we should recreate it or
- //(2) if it is forcing arguments in debug parse mode (instead of regular which we recorded in)
- Js::PropertyId propertyId = Js::Constants::NoProperty;
- this->TTDLog->ReplaySymbolCreationEvent(&propertyId);
- //Don't recreate the symbol below, instead return the known symbol by looking up on the pid
- const Js::PropertyRecord* res = this->GetPropertyName(propertyId);
- AssertMsg(res != nullptr, "This should never happen!!!");
- return res;
- }
- }
- #endif
- this->propertyMap->EnsureCapacity();
- // Automatically bind direct (single-character) property names, so that they can be
- // stored in the direct property table
- if (IsDirectPropertyName(propertyName.GetBuffer(), propertyName.GetLength()))
- {
- bind = true;
- }
- // Create the PropertyRecord
- int length = propertyName.GetLength();
- uint bytelength = sizeof(char16) * length;
- uint32 indexVal = 0;
- // Symbol properties cannot be numeric since their description is not to be used!
- bool isNumeric = !isSymbol && Js::PropertyRecord::IsPropertyNameNumeric(propertyName.GetBuffer(), propertyName.GetLength(), &indexVal);
- uint hash = JsUtil::CharacterBuffer<WCHAR>::StaticGetHashCode(propertyName.GetBuffer(), propertyName.GetLength());
- size_t allocLength = bytelength + sizeof(char16) + (isNumeric ? sizeof(uint32) : 0);
- // If it's bound, create it in the thread arena, along with a fake weak ref
- Js::PropertyRecord * propertyRecord;
- if (bind)
- {
- propertyRecord = AnewPlus(GetThreadAlloc(), allocLength, Js::PropertyRecord, bytelength, isNumeric, hash, isSymbol);
- propertyRecord->isBound = true;
- }
- else
- {
- propertyRecord = RecyclerNewFinalizedLeafPlus(recycler, allocLength, Js::PropertyRecord, bytelength, isNumeric, hash, isSymbol);
- }
- // Copy string and numeric info
- char16* buffer = (char16 *)(propertyRecord + 1);
- js_memcpy_s(buffer, bytelength, propertyName.GetBuffer(), bytelength);
- buffer[length] = _u('\0');
- if (isNumeric)
- {
- *(uint32 *)(buffer + length + 1) = indexVal;
- Assert(propertyRecord->GetNumericValue() == indexVal);
- }
- Js::PropertyId propertyId = this->GetNextPropertyId();
- #if ENABLE_TTD
- if(isSymbol & this->IsRuntimeInTTDMode())
- {
- if(this->TTDContext->GetActiveScriptContext() != nullptr && this->TTDContext->GetActiveScriptContext()->ShouldPerformRecordAction())
- {
- this->TTDLog->RecordSymbolCreationEvent(propertyId);
- }
- }
- #endif
- propertyRecord->pid = propertyId;
- AddPropertyRecordInternal(propertyRecord);
- return propertyRecord;
- }
- void
- ThreadContext::AddPropertyRecordInternal(const Js::PropertyRecord * propertyRecord)
- {
- // At this point the PropertyRecord is constructed but not added to the map.
- const char16 * propertyName = propertyRecord->GetBuffer();
- int propertyNameLength = propertyRecord->GetLength();
- Js::PropertyId propertyId = propertyRecord->GetPropertyId();
- Assert(propertyId == GetNextPropertyId());
- Assert(!IsActivePropertyId(propertyId));
- #if DBG
- // Only Assert we can't find the property if we are not adding a symbol.
- // For a symbol, the propertyName is not used and may collide with something in the map already.
- if (!propertyRecord->IsSymbol())
- {
- Assert(FindPropertyRecord(propertyName, propertyNameLength) == nullptr);
- }
- #endif
- #if ENABLE_TTD
- if(this->IsRuntimeInTTDMode())
- {
- this->TTDLog->AddPropertyRecord(propertyRecord);
- }
- #endif
- // Add to the map
- propertyMap->Add(propertyRecord);
- #if ENABLE_NATIVE_CODEGEN
- if (m_jitNumericProperties)
- {
- if (propertyRecord->IsNumeric())
- {
- m_jitNumericProperties->Set(propertyRecord->GetPropertyId());
- m_jitNeedsPropertyUpdate = true;
- }
- }
- #endif
- PropertyRecordTrace(_u("Added property '%s' at 0x%08x, pid = %d\n"), propertyName, propertyRecord, propertyId);
- // Do not store the pid for symbols in the direct property name table.
- // We don't want property ids for symbols to be searchable anyway.
- if (!propertyRecord->IsSymbol() && IsDirectPropertyName(propertyName, propertyNameLength))
- {
- // Store the pids for single character properties in the propertyNamesDirect array.
- // This property record should have been created as bound by the caller.
- Assert(propertyRecord->IsBound());
- Assert(propertyNamesDirect[propertyName[0]] == nullptr);
- propertyNamesDirect[propertyName[0]] = propertyRecord;
- }
- if (caseInvariantPropertySet)
- {
- AddCaseInvariantPropertyRecord(propertyRecord);
- }
- // Check that everything was added correctly
- #if DBG
- // Only Assert we can find the property if we are not adding a symbol.
- // For a symbol, the propertyName is not used and we won't be able to look the pid up via name.
- if (!propertyRecord->IsSymbol())
- {
- Assert(FindPropertyRecord(propertyName, propertyNameLength) == propertyRecord);
- }
- // We will still be able to lookup the symbol property by the property id, so go ahead and check that.
- Assert(GetPropertyName(propertyRecord->GetPropertyId()) == propertyRecord);
- #endif
- JS_ETW_INTERNAL(EventWriteJSCRIPT_HOSTING_PROPERTYID_LIST(propertyRecord, propertyRecord->GetBuffer()));
- }
- void
- ThreadContext::AddCaseInvariantPropertyRecord(const Js::PropertyRecord * propertyRecord)
- {
- Assert(this->caseInvariantPropertySet != nullptr);
- // Create a weak reference to the property record here (since we no longer use weak refs in the property map)
- RecyclerWeakReference<const Js::PropertyRecord> * propertyRecordWeakRef = CreatePropertyRecordWeakRef(propertyRecord);
- JsUtil::CharacterBuffer<WCHAR> newPropertyName(propertyRecord->GetBuffer(), propertyRecord->GetLength());
- Js::CaseInvariantPropertyListWithHashCode* list;
- if (!FindExistingPropertyRecord(newPropertyName, &list))
- {
- // This binds all the property string that is key in this map with no hope of reclaiming them
- // TODO: do better
- list = RecyclerNew(recycler, Js::CaseInvariantPropertyListWithHashCode, recycler, 1);
- // Do the add first so that the list is non-empty and we can calculate its hashcode correctly
- list->Add(propertyRecordWeakRef);
- // This will calculate the hashcode
- caseInvariantPropertySet->Add(list);
- }
- else
- {
- list->Add(propertyRecordWeakRef);
- }
- }
- void
- ThreadContext::BindPropertyRecord(const Js::PropertyRecord * propertyRecord)
- {
- if (!propertyRecord->IsBound())
- {
- Assert(!this->recyclableData->boundPropertyStrings->Contains(propertyRecord));
- this->recyclableData->boundPropertyStrings->Add(propertyRecord);
- // Cast around constness to set propertyRecord as bound
- const_cast<Js::PropertyRecord *>(propertyRecord)->isBound = true;
- }
- }
- void ThreadContext::GetOrAddPropertyId(__in LPCWSTR propertyName, __in int propertyNameLength, Js::PropertyRecord const ** propertyRecord)
- {
- GetOrAddPropertyId(JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength), propertyRecord);
- }
- void ThreadContext::GetOrAddPropertyId(JsUtil::CharacterBuffer<WCHAR> const& propertyName, Js::PropertyRecord const ** propRecord)
- {
- EnterPinnedScope((volatile void **)propRecord);
- *propRecord = GetOrAddPropertyRecord(propertyName);
- LeavePinnedScope();
- }
- const Js::PropertyRecord *
- ThreadContext::GetOrAddPropertyRecordImpl(JsUtil::CharacterBuffer<char16> propertyName, bool bind)
- {
- // Make sure the recycler is around so that we can take weak references to the property strings
- EnsureRecycler();
- const Js::PropertyRecord * propertyRecord;
- FindPropertyRecord(propertyName.GetBuffer(), propertyName.GetLength(), &propertyRecord);
- if (propertyRecord == nullptr)
- {
- propertyRecord = UncheckedAddPropertyId(propertyName, bind);
- }
- else
- {
- // PropertyRecord exists, but may not be bound. Bind now if requested.
- if (bind)
- {
- BindPropertyRecord(propertyRecord);
- }
- }
- Assert(propertyRecord != nullptr);
- Assert(!bind || propertyRecord->IsBound());
- return propertyRecord;
- }
- void ThreadContext::AddBuiltInPropertyRecord(const Js::PropertyRecord *propertyRecord)
- {
- this->AddPropertyRecordInternal(propertyRecord);
- }
- BOOL ThreadContext::IsNumericPropertyId(Js::PropertyId propertyId, uint32* value)
- {
- Js::PropertyRecord const * propertyRecord = this->GetPropertyName(propertyId);
- Assert(propertyRecord != nullptr);
- if (propertyRecord == nullptr || !propertyRecord->IsNumeric())
- {
- return false;
- }
- *value = propertyRecord->GetNumericValue();
- return true;
- }
- bool ThreadContext::IsActivePropertyId(Js::PropertyId pid)
- {
- Assert(pid != Js::Constants::NoProperty);
- if (Js::IsInternalPropertyId(pid))
- {
- return true;
- }
- int propertyIndex = pid - Js::PropertyIds::_none;
- const Js::PropertyRecord * propertyRecord;
- if (propertyMap->TryGetValueAt(propertyIndex, &propertyRecord) && propertyRecord != nullptr)
- {
- return true;
- }
- return false;
- }
- void ThreadContext::InvalidatePropertyRecord(const Js::PropertyRecord * propertyRecord)
- {
- InternalInvalidateProtoTypePropertyCaches(propertyRecord->GetPropertyId()); // use the internal version so we don't check for active property id
- #if ENABLE_NATIVE_CODEGEN
- if (propertyRecord->IsNumeric() && m_jitNumericProperties)
- {
- m_jitNumericProperties->Clear(propertyRecord->GetPropertyId());
- m_jitNeedsPropertyUpdate = true;
- }
- #endif
- this->propertyMap->Remove(propertyRecord);
- PropertyRecordTrace(_u("Reclaimed property '%s' at 0x%08x, pid = %d\n"),
- propertyRecord->GetBuffer(), propertyRecord, propertyRecord->GetPropertyId());
- }
- Js::PropertyId ThreadContext::GetNextPropertyId()
- {
- return this->propertyMap->GetNextIndex() + Js::PropertyIds::_none;
- }
- Js::PropertyId ThreadContext::GetMaxPropertyId()
- {
- auto maxPropertyId = this->propertyMap->Count() + Js::InternalPropertyIds::Count;
- return maxPropertyId;
- }
- void ThreadContext::CreateNoCasePropertyMap()
- {
- Assert(caseInvariantPropertySet == nullptr);
- caseInvariantPropertySet = RecyclerNew(recycler, PropertyNoCaseSetType, recycler, 173);
- // Prevent the set from being reclaimed
- // Individual items in the set can be reclaimed though since they're lists of weak references
- // The lists themselves can be reclaimed when all the weak references in them are cleared
- this->recyclableData->caseInvariantPropertySet = caseInvariantPropertySet;
- // Note that we are allocating from the recycler below, so we may cause a GC at any time, which
- // could cause PropertyRecords to be collected and removed from the propertyMap.
- // Thus, don't use BaseDictionary::Map here, as it cannot tolerate changes while mapping.
- // Instead, walk the PropertyRecord entries in index order. This will work even if a GC occurs.
- for (int propertyIndex = 0; propertyIndex <= this->propertyMap->GetLastIndex(); propertyIndex++)
- {
- const Js::PropertyRecord * propertyRecord;
- if (this->propertyMap->TryGetValueAt(propertyIndex, &propertyRecord) && propertyRecord != nullptr)
- {
- AddCaseInvariantPropertyRecord(propertyRecord);
- }
- }
- }
- JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>*
- ThreadContext::FindPropertyIdNoCase(Js::ScriptContext * scriptContext, LPCWSTR propertyName, int propertyNameLength)
- {
- return ThreadContext::FindPropertyIdNoCase(scriptContext, JsUtil::CharacterBuffer<WCHAR>(propertyName, propertyNameLength));
- }
- JsUtil::List<const RecyclerWeakReference<Js::PropertyRecord const>*>*
- ThreadContext::FindPropertyIdNoCase(Js::ScriptContext * scriptContext, JsUtil::CharacterBuffer<WCHAR> const& propertyName)
- {
- if (caseInvariantPropertySet == nullptr)
- {
- this->CreateNoCasePropertyMap();
- }
- Js::CaseInvariantPropertyListWithHashCode* list;
- if (FindExistingPropertyRecord(propertyName, &list))
- {
- return list;
- }
- return nullptr;
- }
- bool
- ThreadContext::FindExistingPropertyRecord(_In_ JsUtil::CharacterBuffer<WCHAR> const& propertyName, Js::CaseInvariantPropertyListWithHashCode** list)
- {
- Js::CaseInvariantPropertyListWithHashCode* l = this->caseInvariantPropertySet->LookupWithKey(propertyName);
- (*list) = l;
- return (l != nullptr);
- }
- void ThreadContext::CleanNoCasePropertyMap()
- {
- if (this->caseInvariantPropertySet != nullptr)
- {
- this->caseInvariantPropertySet->MapAndRemoveIf([](Js::CaseInvariantPropertyListWithHashCode* value) -> bool {
- if (value && value->Count() == 0)
- {
- // Remove entry
- return true;
- }
- // Keep entry
- return false;
- });
- }
- }
- void
- ThreadContext::ForceCleanPropertyMap()
- {
- // No-op now that we no longer use weak refs
- }
- #if ENABLE_NATIVE_CODEGEN
- JsUtil::JobProcessor *
- ThreadContext::GetJobProcessor()
- {
- if(bgJit && isOptimizedForManyInstances)
- {
- return ThreadBoundThreadContextManager::GetSharedJobProcessor();
- }
- if (!jobProcessor)
- {
- if(bgJit && !isOptimizedForManyInstances)
- {
- jobProcessor = HeapNew(JsUtil::BackgroundJobProcessor, GetAllocationPolicyManager(), &threadService, false /*disableParallelThreads*/);
- }
- else
- {
- jobProcessor = HeapNew(JsUtil::ForegroundJobProcessor);
- }
- }
- return jobProcessor;
- }
- #endif
- void
- ThreadContext::RegisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData)
- {
- Assert(codeGenRecyclableData);
- Assert(recyclableData);
- // Linking must not be done concurrently with unlinking (caller must use lock)
- recyclableData->codeGenRecyclableDatas.LinkToEnd(codeGenRecyclableData);
- }
- void
- ThreadContext::UnregisterCodeGenRecyclableData(Js::CodeGenRecyclableData *const codeGenRecyclableData)
- {
- Assert(codeGenRecyclableData);
- if(!recyclableData)
- {
- // The thread context's recyclable data may have already been released to the recycler if we're shutting down
- return;
- }
- // Unlinking may be done from a background thread, but not concurrently with linking (caller must use lock). Partial unlink
- // does not zero the previous and next links for the unlinked node so that the recycler can scan through the node from the
- // main thread.
- recyclableData->codeGenRecyclableDatas.UnlinkPartial(codeGenRecyclableData);
- }
- uint
- ThreadContext::EnterScriptStart(Js::ScriptEntryExitRecord * record, bool doCleanup)
- {
- Recycler * recycler = this->GetRecycler();
- Assert(recycler->IsReentrantState());
- JS_ETW_INTERNAL(EventWriteJSCRIPT_RUN_START(this,0));
- // Increment the callRootLevel early so that Dispose ran during FinishConcurrent will not close the current scriptContext
- uint oldCallRootLevel = this->callRootLevel++;
- if (oldCallRootLevel == 0)
- {
- Assert(!this->hasThrownPendingException);
- RECORD_TIMESTAMP(lastScriptStartTime);
- InterruptPoller *poller = this->interruptPoller;
- if (poller)
- {
- poller->StartScript();
- }
- recycler->SetIsInScript(true);
- if (doCleanup)
- {
- recycler->EnterIdleDecommit();
- #if ENABLE_CONCURRENT_GC
- recycler->FinishConcurrent<FinishConcurrentOnEnterScript>();
- #endif
- if (threadServiceWrapper == NULL)
- {
- // Reschedule the next collection at the start of the script.
- recycler->ScheduleNextCollection();
- }
- }
- }
- this->PushEntryExitRecord(record);
- AssertMsg(!this->IsScriptActive(),
- "Missing EnterScriptEnd or LeaveScriptStart");
- this->isScriptActive = true;
- recycler->SetIsScriptActive(true);
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
- {
- Output::Trace(Js::RunPhase, _u("%p> EnterScriptStart(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
- Output::Flush();
- }
- #endif
- return oldCallRootLevel;
- }
- void
- ThreadContext::EnterScriptEnd(Js::ScriptEntryExitRecord * record, bool doCleanup)
- {
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
- {
- Output::Trace(Js::RunPhase, _u("%p> EnterScriptEnd (%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
- Output::Flush();
- }
- #endif
- this->PopEntryExitRecord(record);
- AssertMsg(this->IsScriptActive(),
- "Missing EnterScriptStart or LeaveScriptEnd");
- this->isScriptActive = false;
- this->GetRecycler()->SetIsScriptActive(false);
- this->callRootLevel--;
- #ifdef EXCEPTION_CHECK
- ExceptionCheck::SetHandledExceptionType(record->handledExceptionType);
- #endif
- #ifdef RECYCLER_MEMORY_VERIFY
- recycler->Verify(Js::RunPhase);
- #endif
- if (this->callRootLevel == 0)
- {
- RECORD_TIMESTAMP(lastScriptEndTime);
- this->GetRecycler()->SetIsInScript(false);
- InterruptPoller *poller = this->interruptPoller;
- if (poller)
- {
- poller->EndScript();
- }
- ClosePendingProjectionContexts();
- ClosePendingScriptContexts();
- Assert(rootPendingClose == nullptr);
- if (this->hasThrownPendingException)
- {
- // We have some cases where the thread instant of JavascriptExceptionObject
- // are ignored and not clear. To avoid leaks, clear it here when
- // we are not in script, where no one should be using these JavascriptExceptionObject
- this->ClearPendingOOMError();
- this->ClearPendingSOError();
- this->hasThrownPendingException = false;
- }
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.FreeRejittedCode)
- #endif
- {
- // Since we're no longer in script, old entry points can now be collected
- Js::FunctionEntryPointInfo* current = this->recyclableData->oldEntryPointInfo;
- this->recyclableData->oldEntryPointInfo = nullptr;
- // Clear out the next pointers so older entry points wont be held on
- // as a false positive
- while (current != nullptr)
- {
- Js::FunctionEntryPointInfo* next = current->nextEntryPoint;
- current->nextEntryPoint = nullptr;
- current = next;
- }
- }
- if (doCleanup)
- {
- ThreadServiceWrapper* threadServiceWrapper = GetThreadServiceWrapper();
- if (!threadServiceWrapper || !threadServiceWrapper->ScheduleNextCollectOnExit())
- {
- // Do the idle GC now if we fail schedule one.
- recycler->CollectNow<CollectOnScriptExit>();
- }
- recycler->LeaveIdleDecommit();
- }
- }
- if (doCleanup)
- {
- PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in EnterScriptEnd: %d\n"), this->recycler->NeedDispose());
- if (this->recycler->NeedDispose())
- {
- this->recycler->FinishDisposeObjectsNow<FinishDispose>();
- }
- }
- JS_ETW_INTERNAL(EventWriteJSCRIPT_RUN_STOP(this,0));
- }
- void
- ThreadContext::SetForceOneIdleCollection()
- {
- ThreadServiceWrapper* threadServiceWrapper = GetThreadServiceWrapper();
- if (threadServiceWrapper)
- {
- threadServiceWrapper->SetForceOneIdleCollection();
- }
- }
- BOOLEAN
- ThreadContext::IsOnStack(void const *ptr)
- {
- #if defined(_M_IX86) && defined(_MSC_VER)
- return ptr < (void*)__readfsdword(0x4) && ptr >= (void*)__readfsdword(0xE0C);
- #elif defined(_M_AMD64) && defined(_MSC_VER)
- return ptr < (void*)__readgsqword(0x8) && ptr >= (void*)__readgsqword(0x1478);
- #elif defined(_M_ARM)
- ULONG lowLimit, highLimit;
- ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
- bool isOnStack = (void*)lowLimit <= ptr && ptr < (void*)highLimit;
- return isOnStack;
- #elif defined(_M_ARM64)
- ULONG64 lowLimit, highLimit;
- ::GetCurrentThreadStackLimits(&lowLimit, &highLimit);
- bool isOnStack = (void*)lowLimit <= ptr && ptr < (void*)highLimit;
- return isOnStack;
- #elif !defined(_MSC_VER)
- return ::IsAddressOnStack((ULONG_PTR) ptr);
- #else
- AssertMsg(FALSE, "IsOnStack -- not implemented yet case");
- Js::Throw::NotImplemented();
- return false;
- #endif
- }
- size_t
- ThreadContext::GetStackLimitForCurrentThread() const
- {
- FAULTINJECT_SCRIPT_TERMINATION;
- size_t limit = this->stackLimitForCurrentThread;
- Assert(limit == Js::Constants::StackLimitForScriptInterrupt
- || !this->GetStackProber()
- || limit == this->GetStackProber()->GetScriptStackLimit());
- return limit;
- }
- void
- ThreadContext::SetStackLimitForCurrentThread(size_t limit)
- {
- this->stackLimitForCurrentThread = limit;
- }
- _NOINLINE //Win8 947081: might use wrong _AddressOfReturnAddress() if this and caller are inlined
- bool
- ThreadContext::IsStackAvailable(size_t size)
- {
- size_t sp = (size_t)_AddressOfReturnAddress();
- size_t stackLimit = this->GetStackLimitForCurrentThread();
- bool stackAvailable = (sp > size && (sp - size) > stackLimit);
- // Verify that JIT'd frames didn't mess up the ABI stack alignment
- Assert(((uintptr_t)sp & (AutoSystemInfo::StackAlign - 1)) == (sizeof(void*) & (AutoSystemInfo::StackAlign - 1)));
- #if DBG
- this->GetStackProber()->AdjustKnownStackLimit(sp, size);
- #endif
- FAULTINJECT_STACK_PROBE
- if (stackAvailable)
- {
- return true;
- }
- if (sp <= stackLimit)
- {
- if (stackLimit == Js::Constants::StackLimitForScriptInterrupt)
- {
- if (sp <= this->GetStackProber()->GetScriptStackLimit())
- {
- // Take down the process if we cant recover from the stack overflow
- Js::Throw::FatalInternalError();
- }
- }
- }
- return false;
- }
- _NOINLINE //Win8 947081: might use wrong _AddressOfReturnAddress() if this and caller are inlined
- bool
- ThreadContext::IsStackAvailableNoThrow(size_t size)
- {
- size_t sp = (size_t)_AddressOfReturnAddress();
- size_t stackLimit = this->GetStackLimitForCurrentThread();
- bool stackAvailable = (sp > stackLimit) && (sp > size) && ((sp - size) > stackLimit);
- FAULTINJECT_STACK_PROBE
- return stackAvailable;
- }
- /* static */ bool
- ThreadContext::IsCurrentStackAvailable(size_t size)
- {
- ThreadContext *currentContext = GetContextForCurrentThread();
- Assert(currentContext);
- return currentContext->IsStackAvailable(size);
- }
- /*
- returnAddress will be passed in the stackprobe call at the beginning of interpreter frame.
- We need to probe the stack before we link up the InterpreterFrame structure in threadcontext,
- and if we throw there, the stack walker might get confused when trying to identify a frame
- is interpreter frame by comparing the current ebp in ebp chain with return address specified
- in the last InterpreterFrame linked in threadcontext. We need to pass in the return address
- of the probing frame to skip the right one (we need to skip first match in a->a->a recursion,
- but not in a->b->a recursion).
- */
- void
- ThreadContext::ProbeStackNoDispose(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress)
- {
- AssertCanHandleStackOverflow();
- if (!this->IsStackAvailable(size))
- {
- if (this->IsExecutionDisabled())
- {
- // The probe failed because we hammered the stack limit to trigger script interrupt.
- Assert(this->DoInterruptProbe());
- throw Js::ScriptAbortException();
- }
- Js::Throw::StackOverflow(scriptContext, returnAddress);
- }
- #if defined(NTBUILD) || defined(__IOS__) || defined(__ANDROID__)
- // Use every Nth stack probe as a QC trigger.
- if (AutoSystemInfo::ShouldQCMoreFrequently() && this->HasInterruptPoller() && this->IsScriptActive())
- {
- ++(this->stackProbeCount);
- if (this->stackProbeCount > ThreadContext::StackProbePollThreshold)
- {
- this->stackProbeCount = 0;
- this->CheckInterruptPoll();
- }
- }
- #endif
- }
- void
- ThreadContext::ProbeStack(size_t size, Js::ScriptContext *scriptContext, PVOID returnAddress)
- {
- this->ProbeStackNoDispose(size, scriptContext, returnAddress);
- // BACKGROUND-GC TODO: If we're stuck purely in JITted code, we should have the
- // background GC thread modify the threads stack limit to trigger the runtime stack probe
- if (this->callDispose && this->recycler->NeedDispose())
- {
- PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in ProbeStack: %d\n"), this->recycler->NeedDispose());
- this->recycler->FinishDisposeObjectsNow<FinishDisposeTimed>();
- }
- }
- void
- ThreadContext::ProbeStack(size_t size, Js::RecyclableObject * obj, Js::ScriptContext *scriptContext)
- {
- AssertCanHandleStackOverflowCall(obj->IsExternal() ||
- (Js::JavascriptOperators::GetTypeId(obj) == Js::TypeIds_Function &&
- Js::JavascriptFunction::FromVar(obj)->IsExternalFunction()));
- if (!this->IsStackAvailable(size))
- {
- if (this->IsExecutionDisabled())
- {
- // The probe failed because we hammered the stack limit to trigger script interrupt.
- Assert(this->DoInterruptProbe());
- throw Js::ScriptAbortException();
- }
- if (obj->IsExternal() ||
- (Js::JavascriptOperators::GetTypeId(obj) == Js::TypeIds_Function &&
- Js::JavascriptFunction::FromVar(obj)->IsExternalFunction()))
- {
- Js::JavascriptError::ThrowStackOverflowError(scriptContext);
- }
- Js::Throw::StackOverflow(scriptContext, NULL);
- }
- }
- void
- ThreadContext::ProbeStack(size_t size)
- {
- Assert(this->IsScriptActive());
- Js::ScriptEntryExitRecord *entryExitRecord = this->GetScriptEntryExit();
- Assert(entryExitRecord);
- Js::ScriptContext *scriptContext = entryExitRecord->scriptContext;
- Assert(scriptContext);
- this->ProbeStack(size, scriptContext);
- }
- /* static */ void
- ThreadContext::ProbeCurrentStack(size_t size, Js::ScriptContext *scriptContext)
- {
- Assert(scriptContext != nullptr);
- Assert(scriptContext->GetThreadContext() == GetContextForCurrentThread());
- scriptContext->GetThreadContext()->ProbeStack(size, scriptContext);
- }
- /* static */ void
- ThreadContext::ProbeCurrentStackNoDispose(size_t size, Js::ScriptContext *scriptContext)
- {
- Assert(scriptContext != nullptr);
- Assert(scriptContext->GetThreadContext() == GetContextForCurrentThread());
- scriptContext->GetThreadContext()->ProbeStackNoDispose(size, scriptContext);
- }
- template <bool leaveForHost>
- void
- ThreadContext::LeaveScriptStart(void * frameAddress)
- {
- Assert(this->IsScriptActive());
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
- {
- Output::Trace(Js::RunPhase, _u("%p> LeaveScriptStart(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
- Output::Flush();
- }
- #endif
- Js::ScriptEntryExitRecord * entryExitRecord = this->GetScriptEntryExit();
- AssertMsg(entryExitRecord && entryExitRecord->frameIdOfScriptExitFunction == nullptr,
- "Missing LeaveScriptEnd or EnterScriptStart");
- entryExitRecord->frameIdOfScriptExitFunction = frameAddress;
- this->isScriptActive = false;
- this->GetRecycler()->SetIsScriptActive(false);
- AssertMsg(!(leaveForHost && this->IsDisableImplicitCall()),
- "Disable implicit call should have been caught before leaving script for host");
- // Save the implicit call flags
- entryExitRecord->savedImplicitCallFlags = this->GetImplicitCallFlags();
- // clear the hasReentered to detect if we have reentered into script
- entryExitRecord->hasReentered = false;
- #if DBG || defined(PROFILE_EXEC)
- entryExitRecord->leaveForHost = leaveForHost;
- #endif
- #if DBG
- entryExitRecord->leaveForAsyncHostOperation = false;
- #endif
- #ifdef PROFILE_EXEC
- if (leaveForHost)
- {
- entryExitRecord->scriptContext->ProfileEnd(Js::RunPhase);
- }
- #endif
- }
- void ThreadContext::DisposeOnLeaveScript()
- {
- PHASE_PRINT_TRACE1(Js::DisposePhase, _u("[Dispose] NeedDispose in LeaveScriptStart: %d\n"), this->recycler->NeedDispose());
- if (this->callDispose && this->recycler->NeedDispose())
- {
- this->recycler->FinishDisposeObjectsNow<FinishDispose>();
- }
- }
- template <bool leaveForHost>
- void
- ThreadContext::LeaveScriptEnd(void * frameAddress)
- {
- Assert(!this->IsScriptActive());
- #if DBG_DUMP
- if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::RunPhase))
- {
- Output::Trace(Js::RunPhase, _u("%p> LeaveScriptEnd(%p): Level %d\n"), ::GetCurrentThreadId(), this, this->callRootLevel);
- Output::Flush();
- }
- #endif
- Js::ScriptEntryExitRecord * entryExitRecord = this->GetScriptEntryExit();
- AssertMsg(entryExitRecord && entryExitRecord->frameIdOfScriptExitFunction,
- "LeaveScriptEnd without LeaveScriptStart");
- AssertMsg(frameAddress == nullptr || frameAddress == entryExitRecord->frameIdOfScriptExitFunction,
- "Mismatched script exit frames");
- Assert(!!entryExitRecord->leaveForHost == leaveForHost);
- entryExitRecord->frameIdOfScriptExitFunction = nullptr;
- AssertMsg(!this->IsScriptActive(), "Missing LeaveScriptStart or LeaveScriptStart");
- this->isScriptActive = true;
- this->GetRecycler()->SetIsScriptActive(true);
- Js::ImplicitCallFlags savedImplicitCallFlags = entryExitRecord->savedImplicitCallFlags;
- if (leaveForHost)
- {
- savedImplicitCallFlags = (Js::ImplicitCallFlags)(savedImplicitCallFlags | Js::ImplicitCall_External);
- }
- else if (entryExitRecord->hasReentered)
- {
- savedImplicitCallFlags = (Js::ImplicitCallFlags)(savedImplicitCallFlags | Js::ImplicitCall_AsyncHostOperation);
- }
- // Restore the implicit call flags
- this->SetImplicitCallFlags(savedImplicitCallFlags);
- #ifdef PROFILE_EXEC
- if (leaveForHost)
- {
- entryExitRecord->scriptContext->ProfileBegin(Js::RunPhase);
- }
- #endif
- }
- // explicit instantiations
- template void ThreadContext::LeaveScriptStart<true>(void * frameAddress);
- template void ThreadContext::LeaveScriptStart<false>(void * frameAddress);
- template void ThreadContext::LeaveScriptEnd<true>(void * frameAddress);
- template void ThreadContext::LeaveScriptEnd<false>(void * frameAddress);
- void
- ThreadContext::PushInterpreterFrame(Js::InterpreterStackFrame *interpreterFrame)
- {
- interpreterFrame->SetPreviousFrame(this->leafInterpreterFrame);
- this->leafInterpreterFrame = interpreterFrame;
- }
- Js::InterpreterStackFrame *
- ThreadContext::PopInterpreterFrame()
- {
- Js::InterpreterStackFrame *interpreterFrame = this->leafInterpreterFrame;
- Assert(interpreterFrame);
- this->leafInterpreterFrame = interpreterFrame->GetPreviousFrame();
- return interpreterFrame;
- }
- BOOL
- ThreadContext::ExecuteRecyclerCollectionFunctionCommon(Recycler * recycler, CollectionFunction function, CollectionFlags flags)
- {
- return __super::ExecuteRecyclerCollectionFunction(recycler, function, flags);
- }
- #if DBG
- bool
- ThreadContext::IsInAsyncHostOperation() const
- {
- if (!this->IsScriptActive())
- {
- Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
- if (lastRecord != NULL)
- {
- return !!lastRecord->leaveForAsyncHostOperation;
- }
- }
- return false;
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- void
- ThreadContext::SetJITConnectionInfo(HANDLE processHandle, void* serverSecurityDescriptor, UUID connectionId)
- {
- Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
- if (!JITManager::GetJITManager()->IsConnected())
- {
- // TODO: return HRESULT
- JITManager::GetJITManager()->ConnectRpcServer(processHandle, serverSecurityDescriptor, connectionId);
- }
- }
- bool
- ThreadContext::EnsureJITThreadContext(bool allowPrereserveAlloc)
- {
- #if ENABLE_OOP_NATIVE_CODEGEN
- Assert(JITManager::GetJITManager()->IsOOPJITEnabled());
- if (!JITManager::GetJITManager()->IsConnected())
- {
- return false;
- }
- if (m_remoteThreadContextInfo)
- {
- return true;
- }
- ThreadContextDataIDL contextData;
- HANDLE serverHandle = JITManager::GetJITManager()->GetServerHandle();
- HANDLE jitTargetHandle = nullptr;
- if (!DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), serverHandle, &jitTargetHandle, 0, FALSE, DUPLICATE_SAME_ACCESS))
- {
- return false;
- }
- contextData.processHandle = (intptr_t)jitTargetHandle;
- contextData.chakraBaseAddress = (intptr_t)AutoSystemInfo::Data.GetChakraBaseAddr();
- ucrtC99MathApis.Ensure();
- contextData.crtBaseAddress = (intptr_t)ucrtC99MathApis.GetHandle();
- contextData.threadStackLimitAddr = reinterpret_cast<intptr_t>(GetAddressOfStackLimitForCurrentThread());
- contextData.bailOutRegisterSaveSpaceAddr = (intptr_t)bailOutRegisterSaveSpace;
- contextData.disableImplicitFlagsAddr = (intptr_t)GetAddressOfDisableImplicitFlags();
- contextData.implicitCallFlagsAddr = (intptr_t)GetAddressOfImplicitCallFlags();
- contextData.scriptStackLimit = GetScriptStackLimit();
- contextData.isThreadBound = IsThreadBound();
- contextData.allowPrereserveAlloc = allowPrereserveAlloc;
- #if defined(ENABLE_SIMDJS) && (_M_IX86 || _M_AMD64)
- contextData.simdTempAreaBaseAddr = (intptr_t)GetSimdTempArea();
- #endif
- m_jitNumericProperties = HeapNew(BVSparse<HeapAllocator>, &HeapAllocator::Instance);
- for (auto iter = propertyMap->GetIterator(); iter.IsValid(); iter.MoveNext())
- {
- if (iter.CurrentKey()->IsNumeric())
- {
- m_jitNumericProperties->Set(iter.CurrentKey()->GetPropertyId());
- m_jitNeedsPropertyUpdate = true;
- }
- }
- HRESULT hr = JITManager::GetJITManager()->InitializeThreadContext(&contextData, &m_remoteThreadContextInfo, &m_prereservedRegionAddr);
- JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate);
- return m_remoteThreadContextInfo != nullptr;
- #endif
- }
- #endif
- #if ENABLE_TTD
- void ThreadContext::InitTimeTravel(ThreadContext* threadContext, void* runtimeHandle, uint32 snapInterval, uint32 snapHistoryLength)
- {
- TTDAssert(!this->IsRuntimeInTTDMode(), "We should only init once.");
- this->TTDContext = HeapNew(TTD::ThreadContextTTD, this, runtimeHandle, snapInterval, snapHistoryLength);
- this->TTDLog = HeapNew(TTD::EventLog, this);
- }
- void ThreadContext::InitHostFunctionsAndTTData(bool record, bool replay, bool debug, size_t optTTUriLength, const char* optTTUri,
- TTD::TTDOpenResourceStreamCallback openResourceStreamfp, TTD::TTDReadBytesFromStreamCallback readBytesFromStreamfp,
- TTD::TTDWriteBytesToStreamCallback writeBytesToStreamfp, TTD::TTDFlushAndCloseStreamCallback flushAndCloseStreamfp,
- TTD::TTDCreateExternalObjectCallback createExternalObjectfp,
- TTD::TTDCreateJsRTContextCallback createJsRTContextCallbackfp, TTD::TTDReleaseJsRTContextCallback releaseJsRTContextCallbackfp, TTD::TTDSetActiveJsRTContext setActiveJsRTContextfp)
- {
- AssertMsg(this->IsRuntimeInTTDMode(), "Need to call init first.");
- this->TTDContext->TTDataIOInfo = { openResourceStreamfp, readBytesFromStreamfp, writeBytesToStreamfp, flushAndCloseStreamfp, 0, nullptr };
- this->TTDContext->TTDExternalObjectFunctions = { createExternalObjectfp, createJsRTContextCallbackfp, releaseJsRTContextCallbackfp, setActiveJsRTContextfp };
- if(record)
- {
- TTDAssert(optTTUri == nullptr, "No URI is needed in record mode (the host explicitly provides it when writing.");
- this->TTDLog->InitForTTDRecord();
- }
- else
- {
- TTDAssert(optTTUri != nullptr, "We need a URI in replay mode so we can initialize the log from it");
- this->TTDLog->InitForTTDReplay(this->TTDContext->TTDataIOInfo, optTTUri, optTTUriLength, debug);
- this->sourceInfoCount = this->TTDLog->GetSourceInfoCount();
- }
- #if !ENABLE_TTD_DIAGNOSTICS_TRACING
- if(debug)
- {
- #endif
- this->TTDExecutionInfo = HeapNew(TTD::ExecutionInfoManager);
- #if !ENABLE_TTD_DIAGNOSTICS_TRACING
- }
- #endif
- }
- #endif
- BOOL
- ThreadContext::ExecuteRecyclerCollectionFunction(Recycler * recycler, CollectionFunction function, CollectionFlags flags)
- {
- // If the thread context doesn't have an associated Recycler set, don't do anything
- if (this->recycler == nullptr)
- {
- return FALSE;
- }
- // Take etw rundown lock on this thread context. We can't collect entryPoints if we are in etw rundown.
- AutoCriticalSection autocs(this->GetEtwRundownCriticalSection());
- // Disable calling dispose from leave script or the stack probe
- // while we're executing the recycler wrapper
- AutoRestoreValue<bool> callDispose(&this->callDispose, false);
- BOOL ret = FALSE;
- #if ENABLE_TTD
- //
- //TODO: We lose any events that happen in the callbacks (such as JsRelease) which may be a problem in the future.
- // It may be possible to defer the collection of these objects to an explicit collection at the yield loop (same for weak set/map).
- // We already indirectly do this for ScriptContext collection (but that is buggy so needs to be fixed too).
- //
- if(this->IsRuntimeInTTDMode())
- {
- this->TTDLog->PushMode(TTD::TTDMode::ExcludedExecutionTTAction);
- }
- #endif
- if (!this->IsScriptActive())
- {
- Assert(!this->IsDisableImplicitCall() || this->IsInAsyncHostOperation());
- ret = this->ExecuteRecyclerCollectionFunctionCommon(recycler, function, flags);
- // Make sure that we finish a collect that is activated outside of script, since
- // we won't have exit script to schedule it
- if (!this->IsInScript() && recycler->CollectionInProgress()
- && ((flags & CollectOverride_DisableIdleFinish) == 0) && threadServiceWrapper)
- {
- threadServiceWrapper->ScheduleFinishConcurrent();
- }
- }
- else
- {
- void * frameAddr = nullptr;
- GET_CURRENT_FRAME_ID(frameAddr);
- // We may need stack to call out from Dispose or QC
- if (!this->IsDisableImplicitCall()) // otherwise Dispose/QC disabled
- {
- // If we don't have stack space to call out from Dispose or QC,
- // don't throw, simply return false. This gives SnailAlloc a better
- // chance of allocating in low stack-space situations (like allocating
- // a StackOverflowException object)
- if (!this->IsStackAvailableNoThrow(Js::Constants::MinStackCallout))
- {
- return false;
- }
- }
- this->LeaveScriptStart<false>(frameAddr);
- ret = this->ExecuteRecyclerCollectionFunctionCommon(recycler, function, flags);
- this->LeaveScriptEnd<false>(frameAddr);
- if (this->callRootLevel != 0)
- {
- this->CheckScriptInterrupt();
- }
- }
- #if ENABLE_TTD
- if(this->IsRuntimeInTTDMode())
- {
- this->TTDLog->PopMode(TTD::TTDMode::ExcludedExecutionTTAction);
- }
- #endif
- return ret;
- }
- void
- ThreadContext::DisposeObjects(Recycler * recycler)
- {
- if (this->IsDisableImplicitCall())
- {
- // Don't dispose objects when implicit calls are disabled, since disposing may cause implicit calls. Objects will remain
- // in the dispose queue and will be disposed later when implicit calls are not disabled.
- return;
- }
- // We shouldn't DisposeObjects in NoScriptScope as this might lead to script execution.
- // Callers of DisposeObjects should ensure !IsNoScriptScope() before calling DisposeObjects.
- if (this->IsNoScriptScope())
- {
- FromDOM_NoScriptScope_fatal_error();
- }
- if (!this->IsScriptActive())
- {
- __super::DisposeObjects(recycler);
- }
- else
- {
- void * frameAddr = nullptr;
- GET_CURRENT_FRAME_ID(frameAddr);
- // We may need stack to call out from Dispose
- this->ProbeStack(Js::Constants::MinStackCallout);
- this->LeaveScriptStart<false>(frameAddr);
- __super::DisposeObjects(recycler);
- this->LeaveScriptEnd<false>(frameAddr);
- }
- }
- void
- ThreadContext::PushEntryExitRecord(Js::ScriptEntryExitRecord * record)
- {
- AssertMsg(record, "Didn't provide a script entry record to push");
- Assert(record->next == nullptr);
- Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
- if (lastRecord != nullptr)
- {
- // If we enter script again, we should have leave with leaveForHost or leave for dispose.
- Assert(lastRecord->leaveForHost || lastRecord->leaveForAsyncHostOperation);
- lastRecord->hasReentered = true;
- record->next = lastRecord;
- // these are on stack, which grows down. if this condition doesn't hold, then the list somehow got messed up
- if (!IsOnStack(lastRecord) || (uintptr_t)record >= (uintptr_t)lastRecord)
- {
- EntryExitRecord_Corrupted_fatal_error();
- }
- }
- this->entryExitRecord = record;
- }
- void ThreadContext::PopEntryExitRecord(Js::ScriptEntryExitRecord * record)
- {
- AssertMsg(record && record == this->entryExitRecord, "Mismatch script entry/exit");
- // these are on stack, which grows down. if this condition doesn't hold, then the list somehow got messed up
- Js::ScriptEntryExitRecord * next = this->entryExitRecord->next;
- if (next && (!IsOnStack(next) || (uintptr_t)this->entryExitRecord >= (uintptr_t)next))
- {
- EntryExitRecord_Corrupted_fatal_error();
- }
- this->entryExitRecord = next;
- }
- BOOL ThreadContext::ReserveStaticTypeIds(__in int first, __in int last)
- {
- if ( nextTypeId <= first )
- {
- nextTypeId = (Js::TypeId) last;
- return TRUE;
- }
- else
- {
- return FALSE;
- }
- }
- Js::TypeId ThreadContext::ReserveTypeIds(int count)
- {
- Js::TypeId firstTypeId = nextTypeId;
- nextTypeId = (Js::TypeId)(nextTypeId + count);
- return firstTypeId;
- }
- Js::TypeId ThreadContext::CreateTypeId()
- {
- return nextTypeId = (Js::TypeId)(nextTypeId + 1);
- }
- WellKnownHostType ThreadContext::GetWellKnownHostType(Js::TypeId typeId)
- {
- if (this->wellKnownHostTypeHTMLAllCollectionTypeId == typeId)
- {
- return WellKnownHostType_HTMLAllCollection;
- }
- return WellKnownHostType_Invalid;
- }
- void ThreadContext::SetWellKnownHostTypeId(WellKnownHostType wellKnownType, Js::TypeId typeId)
- {
- AssertMsg(WellKnownHostType_HTMLAllCollection == wellKnownType, "ThreadContext::SetWellKnownHostTypeId called on type other than HTMLAllCollection");
- if (WellKnownHostType_HTMLAllCollection == wellKnownType)
- {
- this->wellKnownHostTypeHTMLAllCollectionTypeId = typeId;
- #if ENABLE_NATIVE_CODEGEN
- if (this->m_remoteThreadContextInfo)
- {
- HRESULT hr = JITManager::GetJITManager()->SetWellKnownHostTypeId(this->m_remoteThreadContextInfo, (int)typeId);
- JITManager::HandleServerCallResult(hr, RemoteCallType::StateUpdate);
- }
- #endif
- }
- }
- void ThreadContext::EnsureDebugManager()
- {
- if (this->debugManager == nullptr)
- {
- this->debugManager = HeapNew(Js::DebugManager, this, this->GetAllocationPolicyManager());
- }
- InterlockedIncrement(&crefSContextForDiag);
- Assert(this->debugManager != nullptr);
- }
- void ThreadContext::ReleaseDebugManager()
- {
- Assert(crefSContextForDiag > 0);
- Assert(this->debugManager != nullptr);
- LONG lref = InterlockedDecrement(&crefSContextForDiag);
- if (lref == 0)
- {
- if (this->recyclableData != nullptr)
- {
- this->recyclableData->returnedValueList = nullptr;
- }
- if (this->debugManager != nullptr)
- {
- this->debugManager->Close();
- HeapDelete(this->debugManager);
- this->debugManager = nullptr;
- }
- }
- }
- Js::TempArenaAllocatorObject *
- ThreadContext::GetTemporaryAllocator(LPCWSTR name)
- {
- AssertCanHandleOutOfMemory();
- if (temporaryArenaAllocatorCount != 0)
- {
- temporaryArenaAllocatorCount--;
- Js::TempArenaAllocatorObject * allocator = recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount];
- recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount] = nullptr;
- return allocator;
- }
- return Js::TempArenaAllocatorObject::Create(this);
- }
- void
- ThreadContext::ReleaseTemporaryAllocator(Js::TempArenaAllocatorObject * tempAllocator)
- {
- if (temporaryArenaAllocatorCount < MaxTemporaryArenaAllocators)
- {
- tempAllocator->GetAllocator()->Reset();
- recyclableData->temporaryArenaAllocators[temporaryArenaAllocatorCount] = tempAllocator;
- temporaryArenaAllocatorCount++;
- return;
- }
- tempAllocator->Dispose(false);
- }
- Js::TempGuestArenaAllocatorObject *
- ThreadContext::GetTemporaryGuestAllocator(LPCWSTR name)
- {
- AssertCanHandleOutOfMemory();
- if (temporaryGuestArenaAllocatorCount != 0)
- {
- temporaryGuestArenaAllocatorCount--;
- Js::TempGuestArenaAllocatorObject * allocator = recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount];
- allocator->AdviseInUse();
- recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount] = nullptr;
- return allocator;
- }
- return Js::TempGuestArenaAllocatorObject::Create(this);
- }
- void
- ThreadContext::ReleaseTemporaryGuestAllocator(Js::TempGuestArenaAllocatorObject * tempGuestAllocator)
- {
- if (temporaryGuestArenaAllocatorCount < MaxTemporaryArenaAllocators)
- {
- tempGuestAllocator->AdviseNotInUse();
- recyclableData->temporaryGuestArenaAllocators[temporaryGuestArenaAllocatorCount] = tempGuestAllocator;
- temporaryGuestArenaAllocatorCount++;
- return;
- }
- tempGuestAllocator->Dispose(false);
- }
- void
- ThreadContext::AddToPendingScriptContextCloseList(Js::ScriptContext * scriptContext)
- {
- Assert(scriptContext != nullptr);
- if (rootPendingClose == nullptr)
- {
- rootPendingClose = scriptContext;
- return;
- }
- // Prepend to the list.
- scriptContext->SetNextPendingClose(rootPendingClose);
- rootPendingClose = scriptContext;
- }
- void
- ThreadContext::RemoveFromPendingClose(Js::ScriptContext * scriptContext)
- {
- Assert(scriptContext != nullptr);
- if (rootPendingClose == nullptr)
- {
- // We already sent a close message, ignore the notification.
- return;
- }
- // Base case: The root is being removed. Move the root along.
- if (scriptContext == rootPendingClose)
- {
- rootPendingClose = rootPendingClose->GetNextPendingClose();
- return;
- }
- Js::ScriptContext * currScriptContext = rootPendingClose;
- Js::ScriptContext * nextScriptContext = nullptr;
- while (currScriptContext)
- {
- nextScriptContext = currScriptContext->GetNextPendingClose();
- if (!nextScriptContext)
- {
- break;
- }
- if (nextScriptContext == scriptContext) {
- // The next pending close ScriptContext is the one to be removed - set prev->next to next->next
- currScriptContext->SetNextPendingClose(nextScriptContext->GetNextPendingClose());
- return;
- }
- currScriptContext = nextScriptContext;
- }
- // We expect to find scriptContext in the pending close list.
- Assert(false);
- }
- void ThreadContext::ClosePendingScriptContexts()
- {
- Js::ScriptContext * scriptContext = rootPendingClose;
- if (scriptContext == nullptr)
- {
- return;
- }
- Js::ScriptContext * nextScriptContext;
- do
- {
- nextScriptContext = scriptContext->GetNextPendingClose();
- scriptContext->Close(false);
- scriptContext = nextScriptContext;
- }
- while (scriptContext);
- rootPendingClose = nullptr;
- }
- void
- ThreadContext::AddToPendingProjectionContextCloseList(IProjectionContext *projectionContext)
- {
- pendingProjectionContextCloseList->Add(projectionContext);
- }
- void
- ThreadContext::RemoveFromPendingClose(IProjectionContext* projectionContext)
- {
- pendingProjectionContextCloseList->Remove(projectionContext);
- }
- void ThreadContext::ClosePendingProjectionContexts()
- {
- IProjectionContext* projectionContext;
- for (int i = 0 ; i < pendingProjectionContextCloseList->Count(); i++)
- {
- projectionContext = pendingProjectionContextCloseList->Item(i);
- projectionContext->Close();
- }
- pendingProjectionContextCloseList->Clear();
- }
- void
- ThreadContext::RegisterScriptContext(Js::ScriptContext *scriptContext)
- {
- // NOTE: ETW rundown thread may be reading the scriptContextList concurrently. We don't need to
- // lock access because we only insert to the front here.
- scriptContext->next = this->scriptContextList;
- if (this->scriptContextList)
- {
- Assert(this->scriptContextList->prev == NULL);
- this->scriptContextList->prev = scriptContext;
- }
- scriptContext->prev = NULL;
- this->scriptContextList = scriptContext;
- if(NoJIT())
- {
- scriptContext->ForceNoNative();
- }
- #if DBG || defined(RUNTIME_DATA_COLLECTION)
- scriptContextCount++;
- #endif
- scriptContextEverRegistered = true;
- }
- void
- ThreadContext::UnregisterScriptContext(Js::ScriptContext *scriptContext)
- {
- // NOTE: ETW rundown thread may be reading the scriptContextList concurrently. Since this function
- // is only called by ~ScriptContext() which already synchronized to ETW rundown, we are safe here.
- if (scriptContext == this->scriptContextList)
- {
- Assert(scriptContext->prev == NULL);
- this->scriptContextList = scriptContext->next;
- }
- else
- {
- scriptContext->prev->next = scriptContext->next;
- }
- if (scriptContext->next)
- {
- scriptContext->next->prev = scriptContext->prev;
- }
- scriptContext->prev = nullptr;
- scriptContext->next = nullptr;
- #if DBG || defined(RUNTIME_DATA_COLLECTION)
- scriptContextCount--;
- #endif
- }
- ThreadContext::CollectCallBack *
- ThreadContext::AddRecyclerCollectCallBack(RecyclerCollectCallBackFunction callback, void * context)
- {
- AutoCriticalSection autocs(&csCollectionCallBack);
- CollectCallBack * collectCallBack = this->collectCallBackList.PrependNode(&HeapAllocator::Instance);
- collectCallBack->callback = callback;
- collectCallBack->context = context;
- this->hasCollectionCallBack = true;
- return collectCallBack;
- }
- void
- ThreadContext::RemoveRecyclerCollectCallBack(ThreadContext::CollectCallBack * collectCallBack)
- {
- AutoCriticalSection autocs(&csCollectionCallBack);
- this->collectCallBackList.RemoveElement(&HeapAllocator::Instance, collectCallBack);
- this->hasCollectionCallBack = !this->collectCallBackList.Empty();
- }
- void
- ThreadContext::PreCollectionCallBack(CollectionFlags flags)
- {
- #ifdef PERF_COUNTERS
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: deferparse - # of func: %d # deferparsed: %d\n"), PerfCounter::CodeCounterSet::GetTotalFunctionCounter().GetValue(), PerfCounter::CodeCounterSet::GetDeferredFunctionCounter().GetValue());
- #endif
- // This needs to be done before ClearInlineCaches since that method can empty the list of
- // script contexts with inline caches
- this->ClearScriptContextCaches();
- // Clear up references to types to avoid keep them alive
- this->ClearPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesCaches();
- // Clean up unused memory before we start collecting
- this->CleanNoCasePropertyMap();
- this->TryEnterExpirableCollectMode();
- const BOOL concurrent = flags & CollectMode_Concurrent;
- const BOOL partial = flags & CollectMode_Partial;
- if (!partial)
- {
- // Integrate allocated pages from background JIT threads
- #if ENABLE_NATIVE_CODEGEN
- #if !FLOATVAR
- if (codeGenNumberThreadAllocator)
- {
- codeGenNumberThreadAllocator->Integrate();
- }
- if (this->xProcNumberPageSegmentManager)
- {
- this->xProcNumberPageSegmentManager->Integrate();
- }
- #endif
- #endif
- }
- RecyclerCollectCallBackFlags callBackFlags = (RecyclerCollectCallBackFlags)
- ((concurrent ? Collect_Begin_Concurrent : Collect_Begin) | (partial? Collect_Begin_Partial : Collect_Begin));
- CollectionCallBack(callBackFlags);
- }
- void
- ThreadContext::PreSweepCallback()
- {
- #ifdef PERSISTENT_INLINE_CACHES
- ClearInlineCachesWithDeadWeakRefs();
- #else
- ClearInlineCaches();
- #endif
- ClearIsInstInlineCaches();
- ClearEquivalentTypeCaches();
- ClearForInCaches();
- this->dynamicObjectEnumeratorCacheMap.Clear();
- }
- void
- ThreadContext::CollectionCallBack(RecyclerCollectCallBackFlags flags)
- {
- DListBase<CollectCallBack>::Iterator i(&this->collectCallBackList);
- while (i.Next())
- {
- i.Data().callback(i.Data().context, flags);
- }
- }
- void
- ThreadContext::WaitCollectionCallBack()
- {
- // Avoid taking the lock if there are no call back
- if (hasCollectionCallBack)
- {
- AutoCriticalSection autocs(&csCollectionCallBack);
- CollectionCallBack(Collect_Wait);
- }
- }
- void
- ThreadContext::PostCollectionCallBack()
- {
- CollectionCallBack(Collect_End);
- TryExitExpirableCollectMode();
- // Recycler is null in the case where the ThreadContext is in the process of creating the recycler and
- // we have a GC triggered (say because the -recyclerStress flag is passed in)
- if (this->recycler != NULL)
- {
- if (this->recycler->InCacheCleanupCollection())
- {
- this->recycler->ClearCacheCleanupCollection();
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext; scriptContext = scriptContext->next)
- {
- scriptContext->CleanupWeakReferenceDictionaries();
- }
- }
- }
- }
- void
- ThreadContext::PostSweepRedeferralCallBack()
- {
- if (this->DoTryRedeferral())
- {
- HRESULT hr = S_OK;
- BEGIN_TRANSLATE_OOM_TO_HRESULT
- {
- this->TryRedeferral();
- }
- END_TRANSLATE_OOM_TO_HRESULT(hr);
- }
- this->UpdateRedeferralState();
- }
- bool
- ThreadContext::DoTryRedeferral() const
- {
- if (PHASE_FORCE1(Js::RedeferralPhase) || PHASE_STRESS1(Js::RedeferralPhase))
- {
- return true;
- }
- if (PHASE_OFF1(Js::RedeferralPhase))
- {
- return false;
- }
- switch (this->redeferralState)
- {
- case InitialRedeferralState:
- return false;
- case StartupRedeferralState:
- return gcSinceCallCountsCollected >= StartupRedeferralInactiveThreshold;
- case MainRedeferralState:
- return gcSinceCallCountsCollected >= MainRedeferralInactiveThreshold;
- default:
- Assert(0);
- return false;
- };
- }
- bool
- ThreadContext::DoRedeferFunctionBodies() const
- {
- #if ENABLE_TTD
- if (this->IsRuntimeInTTDMode())
- {
- return false;
- }
- #endif
- if (PHASE_FORCE1(Js::RedeferralPhase) || PHASE_STRESS1(Js::RedeferralPhase))
- {
- return true;
- }
- if (PHASE_OFF1(Js::RedeferralPhase))
- {
- return false;
- }
- switch (this->redeferralState)
- {
- case InitialRedeferralState:
- return false;
- case StartupRedeferralState:
- return gcSinceLastRedeferral >= StartupRedeferralCheckInterval;
- case MainRedeferralState:
- return gcSinceLastRedeferral >= MainRedeferralCheckInterval;
- default:
- Assert(0);
- return false;
- };
- }
- uint
- ThreadContext::GetRedeferralCollectionInterval() const
- {
- switch(this->redeferralState)
- {
- case InitialRedeferralState:
- return InitialRedeferralDelay;
- case StartupRedeferralState:
- return StartupRedeferralCheckInterval;
- case MainRedeferralState:
- return MainRedeferralCheckInterval;
- default:
- Assert(0);
- return (uint)-1;
- }
- }
- uint
- ThreadContext::GetRedeferralInactiveThreshold() const
- {
- switch(this->redeferralState)
- {
- case InitialRedeferralState:
- return InitialRedeferralDelay;
- case StartupRedeferralState:
- return StartupRedeferralInactiveThreshold;
- case MainRedeferralState:
- return MainRedeferralInactiveThreshold;
- default:
- Assert(0);
- return (uint)-1;
- }
- }
- void
- ThreadContext::TryRedeferral()
- {
- bool doRedefer = this->DoRedeferFunctionBodies();
- // Collect the set of active functions.
- ActiveFunctionSet *pActiveFuncs = nullptr;
- if (doRedefer)
- {
- pActiveFuncs = Anew(this->GetThreadAlloc(), ActiveFunctionSet, this->GetThreadAlloc());
- this->GetActiveFunctions(pActiveFuncs);
- #if DBG
- this->redeferredFunctions = 0;
- this->recoveredBytes = 0;
- #endif
- }
- uint inactiveThreshold = this->GetRedeferralInactiveThreshold();
- Js::ScriptContext *scriptContext;
- for (scriptContext = GetScriptContextList(); scriptContext; scriptContext = scriptContext->next)
- {
- if (scriptContext->IsClosed())
- {
- continue;
- }
- scriptContext->RedeferFunctionBodies(pActiveFuncs, inactiveThreshold);
- }
- if (pActiveFuncs)
- {
- Adelete(this->GetThreadAlloc(), pActiveFuncs);
- #if DBG
- if (PHASE_STATS1(Js::RedeferralPhase) && this->redeferredFunctions)
- {
- Output::Print(_u("Redeferred: %d, Bytes: 0x%x\n"), this->redeferredFunctions, this->recoveredBytes);
- }
- #endif
- }
- }
- void
- ThreadContext::GetActiveFunctions(ActiveFunctionSet * pActiveFuncs)
- {
- if (!this->IsInScript() || this->entryExitRecord == nullptr)
- {
- return;
- }
- Js::JavascriptStackWalker walker(GetScriptContextList(), TRUE, NULL, true);
- Js::JavascriptFunction *function = nullptr;
- while (walker.GetCallerWithoutInlinedFrames(&function))
- {
- if (function->GetFunctionInfo()->HasBody())
- {
- Js::FunctionBody *body = function->GetFunctionInfo()->GetFunctionBody();
- body->UpdateActiveFunctionSet(pActiveFuncs, nullptr);
- }
- }
- }
- void
- ThreadContext::UpdateRedeferralState()
- {
- uint inactiveThreshold = this->GetRedeferralInactiveThreshold();
- uint collectInterval = this->GetRedeferralCollectionInterval();
- if (this->gcSinceCallCountsCollected >= inactiveThreshold)
- {
- this->gcSinceCallCountsCollected = 0;
- if (this->gcSinceLastRedeferral >= collectInterval)
- {
- // Advance state
- switch (this->redeferralState)
- {
- case InitialRedeferralState:
- this->redeferralState = StartupRedeferralState;
- break;
- case StartupRedeferralState:
- this->redeferralState = MainRedeferralState;
- break;
- case MainRedeferralState:
- break;
- default:
- Assert(0);
- break;
- }
- this->gcSinceLastRedeferral = 0;
- }
- }
- else
- {
- this->gcSinceCallCountsCollected++;
- this->gcSinceLastRedeferral++;
- }
- }
- #ifdef FAULT_INJECTION
- void
- ThreadContext::DisposeScriptContextByFaultInjectionCallBack()
- {
- if (FAULTINJECT_SCRIPT_TERMINATION_ON_DISPOSE) {
- int scriptContextToClose = -1;
- /* inject only if we have more than 1 script context*/
- uint totalScriptCount = GetScriptContextCount();
- if (totalScriptCount > 1) {
- if (Js::Configuration::Global.flags.FaultInjectionScriptContextToTerminateCount > 0)
- {
- scriptContextToClose = Js::Configuration::Global.flags.FaultInjectionScriptContextToTerminateCount % totalScriptCount;
- for (Js::ScriptContext *scriptContext = GetScriptContextList(); scriptContext; scriptContext = scriptContext->next)
- {
- if (scriptContextToClose-- == 0)
- {
- scriptContext->DisposeScriptContextByFaultInjection();
- break;
- }
- }
- }
- else
- {
- fwprintf(stderr, _u("***FI: FaultInjectionScriptContextToTerminateCount Failed, Value should be > 0. \n"));
- }
- }
- }
- }
- #endif
- #pragma region "Expirable Object Methods"
- void
- ThreadContext::TryExitExpirableCollectMode()
- {
- // If this feature is turned off or if we're already in profile collection mode, do nothing
- // We also do nothing if expiration is explicitly disabled by someone lower down the stack
- if (PHASE_OFF1(Js::ExpirableCollectPhase) || !InExpirableCollectMode() || this->disableExpiration)
- {
- return;
- }
- if (InExpirableCollectMode())
- {
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Checking to see whether to complete Expirable Object Collection: GC Count is %d\n"), this->expirableCollectModeGcCount);
- if (this->expirableCollectModeGcCount > 0)
- {
- this->expirableCollectModeGcCount--;
- }
- if (this->expirableCollectModeGcCount == 0 &&
- (this->recycler->InCacheCleanupCollection() || CONFIG_FLAG(ForceExpireOnNonCacheCollect)))
- {
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Completing Expirable Object Collection\n"));
- ExpirableObjectList::Iterator expirableObjectIterator(this->expirableObjectList);
- while (expirableObjectIterator.Next())
- {
- ExpirableObject* object = expirableObjectIterator.Data();
- Assert(object);
- if (!object->IsObjectUsed())
- {
- object->Expire();
- }
- }
- // Leave expirable collection mode
- expirableCollectModeGcCount = -1;
- }
- }
- }
- bool
- ThreadContext::InExpirableCollectMode()
- {
- // We're in expirable collect if we have expirable objects registered,
- // and expirableCollectModeGcCount is not negative
- // and when debugger is attaching, it might have set the function to deferredParse.
- return (expirableObjectList != nullptr &&
- numExpirableObjects > 0 &&
- expirableCollectModeGcCount >= 0 &&
- (this->GetDebugManager() != nullptr &&
- !this->GetDebugManager()->IsDebuggerAttaching()));
- }
- void
- ThreadContext::TryEnterExpirableCollectMode()
- {
- // If this feature is turned off or if we're already in profile collection mode, do nothing
- if (PHASE_OFF1(Js::ExpirableCollectPhase) || InExpirableCollectMode())
- {
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Not running Expirable Object Collection\n"));
- return;
- }
- double entryPointCollectionThreshold = Js::Configuration::Global.flags.ExpirableCollectionTriggerThreshold / 100.0;
- double currentThreadNativeCodeRatio = ((double) GetCodeSize()) / Js::Constants::MaxThreadJITCodeHeapSize;
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Current native code ratio: %f\n"), currentThreadNativeCodeRatio);
- if (currentThreadNativeCodeRatio > entryPointCollectionThreshold)
- {
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Setting up Expirable Object Collection\n"));
- this->expirableCollectModeGcCount = Js::Configuration::Global.flags.ExpirableCollectionGCCount;
- ExpirableObjectList::Iterator expirableObjectIterator(this->expirableObjectList);
- while (expirableObjectIterator.Next())
- {
- ExpirableObject* object = expirableObjectIterator.Data();
- Assert(object);
- object->EnterExpirableCollectMode();
- }
- if (this->entryExitRecord != nullptr)
- {
- // If we're in script, we will do a stack walk, find the JavascriptFunction's on the stack
- // and mark their entry points as being used so that we don't prematurely expire them
- Js::ScriptContext* topScriptContext = this->entryExitRecord->scriptContext;
- Js::JavascriptStackWalker walker(topScriptContext, TRUE);
- Js::JavascriptFunction* javascriptFunction = nullptr;
- while (walker.GetCallerWithoutInlinedFrames(&javascriptFunction))
- {
- if (javascriptFunction != nullptr && Js::ScriptFunction::Is(javascriptFunction))
- {
- Js::ScriptFunction* scriptFunction = (Js::ScriptFunction*) javascriptFunction;
- Js::FunctionEntryPointInfo* entryPointInfo = scriptFunction->GetFunctionEntryPointInfo();
- entryPointInfo->SetIsObjectUsed();
- scriptFunction->GetFunctionBody()->MapEntryPoints([](int index, Js::FunctionEntryPointInfo* entryPoint){
- entryPoint->SetIsObjectUsed();
- });
- }
- }
- }
- }
- }
- void
- ThreadContext::RegisterExpirableObject(ExpirableObject* object)
- {
- Assert(this->expirableObjectList);
- Assert(object->registrationHandle == nullptr);
- ExpirableObject** registrationData = this->expirableObjectList->PrependNode();
- (*registrationData) = object;
- object->registrationHandle = (void*) registrationData;
- OUTPUT_VERBOSE_TRACE(Js::ExpirableCollectPhase, _u("Registered 0x%p\n"), object);
- numExpirableObjects++;
- }
- void
- ThreadContext::UnregisterExpirableObject(ExpirableObject* object)
- {
- Assert(this->expirableObjectList);
- Assert(object->registrationHandle != nullptr);
- Assert(this->expirableObjectList->HasElement(
- (ExpirableObject* const *)PointerValue(object->registrationHandle)));
- ExpirableObject** registrationData = (ExpirableObject**)PointerValue(object->registrationHandle);
- Assert(*registrationData == object);
- this->expirableObjectList->MoveElementTo(registrationData, this->expirableObjectDisposeList);
- object->registrationHandle = nullptr;
- OUTPUT_VERBOSE_TRACE(Js::ExpirableCollectPhase, _u("Unregistered 0x%p\n"), object);
- numExpirableObjects--;
- }
- void
- ThreadContext::DisposeExpirableObject(ExpirableObject* object)
- {
- Assert(this->expirableObjectDisposeList);
- Assert(object->registrationHandle == nullptr);
- this->expirableObjectDisposeList->Remove(object);
- OUTPUT_VERBOSE_TRACE(Js::ExpirableCollectPhase, _u("Disposed 0x%p\n"), object);
- }
- #pragma endregion
- void
- ThreadContext::ClearScriptContextCaches()
- {
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext != nullptr; scriptContext = scriptContext->next)
- {
- scriptContext->ClearScriptContextCaches();
- }
- }
- #ifdef PERSISTENT_INLINE_CACHES
- void
- ThreadContext::ClearInlineCachesWithDeadWeakRefs()
- {
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext != nullptr; scriptContext = scriptContext->next)
- {
- scriptContext->ClearInlineCachesWithDeadWeakRefs();
- }
- if (PHASE_TRACE1(Js::InlineCachePhase))
- {
- size_t size = 0;
- size_t freeListSize = 0;
- size_t polyInlineCacheSize = 0;
- uint scriptContextCount = 0;
- for (Js::ScriptContext *scriptContext = scriptContextList;
- scriptContext;
- scriptContext = scriptContext->next)
- {
- scriptContextCount++;
- size += scriptContext->GetInlineCacheAllocator()->AllocatedSize();
- freeListSize += scriptContext->GetInlineCacheAllocator()->FreeListSize();
- #ifdef POLY_INLINE_CACHE_SIZE_STATS
- polyInlineCacheSize += scriptContext->GetInlineCacheAllocator()->GetPolyInlineCacheSize();
- #endif
- };
- printf("Inline cache arena: total = %5I64u KB, free list = %5I64u KB, poly caches = %5I64u KB, script contexts = %u\n",
- static_cast<uint64>(size / 1024), static_cast<uint64>(freeListSize / 1024), static_cast<uint64>(polyInlineCacheSize / 1024), scriptContextCount);
- }
- }
- void
- ThreadContext::ClearInvalidatedUniqueGuards()
- {
- // If a propertyGuard was invalidated, make sure to remove it's entry from unique property guard table of other property records.
- PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
- guards.Map([this](Js::PropertyRecord const * propertyRecord, PropertyGuardEntry* entry, const RecyclerWeakReference<const Js::PropertyRecord>* weakRef)
- {
- entry->uniqueGuards.MapAndRemoveIf([=](RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
- {
- Js::PropertyGuard* guard = guardWeakRef->Get();
- bool shouldRemove = guard != nullptr && !guard->IsValid();
- if (shouldRemove)
- {
- if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
- propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
- Output::Flush();
- }
- if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"),
- propertyRecord->GetBuffer(), guard->GetValue());
- Output::Flush();
- }
- }
- return shouldRemove;
- });
- });
- }
- void
- ThreadContext::ClearInlineCaches()
- {
- if (PHASE_TRACE1(Js::InlineCachePhase))
- {
- size_t size = 0;
- size_t freeListSize = 0;
- size_t polyInlineCacheSize = 0;
- uint scriptContextCount = 0;
- for (Js::ScriptContext *scriptContext = scriptContextList;
- scriptContext;
- scriptContext = scriptContext->next)
- {
- scriptContextCount++;
- size += scriptContext->GetInlineCacheAllocator()->AllocatedSize();
- freeListSize += scriptContext->GetInlineCacheAllocator()->FreeListSize();
- #ifdef POLY_INLINE_CACHE_SIZE_STATS
- polyInlineCacheSize += scriptContext->GetInlineCacheAllocator()->GetPolyInlineCacheSize();
- #endif
- };
- printf("Inline cache arena: total = %5I64u KB, free list = %5I64u KB, poly caches = %5I64u KB, script contexts = %u\n",
- static_cast<uint64>(size / 1024), static_cast<uint64>(freeListSize / 1024), static_cast<uint64>(polyInlineCacheSize / 1024), scriptContextCount);
- }
- Js::ScriptContext *scriptContext = this->scriptContextList;
- while (scriptContext != nullptr)
- {
- scriptContext->ClearInlineCaches();
- scriptContext = scriptContext->next;
- }
- inlineCacheThreadInfoAllocator.Reset();
- protoInlineCacheByPropId.ResetNoDelete();
- storeFieldInlineCacheByPropId.ResetNoDelete();
- registeredInlineCacheCount = 0;
- unregisteredInlineCacheCount = 0;
- }
- #endif //PERSISTENT_INLINE_CACHES
- void
- ThreadContext::ClearIsInstInlineCaches()
- {
- Js::ScriptContext *scriptContext = this->scriptContextList;
- while (scriptContext != nullptr)
- {
- scriptContext->ClearIsInstInlineCaches();
- scriptContext = scriptContext->next;
- }
- isInstInlineCacheThreadInfoAllocator.Reset();
- isInstInlineCacheByFunction.ResetNoDelete();
- }
- void
- ThreadContext::ClearForInCaches()
- {
- Js::ScriptContext *scriptContext = this->scriptContextList;
- while (scriptContext != nullptr)
- {
- scriptContext->ClearForInCaches();
- scriptContext = scriptContext->next;
- }
- }
- void
- ThreadContext::ClearEquivalentTypeCaches()
- {
- #if ENABLE_NATIVE_CODEGEN
- // Called from PreSweepCallback to clear pointers to types that have no live object references left.
- // The EquivalentTypeCache used to keep these types alive, but this caused memory growth in cases where
- // entry points stayed around for a long time.
- // In future we may want to pin the reference/guard type to the entry point, but that choice will depend
- // on a use case where pinning the type helps us optimize. Lacking that, clearing the guard type is a
- // simpler short-term solution.
- // Note that clearing unmarked types from the cache and guard is needed for correctness if the cache doesn't keep
- // the types alive.
- FOREACH_DLISTBASE_ENTRY_EDITING(Js::EntryPointInfo *, entryPoint, &equivalentTypeCacheEntryPoints, iter)
- {
- bool isLive = entryPoint->ClearEquivalentTypeCaches();
- if (!isLive)
- {
- iter.RemoveCurrent(&equivalentTypeCacheInfoAllocator);
- }
- }
- NEXT_DLISTBASE_ENTRY_EDITING;
- // Note: Don't reset the list, because we're only clearing the dead types from these caches.
- // There may still be type references we need to keep an eye on.
- #endif
- }
- Js::EntryPointInfo **
- ThreadContext::RegisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo * entryPoint)
- {
- return equivalentTypeCacheEntryPoints.PrependNode(&equivalentTypeCacheInfoAllocator, entryPoint);
- }
- void
- ThreadContext::UnregisterEquivalentTypeCacheEntryPoint(Js::EntryPointInfo ** entryPoint)
- {
- equivalentTypeCacheEntryPoints.RemoveElement(&equivalentTypeCacheInfoAllocator, entryPoint);
- }
- void
- ThreadContext::RegisterProtoInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId)
- {
- if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: registering proto cache 0x%p for property %s(%u)\n"),
- inlineCache, GetPropertyName(propertyId)->GetBuffer(), propertyId);
- Output::Flush();
- }
- RegisterInlineCache(protoInlineCacheByPropId, inlineCache, propertyId);
- }
- void
- ThreadContext::RegisterStoreFieldInlineCache(Js::InlineCache * inlineCache, Js::PropertyId propertyId)
- {
- if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: registering store field cache 0x%p for property %s(%u)\n"),
- inlineCache, GetPropertyName(propertyId)->GetBuffer(), propertyId);
- Output::Flush();
- }
- RegisterInlineCache(storeFieldInlineCacheByPropId, inlineCache, propertyId);
- }
- void
- ThreadContext::RegisterInlineCache(InlineCacheListMapByPropertyId& inlineCacheMap, Js::InlineCache * inlineCache, Js::PropertyId propertyId)
- {
- InlineCacheList* inlineCacheList;
- if (!inlineCacheMap.TryGetValue(propertyId, &inlineCacheList))
- {
- inlineCacheList = Anew(&this->inlineCacheThreadInfoAllocator, InlineCacheList, &this->inlineCacheThreadInfoAllocator);
- inlineCacheMap.AddNew(propertyId, inlineCacheList);
- }
- Js::InlineCache** inlineCacheRef = inlineCacheList->PrependNode();
- Assert(inlineCacheRef != nullptr);
- *inlineCacheRef = inlineCache;
- inlineCache->invalidationListSlotPtr = inlineCacheRef;
- this->registeredInlineCacheCount++;
- }
- void ThreadContext::NotifyInlineCacheBatchUnregistered(uint count)
- {
- this->unregisteredInlineCacheCount += count;
- // Negative or 0 InlineCacheInvalidationListCompactionThreshold forces compaction all the time.
- if (CONFIG_FLAG(InlineCacheInvalidationListCompactionThreshold) <= 0 ||
- this->registeredInlineCacheCount / this->unregisteredInlineCacheCount < (uint)CONFIG_FLAG(InlineCacheInvalidationListCompactionThreshold))
- {
- CompactInlineCacheInvalidationLists();
- }
- }
- void
- ThreadContext::InvalidateProtoInlineCaches(Js::PropertyId propertyId)
- {
- InlineCacheList* inlineCacheList;
- if (protoInlineCacheByPropId.TryGetValueAndRemove(propertyId, &inlineCacheList))
- {
- if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: invalidating proto caches for property %s(%u)\n"),
- GetPropertyName(propertyId)->GetBuffer(), propertyId);
- Output::Flush();
- }
- InvalidateAndDeleteInlineCacheList(inlineCacheList);
- }
- }
- void
- ThreadContext::InvalidateStoreFieldInlineCaches(Js::PropertyId propertyId)
- {
- InlineCacheList* inlineCacheList;
- if (storeFieldInlineCacheByPropId.TryGetValueAndRemove(propertyId, &inlineCacheList))
- {
- if (PHASE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: invalidating store field caches for property %s(%u)\n"),
- GetPropertyName(propertyId)->GetBuffer(), propertyId);
- Output::Flush();
- }
- InvalidateAndDeleteInlineCacheList(inlineCacheList);
- }
- }
- void
- ThreadContext::InvalidateAndDeleteInlineCacheList(InlineCacheList* inlineCacheList)
- {
- Assert(inlineCacheList != nullptr);
- uint cacheCount = 0;
- uint nullCacheCount = 0;
- FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
- {
- cacheCount++;
- if (inlineCache != nullptr)
- {
- if (PHASE_VERBOSE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: invalidating cache 0x%p\n"), inlineCache);
- Output::Flush();
- }
- memset(inlineCache, 0, sizeof(Js::InlineCache));
- }
- else
- {
- nullCacheCount++;
- }
- }
- NEXT_SLISTBASE_ENTRY;
- Adelete(&this->inlineCacheThreadInfoAllocator, inlineCacheList);
- this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ? this->registeredInlineCacheCount - cacheCount : 0;
- this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > nullCacheCount ? this->unregisteredInlineCacheCount - nullCacheCount : 0;
- }
- void
- ThreadContext::CompactInlineCacheInvalidationLists()
- {
- #if DBG
- uint countOfNodesToCompact = this->unregisteredInlineCacheCount;
- this->totalUnregisteredCacheCount = 0;
- #endif
- Assert(this->unregisteredInlineCacheCount > 0);
- CompactProtoInlineCaches();
- if (this->unregisteredInlineCacheCount > 0)
- {
- CompactStoreFieldInlineCaches();
- }
- Assert(countOfNodesToCompact == this->totalUnregisteredCacheCount);
- }
- void
- ThreadContext::CompactProtoInlineCaches()
- {
- protoInlineCacheByPropId.MapUntil([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- CompactInlineCacheList(inlineCacheList);
- return this->unregisteredInlineCacheCount == 0;
- });
- }
- void
- ThreadContext::CompactStoreFieldInlineCaches()
- {
- storeFieldInlineCacheByPropId.MapUntil([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- CompactInlineCacheList(inlineCacheList);
- return this->unregisteredInlineCacheCount == 0;
- });
- }
- void
- ThreadContext::CompactInlineCacheList(InlineCacheList* inlineCacheList)
- {
- Assert(inlineCacheList != nullptr);
- uint cacheCount = 0;
- FOREACH_SLISTBASE_ENTRY_EDITING(Js::InlineCache*, inlineCache, inlineCacheList, iterator)
- {
- if (inlineCache == nullptr)
- {
- iterator.RemoveCurrent();
- cacheCount++;
- }
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- #if DBG
- this->totalUnregisteredCacheCount += cacheCount;
- #endif
- if (cacheCount > 0)
- {
- AssertMsg(this->unregisteredInlineCacheCount >= cacheCount, "Some codepaths didn't unregistered the inlineCaches which might leak memory.");
- this->unregisteredInlineCacheCount = this->unregisteredInlineCacheCount > cacheCount ?
- this->unregisteredInlineCacheCount - cacheCount : 0;
- AssertMsg(this->registeredInlineCacheCount >= cacheCount, "Some codepaths didn't registered the inlineCaches which might leak memory.");
- this->registeredInlineCacheCount = this->registeredInlineCacheCount > cacheCount ?
- this->registeredInlineCacheCount - cacheCount : 0;
- }
- }
- #if DBG
- bool
- ThreadContext::IsProtoInlineCacheRegistered(const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
- {
- return IsInlineCacheRegistered(protoInlineCacheByPropId, inlineCache, propertyId);
- }
- bool
- ThreadContext::IsStoreFieldInlineCacheRegistered(const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
- {
- return IsInlineCacheRegistered(storeFieldInlineCacheByPropId, inlineCache, propertyId);
- }
- bool
- ThreadContext::IsInlineCacheRegistered(InlineCacheListMapByPropertyId& inlineCacheMap, const Js::InlineCache* inlineCache, Js::PropertyId propertyId)
- {
- InlineCacheList* inlineCacheList;
- if (inlineCacheMap.TryGetValue(propertyId, &inlineCacheList))
- {
- return IsInlineCacheInList(inlineCache, inlineCacheList);
- }
- else
- {
- return false;
- }
- }
- bool
- ThreadContext::IsInlineCacheInList(const Js::InlineCache* inlineCache, const InlineCacheList* inlineCacheList)
- {
- Assert(inlineCache != nullptr);
- Assert(inlineCacheList != nullptr);
- FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, curInlineCache, inlineCacheList)
- {
- if (curInlineCache == inlineCache)
- {
- return true;
- }
- }
- NEXT_SLISTBASE_ENTRY;
- return false;
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- ThreadContext::PropertyGuardEntry*
- ThreadContext::EnsurePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, bool& foundExistingEntry)
- {
- PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
- PropertyGuardEntry* entry;
- foundExistingEntry = guards.TryGetValue(propertyRecord, &entry);
- if (!foundExistingEntry)
- {
- entry = RecyclerNew(GetRecycler(), PropertyGuardEntry, GetRecycler());
- guards.UncheckedAdd(CreatePropertyRecordWeakRef(propertyRecord), entry);
- }
- return entry;
- }
- Js::PropertyGuard*
- ThreadContext::RegisterSharedPropertyGuard(Js::PropertyId propertyId)
- {
- Assert(IsActivePropertyId(propertyId));
- const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
- bool foundExistingGuard;
- PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
- if (entry->sharedGuard == nullptr)
- {
- entry->sharedGuard = Js::PropertyGuard::New(GetRecycler());
- }
- Js::PropertyGuard* guard = entry->sharedGuard;
- 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"),
- propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue(), foundExistingGuard ? _u("existing") : _u("new"));
- PHASE_PRINT_TESTTRACE1(Js::FixedMethodsPhase, _u("FixedFields: registered shared guard: name: %s, value: 0x%p, %s\n"),
- propertyRecord->GetBuffer(), guard->GetValue(), foundExistingGuard ? _u("existing") : _u("new"));
- return guard;
- }
- void
- ThreadContext::RegisterLazyBailout(Js::PropertyId propertyId, Js::EntryPointInfo* entryPoint)
- {
- const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
- bool foundExistingGuard;
- PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
- if (!entry->entryPoints)
- {
- entry->entryPoints = RecyclerNew(recycler, PropertyGuardEntry::EntryPointDictionary, recycler, /*capacity*/ 3);
- }
- entry->entryPoints->UncheckedAdd(entryPoint, NULL);
- }
- void
- ThreadContext::RegisterUniquePropertyGuard(Js::PropertyId propertyId, Js::PropertyGuard* guard)
- {
- Assert(IsActivePropertyId(propertyId));
- Assert(guard != nullptr);
- RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef = this->recycler->CreateWeakReferenceHandle(guard);
- RegisterUniquePropertyGuard(propertyId, guardWeakRef);
- }
- void
- ThreadContext::RegisterUniquePropertyGuard(Js::PropertyId propertyId, RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
- {
- Assert(IsActivePropertyId(propertyId));
- Assert(guardWeakRef != nullptr);
- Js::PropertyGuard* guard = guardWeakRef->Get();
- Assert(guard != nullptr);
- const Js::PropertyRecord * propertyRecord = GetPropertyName(propertyId);
- bool foundExistingGuard;
- PropertyGuardEntry* entry = EnsurePropertyGuardEntry(propertyRecord, foundExistingGuard);
- entry->uniqueGuards.Item(guardWeakRef);
- if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: registered unique guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p, %s entry\n"),
- propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue(), foundExistingGuard ? _u("existing") : _u("new"));
- Output::Flush();
- }
- if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: registered unique guard: name: %s, value: 0x%p, %s entry\n"),
- propertyRecord->GetBuffer(), guard->GetValue(), foundExistingGuard ? _u("existing") : _u("new"));
- Output::Flush();
- }
- }
- void
- ThreadContext::RegisterConstructorCache(Js::PropertyId propertyId, Js::ConstructorCache* cache)
- {
- Assert(Js::ConstructorCache::GetOffsetOfGuardValue() == Js::PropertyGuard::GetOffsetOfValue());
- Assert(Js::ConstructorCache::GetSizeOfGuardValue() == Js::PropertyGuard::GetSizeOfValue());
- RegisterUniquePropertyGuard(propertyId, reinterpret_cast<Js::PropertyGuard*>(cache));
- }
- void
- ThreadContext::InvalidatePropertyGuardEntry(const Js::PropertyRecord* propertyRecord, PropertyGuardEntry* entry, bool isAllPropertyGuardsInvalidation)
- {
- Assert(entry != nullptr);
- if (entry->sharedGuard != nullptr)
- {
- Js::PropertyGuard* guard = entry->sharedGuard;
- if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
- propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
- Output::Flush();
- }
- if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"), propertyRecord->GetBuffer(), guard->GetValue());
- Output::Flush();
- }
- guard->Invalidate();
- }
- uint count = 0;
- entry->uniqueGuards.Map([&count, propertyRecord](RecyclerWeakReference<Js::PropertyGuard>* guardWeakRef)
- {
- Js::PropertyGuard* guard = guardWeakRef->Get();
- if (guard != nullptr)
- {
- if (PHASE_TRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, address: 0x%p, value: 0x%p, value address: 0x%p\n"),
- propertyRecord->GetBuffer(), guard, guard->GetValue(), guard->GetAddressOfValue());
- Output::Flush();
- }
- if (PHASE_TESTTRACE1(Js::TracePropertyGuardsPhase) || PHASE_VERBOSE_TESTTRACE1(Js::FixedMethodsPhase))
- {
- Output::Print(_u("FixedFields: invalidating guard: name: %s, value: 0x%p\n"),
- propertyRecord->GetBuffer(), guard->GetValue());
- Output::Flush();
- }
- guard->Invalidate();
- count++;
- }
- });
- entry->uniqueGuards.Clear();
- // Count no. of invalidations done so far. Exclude if this is all property guards invalidation in which case
- // the unique Guards will be cleared anyway.
- if (!isAllPropertyGuardsInvalidation)
- {
- this->recyclableData->constructorCacheInvalidationCount += count;
- if (this->recyclableData->constructorCacheInvalidationCount > (uint)CONFIG_FLAG(ConstructorCacheInvalidationThreshold))
- {
- // TODO: In future, we should compact the uniqueGuards dictionary so this function can be called from PreCollectionCallback
- // instead
- this->ClearInvalidatedUniqueGuards();
- this->recyclableData->constructorCacheInvalidationCount = 0;
- }
- }
- if (entry->entryPoints && entry->entryPoints->Count() > 0)
- {
- Js::JavascriptStackWalker stackWalker(this->GetScriptContextList());
- Js::JavascriptFunction* caller;
- while (stackWalker.GetCaller(&caller, /*includeInlineFrames*/ false))
- {
- // If the current frame is already from a bailout - we do not need to do on stack invalidation
- if (caller != nullptr && Js::ScriptFunction::Is(caller) && !stackWalker.GetCurrentFrameFromBailout())
- {
- BYTE dummy;
- Js::FunctionEntryPointInfo* functionEntryPoint = caller->GetFunctionBody()->GetDefaultFunctionEntryPointInfo();
- if (functionEntryPoint->IsInNativeAddressRange((DWORD_PTR)stackWalker.GetInstructionPointer()))
- {
- if (entry->entryPoints->TryGetValue(functionEntryPoint, &dummy))
- {
- functionEntryPoint->DoLazyBailout(stackWalker.GetCurrentAddressOfInstructionPointer(),
- caller->GetFunctionBody(), propertyRecord);
- }
- }
- }
- }
- entry->entryPoints->Map([=](Js::EntryPointInfo* info, BYTE& dummy, const RecyclerWeakReference<Js::EntryPointInfo>* infoWeakRef)
- {
- OUTPUT_TRACE2(Js::LazyBailoutPhase, info->GetFunctionBody(), _u("Lazy bailout - Invalidation due to property: %s \n"), propertyRecord->GetBuffer());
- info->Invalidate(true);
- });
- entry->entryPoints->Clear();
- }
- }
- void
- ThreadContext::InvalidatePropertyGuards(Js::PropertyId propertyId)
- {
- const Js::PropertyRecord* propertyRecord = GetPropertyName(propertyId);
- PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
- PropertyGuardEntry* entry;
- if (guards.TryGetValueAndRemove(propertyRecord, &entry))
- {
- InvalidatePropertyGuardEntry(propertyRecord, entry, false);
- }
- }
- void
- ThreadContext::InvalidateAllPropertyGuards()
- {
- PropertyGuardDictionary &guards = this->recyclableData->propertyGuards;
- if (guards.Count() > 0)
- {
- guards.Map([this](Js::PropertyRecord const * propertyRecord, PropertyGuardEntry* entry, const RecyclerWeakReference<const Js::PropertyRecord>* weakRef)
- {
- InvalidatePropertyGuardEntry(propertyRecord, entry, true);
- });
- guards.Clear();
- }
- }
- #endif
- void
- ThreadContext::InvalidateAllProtoInlineCaches()
- {
- protoInlineCacheByPropId.Map([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- InvalidateAndDeleteInlineCacheList(inlineCacheList);
- });
- protoInlineCacheByPropId.Reset();
- }
- #if DBG
- // Verifies if object is registered in any proto InlineCache
- bool
- ThreadContext::IsObjectRegisteredInProtoInlineCaches(Js::DynamicObject * object)
- {
- return protoInlineCacheByPropId.MapUntil([object](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
- {
- if (inlineCache != nullptr && !inlineCache->IsEmpty())
- {
- // Verify this object is not present in prototype chain of inlineCache's type
- bool isObjectPresentOnPrototypeChain =
- Js::JavascriptOperators::MapObjectAndPrototypesUntil<true>(inlineCache->GetType()->GetPrototype(), [=](Js::RecyclableObject* prototype)
- {
- return prototype == object;
- });
- if (isObjectPresentOnPrototypeChain) {
- return true;
- }
- }
- }
- NEXT_SLISTBASE_ENTRY;
- return false;
- });
- }
- // Verifies if object is registered in any storeField InlineCache
- bool
- ThreadContext::IsObjectRegisteredInStoreFieldInlineCaches(Js::DynamicObject * object)
- {
- return storeFieldInlineCacheByPropId.MapUntil([object](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- FOREACH_SLISTBASE_ENTRY(Js::InlineCache*, inlineCache, inlineCacheList)
- {
- if (inlineCache != nullptr && !inlineCache->IsEmpty())
- {
- // Verify this object is not present in prototype chain of inlineCache's type
- bool isObjectPresentOnPrototypeChain =
- Js::JavascriptOperators::MapObjectAndPrototypesUntil<true>(inlineCache->GetType()->GetPrototype(), [=](Js::RecyclableObject* prototype)
- {
- return prototype == object;
- });
- if (isObjectPresentOnPrototypeChain) {
- return true;
- }
- }
- }
- NEXT_SLISTBASE_ENTRY;
- return false;
- });
- }
- #endif
- bool
- ThreadContext::AreAllProtoInlineCachesInvalidated()
- {
- return protoInlineCacheByPropId.Count() == 0;
- }
- void
- ThreadContext::InvalidateAllStoreFieldInlineCaches()
- {
- storeFieldInlineCacheByPropId.Map([this](Js::PropertyId propertyId, InlineCacheList* inlineCacheList)
- {
- InvalidateAndDeleteInlineCacheList(inlineCacheList);
- });
- storeFieldInlineCacheByPropId.Reset();
- }
- bool
- ThreadContext::AreAllStoreFieldInlineCachesInvalidated()
- {
- return storeFieldInlineCacheByPropId.Count() == 0;
- }
- #if DBG
- bool
- ThreadContext::IsIsInstInlineCacheRegistered(Js::IsInstInlineCache * inlineCache, Js::Var function)
- {
- Assert(inlineCache != nullptr);
- Assert(function != nullptr);
- Js::IsInstInlineCache* firstInlineCache;
- if (this->isInstInlineCacheByFunction.TryGetValue(function, &firstInlineCache))
- {
- return IsIsInstInlineCacheInList(inlineCache, firstInlineCache);
- }
- else
- {
- return false;
- }
- }
- #endif
- void
- ThreadContext::RegisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function)
- {
- Assert(function != nullptr);
- Assert(inlineCache != nullptr);
- // We should never cross-register or re-register a cache that is already on some invalidation list (for its function or some other function).
- // Every cache must be first cleared and unregistered before being registered again.
- AssertMsg(inlineCache->function == nullptr, "We should only register instance-of caches that have not yet been populated.");
- Js::IsInstInlineCache** inlineCacheRef = nullptr;
- if (this->isInstInlineCacheByFunction.TryGetReference(function, &inlineCacheRef))
- {
- AssertMsg(!IsIsInstInlineCacheInList(inlineCache, *inlineCacheRef), "Why are we registering a cache that is already registered?");
- inlineCache->next = *inlineCacheRef;
- *inlineCacheRef = inlineCache;
- }
- else
- {
- inlineCache->next = nullptr;
- this->isInstInlineCacheByFunction.Add(function, inlineCache);
- }
- }
- void
- ThreadContext::UnregisterIsInstInlineCache(Js::IsInstInlineCache * inlineCache, Js::Var function)
- {
- Assert(inlineCache != nullptr);
- Js::IsInstInlineCache** inlineCacheRef = nullptr;
- if (this->isInstInlineCacheByFunction.TryGetReference(function, &inlineCacheRef))
- {
- Assert(*inlineCacheRef != nullptr);
- if (inlineCache == *inlineCacheRef)
- {
- *inlineCacheRef = (*inlineCacheRef)->next;
- if (*inlineCacheRef == nullptr)
- {
- this->isInstInlineCacheByFunction.Remove(function);
- }
- }
- else
- {
- Js::IsInstInlineCache * prevInlineCache;
- Js::IsInstInlineCache * curInlineCache;
- for (prevInlineCache = *inlineCacheRef, curInlineCache = (*inlineCacheRef)->next; curInlineCache != nullptr;
- prevInlineCache = curInlineCache, curInlineCache = curInlineCache->next)
- {
- if (curInlineCache == inlineCache)
- {
- prevInlineCache->next = curInlineCache->next;
- return;
- }
- }
- AssertMsg(false, "Why are we unregistering a cache that is not registered?");
- }
- }
- }
- void
- ThreadContext::InvalidateIsInstInlineCacheList(Js::IsInstInlineCache* inlineCacheList)
- {
- Assert(inlineCacheList != nullptr);
- Js::IsInstInlineCache* curInlineCache;
- Js::IsInstInlineCache* nextInlineCache;
- for (curInlineCache = inlineCacheList; curInlineCache != nullptr; curInlineCache = nextInlineCache)
- {
- if (PHASE_VERBOSE_TRACE1(Js::TraceInlineCacheInvalidationPhase))
- {
- Output::Print(_u("InlineCacheInvalidation: invalidating instanceof cache 0x%p\n"), curInlineCache);
- Output::Flush();
- }
- // Stash away the next cache before we zero out the current one (including its next pointer).
- nextInlineCache = curInlineCache->next;
- // Clear the current cache to invalidate it.
- memset(curInlineCache, 0, sizeof(Js::IsInstInlineCache));
- }
- }
- void
- ThreadContext::InvalidateIsInstInlineCachesForFunction(Js::Var function)
- {
- Js::IsInstInlineCache* inlineCacheList;
- if (this->isInstInlineCacheByFunction.TryGetValueAndRemove(function, &inlineCacheList))
- {
- InvalidateIsInstInlineCacheList(inlineCacheList);
- }
- }
- void
- ThreadContext::InvalidateAllIsInstInlineCaches()
- {
- isInstInlineCacheByFunction.Map([this](const Js::Var function, Js::IsInstInlineCache* inlineCacheList)
- {
- InvalidateIsInstInlineCacheList(inlineCacheList);
- });
- isInstInlineCacheByFunction.Clear();
- }
- bool
- ThreadContext::AreAllIsInstInlineCachesInvalidated() const
- {
- return isInstInlineCacheByFunction.Count() == 0;
- }
- #if DBG
- bool
- ThreadContext::IsIsInstInlineCacheInList(const Js::IsInstInlineCache* inlineCache, const Js::IsInstInlineCache* inlineCacheList)
- {
- Assert(inlineCache != nullptr);
- Assert(inlineCacheList != nullptr);
- for (const Js::IsInstInlineCache* curInlineCache = inlineCacheList; curInlineCache != nullptr; curInlineCache = curInlineCache->next)
- {
- if (curInlineCache == inlineCache)
- {
- return true;
- }
- }
- return false;
- }
- #endif
- void ThreadContext::RegisterTypeWithProtoPropertyCache(const Js::PropertyId propertyId, Js::Type *const type)
- {
- Assert(propertyId != Js::Constants::NoProperty);
- Assert(IsActivePropertyId(propertyId));
- Assert(type);
- PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
- TypeHashSet *typeHashSet;
- if(!typesWithProtoPropertyCache.TryGetValue(propertyId, &typeHashSet))
- {
- typeHashSet = RecyclerNew(recycler, TypeHashSet, recycler);
- typesWithProtoPropertyCache.Add(propertyId, typeHashSet);
- }
- typeHashSet->Item(type, false);
- }
- void ThreadContext::InvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId)
- {
- Assert(propertyId != Js::Constants::NoProperty);
- Assert(IsActivePropertyId(propertyId));
- InternalInvalidateProtoTypePropertyCaches(propertyId);
- }
- void ThreadContext::InternalInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId)
- {
- // Get the hash set of registered types associated with the property ID, invalidate each type in the hash set, and
- // remove the property ID and its hash set from the map
- PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
- TypeHashSet *typeHashSet;
- if(typesWithProtoPropertyCache.Count() != 0 && typesWithProtoPropertyCache.TryGetValueAndRemove(propertyId, &typeHashSet))
- {
- DoInvalidateProtoTypePropertyCaches(propertyId, typeHashSet);
- }
- }
- void ThreadContext::InvalidateAllProtoTypePropertyCaches()
- {
- PropertyIdToTypeHashSetDictionary &typesWithProtoPropertyCache = recyclableData->typesWithProtoPropertyCache;
- if (typesWithProtoPropertyCache.Count() > 0)
- {
- typesWithProtoPropertyCache.Map([this](Js::PropertyId propertyId, TypeHashSet * typeHashSet)
- {
- DoInvalidateProtoTypePropertyCaches(propertyId, typeHashSet);
- });
- typesWithProtoPropertyCache.Clear();
- }
- }
- void ThreadContext::DoInvalidateProtoTypePropertyCaches(const Js::PropertyId propertyId, TypeHashSet *const typeHashSet)
- {
- Assert(propertyId != Js::Constants::NoProperty);
- Assert(typeHashSet);
- typeHashSet->Map(
- [propertyId](Js::Type *const type, const bool unused, const RecyclerWeakReference<Js::Type>*)
- {
- type->GetPropertyCache()->ClearIfPropertyIsOnAPrototype(propertyId);
- });
- }
- Js::ScriptContext **
- ThreadContext::RegisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(Js::ScriptContext * scriptContext)
- {
- return prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext.PrependNode(&prototypeChainEnsuredToHaveOnlyWritableDataPropertiesAllocator, scriptContext);
- }
- void
- ThreadContext::UnregisterPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext(Js::ScriptContext ** scriptContext)
- {
- prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext.RemoveElement(&prototypeChainEnsuredToHaveOnlyWritableDataPropertiesAllocator, scriptContext);
- }
- void
- ThreadContext::ClearPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesCaches()
- {
- bool hasItem = false;
- FOREACH_DLISTBASE_ENTRY(Js::ScriptContext *, scriptContext, &prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext)
- {
- scriptContext->ClearPrototypeChainEnsuredToHaveOnlyWritableDataPropertiesCaches();
- hasItem = true;
- }
- NEXT_DLISTBASE_ENTRY;
- if (!hasItem)
- {
- return;
- }
- prototypeChainEnsuredToHaveOnlyWritableDataPropertiesScriptContext.Reset();
- prototypeChainEnsuredToHaveOnlyWritableDataPropertiesAllocator.Reset();
- }
- BOOL ThreadContext::HasPreviousHostScriptContext()
- {
- return hostScriptContextStack->Count() != 0;
- }
- HostScriptContext* ThreadContext::GetPreviousHostScriptContext()
- {
- return hostScriptContextStack->Peek();
- }
- void ThreadContext::PushHostScriptContext(HostScriptContext* topProvider)
- {
- // script engine can be created coming from GetDispID, so push/pop can be
- // happening after the first round of enterscript as well. we might need to
- // revisit the whole callRootLevel but probably not now.
- // Assert(HasPreviousHostScriptContext() || callRootLevel == 0);
- hostScriptContextStack->Push(topProvider);
- }
- HostScriptContext* ThreadContext::PopHostScriptContext()
- {
- return hostScriptContextStack->Pop();
- // script engine can be created coming from GetDispID, so push/pop can be
- // happening after the first round of enterscript as well. we might need to
- // revisit the whole callRootLevel but probably not now.
- // Assert(HasPreviousHostScriptContext() || callRootLevel == 0);
- }
- #if DBG || defined(PROFILE_EXEC)
- bool
- ThreadContext::AsyncHostOperationStart(void * suspendRecord)
- {
- bool wasInAsync = false;
- Assert(!this->IsScriptActive());
- Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
- if (lastRecord != NULL)
- {
- if (!lastRecord->leaveForHost)
- {
- #if DBG
- wasInAsync = !!lastRecord->leaveForAsyncHostOperation;
- lastRecord->leaveForAsyncHostOperation = true;
- #endif
- #ifdef PROFILE_EXEC
- lastRecord->scriptContext->ProfileSuspend(Js::RunPhase, (Js::Profiler::SuspendRecord *)suspendRecord);
- #endif
- }
- else
- {
- Assert(!lastRecord->leaveForAsyncHostOperation);
- }
- }
- return wasInAsync;
- }
- void
- ThreadContext::AsyncHostOperationEnd(bool wasInAsync, void * suspendRecord)
- {
- Assert(!this->IsScriptActive());
- Js::ScriptEntryExitRecord * lastRecord = this->entryExitRecord;
- if (lastRecord != NULL)
- {
- if (!lastRecord->leaveForHost)
- {
- Assert(lastRecord->leaveForAsyncHostOperation);
- #if DBG
- lastRecord->leaveForAsyncHostOperation = wasInAsync;
- #endif
- #ifdef PROFILE_EXEC
- lastRecord->scriptContext->ProfileResume((Js::Profiler::SuspendRecord *)suspendRecord);
- #endif
- }
- else
- {
- Assert(!lastRecord->leaveForAsyncHostOperation);
- Assert(!wasInAsync);
- }
- }
- }
- #endif
- #ifdef RECYCLER_DUMP_OBJECT_GRAPH
- void DumpRecyclerObjectGraph()
- {
- ThreadContext * threadContext = ThreadContext::GetContextForCurrentThread();
- if (threadContext == nullptr)
- {
- Output::Print(_u("No thread context"));
- }
- threadContext->GetRecycler()->DumpObjectGraph();
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- bool ThreadContext::IsNativeAddressHelper(void * pCodeAddr, Js::ScriptContext* currentScriptContext)
- {
- bool isNativeAddr = false;
- if (currentScriptContext && currentScriptContext->GetJitFuncRangeCache() != nullptr)
- {
- isNativeAddr = currentScriptContext->GetJitFuncRangeCache()->IsNativeAddr(pCodeAddr);
- }
- for (Js::ScriptContext *scriptContext = scriptContextList; scriptContext && !isNativeAddr; scriptContext = scriptContext->next)
- {
- if (scriptContext == currentScriptContext || scriptContext->GetJitFuncRangeCache() == nullptr)
- {
- continue;
- }
- isNativeAddr = scriptContext->GetJitFuncRangeCache()->IsNativeAddr(pCodeAddr);
- }
- return isNativeAddr;
- }
- BOOL ThreadContext::IsNativeAddress(void * pCodeAddr, Js::ScriptContext* currentScriptContext)
- {
- #if ENABLE_OOP_NATIVE_CODEGEN
- if (JITManager::GetJITManager()->IsOOPJITEnabled())
- {
- if (PreReservedVirtualAllocWrapper::IsInRange((void*)m_prereservedRegionAddr, pCodeAddr))
- {
- return true;
- }
- if (IsAllJITCodeInPreReservedRegion())
- {
- return false;
- }
- if (AutoSystemInfo::IsJscriptModulePointer(pCodeAddr))
- {
- return false;
- }
- #if DBG
- boolean result;
- HRESULT hr = JITManager::GetJITManager()->IsNativeAddr(this->m_remoteThreadContextInfo, (intptr_t)pCodeAddr, &result);
- JITManager::HandleServerCallResult(hr, RemoteCallType::HeapQuery);
- #endif
- bool isNativeAddr = IsNativeAddressHelper(pCodeAddr, currentScriptContext);
- #if DBG
- Assert(result == (isNativeAddr? TRUE:FALSE));
- #endif
- return isNativeAddr;
- }
- else
- #endif
- {
- PreReservedVirtualAllocWrapper *preReservedVirtualAllocWrapper = this->GetPreReservedVirtualAllocator();
- if (preReservedVirtualAllocWrapper->IsInRange(pCodeAddr))
- {
- return TRUE;
- }
- if (!this->IsAllJITCodeInPreReservedRegion())
- {
- #if DBG
- AutoCriticalSection autoLock(&this->codePageAllocators.cs);
- #endif
- bool isNativeAddr = IsNativeAddressHelper(pCodeAddr, currentScriptContext);
- #if DBG
- Assert(this->codePageAllocators.IsInNonPreReservedPageAllocator(pCodeAddr) == isNativeAddr);
- #endif
- return isNativeAddr;
- }
- return FALSE;
- }
- }
- #endif
- #if ENABLE_PROFILE_INFO
- void ThreadContext::EnsureSourceProfileManagersByUrlMap()
- {
- if(this->recyclableData->sourceProfileManagersByUrl == nullptr)
- {
- this->EnsureRecycler();
- this->recyclableData->sourceProfileManagersByUrl = RecyclerNew(GetRecycler(), SourceProfileManagersByUrlMap, GetRecycler());
- }
- }
- //
- // 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
- // that references the shared profile manager info.
- //
- Js::SourceDynamicProfileManager* ThreadContext::GetSourceDynamicProfileManager(_In_z_ const WCHAR* url, _In_ uint hash, _Inout_ bool* addRef)
- {
- EnsureSourceProfileManagersByUrlMap();
- Js::SourceDynamicProfileManager* profileManager = nullptr;
- SourceDynamicProfileManagerCache* managerCache;
- bool newCache = false;
- if(!this->recyclableData->sourceProfileManagersByUrl->TryGetValue(url, &managerCache))
- {
- if(this->recyclableData->sourceProfileManagersByUrl->Count() >= INMEMORY_CACHE_MAX_URL)
- {
- return nullptr;
- }
- managerCache = RecyclerNewZ(this->GetRecycler(), SourceDynamicProfileManagerCache);
- newCache = true;
- }
- bool createProfileManager = false;
- if(!managerCache->sourceProfileManagerMap)
- {
- managerCache->sourceProfileManagerMap = RecyclerNew(this->GetRecycler(), SourceDynamicProfileManagerMap, this->GetRecycler());
- createProfileManager = true;
- }
- else
- {
- createProfileManager = !managerCache->sourceProfileManagerMap->TryGetValue(hash, &profileManager);
- }
- if(createProfileManager)
- {
- if(managerCache->sourceProfileManagerMap->Count() < INMEMORY_CACHE_MAX_PROFILE_MANAGER)
- {
- profileManager = RecyclerNewZ(this->GetRecycler(), Js::SourceDynamicProfileManager, this->GetRecycler());
- managerCache->sourceProfileManagerMap->Add(hash, profileManager);
- }
- }
- else
- {
- profileManager->Reuse();
- }
- if(!*addRef)
- {
- managerCache->AddRef();
- *addRef = true;
- OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, _u("Addref dynamic source profile manger - Url: %s\n"), url);
- }
- if (newCache)
- {
- // Let's make a copy of the URL because there is no guarantee this URL will remain alive in the future.
- size_t lengthInChars = wcslen(url) + 1;
- WCHAR* urlCopy = RecyclerNewArrayLeaf(GetRecycler(), WCHAR, lengthInChars);
- js_memcpy_s(urlCopy, lengthInChars * sizeof(WCHAR), url, lengthInChars * sizeof(WCHAR));
- this->recyclableData->sourceProfileManagersByUrl->Add(urlCopy, managerCache);
- }
- return profileManager;
- }
- //
- // Decrement the ref count for this URL and cleanup the corresponding record if there are no other references to it.
- //
- uint ThreadContext::ReleaseSourceDynamicProfileManagers(const WCHAR* url)
- {
- // If we've already freed the recyclable data, we're shutting down the thread context so skip clean up
- if (this->recyclableData == nullptr) return 0;
- SourceDynamicProfileManagerCache* managerCache = this->recyclableData->sourceProfileManagersByUrl->Lookup(url, nullptr);
- uint refCount = 0;
- if(managerCache) // manager cache may be null we exceeded -INMEMORY_CACHE_MAX_URL
- {
- refCount = managerCache->Release();
- OUTPUT_VERBOSE_TRACE(Js::DynamicProfilePhase, _u("Release dynamic source profile manger %d Url: %s\n"), refCount, url);
- Output::Flush();
- if(refCount == 0)
- {
- this->recyclableData->sourceProfileManagersByUrl->Remove(url);
- }
- }
- return refCount;
- }
- #endif
- void ThreadContext::EnsureSymbolRegistrationMap()
- {
- if (this->recyclableData->symbolRegistrationMap == nullptr)
- {
- this->EnsureRecycler();
- this->recyclableData->symbolRegistrationMap = RecyclerNew(GetRecycler(), SymbolRegistrationMap, GetRecycler());
- }
- }
- const Js::PropertyRecord* ThreadContext::GetSymbolFromRegistrationMap(const char16* stringKey)
- {
- this->EnsureSymbolRegistrationMap();
- return this->recyclableData->symbolRegistrationMap->Lookup(stringKey, nullptr);
- }
- const Js::PropertyRecord* ThreadContext::AddSymbolToRegistrationMap(const char16* stringKey, charcount_t stringLength)
- {
- this->EnsureSymbolRegistrationMap();
- const Js::PropertyRecord* propertyRecord = this->UncheckedAddPropertyId(stringKey, stringLength, /*bind*/false, /*isSymbol*/true);
- Assert(propertyRecord);
- // The key is the PropertyRecord's buffer (the PropertyRecord itself) which is being pinned as long as it's in this map.
- // If that's ever not the case, we'll need to duplicate the key here and put that in the map instead.
- this->recyclableData->symbolRegistrationMap->Add(propertyRecord->GetBuffer(), propertyRecord);
- return propertyRecord;
- }
- #if ENABLE_TTD
- JsUtil::BaseDictionary<const char16*, const Js::PropertyRecord*, Recycler, PowerOf2SizePolicy>* ThreadContext::GetSymbolRegistrationMap_TTD()
- {
- //This adds a little memory but makes simplifies other logic -- maybe revise later
- this->EnsureSymbolRegistrationMap();
- return this->recyclableData->symbolRegistrationMap;
- }
- #endif
- void ThreadContext::ClearImplicitCallFlags()
- {
- SetImplicitCallFlags(Js::ImplicitCall_None);
- }
- void ThreadContext::ClearImplicitCallFlags(Js::ImplicitCallFlags flags)
- {
- Assert((flags & Js::ImplicitCall_None) == 0);
- SetImplicitCallFlags((Js::ImplicitCallFlags)(implicitCallFlags & ~flags));
- }
- void ThreadContext::CheckAndResetImplicitCallAccessorFlag()
- {
- Js::ImplicitCallFlags accessorCallFlag = (Js::ImplicitCallFlags)(Js::ImplicitCall_Accessor & ~Js::ImplicitCall_None);
- if ((GetImplicitCallFlags() & accessorCallFlag) != 0)
- {
- ClearImplicitCallFlags(accessorCallFlag);
- AddImplicitCallFlags(Js::ImplicitCall_NonProfiledAccessor);
- }
- }
- bool ThreadContext::HasNoSideEffect(Js::RecyclableObject * function) const
- {
- Js::FunctionInfo::Attributes attributes = Js::FunctionInfo::GetAttributes(function);
- return this->HasNoSideEffect(function, attributes);
- }
- bool ThreadContext::HasNoSideEffect(Js::RecyclableObject * function, Js::FunctionInfo::Attributes attributes) const
- {
- if (((attributes & Js::FunctionInfo::CanBeHoisted) != 0)
- || ((attributes & Js::FunctionInfo::HasNoSideEffect) != 0 && !IsDisableImplicitException()))
- {
- Assert((attributes & Js::FunctionInfo::HasNoSideEffect) != 0);
- return true;
- }
- return false;
- }
- bool
- ThreadContext::RecordImplicitException()
- {
- // Record the exception in the implicit call flag
- AddImplicitCallFlags(Js::ImplicitCall_Exception);
- if (IsDisableImplicitException())
- {
- // Indicate that we shouldn't throw if ImplicitExceptions have been disabled
- return false;
- }
- // Disabling implicit exception when disabling implicit calls can result in valid exceptions not being thrown.
- // Instead we tell not to throw only if an implicit call happened and they are disabled. This is to cover the case
- // of an exception being thrown because an implicit call not executed left the execution in a bad state.
- // Since there is an implicit call, we expect to bailout and handle this operation in the interpreter instead.
- bool hasImplicitCallHappened = IsDisableImplicitCall() && (GetImplicitCallFlags() & ~Js::ImplicitCall_Exception);
- return !hasImplicitCallHappened;
- }
- void ThreadContext::SetThreadServiceWrapper(ThreadServiceWrapper* inThreadServiceWrapper)
- {
- AssertMsg(threadServiceWrapper == NULL || inThreadServiceWrapper == NULL, "double set ThreadServiceWrapper");
- threadServiceWrapper = inThreadServiceWrapper;
- }
- ThreadServiceWrapper* ThreadContext::GetThreadServiceWrapper()
- {
- return threadServiceWrapper;
- }
- uint ThreadContext::GetRandomNumber()
- {
- #ifdef ENABLE_CUSTOM_ENTROPY
- return (uint)GetEntropy().GetRand();
- #else
- uint randomNumber = 0;
- errno_t e = rand_s(&randomNumber);
- Assert(e == 0);
- return randomNumber;
- #endif
- }
- #if defined(ENABLE_JS_ETW) && defined(NTBUILD)
- void ThreadContext::EtwLogPropertyIdList()
- {
- propertyMap->Map([&](const Js::PropertyRecord* propertyRecord){
- EventWriteJSCRIPT_HOSTING_PROPERTYID_LIST(propertyRecord, propertyRecord->GetBuffer());
- });
- }
- #endif
- #ifdef ENABLE_PROJECTION
- void ThreadContext::AddExternalWeakReferenceCache(ExternalWeakReferenceCache *externalWeakReferenceCache)
- {
- this->externalWeakReferenceCacheList.Prepend(&HeapAllocator::Instance, externalWeakReferenceCache);
- }
- void ThreadContext::RemoveExternalWeakReferenceCache(ExternalWeakReferenceCache *externalWeakReferenceCache)
- {
- Assert(!externalWeakReferenceCacheList.Empty());
- this->externalWeakReferenceCacheList.Remove(&HeapAllocator::Instance, externalWeakReferenceCache);
- }
- void ThreadContext::MarkExternalWeakReferencedObjects(bool inPartialCollect)
- {
- SListBase<ExternalWeakReferenceCache *, HeapAllocator>::Iterator iteratorWeakRefCache(&this->externalWeakReferenceCacheList);
- while (iteratorWeakRefCache.Next())
- {
- iteratorWeakRefCache.Data()->MarkNow(recycler, inPartialCollect);
- }
- }
- void ThreadContext::ResolveExternalWeakReferencedObjects()
- {
- SListBase<ExternalWeakReferenceCache *, HeapAllocator>::Iterator iteratorWeakRefCache(&this->externalWeakReferenceCacheList);
- while (iteratorWeakRefCache.Next())
- {
- iteratorWeakRefCache.Data()->ResolveNow(recycler);
- }
- }
- #if DBG_DUMP
- void ThreadContext::RegisterProjectionMemoryInformation(IProjectionContextMemoryInfo* projectionContextMemoryInfo)
- {
- Assert(this->projectionMemoryInformation == nullptr || this->projectionMemoryInformation == projectionContextMemoryInfo);
- this->projectionMemoryInformation = projectionContextMemoryInfo;
- }
- void ThreadContext::DumpProjectionContextMemoryStats(LPCWSTR headerMsg, bool forceDetailed)
- {
- if (this->projectionMemoryInformation)
- {
- this->projectionMemoryInformation->DumpCurrentStats(headerMsg, forceDetailed);
- }
- }
- IProjectionContextMemoryInfo* ThreadContext::GetProjectionContextMemoryInformation()
- {
- return this->projectionMemoryInformation;
- }
- #endif
- #endif
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- Js::Var ThreadContext::GetMemoryStat(Js::ScriptContext* scriptContext)
- {
- ScriptMemoryDumper dumper(scriptContext);
- return dumper.Dump();
- }
- void ThreadContext::SetAutoProxyName(LPCWSTR objectName)
- {
- recyclableData->autoProxyName = objectName;
- }
- #endif
- //
- // Regex helpers
- //
- UnifiedRegex::StandardChars<uint8>* ThreadContext::GetStandardChars(__inout_opt uint8* dummy)
- {
- if (standardUTF8Chars == 0)
- {
- ArenaAllocator* allocator = GetThreadAlloc();
- standardUTF8Chars = Anew(allocator, UnifiedRegex::UTF8StandardChars, allocator);
- }
- return standardUTF8Chars;
- }
- UnifiedRegex::StandardChars<char16>* ThreadContext::GetStandardChars(__inout_opt char16* dummy)
- {
- if (standardUnicodeChars == 0)
- {
- ArenaAllocator* allocator = GetThreadAlloc();
- standardUnicodeChars = Anew(allocator, UnifiedRegex::UnicodeStandardChars, allocator);
- }
- return standardUnicodeChars;
- }
- void ThreadContext::CheckScriptInterrupt()
- {
- if (TestThreadContextFlag(ThreadContextFlagCanDisableExecution))
- {
- if (this->IsExecutionDisabled())
- {
- Assert(this->DoInterruptProbe());
- throw Js::ScriptAbortException();
- }
- }
- else
- {
- this->CheckInterruptPoll();
- }
- }
- void ThreadContext::CheckInterruptPoll()
- {
- // Disable QC when implicit calls are disabled since the host can do anything before returning back, like servicing the
- // message loop, which may in turn cause script code to be executed and implicit calls to be made
- if (!IsDisableImplicitCall())
- {
- InterruptPoller *poller = this->interruptPoller;
- if (poller)
- {
- poller->CheckInterruptPoll();
- }
- }
- }
- void *
- ThreadContext::GetDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType)
- {
- void * data;
- return this->dynamicObjectEnumeratorCacheMap.TryGetValue(dynamicType, &data)? data : nullptr;
- }
- void
- ThreadContext::AddDynamicObjectEnumeratorCache(Js::DynamicType const * dynamicType, void * cache)
- {
- this->dynamicObjectEnumeratorCacheMap.Item(dynamicType, cache);
- }
- InterruptPoller::InterruptPoller(ThreadContext *tc) :
- threadContext(tc),
- lastPollTick(0),
- lastResetTick(0),
- isDisabled(FALSE)
- {
- tc->SetInterruptPoller(this);
- }
- void InterruptPoller::CheckInterruptPoll()
- {
- if (!isDisabled)
- {
- Js::ScriptEntryExitRecord *entryExitRecord = this->threadContext->GetScriptEntryExit();
- if (entryExitRecord)
- {
- Js::ScriptContext *scriptContext = entryExitRecord->scriptContext;
- if (scriptContext)
- {
- this->TryInterruptPoll(scriptContext);
- }
- }
- }
- }
- void InterruptPoller::GetStatementCount(ULONG *pluHi, ULONG *pluLo)
- {
- DWORD resetTick = this->lastResetTick;
- DWORD pollTick = this->lastPollTick;
- DWORD elapsed;
- elapsed = pollTick - resetTick;
- ULONGLONG statements = (ULONGLONG)elapsed * InterruptPoller::TicksToStatements;
- *pluLo = (ULONG)statements;
- *pluHi = (ULONG)(statements >> 32);
- }
- void ThreadContext::DisableExecution()
- {
- Assert(TestThreadContextFlag(ThreadContextFlagCanDisableExecution));
- // Hammer the stack limit with a value that will cause script abort on the next stack probe.
- this->SetStackLimitForCurrentThread(Js::Constants::StackLimitForScriptInterrupt);
- return;
- }
- void ThreadContext::EnableExecution()
- {
- Assert(this->GetStackProber());
- // Restore the normal stack limit.
- this->SetStackLimitForCurrentThread(this->GetStackProber()->GetScriptStackLimit());
- // It's possible that the host disabled execution after the script threw an exception
- // of it's own, so we shouldn't clear that. Only exceptions for script termination
- // should be cleared.
- if (GetRecordedException() == GetPendingTerminatedErrorObject())
- {
- SetRecordedException(NULL);
- }
- }
- bool ThreadContext::TestThreadContextFlag(ThreadContextFlags contextFlag) const
- {
- return (this->threadContextFlags & contextFlag) != 0;
- }
- void ThreadContext::SetThreadContextFlag(ThreadContextFlags contextFlag)
- {
- this->threadContextFlags = (ThreadContextFlags)(this->threadContextFlags | contextFlag);
- }
- void ThreadContext::ClearThreadContextFlag(ThreadContextFlags contextFlag)
- {
- this->threadContextFlags = (ThreadContextFlags)(this->threadContextFlags & ~contextFlag);
- }
- #ifdef ENABLE_GLOBALIZATION
- Js::DelayLoadWinRtString * ThreadContext::GetWinRTStringLibrary()
- {
- delayLoadWinRtString.EnsureFromSystemDirOnly();
- return &delayLoadWinRtString;
- }
- #ifdef ENABLE_PROJECTION
- Js::DelayLoadWinRtError * ThreadContext::GetWinRTErrorLibrary()
- {
- delayLoadWinRtError.EnsureFromSystemDirOnly();
- return &delayLoadWinRtError;
- }
- Js::DelayLoadWinRtTypeResolution* ThreadContext::GetWinRTTypeResolutionLibrary()
- {
- delayLoadWinRtTypeResolution.EnsureFromSystemDirOnly();
- return &delayLoadWinRtTypeResolution;
- }
- Js::DelayLoadWinRtRoParameterizedIID* ThreadContext::GetWinRTRoParameterizedIIDLibrary()
- {
- delayLoadWinRtRoParameterizedIID.EnsureFromSystemDirOnly();
- return &delayLoadWinRtRoParameterizedIID;
- }
- #endif
- #if defined(ENABLE_INTL_OBJECT) || defined(ENABLE_ES6_CHAR_CLASSIFIER)
- Js::WindowsGlobalizationAdapter* ThreadContext::GetWindowsGlobalizationAdapter()
- {
- return &windowsGlobalizationAdapter;
- }
- Js::DelayLoadWindowsGlobalization* ThreadContext::GetWindowsGlobalizationLibrary()
- {
- delayLoadWindowsGlobalizationLibrary.Ensure(this->GetWinRTStringLibrary());
- return &delayLoadWindowsGlobalizationLibrary;
- }
- #endif
- #ifdef ENABLE_FOUNDATION_OBJECT
- Js::WindowsFoundationAdapter* ThreadContext::GetWindowsFoundationAdapter()
- {
- return &windowsFoundationAdapter;
- }
- Js::DelayLoadWinRtFoundation* ThreadContext::GetWinRtFoundationLibrary()
- {
- delayLoadWinRtFoundationLibrary.EnsureFromSystemDirOnly();
- return &delayLoadWinRtFoundationLibrary;
- }
- #endif
- #endif // ENABLE_GLOBALIZATION
- // Despite the name, callers expect this to return the highest propid + 1.
- uint ThreadContext::GetHighestPropertyNameIndex() const
- {
- return propertyMap->GetLastIndex() + 1 + Js::InternalPropertyIds::Count;
- }
- #if defined(CHECK_MEMORY_LEAK) || defined(LEAK_REPORT)
- void ThreadContext::ReportAndCheckLeaksOnProcessDetach()
- {
- bool needReportOrCheck = false;
- #ifdef LEAK_REPORT
- needReportOrCheck = needReportOrCheck || Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag);
- #endif
- #ifdef CHECK_MEMORY_LEAK
- needReportOrCheck = needReportOrCheck ||
- (Js::Configuration::Global.flags.CheckMemoryLeak && MemoryLeakCheck::IsEnableOutput());
- #endif
- if (!needReportOrCheck)
- {
- return;
- }
- // Report leaks even if this is a force termination and we have not clean up the thread
- // This is call during process detach. No one should be creating new thread context.
- // So don't need to take the lock
- ThreadContext * current = GetThreadContextList();
- while (current)
- {
- #if DBG
- current->pageAllocator.ClearConcurrentThreadId();
- #endif
- Recycler * recycler = current->GetRecycler();
- #ifdef LEAK_REPORT
- if (Js::Configuration::Global.flags.IsEnabled(Js::LeakReportFlag))
- {
- AUTO_LEAK_REPORT_SECTION(Js::Configuration::Global.flags, _u("Thread Context (%p): Process Termination (TID: %d)"), current, current->threadId);
- LeakReport::DumpUrl(current->threadId);
- // Heuristically figure out which one is the root tracker script engine
- // and force close on it
- if (current->rootTrackerScriptContext != nullptr)
- {
- current->rootTrackerScriptContext->Close(false);
- }
- recycler->ReportLeaksOnProcessDetach();
- }
- #endif
- #ifdef CHECK_MEMORY_LEAK
- recycler->CheckLeaksOnProcessDetach(_u("Process Termination"));
- #endif
- current = current->Next();
- }
- }
- #endif
- #ifdef LEAK_REPORT
- void
- ThreadContext::SetRootTrackerScriptContext(Js::ScriptContext * scriptContext)
- {
- Assert(this->rootTrackerScriptContext == nullptr);
- this->rootTrackerScriptContext = scriptContext;
- scriptContext->isRootTrackerScriptContext = true;
- }
- void
- ThreadContext::ClearRootTrackerScriptContext(Js::ScriptContext * scriptContext)
- {
- Assert(this->rootTrackerScriptContext == scriptContext);
- this->rootTrackerScriptContext->isRootTrackerScriptContext = false;
- this->rootTrackerScriptContext = nullptr;
- }
- #endif
- AutoTagNativeLibraryEntry::AutoTagNativeLibraryEntry(Js::RecyclableObject* function, Js::CallInfo callInfo, PCWSTR name, void* addr)
- {
- // Save function/callInfo values (for StackWalker). Compiler may stackpack/optimize them for built-in native functions.
- entry.function = function;
- entry.callInfo = callInfo;
- entry.name = name;
- entry.addr = addr;
- ThreadContext* threadContext = function->GetScriptContext()->GetThreadContext();
- threadContext->PushNativeLibraryEntry(&entry);
- }
- AutoTagNativeLibraryEntry::~AutoTagNativeLibraryEntry()
- {
- ThreadContext* threadContext = entry.function->GetScriptContext()->GetThreadContext();
- Assert(threadContext->PeekNativeLibraryEntry() == &entry);
- threadContext->PopNativeLibraryEntry();
- }
|