| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeBasePch.h"
- #include "ByteCode/ByteCodeApi.h"
- #include "ByteCode/ByteCodeDumper.h"
- #include "Language/AsmJsTypes.h"
- #include "Language/AsmJsModule.h"
- #include "ByteCode/ByteCodeSerializer.h"
- #include "Language/FunctionCodeGenRuntimeData.h"
- #include "ByteCode/ScopeInfo.h"
- #include "Base/EtwTrace.h"
- #ifdef VTUNE_PROFILING
- #include "Base/VTuneChakraProfile.h"
- #endif
- #ifdef DYNAMIC_PROFILE_MUTATOR
- #include "Language/DynamicProfileMutator.h"
- #endif
- #include "Language/SourceDynamicProfileManager.h"
- #ifdef ENABLE_SCRIPT_DEBUGGING
- #include "Debug/ProbeContainer.h"
- #include "Debug/DebugContext.h"
- #endif
- #include "Parser.h"
- #include "RegexCommon.h"
- #include "RegexPattern.h"
- #include "Library/RegexHelper.h"
- #include "Language/InterpreterStackFrame.h"
- #include "Library/ModuleRoot.h"
- #include "Types/PathTypeHandler.h"
- #include "Common/MathUtil.h"
- #if ENABLE_NATIVE_CODEGEN
- #include "NativeEntryPointData.h"
- #include "JitTransferData.h"
- #endif
- namespace Js
- {
- // The VS2013 linker treats this as a redefinition of an already
- // defined constant and complains. So skip the declaration if we're compiling
- // with VS2013 or below.
- #if !defined(_MSC_VER) || _MSC_VER >= 1900
- uint const ScopeSlots::MaxEncodedSlotCount;
- #endif
- #ifdef FIELD_ACCESS_STATS
- void FieldAccessStats::Add(FieldAccessStats* other)
- {
- Assert(other != nullptr);
- this->totalInlineCacheCount += other->totalInlineCacheCount;
- this->noInfoInlineCacheCount += other->noInfoInlineCacheCount;
- this->monoInlineCacheCount += other->monoInlineCacheCount;
- this->emptyMonoInlineCacheCount += other->emptyMonoInlineCacheCount;
- this->polyInlineCacheCount += other->polyInlineCacheCount;
- this->nullPolyInlineCacheCount += other->nullPolyInlineCacheCount;
- this->emptyPolyInlineCacheCount += other->emptyPolyInlineCacheCount;
- this->ignoredPolyInlineCacheCount += other->ignoredPolyInlineCacheCount;
- this->highUtilPolyInlineCacheCount += other->highUtilPolyInlineCacheCount;
- this->lowUtilPolyInlineCacheCount += other->lowUtilPolyInlineCacheCount;
- this->equivPolyInlineCacheCount += other->equivPolyInlineCacheCount;
- this->nonEquivPolyInlineCacheCount += other->nonEquivPolyInlineCacheCount;
- this->disabledPolyInlineCacheCount += other->disabledPolyInlineCacheCount;
- this->clonedMonoInlineCacheCount += other->clonedMonoInlineCacheCount;
- this->clonedPolyInlineCacheCount += other->clonedPolyInlineCacheCount;
- }
- #endif
- // FunctionProxy methods
- FunctionProxy::FunctionProxy(ScriptContext* scriptContext, Utf8SourceInfo* utf8SourceInfo, uint functionNumber):
- m_isTopLevel(false),
- m_isPublicLibraryCode(false),
- m_scriptContext(scriptContext),
- deferredPrototypeType(nullptr),
- undeferredFunctionType(nullptr),
- m_utf8SourceInfo(utf8SourceInfo),
- m_functionNumber(functionNumber),
- m_defaultEntryPointInfo(nullptr),
- m_displayNameIsRecyclerAllocated(false),
- m_tag11(true),
- m_isJsBuiltInCode(false)
- {
- PERF_COUNTER_INC(Code, TotalFunction);
- }
- bool FunctionProxy::IsWasmFunction() const
- {
- return GetFunctionInfo()->HasParseableInfo() &&
- GetFunctionInfo()->GetFunctionBody()->IsWasmFunction();
- }
- Recycler* FunctionProxy::GetRecycler() const
- {
- return m_scriptContext->GetRecycler();
- }
- void* FunctionProxy::GetAuxPtr(AuxPointerType e) const
- {
- if (this->auxPtrs == nullptr)
- {
- return nullptr;
- }
- // On process detach this can be called from another thread but the ThreadContext should be locked
- Assert(ThreadContext::GetContextForCurrentThread() || ThreadContext::GetCriticalSection()->IsLocked());
- return AuxPtrsT::GetAuxPtr(this, e);
- }
- void* FunctionProxy::GetAuxPtrWithLock(AuxPointerType e) const
- {
- if (this->auxPtrs == nullptr)
- {
- return nullptr;
- }
- #if DBG && ENABLE_NATIVE_CODEGEN && defined(_WIN32)
- // the lock for work item queue should not be locked while accessing AuxPtrs in background thread
- auto jobProcessor = this->GetScriptContext()->GetThreadContext()->GetJobProcessor();
- auto jobProcessorCS = jobProcessor->GetCriticalSection();
- // ->IsLocked is not supported on xplat
- Assert(!jobProcessorCS || !jobProcessor->ProcessesInBackground() || !jobProcessorCS->IsLocked());
- #endif
- AutoCriticalSection autoCS(this->GetScriptContext()->GetThreadContext()->GetFunctionBodyLock());
- return AuxPtrsT::GetAuxPtr(this, e);
- }
- void FunctionProxy::SetAuxPtr(AuxPointerType e, void* ptr)
- {
- // On process detach this can be called from another thread but the ThreadContext should be locked
- Assert(ThreadContext::GetContextForCurrentThread() || ThreadContext::GetCriticalSection()->IsLocked());
- if (ptr == nullptr && GetAuxPtr(e) == nullptr)
- {
- return;
- }
- // when setting ptr to null we never need to promote
- AutoCriticalSection autoCS(this->GetScriptContext()->GetThreadContext()->GetFunctionBodyLock());
- AuxPtrsT::SetAuxPtr(this, e, ptr);
- }
- uint FunctionProxy::GetSourceContextId() const
- {
- return this->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->sourceContextId;
- }
- char16* FunctionProxy::GetDebugNumberSet(wchar(&bufferToWriteTo)[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE]) const
- {
- // (#%u.%u), #%u --> (source file Id . function Id) , function Number
- int len = swprintf_s(bufferToWriteTo, MAX_FUNCTION_BODY_DEBUG_STRING_SIZE, _u(" (#%d.%u), #%u"),
- (int)this->GetSourceContextId(), this->GetLocalFunctionId(), this->GetFunctionNumber());
- Assert(len > 8);
- return bufferToWriteTo;
- }
- bool
- FunctionProxy::IsFunctionBody() const
- {
- return !IsDeferredDeserializeFunction() && GetParseableFunctionInfo()->IsFunctionParsed();
- }
- uint
- ParseableFunctionInfo::GetSourceIndex() const
- {
- return this->m_sourceIndex;
- }
- LPCUTF8
- ParseableFunctionInfo::GetSource(const char16* reason) const
- {
- return this->GetUtf8SourceInfo()->GetSource(reason == nullptr ? _u("ParseableFunctionInfo::GetSource") : reason) + this->StartOffset();
- }
- LPCUTF8
- ParseableFunctionInfo::GetToStringSource(const char16* reason) const
- {
- return this->GetUtf8SourceInfo()->GetSource(reason == nullptr ? _u("ParseableFunctionInfo::GetToStringSource") : reason) + this->PrintableStartOffset();
- }
- LPCUTF8
- ParseableFunctionInfo::GetStartOfDocument(const char16* reason) const
- {
- return this->GetUtf8SourceInfo()->GetSource(reason == nullptr ? _u("ParseableFunctionInfo::GetStartOfDocument") : reason);
- }
- bool
- ParseableFunctionInfo::IsDynamicFunction() const
- {
- return this->m_isDynamicFunction;
- }
- bool
- ParseableFunctionInfo::IsDynamicScript() const
- {
- return this->GetSourceContextInfo()->IsDynamic();
- }
- charcount_t
- ParseableFunctionInfo::StartInDocument() const
- {
- return this->m_cchStartOffset;
- }
- uint
- ParseableFunctionInfo::StartOffset() const
- {
- return this->m_cbStartOffset;
- }
- uint
- ParseableFunctionInfo::PrintableStartOffset() const
- {
- return this->m_cbStartPrintOffset;
- }
- void ParseableFunctionInfo::RegisterFuncToDiag(ScriptContext * scriptContext, char16 const * pszTitle)
- {
- #ifdef ENABLE_SCRIPT_DEBUGGING
- // Register the function to the PDM as eval code (the debugger app will show file as 'eval code')
- scriptContext->GetDebugContext()->RegisterFunction(this, pszTitle);
- #endif
- }
- bool ParseableFunctionInfo::IsES6ModuleCode() const
- {
- return (GetGrfscr() & fscrIsModuleCode) == fscrIsModuleCode;
- }
- // Given an offset into the source buffer, determine if the end of this SourceInfo
- // lies after the given offset.
- bool
- ParseableFunctionInfo::EndsAfter(size_t offset) const
- {
- return offset < this->StartOffset() + this->LengthInBytes();
- }
- uint32 FunctionBody::GetCountField(FunctionBody::CounterFields fieldEnum) const
- {
- #if DBG
- bool isCountersLockedDown = counters.isLockedDown;
- Assert(ThreadContext::GetContextForCurrentThread() || isCountersLockedDown
- || (ThreadContext::GetCriticalSection()->IsLocked() && this->m_scriptContext->GetThreadContext()->GetFunctionBodyLock()->IsLocked())); // etw rundown
- #endif
- return counters.Get(fieldEnum);
- }
- uint32 FunctionBody::SetCountField(FunctionBody::CounterFields fieldEnum, uint32 val)
- {
- DebugOnly(bool isCountersLockedDown = counters.isLockedDown);
- Assert(!isCountersLockedDown || counters.isClosing);
- return counters.Set(fieldEnum, val, this);
- }
- uint32 FunctionBody::IncreaseCountField(FunctionBody::CounterFields fieldEnum)
- {
- DebugOnly(bool isCountersLockedDown = counters.isLockedDown);
- Assert(!isCountersLockedDown || counters.isClosing);
- return counters.Increase(fieldEnum, this);
- }
- void
- FunctionBody::RecordStatementMap(StatementMap* pStatementMap)
- {
- Assert(!this->m_sourceInfo.pSpanSequence);
- Recycler* recycler = this->m_scriptContext->GetRecycler();
- StatementMapList * statementMaps = this->GetStatementMaps();
- if (!statementMaps)
- {
- statementMaps = RecyclerNew(recycler, StatementMapList, recycler);
- this->SetStatementMaps(statementMaps);
- }
- statementMaps->Add(pStatementMap);
- }
- void
- FunctionBody::RecordStatementMap(SmallSpanSequenceIter &iter, StatementData * data)
- {
- Assert(!this->GetStatementMaps());
- if (!this->m_sourceInfo.pSpanSequence)
- {
- this->m_sourceInfo.pSpanSequence = HeapNew(SmallSpanSequence);
- }
- this->m_sourceInfo.pSpanSequence->RecordARange(iter, data);
- }
- void
- FunctionBody::RecordStatementAdjustment(uint offset, StatementAdjustmentType adjType)
- {
- this->EnsureAuxStatementData();
- Recycler* recycler = this->m_scriptContext->GetRecycler();
- if (this->GetStatementAdjustmentRecords() == nullptr)
- {
- m_sourceInfo.m_auxStatementData->m_statementAdjustmentRecords = RecyclerNew(recycler, StatementAdjustmentRecordList, recycler);
- }
- StatementAdjustmentRecord record(adjType, offset);
- this->GetStatementAdjustmentRecords()->Add(record); // Will copy stack value and put the copy into the container.
- }
- BOOL
- FunctionBody::GetBranchOffsetWithin(uint start, uint end, StatementAdjustmentRecord* record)
- {
- Assert(start < end);
- if (!this->GetStatementAdjustmentRecords())
- {
- // No Offset
- return FALSE;
- }
- int count = this->GetStatementAdjustmentRecords()->Count();
- for (int i = 0; i < count; i++)
- {
- StatementAdjustmentRecord item = this->GetStatementAdjustmentRecords()->Item(i);
- if (item.GetByteCodeOffset() > start && item.GetByteCodeOffset() < end)
- {
- *record = item;
- return TRUE;
- }
- }
- // No offset found in the range.
- return FALSE;
- }
- ScriptContext* EntryPointInfo::GetScriptContext()
- {
- Assert(!IsCleanedUp());
- return this->library->GetScriptContext();
- }
- #if ENABLE_NATIVE_CODEGEN
- #if DBG_DUMP | defined(VTUNE_PROFILING)
- void
- EntryPointInfo::RecordNativeMap(uint32 nativeOffset, uint32 statementIndex)
- {
- auto& nativeOffsetMaps = this->GetNativeEntryPointData()->GetNativeOffsetMaps();
- int count = nativeOffsetMaps.Count();
- if (count)
- {
- NativeEntryPointData::NativeOffsetMap* previous = &nativeOffsetMaps.Item(count-1);
- // Check if the range is still not finished.
- if (previous->nativeOffsetSpan.begin == previous->nativeOffsetSpan.end)
- {
- if (previous->statementIndex == statementIndex)
- {
- // If the statement index is the same, we can continue with the previous range
- return;
- }
- // If the range is empty, replace the previous range.
- if ((uint32)previous->nativeOffsetSpan.begin == nativeOffset)
- {
- if (statementIndex == Js::Constants::NoStatementIndex)
- {
- nativeOffsetMaps.RemoveAtEnd();
- }
- else
- {
- previous->statementIndex = statementIndex;
- }
- return;
- }
- // Close the previous range
- previous->nativeOffsetSpan.end = nativeOffset;
- }
- }
- if (statementIndex == Js::Constants::NoStatementIndex)
- {
- // We do not explicitly record the offsets that do not map to user code.
- return;
- }
- NativeEntryPointData::NativeOffsetMap map;
- map.statementIndex = statementIndex;
- map.nativeOffsetSpan.begin = nativeOffset;
- map.nativeOffsetSpan.end = nativeOffset;
- nativeOffsetMaps.Add(map);
- }
- #endif
- #endif
- void
- FunctionBody::CopySourceInfo(ParseableFunctionInfo* originalFunctionInfo)
- {
- this->FinishSourceInfo();
- }
- // When sourceInfo is complete, register this functionBody to utf8SourceInfo. This ensures we never
- // put incomplete functionBody into utf8SourceInfo map. (Previously we do it in FunctionBody constructor.
- // If an error occurs thereafter before SetSourceInfo, e.g. OOM, we'll have an incomplete functionBody
- // in utf8SourceInfo map whose source range is unknown and can't be reparsed.)
- void FunctionBody::FinishSourceInfo()
- {
- this->GetUtf8SourceInfo()->SetFunctionBody(this);
- }
- RegSlot FunctionBody::GetFrameDisplayRegister() const
- {
- return this->m_sourceInfo.frameDisplayRegister;
- }
- void FunctionBody::SetFrameDisplayRegister(RegSlot frameDisplayRegister)
- {
- this->m_sourceInfo.frameDisplayRegister = frameDisplayRegister;
- }
- RegSlot FunctionBody::GetObjectRegister() const
- {
- return this->m_sourceInfo.objectRegister;
- }
- void FunctionBody::SetObjectRegister(RegSlot objectRegister)
- {
- this->m_sourceInfo.objectRegister = objectRegister;
- }
- ScopeObjectChain *FunctionBody::GetScopeObjectChain() const
- {
- return this->m_sourceInfo.pScopeObjectChain;
- }
- void FunctionBody::SetScopeObjectChain(ScopeObjectChain *pScopeObjectChain)
- {
- this->m_sourceInfo.pScopeObjectChain = pScopeObjectChain;
- }
- ByteBlock *FunctionBody::GetProbeBackingBlock()
- {
- return this->m_sourceInfo.m_probeBackingBlock;
- }
- void FunctionBody::SetProbeBackingBlock(ByteBlock* probeBackingBlock)
- {
- this->m_sourceInfo.m_probeBackingBlock = probeBackingBlock;
- }
- FunctionBody * FunctionBody::NewFromRecycler(ScriptContext * scriptContext, const char16 * displayName, uint displayNameLength, uint displayShortNameOffset, uint nestedCount,
- Utf8SourceInfo* sourceInfo, uint uScriptId, Js::LocalFunctionId functionId, FunctionInfo::Attributes attributes, FunctionBodyFlags flags
- #ifdef PERF_COUNTERS
- , bool isDeserializedFunction
- #endif
- )
- {
- return FunctionBody::NewFromRecycler(scriptContext, displayName, displayNameLength, displayShortNameOffset, nestedCount, sourceInfo,
- scriptContext->GetThreadContext()->NewFunctionNumber(), uScriptId, functionId, attributes, flags
- #ifdef PERF_COUNTERS
- , isDeserializedFunction
- #endif
- );
- }
- FunctionBody * FunctionBody::NewFromRecycler(ScriptContext * scriptContext, const char16 * displayName, uint displayNameLength, uint displayShortNameOffset, uint nestedCount,
- Utf8SourceInfo* sourceInfo, uint uFunctionNumber, uint uScriptId, Js::LocalFunctionId functionId, FunctionInfo::Attributes attributes, FunctionBodyFlags flags
- #ifdef PERF_COUNTERS
- , bool isDeserializedFunction
- #endif
- )
- {
- #ifdef PERF_COUNTERS
- return RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(), FunctionBody, scriptContext, displayName, displayNameLength, displayShortNameOffset, nestedCount, sourceInfo, uFunctionNumber, uScriptId, functionId, attributes, flags, isDeserializedFunction);
- #else
- return RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(), FunctionBody, scriptContext, displayName, displayNameLength, displayShortNameOffset, nestedCount, sourceInfo, uFunctionNumber, uScriptId, functionId, attributes, flags);
- #endif
- }
- FunctionBody *
- FunctionBody::NewFromParseableFunctionInfo(ParseableFunctionInfo * parseableFunctionInfo)
- {
- ScriptContext * scriptContext = parseableFunctionInfo->GetScriptContext();
- uint nestedCount = parseableFunctionInfo->GetNestedCount();
- FunctionBody * functionBody = RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(),
- FunctionBody,
- parseableFunctionInfo);
- // Initialize nested function array, update back pointers
- for (uint i = 0; i < nestedCount; i++)
- {
- FunctionInfo * nestedInfo = parseableFunctionInfo->GetNestedFunc(i);
- functionBody->SetNestedFunc(nestedInfo, i, 0);
- }
- return functionBody;
- }
- FunctionBody::FunctionBody(ScriptContext* scriptContext, const char16* displayName, uint displayNameLength, uint displayShortNameOffset, uint nestedCount,
- Utf8SourceInfo* utf8SourceInfo, uint uFunctionNumber, uint uScriptId,
- Js::LocalFunctionId functionId, FunctionInfo::Attributes attributes, FunctionBodyFlags flags
- #ifdef PERF_COUNTERS
- , bool isDeserializedFunction
- #endif
- ) :
- ParseableFunctionInfo(scriptContext->CurrentThunk, nestedCount, functionId, utf8SourceInfo, scriptContext, uFunctionNumber, displayName, displayNameLength, displayShortNameOffset, attributes, flags),
- counters(this),
- m_uScriptId(uScriptId),
- cleanedUp(false),
- sourceInfoCleanedUp(false),
- profiledLdLenCount(0),
- profiledLdElemCount(0),
- profiledStElemCount(0),
- profiledCallSiteCount(0),
- profiledArrayCallSiteCount(0),
- profiledDivOrRemCount(0),
- profiledSwitchCount(0),
- profiledReturnTypeCount(0),
- profiledSlotCount(0),
- m_isFuncRegistered(false),
- m_isFuncRegisteredToDiag(false),
- m_hasBailoutInstrInJittedCode(false),
- m_depth(0),
- inlineDepth(0),
- m_pendingLoopHeaderRelease(false),
- hasCachedScopePropIds(false),
- m_argUsedForBranch(0),
- m_envDepth((uint16)-1),
- loopInterpreterLimit(CONFIG_FLAG(LoopInterpretCount)),
- savedPolymorphicCacheState(0),
- debuggerScopeIndex(0),
- m_hasFinally(false),
- #if ENABLE_PROFILE_INFO
- dynamicProfileInfo(nullptr),
- #endif
- savedInlinerVersion(0),
- #if ENABLE_NATIVE_CODEGEN
- savedImplicitCallsFlags(ImplicitCall_HasNoInfo),
- #endif
- hasExecutionDynamicProfileInfo(false),
- m_hasAllNonLocalReferenced(false),
- m_hasSetIsObject(false),
- m_hasFunExprNameReference(false),
- m_CallsEval(false),
- m_ChildCallsEval(false),
- m_hasReferenceableBuiltInArguments(false),
- m_isParamAndBodyScopeMerged(true),
- m_firstFunctionObject(true),
- m_inlineCachesOnFunctionObject(false),
- m_hasDoneAllNonLocalReferenced(false),
- m_hasFunctionCompiledSent(false),
- byteCodeCache(nullptr),
- m_hasLocalClosureRegister(false),
- m_hasParamClosureRegister(false),
- m_hasLocalFrameDisplayRegister(false),
- m_hasEnvRegister(false),
- m_hasThisRegisterForEventHandler(false),
- m_hasFirstInnerScopeRegister(false),
- m_hasFuncExprScopeRegister(false),
- m_hasFirstTmpRegister(false),
- m_hasActiveReference(false),
- m_tag31(true),
- m_tag32(true),
- m_tag33(true),
- m_nativeEntryPointUsed(false),
- hasDoneLoopBodyCodeGen(false),
- bailOnMisingProfileCount(0),
- bailOnMisingProfileRejitCount(0),
- byteCodeBlock(nullptr),
- entryPoints(nullptr),
- m_constTable(nullptr),
- inlineCaches(nullptr),
- cacheIdToPropertyIdMap(nullptr),
- wasCalledFromLoop(false),
- hasScopeObject(false),
- hasNestedLoop(false),
- recentlyBailedOutOfJittedLoopBody(false),
- m_isAsmJsScheduledForFullJIT(false),
- m_asmJsTotalLoopCount(0)
- //
- // Even if the function does not require any locals, we must always have "R0" to propagate
- // a return value. By enabling this here, we avoid unnecessary conditionals during execution.
- //
- #ifdef IR_VIEWER
- ,m_isIRDumpEnabled(false)
- ,m_irDumpBaseObject(nullptr)
- #endif /* IR_VIEWER */
- , m_isFromNativeCodeModule(false)
- , hasHotLoop(false)
- , m_isPartialDeserializedFunction(false)
- #if DBG
- , m_isSerialized(false)
- #endif
- #ifdef PERF_COUNTERS
- , m_isDeserializedFunction(isDeserializedFunction)
- #endif
- #if DBG
- , m_DEBUG_executionCount(0)
- , m_nativeEntryPointIsInterpreterThunk(false)
- , m_canDoStackNestedFunc(false)
- , m_inlineCacheTypes(nullptr)
- , m_iProfileSession(-1)
- #endif
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- , regAllocLoadCount(0)
- , regAllocStoreCount(0)
- , callCountStats(0)
- #endif
- {
- SetCountField(CounterFields::ConstantCount, 1);
- this->SetDefaultFunctionEntryPointInfo((FunctionEntryPointInfo*) this->GetDefaultEntryPointInfo(), DefaultEntryThunk);
- this->m_hasBeenParsed = true;
- #ifdef PERF_COUNTERS
- if (isDeserializedFunction)
- {
- PERF_COUNTER_INC(Code, DeserializedFunctionBody);
- }
- #endif
- Assert(!utf8SourceInfo || m_uScriptId == utf8SourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId);
- // Sync entryPoints changes to etw rundown lock
- CriticalSection* syncObj = scriptContext->GetThreadContext()->GetFunctionBodyLock();
- this->entryPoints = RecyclerNew(this->m_scriptContext->GetRecycler(), FunctionEntryPointList, this->m_scriptContext->GetRecycler(), syncObj);
- this->AddEntryPointToEntryPointList(this->GetDefaultFunctionEntryPointInfo());
- Assert(this->GetDefaultEntryPointInfo()->jsMethod != nullptr);
- InitDisableInlineApply();
- InitDisableInlineSpread();
- }
- FunctionBody::FunctionBody(ParseableFunctionInfo * proxy) :
- ParseableFunctionInfo(proxy),
- counters(this),
- m_uScriptId(proxy->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->sourceContextId),
- cleanedUp(false),
- sourceInfoCleanedUp(false),
- profiledLdLenCount(0),
- profiledLdElemCount(0),
- profiledStElemCount(0),
- profiledCallSiteCount(0),
- profiledArrayCallSiteCount(0),
- profiledDivOrRemCount(0),
- profiledSwitchCount(0),
- profiledReturnTypeCount(0),
- profiledSlotCount(0),
- m_isFuncRegistered(false),
- m_isFuncRegisteredToDiag(false),
- m_hasBailoutInstrInJittedCode(false),
- m_depth(0),
- inlineDepth(0),
- m_pendingLoopHeaderRelease(false),
- hasCachedScopePropIds(false),
- m_argUsedForBranch(0),
- m_envDepth((uint16)-1),
- loopInterpreterLimit(CONFIG_FLAG(LoopInterpretCount)),
- savedPolymorphicCacheState(0),
- debuggerScopeIndex(0),
- m_hasFinally(false),
- #if ENABLE_PROFILE_INFO
- dynamicProfileInfo(nullptr),
- #endif
- savedInlinerVersion(0),
- #if ENABLE_NATIVE_CODEGEN
- savedImplicitCallsFlags(ImplicitCall_HasNoInfo),
- #endif
- hasExecutionDynamicProfileInfo(false),
- m_hasAllNonLocalReferenced(false),
- m_hasSetIsObject(false),
- m_hasFunExprNameReference(false),
- m_CallsEval(false),
- m_ChildCallsEval(false),
- m_hasReferenceableBuiltInArguments(false),
- m_isParamAndBodyScopeMerged(true),
- m_firstFunctionObject(true),
- m_inlineCachesOnFunctionObject(false),
- m_hasDoneAllNonLocalReferenced(false),
- m_hasFunctionCompiledSent(false),
- byteCodeCache(nullptr),
- m_hasLocalClosureRegister(false),
- m_hasParamClosureRegister(false),
- m_hasLocalFrameDisplayRegister(false),
- m_hasEnvRegister(false),
- m_hasThisRegisterForEventHandler(false),
- m_hasFirstInnerScopeRegister(false),
- m_hasFuncExprScopeRegister(false),
- m_hasFirstTmpRegister(false),
- m_hasActiveReference(false),
- m_tag31(true),
- m_tag32(true),
- m_tag33(true),
- m_nativeEntryPointUsed(false),
- hasDoneLoopBodyCodeGen(false),
- bailOnMisingProfileCount(0),
- bailOnMisingProfileRejitCount(0),
- byteCodeBlock(nullptr),
- entryPoints(nullptr),
- m_constTable(nullptr),
- inlineCaches(nullptr),
- cacheIdToPropertyIdMap(nullptr),
- wasCalledFromLoop(false),
- hasScopeObject(false),
- hasNestedLoop(false),
- recentlyBailedOutOfJittedLoopBody(false),
- m_isAsmJsScheduledForFullJIT(false),
- m_asmJsTotalLoopCount(0)
- //
- // Even if the function does not require any locals, we must always have "R0" to propagate
- // a return value. By enabling this here, we avoid unnecessary conditionals during execution.
- //
- #ifdef IR_VIEWER
- ,m_isIRDumpEnabled(false)
- ,m_irDumpBaseObject(nullptr)
- #endif /* IR_VIEWER */
- , m_isFromNativeCodeModule(false)
- , hasHotLoop(false)
- , m_isPartialDeserializedFunction(false)
- #if DBG
- , m_isSerialized(false)
- #endif
- #ifdef PERF_COUNTERS
- , m_isDeserializedFunction(false)
- #endif
- #if DBG
- , m_DEBUG_executionCount(0)
- , m_nativeEntryPointIsInterpreterThunk(false)
- , m_canDoStackNestedFunc(false)
- , m_inlineCacheTypes(nullptr)
- , m_iProfileSession(-1)
- #endif
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- , regAllocLoadCount(0)
- , regAllocStoreCount(0)
- , callCountStats(0)
- #endif
- {
- ScriptContext * scriptContext = proxy->GetScriptContext();
- SetCountField(CounterFields::ConstantCount, 1);
- proxy->UpdateFunctionBodyImpl(this);
- this->m_defaultEntryPointInfo = RecyclerNewFinalized(scriptContext->GetRecycler(),
- FunctionEntryPointInfo, this, scriptContext->CurrentThunk, scriptContext->GetThreadContext());
- this->SetDefaultFunctionEntryPointInfo((FunctionEntryPointInfo*) this->GetDefaultEntryPointInfo(), DefaultEntryThunk);
- this->m_hasBeenParsed = true;
- Assert(!proxy->GetUtf8SourceInfo() || m_uScriptId == proxy->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->sourceContextId);
- // Sync entryPoints changes to etw rundown lock
- CriticalSection* syncObj = scriptContext->GetThreadContext()->GetFunctionBodyLock();
- this->entryPoints = RecyclerNew(scriptContext->GetRecycler(), FunctionEntryPointList, scriptContext->GetRecycler(), syncObj);
- this->AddEntryPointToEntryPointList(this->GetDefaultFunctionEntryPointInfo());
- Assert(this->GetDefaultEntryPointInfo()->jsMethod != nullptr);
- InitDisableInlineApply();
- InitDisableInlineSpread();
- }
- bool FunctionBody::InterpretedSinceCallCountCollection() const
- {
- return executionState.InterpretedSinceCallCountCollection();
- }
- void FunctionBody::CollectInterpretedCounts()
- {
- executionState.CollectInterpretedCounts();
- }
- void FunctionBody::IncrInactiveCount(uint increment)
- {
- this->inactiveCount = UInt32Math::Add(this->inactiveCount, increment);
- }
- bool FunctionBody::IsActiveFunction(ActiveFunctionSet * pActiveFuncs) const
- {
- return !!pActiveFuncs->Test(this->GetFunctionNumber());
- }
- bool FunctionBody::TestAndUpdateActiveFunctions(ActiveFunctionSet * pActiveFuncs) const
- {
- return !!pActiveFuncs->TestAndSet(this->GetFunctionNumber());
- }
- void FunctionBody::UpdateActiveFunctionsForOneDataSet(ActiveFunctionSet *pActiveFuncs, FunctionCodeGenRuntimeData *parentData, Field(FunctionCodeGenRuntimeData*)* dataSet, uint count) const
- {
- FunctionCodeGenRuntimeData *inlineeData;
- for (uint i = 0; i < count; i++)
- {
- for (inlineeData = dataSet[i]; inlineeData; inlineeData = inlineeData->GetNext())
- {
- // inlineeData == parentData indicates a cycle in the structure. We've already processed parentData, so don't descend.
- if (inlineeData != parentData)
- {
- inlineeData->GetFunctionBody()->UpdateActiveFunctionSet(pActiveFuncs, inlineeData);
- }
- }
- }
- }
- void FunctionBody::UpdateActiveFunctionSet(ActiveFunctionSet *pActiveFuncs, FunctionCodeGenRuntimeData *callSiteData) const
- {
- // Always walk the inlinee and ldFldInlinee data (if we have them), as they are different at each call site.
- if (callSiteData)
- {
- if (callSiteData->GetInlinees())
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, callSiteData, callSiteData->GetInlinees(), this->GetProfiledCallSiteCount());
- }
- if (callSiteData->GetLdFldInlinees())
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, callSiteData, callSiteData->GetLdFldInlinees(), this->GetInlineCacheCount());
- }
- if (callSiteData->GetCallbackInlinees())
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, callSiteData, callSiteData->GetCallbackInlinees(), this->GetProfiledCallSiteCount());
- }
- }
- // Now walk the top-level data, but only do it once, since it's always the same.
- if (this->TestAndUpdateActiveFunctions(pActiveFuncs))
- {
- return;
- }
- {
- Field(FunctionCodeGenRuntimeData*)* data = this->GetCodeGenRuntimeData();
- if (data != nullptr)
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, nullptr, data, this->GetProfiledCallSiteCount());
- }
- }
- {
- Field(FunctionCodeGenRuntimeData*)* data = this->GetCodeGenGetSetRuntimeData();
- if (data != nullptr)
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, nullptr, data, this->GetInlineCacheCount());
- }
- }
- {
- Field(FunctionCodeGenRuntimeData*)* data = this->GetCodeGenCallbackRuntimeData();
- if (data != nullptr)
- {
- this->UpdateActiveFunctionsForOneDataSet(pActiveFuncs, nullptr, data, this->GetProfiledCallSiteCount());
- }
- }
- }
- bool FunctionBody::DoRedeferFunction(uint inactiveThreshold) const
- {
- if (!(this->GetFunctionInfo()->GetFunctionProxy() == this &&
- this->CanBeDeferred() &&
- this->GetByteCode() &&
- this->GetCanDefer()))
- {
- return false;
- }
- if (!PHASE_FORCE(Js::RedeferralPhase, this) && !PHASE_STRESS(Js::RedeferralPhase, this))
- {
- uint compileCount = this->GetCompileCount();
- if (compileCount >= (uint)CONFIG_FLAG(RedeferralCap))
- {
- return false;
- }
- // Redeferral threshold is k^x, where x is the number of previous compiles.
- bool overflow = false;
- uint currentThreshold = inactiveThreshold;
- if (compileCount > 1)
- {
- currentThreshold = JavascriptNumber::DirectPowIntInt(&overflow, inactiveThreshold, compileCount);
- }
- if (overflow)
- {
- currentThreshold = 0xFFFFFFFF;
- }
- if (this->GetInactiveCount() < currentThreshold)
- {
- return false;
- }
- }
- // Make sure the function won't be jitted
- bool isJitCandidate = false;
- #if ENABLE_NATIVE_CODEGEN
- bool isJitModeFunction = !this->IsInterpreterExecutionMode();
- isJitCandidate = MapEntryPointsUntil([=](int index, FunctionEntryPointInfo *entryPointInfo)
- {
- if ((entryPointInfo->IsCodeGenPending() && isJitModeFunction) || entryPointInfo->IsCodeGenQueued() || entryPointInfo->IsCodeGenRecorded() || (entryPointInfo->IsCodeGenDone() && !entryPointInfo->IsNativeEntryPointProcessed()))
- {
- return true;
- }
- return false;
- });
- if (!isJitCandidate)
- {
- // Now check loop body entry points
- isJitCandidate = MapLoopHeadersUntil([=](uint loopNumber, LoopHeader* header)
- {
- return header->MapEntryPointsUntil([&](int index, LoopEntryPointInfo* entryPointInfo)
- {
- if (entryPointInfo->IsCodeGenPending() || entryPointInfo->IsCodeGenQueued() || entryPointInfo->IsCodeGenRecorded() || (entryPointInfo->IsCodeGenDone() && !entryPointInfo->IsNativeEntryPointProcessed()))
- {
- return true;
- }
- return false;
- });
- });
- }
- #endif
- return !isJitCandidate;
- }
- void FunctionBody::RedeferFunction()
- {
- Assert(this->CanBeDeferred());
- #if DBG
- if (PHASE_STATS(RedeferralPhase, this))
- {
- ThreadContext * threadContext = this->GetScriptContext()->GetThreadContext();
- threadContext->redeferredFunctions++;
- threadContext->recoveredBytes += sizeof(*this) + this->GetInlineCacheCount() * sizeof(InlineCache);
- if (this->byteCodeBlock)
- {
- threadContext->recoveredBytes += this->byteCodeBlock->GetLength();
- if (this->GetAuxiliaryData())
- {
- threadContext->recoveredBytes += this->GetAuxiliaryData()->GetLength();
- }
- }
- this->MapEntryPoints([&](int index, FunctionEntryPointInfo * info) {
- threadContext->recoveredBytes += sizeof(info);
- });
- // TODO: Get size of polymorphic caches, jitted code, etc.
- }
- // We can't get here if the function is being jitted. Jitting was either completed or not begun.
- this->UnlockCounters();
- #endif
- PHASE_PRINT_TRACE(Js::RedeferralPhase, this, _u("Redeferring function %d.%d: %s\n"),
- GetSourceContextId(), GetLocalFunctionId(),
- GetDisplayName() ? GetDisplayName() : _u("Anonymous function)"));
- ParseableFunctionInfo * parseableFunctionInfo =
- Js::ParseableFunctionInfo::NewDeferredFunctionFromFunctionBody(this);
- FunctionInfo * functionInfo = this->GetFunctionInfo();
- this->RedeferFunctionObjectTypes();
- this->Cleanup(false);
- if (GetIsFuncRegistered())
- {
- this->GetUtf8SourceInfo()->RemoveFunctionBody(this);
- }
- // New allocation is done at this point, so update existing structures
- // Adjust functionInfo attributes, point to new proxy
- functionInfo->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
- functionInfo->SetFunctionProxy(parseableFunctionInfo);
- functionInfo->SetOriginalEntryPoint(DefaultEntryThunk);
- }
- void FunctionBody::RedeferFunctionObjectTypes()
- {
- this->MapFunctionObjectTypes([&](ScriptFunctionType* functionType)
- {
- Assert(functionType->GetTypeId() == TypeIds_Function);
- if (!CrossSite::IsThunk(functionType->GetEntryPoint()))
- {
- functionType->SetEntryPoint(GetScriptContext()->DeferredParsingThunk);
- }
- if (!CrossSite::IsThunk(functionType->GetEntryPointInfo()->jsMethod))
- {
- functionType->GetEntryPointInfo()->jsMethod = GetScriptContext()->DeferredParsingThunk;
- }
- });
- }
- void FunctionBody::SetDefaultFunctionEntryPointInfo(FunctionEntryPointInfo* entryPointInfo, const JavascriptMethod originalEntryPoint)
- {
- Assert(entryPointInfo);
- // Need to set twice since ProxyEntryPointInfo cast points to an interior pointer
- this->m_defaultEntryPointInfo = (ProxyEntryPointInfo*) entryPointInfo;
- this->defaultFunctionEntryPointInfo = entryPointInfo;
- SetOriginalEntryPoint(originalEntryPoint);
- }
- Var
- FunctionBody::GetFormalsPropIdArrayOrNullObj()
- {
- Var formalsPropIdArray = this->GetAuxPtrWithLock<AuxPointerType::FormalsPropIdArray>();
- if (formalsPropIdArray == nullptr)
- {
- return GetScriptContext()->GetLibrary()->GetNull();
- }
- return formalsPropIdArray;
- }
- PropertyIdArray*
- FunctionBody::GetFormalsPropIdArray(bool checkForNull)
- {
- if (checkForNull)
- {
- Assert(this->GetAuxPtrWithLock<AuxPointerType::FormalsPropIdArray>());
- }
- return this->GetAuxPtrWithLock<AuxPointerType::FormalsPropIdArray>();
- }
- void
- FunctionBody::SetFormalsPropIdArray(PropertyIdArray * propIdArray)
- {
- AssertMsg(propIdArray == nullptr || this->GetAuxPtrWithLock<AuxPointerType::FormalsPropIdArray>() == nullptr, "Already set?");
- this->SetAuxPtr<AuxPointerType::FormalsPropIdArray>(propIdArray);
- }
- ByteBlock*
- FunctionBody::GetByteCode() const
- {
- return this->byteCodeBlock;
- }
- // Returns original bytecode without probes (such as BPs).
- ByteBlock*
- FunctionBody::GetOriginalByteCode()
- {
- if (m_sourceInfo.m_probeBackingBlock)
- {
- return m_sourceInfo.m_probeBackingBlock;
- }
- else
- {
- return this->GetByteCode();
- }
- }
- // Returns a raw pointer to the display name which may not have a well-known lifetime. It's safer to use
- // GetExternalDisplayNameObject if the end goal is to create an object anyway.
- const char16* ParseableFunctionInfo::GetExternalDisplayName() const
- {
- return GetExternalDisplayName(this);
- }
- // Allocates a new JavascriptString object containing the display name associated with the FunctionBody.
- JavascriptString* ParseableFunctionInfo::GetExternalDisplayNameObject(ScriptContext* scriptContext) const
- {
- const char16* name = GetExternalDisplayName();
- if (!GetDisplayNameIsRecyclerAllocated() && !IsConstantFunctionName(name))
- {
- // The string is allocated in memory that we don't directly control the lifetime of. Copy the string to
- // ensure that the buffer remains valid for the lifetime of the object.
- return Js::JavascriptString::NewCopySz(name, scriptContext);
- }
- else
- {
- // Use the incoming buffer directly to create the object. This only works when the lifetime of the data is
- // static or GC allocated.
- return Js::JavascriptString::NewWithSz(name, scriptContext);
- }
- }
- RegSlot
- FunctionBody::GetLocalsCount()
- {
- return GetConstantCount() + GetVarCount();
- }
- RegSlot
- FunctionBody::GetVarCount()
- {
- return this->GetCountField(CounterFields::VarCount);
- }
- // Returns the number of non-temp local vars.
- uint32
- FunctionBody::GetNonTempLocalVarCount()
- {
- Assert(this->GetEndNonTempLocalIndex() >= this->GetFirstNonTempLocalIndex());
- return this->GetEndNonTempLocalIndex() - this->GetFirstNonTempLocalIndex();
- }
- uint32
- FunctionBody::GetFirstNonTempLocalIndex()
- {
- // First local var starts when the const vars end.
- return GetConstantCount();
- }
- uint32
- FunctionBody::GetEndNonTempLocalIndex()
- {
- // It will give the index on which current non temp locals ends, which is a first temp reg.
- RegSlot firstTmpReg = GetFirstTmpRegister();
- return firstTmpReg != Constants::NoRegister ? firstTmpReg : GetLocalsCount();
- }
- bool
- FunctionBody::IsNonTempLocalVar(uint32 varIndex)
- {
- return GetFirstNonTempLocalIndex() <= varIndex && varIndex < GetEndNonTempLocalIndex();
- }
- bool
- FunctionBody::GetSlotOffset(RegSlot slotId, int32 * slotOffset, bool allowTemp)
- {
- if (IsNonTempLocalVar(slotId) || allowTemp)
- {
- *slotOffset = (slotId - GetFirstNonTempLocalIndex()) * DIAGLOCALSLOTSIZE;
- return true;
- }
- return false;
- }
- void
- FunctionBody::CheckAndSetConstantCount(RegSlot cNewConstants) // New register count
- {
- CheckNotExecuting();
- AssertMsg(GetConstantCount() <= cNewConstants, "Cannot shrink register usage");
- this->SetConstantCount(cNewConstants);
- }
- void
- FunctionBody::SetConstantCount(RegSlot cNewConstants) // New register count
- {
- this->SetCountField(CounterFields::ConstantCount, cNewConstants);
- }
- void
- FunctionBody::CheckAndSetVarCount(RegSlot cNewVars)
- {
- CheckNotExecuting();
- AssertMsg(this->GetVarCount() <= cNewVars, "Cannot shrink register usage");
- this->SetVarCount(cNewVars);
- }
- void
- FunctionBody::SetVarCount(RegSlot cNewVars) // New register count
- {
- this->SetCountField(FunctionBody::CounterFields::VarCount, cNewVars);
- }
- RegSlot
- FunctionBody::GetYieldRegister()
- {
- return GetEndNonTempLocalIndex() - 1;
- }
- RegSlot
- FunctionBody::GetFirstTmpReg()
- {
- AssertMsg(GetFirstTmpRegister() != Constants::NoRegister, "First temp hasn't been set yet");
- return GetFirstTmpRegister();
- }
- void
- FunctionBody::SetFirstTmpReg(
- RegSlot firstTmpReg)
- {
- CheckNotExecuting();
- AssertMsg(GetFirstTmpRegister() == Constants::NoRegister, "Should not be resetting the first temp");
- SetFirstTmpRegister(firstTmpReg);
- }
- RegSlot
- FunctionBody::GetTempCount()
- {
- return GetLocalsCount() - GetFirstTmpRegister();
- }
- void
- FunctionBody::SetOutParamMaxDepth(RegSlot cOutParamsDepth)
- {
- #if _M_X64
- constexpr RegSlot minAsmJsOutParams = MinAsmJsOutParams();
- if (GetIsAsmJsFunction() && cOutParamsDepth < minAsmJsOutParams)
- {
- cOutParamsDepth = minAsmJsOutParams;
- }
- #endif
- SetCountField(CounterFields::OutParamMaxDepth, cOutParamsDepth);
- }
- void
- FunctionBody::CheckAndSetOutParamMaxDepth(RegSlot cOutParamsDepth)
- {
- CheckNotExecuting();
- SetOutParamMaxDepth(cOutParamsDepth);
- }
- RegSlot
- FunctionBody::GetOutParamMaxDepth()
- {
- return GetCountField(CounterFields::OutParamMaxDepth);
- }
- ModuleID
- FunctionBody::GetModuleID() const
- {
- return this->GetHostSrcInfo()->moduleID;
- }
- ///----------------------------------------------------------------------------
- ///
- /// FunctionBody::BeginExecution
- ///
- /// BeginExecution() is called by InterpreterStackFrame when a function begins execution.
- /// - Once started execution, the function may not be modified, as it would
- /// change the stack-frame layout:
- /// - This is a debug-only check because of the runtime cost. At release time,
- /// a stack-walk will be performed by GC to determine which functions are
- /// executing.
- ///
- ///----------------------------------------------------------------------------
- void
- FunctionBody::BeginExecution()
- {
- #if DBG
- m_DEBUG_executionCount++;
- this->LockDownCounters();
- #endif
- // Don't allow loop headers to be released while the function is executing
- ::InterlockedIncrement(&this->m_depth);
- }
- ///----------------------------------------------------------------------------
- ///
- /// FunctionBody::CheckEmpty
- ///
- /// CheckEmpty() validates that the given instance has not been given an
- /// implementation yet.
- ///
- ///----------------------------------------------------------------------------
- void
- FunctionBody::CheckEmpty()
- {
- AssertMsg((this->byteCodeBlock == nullptr) && (this->GetAuxiliaryData() == nullptr) && (this->GetAuxiliaryContextData() == nullptr), "Function body may only be set once");
- }
- ///----------------------------------------------------------------------------
- ///
- /// FunctionBody::CheckNotExecuting
- ///
- /// CheckNotExecuting() checks that function is not currently executing when it
- /// is being modified. See BeginExecution() for details.
- ///
- ///----------------------------------------------------------------------------
- void
- FunctionBody::CheckNotExecuting()
- {
- AssertMsg(m_DEBUG_executionCount == 0, "Function cannot be executing when modified");
- }
- ///----------------------------------------------------------------------------
- ///
- /// FunctionBody::EndExecution
- ///
- /// EndExecution() is called by InterpreterStackFrame when a function ends execution.
- /// See BeginExecution() for details.
- ///
- ///----------------------------------------------------------------------------
- void
- FunctionBody::EndExecution()
- {
- #if DBG
- AssertMsg(m_DEBUG_executionCount > 0, "Must have a previous execution to end");
- m_DEBUG_executionCount--;
- #endif
- uint depth = ::InterlockedDecrement(&this->m_depth);
- // If loop headers were determined to be no longer needed
- // during the execution of the function, we release them now
- if (depth == 0 && this->m_pendingLoopHeaderRelease)
- {
- this->m_pendingLoopHeaderRelease = false;
- ReleaseLoopHeaders();
- }
- }
- void FunctionBody::AddEntryPointToEntryPointList(FunctionEntryPointInfo* entryPointInfo)
- {
- ThreadContext::AutoDisableExpiration disableExpiration(this->m_scriptContext->GetThreadContext());
- Recycler* recycler = this->m_scriptContext->GetRecycler();
- entryPointInfo->entryPointIndex = this->entryPoints->Add(recycler->CreateWeakReferenceHandle(entryPointInfo));
- }
- #if DBG
- BOOL FunctionBody::IsInterpreterThunk() const
- {
- bool isInterpreterThunk = this->GetOriginalEntryPoint_Unchecked() == DefaultEntryThunk;
- #if DYNAMIC_INTERPRETER_THUNK
- bool isStaticInterpreterThunk = this->GetOriginalEntryPoint_Unchecked() == InterpreterStackFrame::StaticInterpreterThunk;
- isInterpreterThunk = isInterpreterThunk || isStaticInterpreterThunk || IsDynamicInterpreterThunk();
- #endif
- return isInterpreterThunk;
- }
- BOOL FunctionBody::IsDynamicInterpreterThunk() const
- {
- #if DYNAMIC_INTERPRETER_THUNK
- return this->GetScriptContext()->IsDynamicInterpreterThunk(this->GetOriginalEntryPoint_Unchecked());
- #else
- return FALSE;
- #endif
- }
- #endif
- FunctionEntryPointInfo * FunctionBody::TryGetEntryPointInfo(int index) const
- {
- // If we've already freed the recyclable data, we're shutting down the script context so skip clean up
- if (this->entryPoints == nullptr) return 0;
- Assert(index < this->entryPoints->Count());
- FunctionEntryPointInfo* entryPoint = this->entryPoints->Item(index)->Get();
- return entryPoint;
- }
- FunctionEntryPointInfo * FunctionBody::GetEntryPointInfo(int index) const
- {
- FunctionEntryPointInfo* entryPoint = TryGetEntryPointInfo(index);
- Assert(entryPoint);
- return entryPoint;
- }
- #if ENABLE_NATIVE_CODEGEN
- uint32 FunctionBody::GetFrameHeight(EntryPointInfo* entryPointInfo) const
- {
- return entryPointInfo->GetNativeEntryPointData()->GetFrameHeight();
- }
- void
- FunctionBody::SetNativeThrowSpanSequence(SmallSpanSequence *seq, uint loopNum, LoopEntryPointInfo* entryPoint)
- {
- Assert(loopNum != LoopHeader::NoLoop);
- LoopHeader *loopHeader = this->GetLoopHeaderWithLock(loopNum);
- Assert(loopHeader);
- Assert(entryPoint->loopHeader == loopHeader);
- entryPoint->SetNativeThrowSpanSequence(seq);
- }
- void
- FunctionBody::RecordNativeThrowMap(SmallSpanSequenceIter& iter, uint32 nativeOffset, uint32 statementIndex, EntryPointInfo* entryPoint, uint loopNum)
- {
- SmallSpanSequence *pSpanSequence;
- pSpanSequence = entryPoint->GetNativeThrowSpanSequence();
- if (!pSpanSequence)
- {
- if (statementIndex == -1)
- {
- return; // No need to initialize native throw map for non-user code
- }
- pSpanSequence = HeapNew(SmallSpanSequence);
- if (loopNum == LoopHeader::NoLoop)
- {
- ((FunctionEntryPointInfo*) entryPoint)->SetNativeThrowSpanSequence(pSpanSequence);
- }
- else
- {
- this->SetNativeThrowSpanSequence(pSpanSequence, loopNum, (LoopEntryPointInfo*) entryPoint);
- }
- }
- else if (iter.accumulatedSourceBegin == static_cast<int>(statementIndex))
- {
- return; // Compress adjacent spans which share the same statementIndex
- }
- StatementData data;
- data.sourceBegin = static_cast<int>(statementIndex); // sourceBegin represents statementIndex here
- data.bytecodeBegin = static_cast<int>(nativeOffset); // bytecodeBegin represents nativeOffset here
- pSpanSequence->RecordARange(iter, &data);
- }
- #endif
- PropertyId
- ParseableFunctionInfo::GetOrAddPropertyIdTracked(JsUtil::CharacterBuffer<WCHAR> const& propName)
- {
- const Js::PropertyRecord* propRecord = nullptr;
- ScriptContext * scriptContext = this->m_scriptContext;
- scriptContext->GetOrAddPropertyRecord(propName, &propRecord);
- PropertyId propertyId = propRecord->GetPropertyId();
- if (!scriptContext->IsTrackedPropertyId(propertyId))
- {
- this->m_utf8SourceInfo->GetBoundedPropertyRecordHashSet()->Item(propRecord);
- }
- return propertyId;
- }
- SmallSpanSequence::SmallSpanSequence()
- : pStatementBuffer(nullptr),
- pActualOffsetList(nullptr),
- baseValue(0)
- {
- }
- BOOL SmallSpanSequence::RecordARange(SmallSpanSequenceIter &iter, StatementData * data)
- {
- Assert(data);
- if (!this->pStatementBuffer)
- {
- this->pStatementBuffer = JsUtil::GrowingUint32HeapArray::Create(4);
- baseValue = data->sourceBegin;
- Reset(iter);
- }
- SmallSpan span(0);
- span.sourceBegin = GetDiff(data->sourceBegin, iter.accumulatedSourceBegin);
- span.bytecodeBegin = GetDiff(data->bytecodeBegin, iter.accumulatedBytecodeBegin);
- this->pStatementBuffer->Add((uint32)span);
- // Update iterator for the next set
- iter.accumulatedSourceBegin = data->sourceBegin;
- iter.accumulatedBytecodeBegin = data->bytecodeBegin;
- return TRUE;
- }
- // FunctionProxy methods
- ScriptContext*
- FunctionProxy::GetScriptContext() const
- {
- return m_scriptContext;
- }
- void FunctionProxy::Copy(FunctionProxy* other)
- {
- Assert(other);
- other->SetIsTopLevel(this->m_isTopLevel);
- if (this->IsPublicLibraryCode())
- {
- other->SetIsPublicLibraryCode();
- }
- if (this->IsJsBuiltInCode())
- {
- other->SetIsJsBuiltInCode();
- }
- #define CopyDeferParseField(field) other->field = this->field;
- CopyDeferParseField(deferredPrototypeType);
- CopyDeferParseField(undeferredFunctionType);
- #undef CopyDeferParseField
- other->SetFunctionObjectTypeList(this->GetFunctionObjectTypeList());
- }
- void ParseableFunctionInfo::Copy(ParseableFunctionInfo * other)
- {
- __super::Copy(other);
- #define CopyDeferParseField(field) other->field = this->field;
- CopyDeferParseField(flags);
- CopyDeferParseField(m_isDeclaration);
- CopyDeferParseField(m_isAccessor);
- CopyDeferParseField(m_isStrictMode);
- CopyDeferParseField(m_isGlobalFunc);
- CopyDeferParseField(m_doBackendArgumentsOptimization);
- CopyDeferParseField(m_doScopeObjectCreation);
- CopyDeferParseField(m_usesArgumentsObject);
- CopyDeferParseField(m_isEval);
- CopyDeferParseField(m_isDynamicFunction);
- CopyDeferParseField(m_hasImplicitArgIns);
- CopyDeferParseField(m_dontInline);
- CopyDeferParseField(m_inParamCount);
- CopyDeferParseField(m_grfscr);
- other->SetScopeInfo(this->GetScopeInfo());
- other->SetDeferredStubs(this->GetDeferredStubs());
- CopyDeferParseField(m_utf8SourceHasBeenSet);
- #if DBG
- CopyDeferParseField(deferredParseNextFunctionId);
- CopyDeferParseField(scopeObjectSize);
- #endif
- CopyDeferParseField(scopeSlotArraySize);
- CopyDeferParseField(paramScopeSlotArraySize);
- other->SetCachedSourceStringWeakRef(this->GetCachedSourceStringWeakRef());
- CopyDeferParseField(m_isAsmjsMode);
- CopyDeferParseField(m_isAsmJsFunction);
- PropertyId * propertyIds = this->GetPropertyIdsForScopeSlotArray();
- if (propertyIds != nullptr)
- {
- other->SetPropertyIdsForScopeSlotArray(propertyIds, this->scopeSlotArraySize, this->paramScopeSlotArraySize);
- }
- CopyDeferParseField(m_sourceIndex);
- CopyDeferParseField(m_cchStartOffset);
- CopyDeferParseField(m_cchLength);
- CopyDeferParseField(m_lineNumber);
- CopyDeferParseField(m_columnNumber);
- CopyDeferParseField(m_cbStartOffset);
- CopyDeferParseField(m_cbStartPrintOffset);
- CopyDeferParseField(m_cbLength);
- this->CopyNestedArray(other);
- #undef CopyDeferParseField
- }
- void ParseableFunctionInfo::Copy(FunctionBody* other)
- {
- this->Copy(static_cast<ParseableFunctionInfo*>(other));
- other->CopySourceInfo(this);
- }
- void ParseableFunctionInfo::CopyNestedArray(ParseableFunctionInfo * other)
- {
- NestedArray * thisNestedArray = this->GetNestedArray();
- NestedArray * otherNestedArray = other->GetNestedArray();
- if (thisNestedArray == nullptr)
- {
- Assert(otherNestedArray == nullptr);
- return;
- }
- Assert(otherNestedArray->nestedCount == thisNestedArray->nestedCount);
- for (uint i = 0; i < thisNestedArray->nestedCount; i++)
- {
- otherNestedArray->functionInfoArray[i] = thisNestedArray->functionInfoArray[i];
- }
- }
- // DeferDeserializeFunctionInfo methods
- DeferDeserializeFunctionInfo::DeferDeserializeFunctionInfo(int nestedCount, LocalFunctionId functionId, ByteCodeCache* byteCodeCache, const byte* serializedFunction, Utf8SourceInfo* sourceInfo, ScriptContext* scriptContext, uint functionNumber, const char16* displayName, uint displayNameLength, uint displayShortNameOffset, NativeModule *nativeModule, FunctionInfo::Attributes attributes) :
- FunctionProxy(scriptContext, sourceInfo, functionNumber),
- m_cache(byteCodeCache),
- m_functionBytes(serializedFunction),
- m_displayName(nullptr),
- m_displayNameLength(0),
- m_nativeModule(nativeModule)
- {
- this->functionInfo = RecyclerNew(scriptContext->GetRecycler(), FunctionInfo, DefaultDeferredDeserializeThunk, (FunctionInfo::Attributes)(attributes | FunctionInfo::Attributes::DeferredDeserialize), functionId, this);
- this->m_defaultEntryPointInfo = RecyclerNew(scriptContext->GetRecycler(), ProxyEntryPointInfo, DefaultDeferredDeserializeThunk);
- PERF_COUNTER_INC(Code, DeferDeserializeFunctionProxy);
- SetDisplayName(displayName, displayNameLength, displayShortNameOffset, FunctionProxy::SetDisplayNameFlagsDontCopy);
- }
- DeferDeserializeFunctionInfo* DeferDeserializeFunctionInfo::New(ScriptContext* scriptContext, int nestedCount, LocalFunctionId functionId, ByteCodeCache* byteCodeCache, const byte* serializedFunction, Utf8SourceInfo* sourceInfo, const char16* displayName, uint displayNameLength, uint displayShortNameOffset, NativeModule *nativeModule, FunctionInfo::Attributes attributes)
- {
- return RecyclerNewFinalized(scriptContext->GetRecycler(),
- DeferDeserializeFunctionInfo,
- nestedCount,
- functionId,
- byteCodeCache,
- serializedFunction,
- sourceInfo,
- scriptContext,
- scriptContext->GetThreadContext()->NewFunctionNumber(),
- displayName,
- displayNameLength,
- displayShortNameOffset,
- nativeModule,
- attributes);
- }
- const char16*
- DeferDeserializeFunctionInfo::GetDisplayName() const
- {
- return this->m_displayName;
- }
- // ParseableFunctionInfo methods
- ParseableFunctionInfo::ParseableFunctionInfo(JavascriptMethod entryPoint, int nestedCount,
- LocalFunctionId functionId, Utf8SourceInfo* sourceInfo, ScriptContext* scriptContext, uint functionNumber,
- const char16* displayName, uint displayNameLength, uint displayShortNameOffset, FunctionInfo::Attributes attributes, FunctionBodyFlags flags) :
- FunctionProxy(scriptContext, sourceInfo, functionNumber),
- #if DYNAMIC_INTERPRETER_THUNK
- m_dynamicInterpreterThunk(nullptr),
- #endif
- flags(flags),
- m_hasBeenParsed(false),
- m_isGlobalFunc(false),
- m_isDeclaration(false),
- m_isNamedFunctionExpression(false),
- m_isNameIdentifierRef (true),
- m_isStaticNameFunction(false),
- m_doBackendArgumentsOptimization(true),
- m_doScopeObjectCreation(true),
- m_usesArgumentsObject(false),
- m_isStrictMode(false),
- m_isAsmjsMode(false),
- m_dontInline(false),
- m_hasImplicitArgIns(true),
- m_grfscr(0),
- m_inParamCount(0),
- m_reportedInParamCount(0),
- m_sourceIndex(Js::Constants::InvalidSourceIndex),
- m_utf8SourceHasBeenSet(false),
- m_cchLength(0),
- m_cbLength(0),
- m_cchStartOffset(0),
- m_cbStartOffset(0),
- m_cbStartPrintOffset(0),
- m_lineNumber(0),
- m_columnNumber(0),
- m_isEval(false),
- m_isDynamicFunction(false),
- m_displayName(nullptr),
- m_displayNameLength(0),
- m_displayShortNameOffset(0),
- scopeSlotArraySize(0),
- paramScopeSlotArraySize(0),
- m_reparsed(false),
- m_isAsmJsFunction(false),
- m_tag21(true),
- m_isMethod(false)
- #if DBG
- ,m_wasEverAsmjsMode(false)
- ,scopeObjectSize(0)
- #endif
- {
- this->functionInfo = RecyclerNew(scriptContext->GetRecycler(), FunctionInfo, entryPoint, attributes, functionId, this);
- if (nestedCount > 0)
- {
- nestedArray = RecyclerNewPlusZ(m_scriptContext->GetRecycler(),
- nestedCount*sizeof(FunctionProxy*), NestedArray, nestedCount);
- }
- else
- {
- nestedArray = nullptr;
- }
- if ((attributes & Js::FunctionInfo::DeferredParse) == 0)
- {
- this->m_defaultEntryPointInfo = RecyclerNewFinalized(scriptContext->GetRecycler(),
- FunctionEntryPointInfo, this, entryPoint, scriptContext->GetThreadContext());
- }
- else
- {
- this->m_defaultEntryPointInfo = RecyclerNew(scriptContext->GetRecycler(), ProxyEntryPointInfo, entryPoint);
- }
- SetDisplayName(displayName, displayNameLength, displayShortNameOffset);
- this->SetOriginalEntryPoint(DefaultEntryThunk);
- }
- ParseableFunctionInfo::ParseableFunctionInfo(ParseableFunctionInfo * proxy) :
- FunctionProxy(proxy->GetScriptContext(), proxy->GetUtf8SourceInfo(), proxy->GetFunctionNumber()),
- #if DYNAMIC_INTERPRETER_THUNK
- m_dynamicInterpreterThunk(nullptr),
- #endif
- m_hasBeenParsed(false),
- m_isNamedFunctionExpression(proxy->GetIsNamedFunctionExpression()),
- m_isNameIdentifierRef (proxy->GetIsNameIdentifierRef()),
- m_isStaticNameFunction(proxy->GetIsStaticNameFunction()),
- m_reportedInParamCount(proxy->GetReportedInParamsCount()),
- m_reparsed(proxy->IsReparsed()),
- m_isMethod(proxy->IsMethod()),
- m_tag21(true)
- #if DBG
- , m_wasEverAsmjsMode(proxy->m_wasEverAsmjsMode)
- #endif
- {
- FunctionInfo * functionInfo = proxy->GetFunctionInfo();
- this->functionInfo = functionInfo;
- uint nestedCount = proxy->GetNestedCount();
- if (nestedCount > 0)
- {
- nestedArray = RecyclerNewPlusZ(m_scriptContext->GetRecycler(),
- nestedCount*sizeof(FunctionProxy*), NestedArray, nestedCount);
- }
- else
- {
- nestedArray = nullptr;
- }
- proxy->Copy(this);
- SetDisplayName(proxy->GetDisplayName(), proxy->GetDisplayNameLength(), proxy->GetShortDisplayNameOffset());
- }
- ParseableFunctionInfo* ParseableFunctionInfo::New(ScriptContext* scriptContext, int nestedCount,
- LocalFunctionId functionId, Utf8SourceInfo* sourceInfo, const char16* displayName, uint displayNameLength, uint displayShortNameOffset, FunctionInfo::Attributes attributes, FunctionBodyFlags flags)
- {
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- Assert(
- scriptContext->DeferredParsingThunk == ProfileDeferredParsingThunk ||
- scriptContext->DeferredParsingThunk == DefaultDeferredParsingThunk);
- #else
- Assert(scriptContext->DeferredParsingThunk == DefaultDeferredParsingThunk);
- #endif
- #ifdef PERF_COUNTERS
- PERF_COUNTER_INC(Code, DeferredFunction);
- #endif
- uint newFunctionNumber = scriptContext->GetThreadContext()->NewFunctionNumber();
- if (!sourceInfo->GetSourceContextInfo()->IsDynamic())
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("Function was deferred from parsing - ID: %d; Display Name: %s; Utf8SourceInfo ID: %d; Source Length: %d; Source Url:%s\n"), newFunctionNumber, displayName, sourceInfo->GetSourceInfoId(), sourceInfo->GetCchLength(), sourceInfo->GetSourceContextInfo()->url);
- }
- else
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("Function was deferred from parsing - ID: %d; Display Name: %s; Utf8SourceInfo ID: %d; Source Length: %d;\n"), newFunctionNumber, displayName, sourceInfo->GetSourceInfoId(), sourceInfo->GetCchLength());
- }
- // When generating a new defer parse function, we always use a new function number
- return RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(),
- ParseableFunctionInfo,
- scriptContext->DeferredParsingThunk,
- nestedCount,
- functionId,
- sourceInfo,
- scriptContext,
- newFunctionNumber,
- displayName,
- displayNameLength,
- displayShortNameOffset,
- (FunctionInfo::Attributes)(attributes | FunctionInfo::Attributes::DeferredParse),
- flags);
- }
- ParseableFunctionInfo *
- ParseableFunctionInfo::NewDeferredFunctionFromFunctionBody(FunctionBody * functionBody)
- {
- ScriptContext * scriptContext = functionBody->GetScriptContext();
- uint nestedCount = functionBody->GetNestedCount();
- ParseableFunctionInfo * info = RecyclerNewWithBarrierFinalized(scriptContext->GetRecycler(),
- ParseableFunctionInfo,
- functionBody);
- // Create new entry point info
- info->m_defaultEntryPointInfo = RecyclerNew(scriptContext->GetRecycler(), ProxyEntryPointInfo, scriptContext->DeferredParsingThunk);
- // Initialize nested function array, update back pointers
- for (uint i = 0; i < nestedCount; i++)
- {
- FunctionInfo * nestedInfo = functionBody->GetNestedFunc(i);
- info->SetNestedFunc(nestedInfo, i, 0);
- }
- // Update function objects
- return info;
- }
- DWORD_PTR FunctionProxy::GetSecondaryHostSourceContext() const
- {
- return this->GetUtf8SourceInfo()->GetSecondaryHostSourceContext();
- }
- DWORD_PTR FunctionProxy::GetHostSourceContext() const
- {
- return this->GetSourceContextInfo()->dwHostSourceContext;
- }
- SourceContextInfo * FunctionProxy::GetSourceContextInfo() const
- {
- return this->GetHostSrcInfo()->sourceContextInfo;
- }
- SRCINFO const * FunctionProxy::GetHostSrcInfo() const
- {
- return this->GetUtf8SourceInfo()->GetSrcInfo();
- }
- //
- // Returns the start line for the script buffer (code buffer for the entire script tag) of this current function.
- // We subtract the lnMinHost because it is the number of lines we have added to augment scriptlets passed through
- // ParseProcedureText to have a function name.
- //
- ULONG FunctionProxy::GetHostStartLine() const
- {
- return this->GetHostSrcInfo()->dlnHost - this->GetHostSrcInfo()->lnMinHost;
- }
- //
- // Returns the start column of the first line for the script buffer of this current function.
- //
- ULONG FunctionProxy::GetHostStartColumn() const
- {
- return this->GetHostSrcInfo()->ulColumnHost;
- }
- //
- // Returns line number in unmodified host buffer (i.e. without extra scriptlet code added by ParseProcedureText --
- // when e.g. we add extra code for event handlers, such as "function onclick(event)\n{\n").
- //
- ULONG FunctionProxy::GetLineNumberInHostBuffer(ULONG relativeLineNumber) const
- {
- ULONG lineNumber = relativeLineNumber;
- if (lineNumber >= this->GetHostSrcInfo()->lnMinHost)
- {
- lineNumber -= this->GetHostSrcInfo()->lnMinHost;
- }
- // Note that '<' is still a valid case -- that would be the case for onclick scriptlet function itself (lineNumber == 0).
- return lineNumber;
- }
- ULONG FunctionProxy::ComputeAbsoluteLineNumber(ULONG relativeLineNumber) const
- {
- // We add 1 because the line numbers start from 0.
- return this->GetHostSrcInfo()->dlnHost + GetLineNumberInHostBuffer(relativeLineNumber) + 1;
- }
- ULONG FunctionProxy::ComputeAbsoluteColumnNumber(ULONG relativeLineNumber, ULONG relativeColumnNumber) const
- {
- if (this->GetLineNumberInHostBuffer(relativeLineNumber) == 0)
- {
- // The host column matters only for the first line.
- return this->GetHostStartColumn() + relativeColumnNumber + 1;
- }
- // We add 1 because the column numbers start from 0.
- return relativeColumnNumber + 1;
- }
- //
- // Returns the line number of the function declaration in the source file.
- //
- ULONG
- ParseableFunctionInfo::GetLineNumber() const
- {
- return this->ComputeAbsoluteLineNumber(this->m_lineNumber);
- }
- //
- // Returns the column number of the function declaration in the source file.
- //
- ULONG
- ParseableFunctionInfo::GetColumnNumber() const
- {
- return ComputeAbsoluteColumnNumber(this->m_lineNumber, m_columnNumber);
- }
- LPCWSTR
- ParseableFunctionInfo::GetSourceName() const
- {
- return GetSourceName(this->GetSourceContextInfo());
- }
- void
- ParseableFunctionInfo::SetGrfscr(uint32 grfscr)
- {
- this->m_grfscr = grfscr;
- }
- uint32
- ParseableFunctionInfo::GetGrfscr() const
- {
- return this->m_grfscr;
- }
- ProxyEntryPointInfo*
- FunctionProxy::GetDefaultEntryPointInfo() const
- {
- return this->m_defaultEntryPointInfo;
- }
- FunctionEntryPointInfo*
- FunctionBody::GetDefaultFunctionEntryPointInfo() const
- {
- Assert(((ProxyEntryPointInfo*) this->defaultFunctionEntryPointInfo) == this->m_defaultEntryPointInfo);
- return this->defaultFunctionEntryPointInfo;
- }
- void
- ParseableFunctionInfo::SetInParamsCount(ArgSlot newInParamCount)
- {
- AssertMsg(m_inParamCount <= newInParamCount, "Cannot shrink register usage");
- m_inParamCount = newInParamCount;
- if (newInParamCount <= 1)
- {
- SetHasImplicitArgIns(false);
- }
- }
- ArgSlot
- ParseableFunctionInfo::GetReportedInParamsCount() const
- {
- return m_reportedInParamCount;
- }
- void
- ParseableFunctionInfo::SetReportedInParamsCount(ArgSlot newInParamCount)
- {
- AssertMsg(m_reportedInParamCount <= newInParamCount, "Cannot shrink register usage");
- m_reportedInParamCount = newInParamCount;
- }
- void
- ParseableFunctionInfo::ResetInParams()
- {
- m_inParamCount = 0;
- m_reportedInParamCount = 0;
- }
- const char16*
- ParseableFunctionInfo::GetDisplayName() const
- {
- return this->m_displayName;
- }
- void ParseableFunctionInfo::BuildDeferredStubs(ParseNodeFnc *pnodeFnc)
- {
- DeferredFunctionStub* deferredStubs = Parser::BuildDeferredStubTree(pnodeFnc, m_scriptContext->GetRecycler());
- this->SetDeferredStubs(deferredStubs);
- }
- JavascriptString * ParseableFunctionInfo::GetCachedSourceString()
- {
- RecyclerWeakReference<JavascriptString> * weakRef = GetCachedSourceStringWeakRef();
- if (weakRef)
- {
- JavascriptString * string = weakRef->Get();
- if (string)
- {
- return string;
- }
- this->SetAuxPtr<AuxPointerType::CachedSourceString>(nullptr);
- }
- return nullptr;
- }
- void ParseableFunctionInfo::SetCachedSourceString(JavascriptString * sourceString)
- {
- Assert(this->GetCachedSourceString() == nullptr);
- if (sourceString)
- {
- this->SetCachedSourceStringWeakRef(this->GetRecycler()->CreateWeakReferenceHandle(sourceString));
- }
- }
- RecyclerWeakReference<JavascriptString> * ParseableFunctionInfo::GetCachedSourceStringWeakRef()
- {
- return this->GetAuxPtr<AuxPointerType::CachedSourceString>();
- }
- void ParseableFunctionInfo::SetCachedSourceStringWeakRef(RecyclerWeakReference<JavascriptString> * weakRef)
- {
- Assert(this->GetCachedSourceString() == nullptr);
- this->SetAuxPtr<AuxPointerType::CachedSourceString>(weakRef);
- }
- FunctionInfoArray ParseableFunctionInfo::GetNestedFuncArray()
- {
- Assert(GetNestedArray() != nullptr);
- return GetNestedArray()->functionInfoArray;
- }
- void ParseableFunctionInfo::SetNestedFunc(FunctionInfo* nestedFunc, uint index, uint32 flags)
- {
- AssertMsg(index < this->GetNestedCount(), "Trying to write past the nested func array");
- FunctionInfoArray nested = this->GetNestedFuncArray();
- nested[index] = nestedFunc;
- }
- FunctionInfo* ParseableFunctionInfo::GetNestedFunc(uint index)
- {
- return *(GetNestedFuncReference(index));
- }
- FunctionProxy* ParseableFunctionInfo::GetNestedFunctionProxy(uint index)
- {
- FunctionInfo *info = GetNestedFunc(index);
- return info ? info->GetFunctionProxy() : nullptr;
- }
- FunctionInfoPtrPtr ParseableFunctionInfo::GetNestedFuncReference(uint index)
- {
- AssertMsg(index < this->GetNestedCount(), "Trying to write past the nested func array");
- FunctionInfoArray nested = this->GetNestedFuncArray();
- return &nested[index];
- }
- ParseableFunctionInfo* ParseableFunctionInfo::GetNestedFunctionForExecution(uint index)
- {
- FunctionInfo* currentNestedFunction = this->GetNestedFunc(index);
- Assert(currentNestedFunction);
- if (currentNestedFunction->IsDeferredDeserializeFunction())
- {
- currentNestedFunction->GetFunctionProxy()->EnsureDeserialized();
- }
- return currentNestedFunction->GetParseableFunctionInfo();
- }
- void
- FunctionProxy::UpdateFunctionBodyImpl(FunctionBody * body)
- {
- FunctionInfo *functionInfo = this->GetFunctionInfo();
- Assert(functionInfo->GetFunctionProxy() == this);
- Assert(!this->IsFunctionBody() || body == this);
- functionInfo->SetFunctionProxy(body);
- body->SetFunctionInfo(functionInfo);
- body->SetAttributes((FunctionInfo::Attributes)(functionInfo->GetAttributes() & ~(FunctionInfo::Attributes::DeferredParse | FunctionInfo::Attributes::DeferredDeserialize)));
- }
- //
- // This method gets a function body for the purposes of execution
- // It has an if within it to avoid making it a virtual- it's called from the interpreter
- // It will cause the function info to get deserialized if it hasn't been deserialized
- // already
- //
- ParseableFunctionInfo * FunctionProxy::EnsureDeserialized()
- {
- Assert(this == this->GetFunctionInfo()->GetFunctionProxy());
- FunctionProxy * executionFunctionBody = this;
- if (IsDeferredDeserializeFunction())
- {
- // No need to deserialize function body if scriptContext closed because we can't execute it.
- // Bigger problem is the script engine might have released bytecode file mapping and we can't deserialize.
- Assert(!m_scriptContext->IsClosed());
- executionFunctionBody = ((DeferDeserializeFunctionInfo*) this)->Deserialize();
- this->GetFunctionInfo()->SetFunctionProxy(executionFunctionBody);
- Assert(executionFunctionBody->GetFunctionInfo()->HasBody());
- Assert(executionFunctionBody != this);
- }
- return (ParseableFunctionInfo *)executionFunctionBody;
- }
- ScriptFunctionType * FunctionProxy::GetDeferredPrototypeType() const
- {
- return deferredPrototypeType;
- }
- ScriptFunctionType * FunctionProxy::EnsureDeferredPrototypeType()
- {
- Assert(this->GetFunctionInfo()->GetFunctionProxy() == this);
- return deferredPrototypeType != nullptr ?
- static_cast<ScriptFunctionType*>(deferredPrototypeType) : AllocDeferredPrototypeType();
- }
- ScriptFunctionType * FunctionProxy::AllocDeferredPrototypeType()
- {
- Assert(deferredPrototypeType == nullptr);
- ScriptFunctionType * type = ScriptFunctionType::New(this, true);
- deferredPrototypeType = type;
- return type;
- }
- ScriptFunctionType * FunctionProxy::GetUndeferredFunctionType() const
- {
- return undeferredFunctionType;
- }
- void FunctionProxy::SetUndeferredFunctionType(ScriptFunctionType * type)
- {
- undeferredFunctionType = type;
- }
- JavascriptMethod FunctionProxy::GetDirectEntryPoint(ProxyEntryPointInfo* entryPoint) const
- {
- Assert(entryPoint->jsMethod != nullptr);
- return entryPoint->jsMethod;
- }
- // Function object type list methods
- FunctionProxy::FunctionTypeWeakRefList* FunctionProxy::GetFunctionObjectTypeList() const
- {
- return this->GetAuxPtr<AuxPointerType::FunctionObjectTypeList>();
- }
- void FunctionProxy::SetFunctionObjectTypeList(FunctionProxy::FunctionTypeWeakRefList* list)
- {
- this->SetAuxPtr<AuxPointerType::FunctionObjectTypeList>(list);
- }
- FunctionProxy::FunctionTypeWeakRefList* FunctionProxy::EnsureFunctionObjectTypeList()
- {
- FunctionTypeWeakRefList* functionObjectTypeList = this->GetFunctionObjectTypeList();
- if (functionObjectTypeList == nullptr)
- {
- Recycler* recycler = this->GetScriptContext()->GetRecycler();
- functionObjectTypeList = RecyclerNew(recycler, FunctionTypeWeakRefList, recycler);
- this->SetFunctionObjectTypeList(functionObjectTypeList);
- }
- return functionObjectTypeList;
- }
- void FunctionProxy::RegisterFunctionObjectType(ScriptFunctionType* functionType)
- {
- FunctionTypeWeakRefList* typeList = EnsureFunctionObjectTypeList();
- Assert(functionType != deferredPrototypeType && functionType != undeferredFunctionType);
- Recycler * recycler = this->GetScriptContext()->GetRecycler();
- FunctionTypeWeakRef* weakRef = recycler->CreateWeakReferenceHandle(functionType);
- typeList->SetAtFirstFreeSpot(weakRef);
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Registered type 0x%p on function body %p, count = %d\n"), functionType, this, typeList->Count());
- }
- void DeferDeserializeFunctionInfo::SetDisplayName(const char16* displayName)
- {
- size_t len = wcslen(displayName);
- if (len > UINT_MAX)
- {
- // Can't support display name that big
- Js::Throw::OutOfMemory();
- }
- SetDisplayName(displayName, (uint)len, 0);
- }
- void DeferDeserializeFunctionInfo::SetDisplayName(const char16* pszDisplayName, uint displayNameLength, uint displayShortNameOffset, SetDisplayNameFlags flags /* default to None */)
- {
- this->m_displayNameLength = displayNameLength;
- this->m_displayShortNameOffset = displayShortNameOffset;
- this->m_displayNameIsRecyclerAllocated = FunctionProxy::SetDisplayName(pszDisplayName, &this->m_displayName, displayNameLength, m_scriptContext, flags);
- }
- LPCWSTR DeferDeserializeFunctionInfo::GetSourceInfo(int& lineNumber, int& columnNumber) const
- {
- // Read all the necessary information from the serialized byte code
- int lineNumberField, columnNumberField;
- bool m_isEval, m_isDynamicFunction;
- ByteCodeSerializer::ReadSourceInfo(this, lineNumberField, columnNumberField, m_isEval, m_isDynamicFunction);
- // Decode them
- lineNumber = ComputeAbsoluteLineNumber(lineNumberField);
- columnNumber = ComputeAbsoluteColumnNumber(lineNumberField, columnNumberField);
- return Js::ParseableFunctionInfo::GetSourceName<SourceContextInfo*>(this->GetSourceContextInfo(), m_isEval, m_isDynamicFunction);
- }
- void DeferDeserializeFunctionInfo::Finalize(bool isShutdown)
- {
- __super::Finalize(isShutdown);
- PERF_COUNTER_DEC(Code, DeferDeserializeFunctionProxy);
- }
- FunctionBody* DeferDeserializeFunctionInfo::Deserialize()
- {
- Assert(this->GetFunctionInfo()->GetFunctionProxy() == this);
- FunctionBody * body = ByteCodeSerializer::DeserializeFunction(this->m_scriptContext, this);
- this->SetLocalFunctionId(body->GetLocalFunctionId());
- this->SetOriginalEntryPoint(body->GetOriginalEntryPoint());
- this->Copy(body);
- this->UpdateFunctionBodyImpl(body);
- Assert(body->GetFunctionBody() == body);
- return body;
- }
- //
- // hrParse can be one of the following from deferred re-parse (check CompileScriptException::ProcessError):
- // E_OUTOFMEMORY
- // E_UNEXPECTED
- // SCRIPT_E_RECORDED,
- // with ei.scode: ERRnoMemory, VBSERR_OutOfStack, E_OUTOFMEMORY, E_FAIL, E_ABORT
- // Any other ei.scode shouldn't appear in deferred re-parse.
- //
- // Map errors like OOM/SOE, return it and clean hrParse. Any other error remaining in hrParse is an internal error.
- //
- HRESULT ParseableFunctionInfo::MapDeferredReparseError(HRESULT& hrParse, const CompileScriptException& se)
- {
- HRESULT hrMapped = NO_ERROR;
- switch (hrParse)
- {
- case E_OUTOFMEMORY:
- hrMapped = E_OUTOFMEMORY;
- break;
- case SCRIPT_E_RECORDED:
- switch (se.ei.scode)
- {
- case ERRnoMemory:
- case E_OUTOFMEMORY:
- case VBSERR_OutOfMemory:
- hrMapped = E_OUTOFMEMORY;
- break;
- case VBSERR_OutOfStack:
- hrMapped = VBSERR_OutOfStack;
- break;
- case JSERR_AsmJsCompileError:
- hrMapped = JSERR_AsmJsCompileError;
- break;
- case E_ABORT:
- hrMapped = E_ABORT;
- break;
- }
- }
- if (FAILED(hrMapped))
- {
- // If we have mapped error, clear hrParse. We'll throw error from hrMapped.
- hrParse = NO_ERROR;
- }
- return hrMapped;
- }
- FunctionBody* ParseableFunctionInfo::Parse(ScriptFunction ** functionRef, bool isByteCodeDeserialization)
- {
- Assert(this == this->GetFunctionInfo()->GetFunctionProxy());
- if (!IsDeferredParseFunction())
- {
- // If not deferredparsed, the functionBodyImpl and this will be the same, just return the current functionBody.
- Assert(GetFunctionBody()->IsFunctionParsed());
- return GetFunctionBody();
- }
- bool asmjsParseFailed = false;
- BOOL fParsed = FALSE;
- FunctionBody* returnFunctionBody = nullptr;
- bool isDebugOrAsmJsReparse = false;
- FunctionBody* funcBody = nullptr;
- {
- AutoRestoreFunctionInfo autoRestoreFunctionInfo(this, DefaultEntryThunk);
- // If m_hasBeenParsed = true, one of the following things happened things happened:
- // - We had multiple function objects which were all defer-parsed, but with the same function body and one of them
- // got the body to be parsed before another was called
- // - We are in debug mode and had our thunks switched to DeferParseThunk
- // - This is an already parsed asm.js module, which has been invalidated at link time and must be reparsed as a non-asm.js function
- if (!this->m_hasBeenParsed)
- {
- this->GetUtf8SourceInfo()->StopTrackingDeferredFunction(this->GetLocalFunctionId());
- funcBody = FunctionBody::NewFromParseableFunctionInfo(this);
- autoRestoreFunctionInfo.funcBody = funcBody;
- PERF_COUNTER_DEC(Code, DeferredFunction);
- if (!this->GetSourceContextInfo()->IsDynamic())
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: Deferred function parsed - ID: %d; Display Name: %s; Length: %d; Nested Function Count: %d; Utf8SourceInfo: %d; Source Length: %d; Is Top Level: %s; Source Url: %s\n"), m_functionNumber, this->GetDisplayName(), this->m_cchLength, this->GetNestedCount(), this->m_utf8SourceInfo->GetSourceInfoId(), this->m_utf8SourceInfo->GetCchLength(), this->GetIsTopLevel() ? _u("True") : _u("False"), this->GetSourceContextInfo()->url);
- }
- else
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: Deferred function parsed - ID: %d; Display Name: %s; Length: %d; Nested Function Count: %d; Utf8SourceInfo: %d; Source Length: %d\n; Is Top Level: %s;"), m_functionNumber, this->GetDisplayName(), this->m_cchLength, this->GetNestedCount(), this->m_utf8SourceInfo->GetSourceInfoId(), this->m_utf8SourceInfo->GetCchLength(), this->GetIsTopLevel() ? _u("True") : _u("False"));
- }
- if (!this->GetIsTopLevel() &&
- !this->GetSourceContextInfo()->IsDynamic() &&
- this->m_scriptContext->DoUndeferGlobalFunctions())
- {
- this->GetUtf8SourceInfo()->UndeferGlobalFunctions([this](const Utf8SourceInfo::DeferredFunctionsDictionary::EntryType& func)
- {
- Js::ParseableFunctionInfo *nextFunc = func.Value();
- JavascriptExceptionObject* pExceptionObject = nullptr;
- if (nextFunc != nullptr && this != nextFunc)
- {
- try
- {
- nextFunc->Parse();
- }
- catch (OutOfMemoryException) {}
- catch (StackOverflowException) {}
- catch (const Js::JavascriptException& err)
- {
- pExceptionObject = err.GetAndClear();
- }
- // Do not do anything with an OOM or SOE, returning true is fine, it will then be undeferred (or attempted to again when called)
- if (pExceptionObject)
- {
- if (pExceptionObject != ThreadContext::GetContextForCurrentThread()->GetPendingOOMErrorObject() &&
- pExceptionObject != ThreadContext::GetContextForCurrentThread()->GetPendingSOErrorObject())
- {
- JavascriptExceptionOperators::DoThrow(pExceptionObject, /*scriptContext*/nullptr);
- }
- }
- }
- return true;
- });
- }
- }
- else
- {
- bool isDebugReparse = m_scriptContext->IsScriptContextInSourceRundownOrDebugMode() && !this->GetUtf8SourceInfo()->GetIsLibraryCode();
- bool isAsmJsReparse = m_isAsmjsMode && !isDebugReparse;
- isDebugOrAsmJsReparse = isAsmJsReparse || isDebugReparse;
- funcBody = this->GetFunctionBody();
- // As we have a valid function body already clear the restore data
- autoRestoreFunctionInfo.Clear();
- if (isDebugOrAsmJsReparse)
- {
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- #if DBG
- Assert(
- funcBody->IsReparsed()
- || m_scriptContext->IsScriptContextInSourceRundownOrDebugMode()
- || m_isAsmjsMode);
- #endif
- OUTPUT_TRACE(Js::DebuggerPhase, _u("Full nested reparse of function: %s (%s)\n"), funcBody->GetDisplayName(), funcBody->GetDebugNumberSet(debugStringBuffer));
- if (funcBody->GetByteCode())
- {
- // The current function needs to be cleaned up before getting generated in the debug mode.
- funcBody->CleanupToReparse();
- }
- }
- }
- // Note that we may be trying to re-gen an already-completed function. (This can happen, for instance,
- // in the case of named function expressions inside "with" statements in compat mode.)
- // In such a case, there's no work to do.
- if (funcBody->GetByteCode() == nullptr)
- {
- #if ENABLE_PROFILE_INFO
- Assert(!funcBody->HasExecutionDynamicProfileInfo());
- #endif
- // In debug or asm.js mode, the scriptlet will be asked to recompile again.
- HRESULT hr = NO_ERROR;
- HRESULT hrParser = NO_ERROR;
- HRESULT hrParseCodeGen = NO_ERROR;
- BEGIN_LEAVE_SCRIPT_INTERNAL(m_scriptContext)
- {
- bool isCesu8 = m_scriptContext->GetSource(funcBody->GetSourceIndex())->IsCesu8();
- size_t offset = this->StartOffset();
- charcount_t charOffset = this->StartInDocument();
- size_t length = this->LengthInBytes();
- LPCUTF8 pszStart = this->GetStartOfDocument();
- uint32 grfscr = funcBody->GetGrfscr() | fscrDeferredFnc;
- // For the global function we want to re-use the glo functionbody which is already created in the non-debug mode
- if (!funcBody->GetIsGlobalFunc())
- {
- grfscr &= ~fscrGlobalCode;
- }
- if (!funcBody->GetIsDeclaration() && !funcBody->GetIsGlobalFunc()) // No refresh may reparse global function (e.g. eval code)
- {
- // Notify the parser that the top-level function was defined in an expression,
- // (not a function declaration statement).
- grfscr |= fscrDeferredFncExpression;
- }
- if (funcBody->IsMethod())
- {
- grfscr |= fscrDeferredFncIsMethod;
- }
- else
- {
- grfscr &= ~fscrDeferredFncIsMethod;
- }
- if (funcBody->IsAsync())
- {
- grfscr |= fscrDeferredFncIsAsync;
- }
- else
- {
- grfscr &= ~fscrDeferredFncIsAsync;
- }
- if (funcBody->IsGenerator())
- {
- grfscr |= fscrDeferredFncIsGenerator;
- }
- else
- {
- grfscr &= ~fscrDeferredFncIsGenerator;
- }
- if (isDebugOrAsmJsReparse)
- {
- // Disable deferred parsing if not DeferNested, or doing a debug/asm.js re-parse
- // Disable asm.js when debugging or if linking failed
- // Disable parser state cache if we're debugging or reparsing asm.js
- grfscr = fscrNoAsmJs | (grfscr & ~(fscrWillDeferFncParse | fscrCreateParserState));
- }
- BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT
- {
- CompileScriptException se;
- Parser ps(m_scriptContext, funcBody->GetIsStrictMode() ? TRUE : FALSE);
- ParseNodeProg * parseTree = nullptr;
- uint nextFunctionId = funcBody->GetLocalFunctionId();
- hrParser = ps.ParseSourceWithOffset(&parseTree, pszStart, offset, length, charOffset, isCesu8, grfscr, &se,
- &nextFunctionId, funcBody->GetRelativeLineNumber(), funcBody->GetSourceContextInfo(),
- funcBody);
- // Assert(FAILED(hrParser) || nextFunctionId == funcBody->deferredParseNextFunctionId || isDebugOrAsmJsReparse || isByteCodeDeserialization);
- if (FAILED(hrParser))
- {
- hrParseCodeGen = MapDeferredReparseError(hrParser, se); // Map certain errors like OOM/SOE
- AssertMsg(hrParseCodeGen == JSERR_AsmJsCompileError // AsmJsCompileError is not a syntax error
- || (FAILED(hrParseCodeGen) && SUCCEEDED(hrParser)), "Syntax errors should never be detected on deferred re-parse");
- }
- else
- {
- TRACE_BYTECODE(_u("\nDeferred parse %s\n"), funcBody->GetDisplayName());
- Js::AutoDynamicCodeReference dynamicFunctionReference(m_scriptContext);
- bool forceNoNative = isDebugOrAsmJsReparse ? this->GetScriptContext()->IsInterpreted() : false;
- ParseableFunctionInfo* rootFunc = funcBody->GetParseableFunctionInfo();
- hrParseCodeGen = GenerateByteCode(parseTree, grfscr, m_scriptContext,
- &rootFunc, funcBody->GetSourceIndex(),
- forceNoNative, &ps, &se, funcBody->GetScopeInfo(), functionRef);
- funcBody->SetParseableFunctionInfo(rootFunc);
- if (SUCCEEDED(hrParseCodeGen))
- {
- fParsed = TRUE;
- }
- else
- {
- Assert(hrParseCodeGen == SCRIPT_E_RECORDED);
- hrParseCodeGen = se.ei.scode;
- }
- }
- }
- END_TRANSLATE_EXCEPTION_TO_HRESULT(hr);
- }
- END_LEAVE_SCRIPT_INTERNAL(m_scriptContext);
- THROW_KNOWN_HRESULT_EXCEPTIONS(hr, m_scriptContext);
- Assert(hr == NO_ERROR);
- if (!SUCCEEDED(hrParser))
- {
- JavascriptError::ThrowError(m_scriptContext, VBSERR_InternalError);
- }
- else if (!SUCCEEDED(hrParseCodeGen))
- {
- /*
- * VBSERR_OutOfStack is of type kjstError but we throw a (more specific) StackOverflowError when a hard stack
- * overflow occurs. To keep the behavior consistent I'm special casing it here.
- */
- if (hrParseCodeGen == VBSERR_OutOfStack)
- {
- JavascriptError::ThrowStackOverflowError(m_scriptContext);
- }
- else if (hrParseCodeGen == JSERR_AsmJsCompileError)
- {
- asmjsParseFailed = true;
- }
- else
- {
- JavascriptError::MapAndThrowError(m_scriptContext, hrParseCodeGen);
- }
- }
- }
- else
- {
- fParsed = FALSE;
- }
- if (!asmjsParseFailed)
- {
- autoRestoreFunctionInfo.Clear();
- }
- }
- if (fParsed == TRUE)
- {
- // Restore if the function has nameIdentifier reference, as that name on the left side will not be parsed again while deferparse.
- funcBody->SetIsNameIdentifierRef(this->GetIsNameIdentifierRef());
- this->m_hasBeenParsed = true;
- returnFunctionBody = funcBody;
- }
- else if(!asmjsParseFailed)
- {
- returnFunctionBody = this->GetFunctionBody();
- }
- if (asmjsParseFailed)
- {
- // disable asm.js and reparse on failure
- m_grfscr |= fscrNoAsmJs;
- return Parse(functionRef, isByteCodeDeserialization);
- }
- return returnFunctionBody;
- }
- #ifdef ASMJS_PLAT
- FunctionBody* ParseableFunctionInfo::ParseAsmJs(Parser * ps, __out CompileScriptException * se, __out ParseNodeProg ** parseTree)
- {
- Assert(IsDeferredParseFunction());
- Assert(m_isAsmjsMode);
- FunctionBody* returnFunctionBody = nullptr;
- FunctionBody* funcBody = nullptr;
- funcBody = FunctionBody::NewFromRecycler(
- this->m_scriptContext,
- this->m_displayName,
- this->m_displayNameLength,
- this->m_displayShortNameOffset,
- this->GetNestedCount(),
- this->GetUtf8SourceInfo(),
- this->m_functionNumber,
- this->GetUtf8SourceInfo()->GetSrcInfo()->sourceContextInfo->sourceContextId,
- this->GetLocalFunctionId(),
- (FunctionInfo::Attributes)(this->GetAttributes() & ~(FunctionInfo::Attributes::DeferredDeserialize | FunctionInfo::Attributes::DeferredParse)),
- Js::FunctionBody::FunctionBodyFlags::Flags_HasNoExplicitReturnValue
- #ifdef PERF_COUNTERS
- , false /* is function from deferred deserialized proxy */
- #endif
- );
- this->Copy(funcBody);
- PERF_COUNTER_DEC(Code, DeferredFunction);
- if (!this->GetSourceContextInfo()->IsDynamic())
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: Deferred function parsed - ID: %d; Display Name: %s; Length: %d; Nested Function Count: %d; Utf8SourceInfo: %d; Source Length: %d; Is Top Level: %s; Source Url: %s\n"), m_functionNumber, this->GetDisplayName(), this->m_cchLength, this->GetNestedCount(), this->m_utf8SourceInfo->GetSourceInfoId(), this->m_utf8SourceInfo->GetCchLength(), this->GetIsTopLevel() ? _u("True") : _u("False"), this->GetSourceContextInfo()->url);
- }
- else
- {
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, _u("TestTrace: Deferred function parsed - ID: %d; Display Name: %s; Length: %d; Nested Function Count: %d; Utf8SourceInfo: %d; Source Length: %d\n; Is Top Level: %s;"), m_functionNumber, this->GetDisplayName(), this->m_cchLength, this->GetNestedCount(), this->m_utf8SourceInfo->GetSourceInfoId(), this->m_utf8SourceInfo->GetCchLength(), this->GetIsTopLevel() ? _u("True") : _u("False"));
- }
- #if ENABLE_PROFILE_INFO
- Assert(!funcBody->HasExecutionDynamicProfileInfo());
- #endif
- HRESULT hrParser = NO_ERROR;
- HRESULT hrParseCodeGen = NO_ERROR;
- bool isCesu8 = m_scriptContext->GetSource(funcBody->GetSourceIndex())->IsCesu8();
- size_t offset = this->StartOffset();
- charcount_t charOffset = this->StartInDocument();
- size_t length = this->LengthInBytes();
- LPCUTF8 pszStart = this->GetStartOfDocument();
- uint32 grfscr = funcBody->GetGrfscr() | fscrDeferredFnc | fscrDeferredFncExpression;
- uint nextFunctionId = funcBody->GetLocalFunctionId();
- // if parser throws, it will be caught by function trying to bytecode gen the asm.js module, so don't need to catch/rethrow here
- hrParser = ps->ParseSourceWithOffset(parseTree, pszStart, offset, length, charOffset, isCesu8, grfscr, se,
- &nextFunctionId, funcBody->GetRelativeLineNumber(), funcBody->GetSourceContextInfo(),
- funcBody);
- Assert(FAILED(hrParser) || funcBody->deferredParseNextFunctionId == nextFunctionId);
- if (FAILED(hrParser))
- {
- hrParseCodeGen = MapDeferredReparseError(hrParser, *se); // Map certain errors like OOM/SOE
- AssertMsg(FAILED(hrParseCodeGen) && SUCCEEDED(hrParser), "Syntax errors should never be detected on deferred re-parse");
- }
- if (!SUCCEEDED(hrParser))
- {
- Throw::InternalError();
- }
- else if (!SUCCEEDED(hrParseCodeGen))
- {
- if (hrParseCodeGen == VBSERR_OutOfStack)
- {
- Throw::StackOverflow(m_scriptContext, nullptr);
- }
- else
- {
- Assert(hrParseCodeGen == E_OUTOFMEMORY);
- Throw::OutOfMemory();
- }
- }
- UpdateFunctionBodyImpl(funcBody);
- m_hasBeenParsed = true;
- Assert(funcBody->GetFunctionBody() == funcBody);
- returnFunctionBody = funcBody;
- return returnFunctionBody;
- }
- #endif
- void ParseableFunctionInfo::Finalize(bool isShutdown)
- {
- __super::Finalize(isShutdown);
- if (this->GetFunctionInfo())
- {
- // (If function info was never set, then initialization didn't finish, so there's nothing to remove from the dictionary.)
- this->GetUtf8SourceInfo()->StopTrackingDeferredFunction(this->GetLocalFunctionId());
- }
- if (!this->m_hasBeenParsed)
- {
- PERF_COUNTER_DEC(Code, DeferredFunction);
- }
- }
- bool ParseableFunctionInfo::IsFakeGlobalFunc(uint32 flags) const
- {
- return GetIsGlobalFunc() && !(flags & fscrGlobalCode);
- }
- #ifdef NTBUILD
- bool ParseableFunctionInfo::GetExternalDisplaySourceName(BSTR* sourceName)
- {
- Assert(sourceName);
- if (IsDynamicScript() && GetUtf8SourceInfo()->GetDebugDocumentName(sourceName))
- {
- return true;
- }
- *sourceName = ::SysAllocString(GetSourceName());
- return *sourceName != nullptr;
- }
- #endif
- const char16* FunctionProxy::WrapWithBrackets(const char16* name, charcount_t sz, ScriptContext* scriptContext)
- {
- char16 * wrappedName = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, sz + 3); //[]\0
- wrappedName[0] = _u('[');
- char16 *next = wrappedName;
- js_wmemcpy_s(++next, sz, name, sz);
- wrappedName[sz + 1] = _u(']');
- wrappedName[sz + 2] = _u('\0');
- return wrappedName;
- }
- const char16* FunctionProxy::GetShortDisplayName(charcount_t * shortNameLength)
- {
- const char16* name = this->GetDisplayName();
- uint nameLength = this->GetDisplayNameLength();
- if (name == nullptr)
- {
- *shortNameLength = 0;
- return Constants::Empty;
- }
- if (IsConstantFunctionName(name))
- {
- *shortNameLength = nameLength;
- return name;
- }
- uint shortNameOffset = this->GetShortDisplayNameOffset();
- const char16 * shortName = name + shortNameOffset;
- bool isBracketCase = shortNameOffset != 0 && name[shortNameOffset-1] == '[';
- Assert(nameLength >= shortNameOffset);
- *shortNameLength = nameLength - shortNameOffset;
- if (!isBracketCase)
- {
- return shortName;
- }
- Assert(name[nameLength - 1] == ']');
- char16 * finalshorterName = RecyclerNewArrayLeaf(this->GetScriptContext()->GetRecycler(), char16, *shortNameLength);
- js_wmemcpy_s(finalshorterName, *shortNameLength, shortName, *shortNameLength - 1); // we don't want the last character in shorterName
- finalshorterName[*shortNameLength - 1] = _u('\0');
- *shortNameLength = *shortNameLength - 1;
- return finalshorterName;
- }
- /*static*/
- bool FunctionProxy::IsConstantFunctionName(const char16* srcName)
- {
- if (srcName == Js::Constants::GlobalFunction ||
- srcName == Js::Constants::AnonymousFunction ||
- srcName == Js::Constants::GlobalCode ||
- srcName == Js::Constants::Anonymous ||
- srcName == Js::Constants::UnknownScriptCode ||
- srcName == Js::Constants::FunctionCode)
- {
- return true;
- }
- return false;
- }
- /*static */
- /*Return value: Whether the target value is a recycler pointer or not*/
- bool FunctionProxy::SetDisplayName(const char16* srcName, const char16** destName, uint displayNameLength, ScriptContext * scriptContext, SetDisplayNameFlags flags /* default to None */)
- {
- Assert(destName);
- Assert(scriptContext);
- if (srcName == nullptr)
- {
- *destName = (_u(""));
- return false;
- }
- else if (IsConstantFunctionName(srcName) || (flags & SetDisplayNameFlagsDontCopy) != 0)
- {
- *destName = srcName;
- return (flags & SetDisplayNameFlagsRecyclerAllocated) != 0; // Return true if array is recycler allocated
- }
- else
- {
- uint numCharacters = displayNameLength + 1;
- Assert((flags & SetDisplayNameFlagsDontCopy) == 0);
- *destName = RecyclerNewArrayLeaf(scriptContext->GetRecycler(), char16, numCharacters);
- js_wmemcpy_s((char16 *)*destName, numCharacters, srcName, numCharacters);
- ((char16 *)(*destName))[numCharacters - 1] = _u('\0');
- return true;
- }
- }
- bool FunctionProxy::SetDisplayName(const char16* srcName, WriteBarrierPtr<const char16>* destName, uint displayNameLength, ScriptContext * scriptContext, SetDisplayNameFlags flags /* default to None */)
- {
- const char16* dest = nullptr;
- bool targetIsRecyclerMemory = SetDisplayName(srcName, &dest, displayNameLength, scriptContext, flags);
- if (targetIsRecyclerMemory)
- {
- *destName = dest;
- }
- else
- {
- destName->NoWriteBarrierSet(dest);
- }
- return targetIsRecyclerMemory;
- }
- void ParseableFunctionInfo::SetDisplayName(const char16* pszDisplayName)
- {
- size_t len = wcslen(pszDisplayName);
- if (len > UINT_MAX)
- {
- // Can't support display name that big
- Js::Throw::OutOfMemory();
- }
- SetDisplayName(pszDisplayName, (uint)len, 0);
- }
- void ParseableFunctionInfo::SetDisplayName(const char16* pszDisplayName, uint displayNameLength, uint displayShortNameOffset, SetDisplayNameFlags flags /* default to None */)
- {
- this->m_displayNameLength = displayNameLength;
- this->m_displayShortNameOffset = displayShortNameOffset;
- this->m_displayNameIsRecyclerAllocated = FunctionProxy::SetDisplayName(pszDisplayName, &this->m_displayName, displayNameLength, m_scriptContext, flags);
- }
- // SourceInfo methods
- /* static */
- template <typename TStatementMapList>
- FunctionBody::StatementMap * FunctionBody::GetNextNonSubexpressionStatementMap(TStatementMapList *statementMapList, int & startingAtIndex)
- {
- AssertMsg(statementMapList != nullptr, "Must have valid statementMapList to execute");
- FunctionBody::StatementMap *map = statementMapList->Item(startingAtIndex);
- while (map->isSubexpression && startingAtIndex < statementMapList->Count() - 1)
- {
- map = statementMapList->Item(++startingAtIndex);
- }
- if (map->isSubexpression) // Didn't find any non inner maps
- {
- return nullptr;
- }
- return map;
- }
- // explicitly instantiate template
- template FunctionBody::StatementMap *
- FunctionBody::GetNextNonSubexpressionStatementMap<FunctionBody::ArenaStatementMapList>(FunctionBody::ArenaStatementMapList *statementMapList, int & startingAtIndex);
- template FunctionBody::StatementMap *
- FunctionBody::GetNextNonSubexpressionStatementMap<FunctionBody::StatementMapList>(FunctionBody::StatementMapList *statementMapList, int & startingAtIndex);
- /* static */ FunctionBody::StatementMap * FunctionBody::GetPrevNonSubexpressionStatementMap(StatementMapList *statementMapList, int & startingAtIndex)
- {
- AssertMsg(statementMapList != nullptr, "Must have valid statementMapList to execute");
- StatementMap *map = statementMapList->Item(startingAtIndex);
- while (startingAtIndex && map->isSubexpression)
- {
- map = statementMapList->Item(--startingAtIndex);
- }
- if (map->isSubexpression) // Didn't find any non inner maps
- {
- return nullptr;
- }
- return map;
- }
- void ParseableFunctionInfo::SetSourceInfo(uint sourceIndex, ParseNodeFnc * node, bool isEval, bool isDynamicFunction)
- {
- if (!m_utf8SourceHasBeenSet)
- {
- this->m_sourceIndex = sourceIndex;
- this->m_cchStartOffset = node->ichMin;
- this->m_cchLength = node->LengthInCodepoints();
- this->m_lineNumber = node->lineNumber;
- this->m_columnNumber = node->columnNumber;
- this->m_isEval = isEval;
- this->m_isDynamicFunction = isDynamicFunction;
- // It would have been better if we detect and reject large source buffer earlier before parsing
- size_t cbMin = node->cbMin;
- size_t lengthInBytes = node->LengthInBytes();
- if (cbMin > UINT_MAX || lengthInBytes > UINT_MAX)
- {
- Js::Throw::OutOfMemory();
- }
- Assert(node->cbStringMin <= node->cbMin);
- this->m_cbStartOffset = (uint)cbMin;
- this->m_cbStartPrintOffset = (uint)node->cbStringMin;
- this->m_cbLength = (uint)lengthInBytes;
- Assert(this->m_utf8SourceInfo != nullptr);
- this->m_utf8SourceHasBeenSet = true;
- if (this->IsFunctionBody())
- {
- this->GetFunctionBody()->FinishSourceInfo();
- }
- }
- #if DBG
- else
- {
- AssertMsg(this->m_sourceIndex == sourceIndex, "Mismatched source index");
- if (!this->GetIsGlobalFunc())
- {
- // In the global function case with a @cc_on, we modify some of these values so it might
- // not match on reparse (see ParseableFunctionInfo::Parse()).
- AssertMsg(this->StartOffset() == node->cbMin, "Mismatched source start offset");
- AssertMsg(this->m_cchStartOffset == node->ichMin, "Mismatched source character start offset");
- AssertMsg(this->m_cchLength == node->LengthInCodepoints(), "Mismatched source length");
- AssertMsg(this->LengthInBytes() == node->LengthInBytes(), "Mismatched source encoded byte length");
- }
- AssertMsg(this->m_isEval == isEval, "Mismatched source type");
- AssertMsg(this->m_isDynamicFunction == isDynamicFunction, "Mismatch source type");
- }
- #endif
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::FunctionSourceInfoParsePhase))
- {
- Assert(this->GetFunctionInfo()->HasBody());
- if (this->IsFunctionBody())
- {
- FunctionBody* functionBody = this->GetFunctionBody();
- Assert( functionBody != nullptr );
- functionBody->PrintStatementSourceLineFromStartOffset(functionBody->StartInDocument());
- Output::Flush();
- }
- }
- #endif
- }
- void ParseableFunctionInfo::SetSourceInfo(uint sourceIndex)
- {
- // TODO (michhol): how do we want to handle wasm source?
- if (!m_utf8SourceHasBeenSet)
- {
- this->m_sourceIndex = sourceIndex;
- this->m_cchStartOffset = 0;
- this->m_cchLength = 0;
- this->m_lineNumber = 0;
- this->m_columnNumber = 0;
- this->m_cbStartOffset = 0;
- this->m_cbStartPrintOffset = 0;
- this->m_cbLength = 0;
- this->m_utf8SourceHasBeenSet = true;
- if (this->IsFunctionBody())
- {
- this->GetFunctionBody()->FinishSourceInfo();
- }
- }
- #if DBG
- else
- {
- AssertMsg(this->m_sourceIndex == sourceIndex, "Mismatched source index");
- }
- #endif
- }
- bool FunctionBody::Is(void* ptr)
- {
- if(!ptr)
- {
- return false;
- }
- return VirtualTableInfo<FunctionBody>::HasVirtualTable(ptr);
- }
- bool FunctionBody::HasLineBreak() const
- {
- return this->HasLineBreak(this->StartOffset(), this->m_cchStartOffset + this->m_cchLength);
- }
- bool FunctionBody::HasLineBreak(charcount_t start, charcount_t end) const
- {
- if (start > end) return false;
- charcount_t cchLength = end - start;
- if (start < this->m_cchStartOffset || cchLength > this->m_cchLength) return false;
- LPCUTF8 src = this->GetSource(_u("FunctionBody::HasLineBreak"));
- LPCUTF8 last = src + this->LengthInBytes();
- size_t offset = this->LengthInBytes() == this->m_cchLength ?
- start - this->m_cchStartOffset :
- utf8::CharacterIndexToByteIndex(src, this->LengthInBytes(), start - this->m_cchStartOffset, utf8::doAllowThreeByteSurrogates);
- src = src + offset;
- utf8::DecodeOptions options = utf8::doAllowThreeByteSurrogates;
- for (charcount_t cch = cchLength; cch > 0; --cch)
- {
- switch (utf8::Decode(src, last, options))
- {
- case '\r':
- case '\n':
- case 0x2028:
- case 0x2029:
- return true;
- }
- }
- return false;
- }
- FunctionBody::StatementMap* FunctionBody::GetMatchingStatementMapFromByteCode(int byteCodeOffset, bool ignoreSubexpressions /* = false */)
- {
- StatementMapList * pStatementMaps = this->GetStatementMaps();
- if (pStatementMaps)
- {
- Assert(m_sourceInfo.pSpanSequence == nullptr);
- for (int index = 0; index < pStatementMaps->Count(); index++)
- {
- FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
- if (!(ignoreSubexpressions && pStatementMap->isSubexpression) && pStatementMap->byteCodeSpan.Includes(byteCodeOffset))
- {
- return pStatementMap;
- }
- }
- }
- return nullptr;
- }
- // Returns the StatementMap for the offset.
- // 1. Current statementMap if bytecodeoffset falls within bytecode's span
- // 2. Previous if the bytecodeoffset is in between previous's end to current's begin
- FunctionBody::StatementMap* FunctionBody::GetEnclosingStatementMapFromByteCode(int byteCodeOffset, bool ignoreSubexpressions /* = false */)
- {
- int index = GetEnclosingStatementIndexFromByteCode(byteCodeOffset, ignoreSubexpressions);
- if (index != -1)
- {
- return this->GetStatementMaps()->Item(index);
- }
- return nullptr;
- }
- // Returns the index of StatementMap for
- // 1. Current statementMap if bytecodeoffset falls within bytecode's span
- // 2. Previous if the bytecodeoffset is in between previous's end to current's begin
- // 3. -1 of the failures.
- int FunctionBody::GetEnclosingStatementIndexFromByteCode(int byteCodeOffset, bool ignoreSubexpressions /* = false */)
- {
- StatementMapList * pStatementMaps = this->GetStatementMaps();
- if (pStatementMaps == nullptr)
- {
- // e.g. internal library.
- return -1;
- }
- Assert(m_sourceInfo.pSpanSequence == nullptr);
- for (int index = 0; index < pStatementMaps->Count(); index++)
- {
- FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
- if (!(ignoreSubexpressions && pStatementMap->isSubexpression) && pStatementMap->byteCodeSpan.Includes(byteCodeOffset))
- {
- return index;
- }
- else if (!pStatementMap->isSubexpression && byteCodeOffset < pStatementMap->byteCodeSpan.begin) // We always ignore sub expressions when checking if we went too far
- {
- return index > 0 ? index - 1 : 0;
- }
- }
- return pStatementMaps->Count() - 1;
- }
- // In some cases in legacy mode, due to the state scriptContext->windowIdList, the parser might not detect an eval call in the first parse but do so in the reparse
- // This fixes up the state at the start of reparse
- void FunctionBody::SaveState(ParseNodeFnc * pnodeFnc)
- {
- Assert(!this->IsReparsed());
- this->SetChildCallsEval(!!pnodeFnc->ChildCallsEval());
- this->SetCallsEval(!!pnodeFnc->CallsEval());
- this->SetHasReferenceableBuiltInArguments(!!pnodeFnc->HasReferenceableBuiltInArguments());
- }
- void FunctionBody::RestoreState(ParseNodeFnc * pnodeFnc)
- {
- Assert(this->IsReparsed());
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- if(!!pnodeFnc->ChildCallsEval() != this->GetChildCallsEval())
- {
- OUTPUT_VERBOSE_TRACE(Js::DebuggerPhase, _u("Child calls eval is different on debug reparse: %s(%s)\n"), this->GetExternalDisplayName(), this->GetDebugNumberSet(debugStringBuffer));
- }
- if(!!pnodeFnc->CallsEval() != this->GetCallsEval())
- {
- OUTPUT_VERBOSE_TRACE(Js::DebuggerPhase, _u("Calls eval is different on debug reparse: %s(%s)\n"), this->GetExternalDisplayName(), this->GetDebugNumberSet(debugStringBuffer));
- }
- if(!!pnodeFnc->HasReferenceableBuiltInArguments() != this->HasReferenceableBuiltInArguments())
- {
- OUTPUT_VERBOSE_TRACE(Js::DebuggerPhase, _u("Referenceable Built in args is different on debug reparse: %s(%s)\n"), this->GetExternalDisplayName(), this->GetDebugNumberSet(debugStringBuffer));
- }
- pnodeFnc->SetChildCallsEval(this->GetChildCallsEval());
- pnodeFnc->SetCallsEval(this->GetCallsEval());
- pnodeFnc->SetHasReferenceableBuiltInArguments(this->HasReferenceableBuiltInArguments());
- }
- // Retrieves statement map for given byte code offset.
- // Parameters:
- // - sourceOffset: byte code offset to get map for.
- // - mapIndex: if not NULL, receives the index of found map.
- FunctionBody::StatementMap* FunctionBody::GetMatchingStatementMapFromSource(int sourceOffset, int* pMapIndex /* = nullptr */)
- {
- StatementMapList * pStatementMaps = this->GetStatementMaps();
- if (pStatementMaps && pStatementMaps->Count() > 0)
- {
- Assert(m_sourceInfo.pSpanSequence == nullptr);
- for (int index = pStatementMaps->Count() - 1; index >= 0; index--)
- {
- FunctionBody::StatementMap* pStatementMap = pStatementMaps->Item(index);
- if (!pStatementMap->isSubexpression && pStatementMap->sourceSpan.Includes(sourceOffset))
- {
- if (pMapIndex)
- {
- *pMapIndex = index;
- }
- return pStatementMap;
- }
- }
- }
- if (pMapIndex)
- {
- *pMapIndex = 0;
- }
- return nullptr;
- }
- //
- // The function determine the line and column for a bytecode offset within the current script buffer.
- //
- bool FunctionBody::GetLineCharOffset(int byteCodeOffset, ULONG* _line, LONG* _charOffset, bool canAllocateLineCache /*= true*/)
- {
- Assert(!this->GetUtf8SourceInfo()->GetIsLibraryCode());
- int startCharOfStatement = this->m_cchStartOffset; // Default to the start of this function
- if (m_sourceInfo.pSpanSequence)
- {
- SmallSpanSequenceIter iter;
- m_sourceInfo.pSpanSequence->Reset(iter);
- StatementData data;
- if (m_sourceInfo.pSpanSequence->GetMatchingStatementFromBytecode(byteCodeOffset, iter, data)
- && EndsAfter(data.sourceBegin))
- {
- startCharOfStatement = data.sourceBegin;
- }
- }
- else
- {
- Js::FunctionBody::StatementMap* map = this->GetEnclosingStatementMapFromByteCode(byteCodeOffset, false);
- if (map && EndsAfter(map->sourceSpan.begin))
- {
- startCharOfStatement = map->sourceSpan.begin;
- }
- }
- return this->GetLineCharOffsetFromStartChar(startCharOfStatement, _line, _charOffset, canAllocateLineCache);
- }
- bool FunctionBody::GetLineCharOffsetFromStartChar(int startCharOfStatement, ULONG* _line, LONG* _charOffset, bool canAllocateLineCache /*= true*/)
- {
- Assert(!this->GetUtf8SourceInfo()->GetIsLibraryCode() || this->IsJsBuiltInCode());
- // The following adjusts for where the script is within the document
- ULONG line = this->GetHostStartLine();
- charcount_t column = 0;
- ULONG lineCharOffset = 0;
- charcount_t lineByteOffset = 0;
- if (startCharOfStatement > 0)
- {
- bool doSlowLookup = !canAllocateLineCache;
- if (canAllocateLineCache)
- {
- HRESULT hr = this->GetUtf8SourceInfo()->EnsureLineOffsetCacheNoThrow();
- if (FAILED(hr))
- {
- if (hr != E_OUTOFMEMORY)
- {
- Assert(hr == E_ABORT); // The only other possible error we know about is ScriptAbort from QueryContinue.
- return false;
- }
- // Clear the cache so it is not used.
- this->GetUtf8SourceInfo()->DeleteLineOffsetCache();
- // We can try and do the slow lookup below
- doSlowLookup = true;
- }
- }
- charcount_t cacheLine = 0;
- this->GetUtf8SourceInfo()->GetLineInfoForCharPosition(startCharOfStatement, &cacheLine, &column, &lineByteOffset, doSlowLookup);
- // Update the tracking variables to jump to the line position (only need to jump if not on the first line).
- if (cacheLine > 0)
- {
- line += cacheLine;
- lineCharOffset = startCharOfStatement - column;
- }
- }
- if (this->GetSourceContextInfo()->IsDynamic() && this->m_isDynamicFunction)
- {
- line -= JavascriptFunction::numberLinesPrependedToAnonymousFunction;
- }
- if(_line)
- {
- *_line = line;
- }
- if(_charOffset)
- {
- *_charOffset = column;
- // If we are at the beginning of the host code, adjust the offset based on the host provided offset
- if (this->GetHostSrcInfo()->dlnHost == line)
- {
- *_charOffset += (LONG)this->GetHostStartColumn();
- }
- }
- return true;
- }
- bool FunctionBody::GetStatementIndexAndLengthAt(int byteCodeOffset, UINT32* statementIndex, UINT32* statementLength)
- {
- Assert(statementIndex != nullptr);
- Assert(statementLength != nullptr);
- Assert(this->IsInDebugMode());
- StatementMap * statement = GetEnclosingStatementMapFromByteCode(byteCodeOffset, false);
- Assert(statement != nullptr);
- // Bailout if we are unable to find a statement.
- // We shouldn't be missing these when a debugger is attached but we don't want to AV on retail builds.
- if (statement == nullptr)
- {
- return false;
- }
- Assert(m_utf8SourceInfo);
- const SRCINFO * srcInfo = GetUtf8SourceInfo()->GetSrcInfo();
- // Offset from the beginning of the document minus any host-supplied source characters.
- // Host supplied characters are inserted (for example) around onload:
- // onload="foo('somestring', 0)" -> function onload(event).{.foo('somestring', 0).}
- ULONG offsetFromDocumentBegin = srcInfo ? srcInfo->ulCharOffset - srcInfo->ichMinHost : 0;
- *statementIndex = statement->sourceSpan.Begin() + offsetFromDocumentBegin;
- *statementLength = statement->sourceSpan.End() - statement->sourceSpan.Begin();
- return true;
- }
- void FunctionBody::RecordFrameDisplayRegister(RegSlot slot)
- {
- AssertMsg(slot != 0, "The assumption that the Frame Display Register cannot be at the 0 slot is wrong.");
- SetFrameDisplayRegister(slot);
- }
- void FunctionBody::RecordObjectRegister(RegSlot slot)
- {
- AssertMsg(slot != 0, "The assumption that the Object Register cannot be at the 0 slot is wrong.");
- SetObjectRegister(slot);
- }
- Js::RootObjectBase * FunctionBody::GetRootObject() const
- {
- // Safe to be used by the JIT thread
- Assert(this->GetConstTable() != nullptr);
- return (Js::RootObjectBase *)PointerValue(this->GetConstTable()[Js::FunctionBody::RootObjectRegSlot - FunctionBody::FirstRegSlot]);
- }
- Js::RootObjectBase * FunctionBody::LoadRootObject() const
- {
- if (this->IsES6ModuleCode() || this->GetModuleID() == kmodGlobal)
- {
- return JavascriptOperators::OP_LdRoot(this->GetScriptContext());
- }
- return JavascriptOperators::GetModuleRoot(this->GetModuleID(), this->GetScriptContext());
- }
- #if ENABLE_NATIVE_CODEGEN
- FunctionEntryPointInfo * FunctionBody::GetEntryPointFromNativeAddress(DWORD_PTR codeAddress)
- {
- FunctionEntryPointInfo * entryPoint = nullptr;
- this->MapEntryPoints([&entryPoint, &codeAddress](int index, FunctionEntryPointInfo * currentEntryPoint)
- {
- // We need to do a second check for IsNativeCode because the entry point could be in the process of
- // being recorded on the background thread
- if (currentEntryPoint->IsInNativeAddressRange(codeAddress))
- {
- entryPoint = currentEntryPoint;
- }
- });
- return entryPoint;
- }
- LoopEntryPointInfo * FunctionBody::GetLoopEntryPointInfoFromNativeAddress(DWORD_PTR codeAddress, uint loopNum) const
- {
- LoopEntryPointInfo * entryPoint = nullptr;
- LoopHeader * loopHeader = this->GetLoopHeader(loopNum);
- Assert(loopHeader);
- loopHeader->MapEntryPoints([&](int index, LoopEntryPointInfo * currentEntryPoint)
- {
- if (currentEntryPoint->IsCodeGenDone() &&
- codeAddress >= currentEntryPoint->GetNativeAddress() &&
- codeAddress < currentEntryPoint->GetNativeAddress() + currentEntryPoint->GetCodeSize())
- {
- entryPoint = currentEntryPoint;
- }
- });
- return entryPoint;
- }
- int FunctionBody::GetStatementIndexFromNativeOffset(SmallSpanSequence *pThrowSpanSequence, uint32 nativeOffset)
- {
- int statementIndex = -1;
- if (pThrowSpanSequence)
- {
- SmallSpanSequenceIter iter;
- StatementData tmpData;
- if (pThrowSpanSequence->GetMatchingStatementFromBytecode(nativeOffset, iter, tmpData))
- {
- statementIndex = tmpData.sourceBegin; // sourceBegin represents statementIndex here
- }
- else
- {
- // If nativeOffset falls on the last span, GetMatchingStatement would miss it because SmallSpanSequence
- // does not know about the last span end. Since we checked that codeAddress is within our range,
- // we can safely consider it matches the last span.
- statementIndex = iter.accumulatedSourceBegin;
- }
- }
- return statementIndex;
- }
- int FunctionBody::GetStatementIndexFromNativeAddress(SmallSpanSequence *pThrowSpanSequence, DWORD_PTR codeAddress, DWORD_PTR nativeBaseAddress)
- {
- uint32 nativeOffset = (uint32)(codeAddress - nativeBaseAddress);
- return GetStatementIndexFromNativeOffset(pThrowSpanSequence, nativeOffset);
- }
- #endif
- BOOL FunctionBody::GetMatchingStatementMap(StatementData &data, int statementIndex, FunctionBody *inlinee)
- {
- SourceInfo *si = &this->m_sourceInfo;
- if (inlinee)
- {
- si = &inlinee->m_sourceInfo;
- Assert(si);
- }
- if (statementIndex >= 0)
- {
- SmallSpanSequence *pSpanSequence = si->pSpanSequence;
- if (pSpanSequence)
- {
- SmallSpanSequenceIter iter;
- pSpanSequence->Reset(iter);
- if (pSpanSequence->Item(statementIndex, iter, data))
- {
- return TRUE;
- }
- }
- else
- {
- StatementMapList* pStatementMaps = GetStatementMaps();
- Assert(pStatementMaps);
- if (statementIndex >= pStatementMaps->Count())
- {
- return FALSE;
- }
- data.sourceBegin = pStatementMaps->Item(statementIndex)->sourceSpan.begin;
- data.bytecodeBegin = pStatementMaps->Item(statementIndex)->byteCodeSpan.begin;
- return TRUE;
- }
- }
- return FALSE;
- }
- void FunctionBody::FindClosestStatements(int32 characterOffset, StatementLocation *firstStatementLocation, StatementLocation *secondStatementLocation)
- {
- auto statementMaps = this->GetStatementMaps();
- if (statementMaps)
- {
- for(int i = 0; i < statementMaps->Count(); i++)
- {
- regex::Interval* pSourceSpan = &(statementMaps->Item(i)->sourceSpan);
- if (FunctionBody::IsDummyGlobalRetStatement(pSourceSpan))
- {
- // Workaround for handling global return, which is an empty range.
- continue;
- }
- if (pSourceSpan->begin < characterOffset
- && (firstStatementLocation->function == nullptr || firstStatementLocation->statement.begin < pSourceSpan->begin))
- {
- firstStatementLocation->function = this;
- firstStatementLocation->statement = *pSourceSpan;
- firstStatementLocation->bytecodeSpan = statementMaps->Item(i)->byteCodeSpan;
- }
- else if (pSourceSpan->begin >= characterOffset
- && (secondStatementLocation->function == nullptr || secondStatementLocation->statement.begin > pSourceSpan->begin))
- {
- secondStatementLocation->function = this;
- secondStatementLocation->statement = *pSourceSpan;
- secondStatementLocation->bytecodeSpan = statementMaps->Item(i)->byteCodeSpan;
- }
- }
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- BOOL FunctionBody::GetMatchingStatementMapFromNativeAddress(DWORD_PTR codeAddress, StatementData &data, uint loopNum, FunctionBody *inlinee /* = nullptr */)
- {
- SmallSpanSequence * spanSequence = nullptr;
- DWORD_PTR nativeBaseAddress = NULL;
- EntryPointInfo * entryPoint;
- if (loopNum == -1)
- {
- entryPoint = GetEntryPointFromNativeAddress(codeAddress);
- }
- else
- {
- entryPoint = GetLoopEntryPointInfoFromNativeAddress(codeAddress, loopNum);
- }
- if (entryPoint != nullptr)
- {
- spanSequence = entryPoint->GetNativeThrowSpanSequence();
- nativeBaseAddress = entryPoint->GetNativeAddress();
- }
- int statementIndex = GetStatementIndexFromNativeAddress(spanSequence, codeAddress, nativeBaseAddress);
- return GetMatchingStatementMap(data, statementIndex, inlinee);
- }
- BOOL FunctionBody::GetMatchingStatementMapFromNativeOffset(DWORD_PTR codeAddress, uint32 offset, StatementData &data, uint loopNum, FunctionBody *inlinee /* = nullptr */)
- {
- EntryPointInfo * entryPoint;
- if (loopNum == -1)
- {
- entryPoint = GetEntryPointFromNativeAddress(codeAddress);
- }
- else
- {
- entryPoint = GetLoopEntryPointInfoFromNativeAddress(codeAddress, loopNum);
- }
- SmallSpanSequence *spanSequence = entryPoint ? entryPoint->GetNativeThrowSpanSequence() : nullptr;
- int statementIndex = GetStatementIndexFromNativeOffset(spanSequence, offset);
- return GetMatchingStatementMap(data, statementIndex, inlinee);
- }
- #endif
- #if ENABLE_PROFILE_INFO
- void FunctionBody::LoadDynamicProfileInfo()
- {
- SourceDynamicProfileManager * sourceDynamicProfileManager = GetSourceContextInfo()->sourceDynamicProfileManager;
- if (sourceDynamicProfileManager != nullptr)
- {
- this->dynamicProfileInfo = sourceDynamicProfileManager->GetDynamicProfileInfo(this);
- #if DBG_DUMP
- if(this->dynamicProfileInfo)
- {
- if (Configuration::Global.flags.Dump.IsEnabled(DynamicProfilePhase, this->GetSourceContextId(), this->GetLocalFunctionId()))
- {
- Output::Print(_u("Loaded:"));
- this->dynamicProfileInfo->Dump(this);
- }
- }
- #endif
- }
- #ifdef DYNAMIC_PROFILE_MUTATOR
- DynamicProfileMutator::Mutate(this);
- #endif
- }
- bool FunctionBody::NeedEnsureDynamicProfileInfo() const
- {
- // Only need to ensure dynamic profile if we don't already have link up the dynamic profile info
- // and dynamic profile collection is enabled
- return
- !this->m_isFromNativeCodeModule &&
- !this->HasExecutionDynamicProfileInfo() &&
- DynamicProfileInfo::IsEnabled(this);
- }
- DynamicProfileInfo * FunctionBody::EnsureDynamicProfileInfo()
- {
- if (this->NeedEnsureDynamicProfileInfo())
- {
- m_scriptContext->AddDynamicProfileInfo(this, this->dynamicProfileInfo);
- Assert(!this->HasExecutionDynamicProfileInfo());
- this->hasExecutionDynamicProfileInfo = true;
- }
- return this->dynamicProfileInfo;
- }
- DynamicProfileInfo* FunctionBody::AllocateDynamicProfile()
- {
- return DynamicProfileInfo::New(m_scriptContext->GetRecycler(), this);
- }
- #endif
- BOOL FunctionBody::IsNativeOriginalEntryPoint() const
- {
- #if ENABLE_NATIVE_CODEGEN
- JavascriptMethod originalEntryPoint = this->GetOriginalEntryPoint_Unchecked();
- return
- #if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
- (
- #if ENABLE_OOP_NATIVE_CODEGEN
- JITManager::GetJITManager()->IsOOPJITEnabled()
- ? JITThunkEmitter<SectionAllocWrapper>::IsInThunk(this->GetScriptContext()->GetThreadContext()->GetJITThunkStartAddr(), (uintptr_t)originalEntryPoint)
- :
- #endif
- this->GetScriptContext()->GetThreadContext()->GetJITThunkEmitter()->IsInThunk((uintptr_t)originalEntryPoint)
- ) ||
- #endif
- this->GetScriptContext()->IsNativeAddress((void*)originalEntryPoint);
- #else
- return false;
- #endif
- }
- bool FunctionBody::IsSimpleJitOriginalEntryPoint() const
- {
- #if ENABLE_NATIVE_CODEGEN
- const FunctionEntryPointInfo *const simpleJitEntryPointInfo = GetSimpleJitEntryPointInfo();
- return simpleJitEntryPointInfo && simpleJitEntryPointInfo->GetNativeEntrypoint() == GetOriginalEntryPoint_Unchecked();
- #else
- return false;
- #endif
- }
- void FunctionBody::UpdateEntryPointsOnDebugReparse()
- {
- // Update all function types associated with this function body. Note that we can't rely on updating
- // types pointed to by function objects, because the type may evolve to one that is not currently referenced.
- ProxyEntryPointInfo * entryPointInfo = this->GetDefaultFunctionEntryPointInfo();
- JavascriptMethod newEntryPoint = this->GetDirectEntryPoint(entryPointInfo);
- bool isAsmJS = this->GetIsAsmjsMode();
- auto updateOneType = [&](ScriptFunctionType* functionType) {
- // Note that the ScriptFunctionType method will handle cross-site thunks correctly.
- functionType->ChangeEntryPoint(entryPointInfo, newEntryPoint, isAsmJS);
- };
- this->MapFunctionObjectTypes(updateOneType);
- }
- void FunctionProxy::Finalize(bool isShutdown)
- {
- this->CleanupFunctionProxyCounters();
- }
- #if DBG
- bool FunctionBody::HasValidSourceInfo()
- {
- SourceContextInfo* sourceContextInfo;
- if (m_scriptContext->GetSourceContextInfoMap())
- {
- if(m_scriptContext->GetSourceContextInfoMap()->TryGetValue(this->GetHostSourceContext(), &sourceContextInfo) &&
- sourceContextInfo == this->GetSourceContextInfo())
- {
- return true;
- }
- }
- Assert(this->IsDynamicScript());
- if(m_scriptContext->GetDynamicSourceContextInfoMap())
- {
- if(m_scriptContext->GetDynamicSourceContextInfoMap()->TryGetValue(this->GetSourceContextInfo()->hash, &sourceContextInfo) &&
- sourceContextInfo == this->GetSourceContextInfo())
- {
- return true;
- }
- }
- // The SourceContextInfo will not be added to the dynamicSourceContextInfoMap, if they are host provided dynamic code. But they are valid source context info
- if (this->GetSourceContextInfo()->isHostDynamicDocument)
- {
- return true;
- }
- return m_scriptContext->IsNoContextSourceContextInfo(this->GetSourceContextInfo());
- }
- // originalEntryPoint: DefaultDeferredParsingThunk, DefaultDeferredDeserializeThunk, DefaultEntryThunk, dynamic interpreter thunk or native entry point
- // directEntryPoint:
- // if (!profiled) - DefaultDeferredParsingThunk, DefaultDeferredDeserializeThunk, DefaultEntryThunk, CheckCodeGenThunk,
- // dynamic interpreter thunk, native entry point
- // if (profiling) - ProfileDeferredParsingThunk, ProfileDeferredDeserializeThunk, ProfileEntryThunk, CheckCodeGenThunk
- bool FunctionProxy::HasValidNonProfileEntryPoint() const
- {
- JavascriptMethod directEntryPoint = this->GetDefaultEntryPointInfo()->jsMethod;
- JavascriptMethod originalEntryPoint = this->GetOriginalEntryPoint_Unchecked();
- FunctionBody* body = this->GetFunctionBody();
- Unused(body); // in some configuration
- #ifdef ASMJS_PLAT
- if (body->GetIsAsmJsFunction())
- {
- #ifdef ENABLE_WASM
- if (body->IsWasmFunction() && body->GetByteCodeCount() == 0)
- {
- // The only valid 2 entrypoints if the function hasn't been parsed
- return directEntryPoint == AsmJsDefaultEntryThunk || directEntryPoint == WasmLibrary::WasmLazyTrapCallback;
- }
- #endif
- // Entrypoints valid only for asm.js/wasm
- if (directEntryPoint == AsmJsDefaultEntryThunk || IsAsmJsCodeGenThunk(directEntryPoint))
- {
- return true;
- }
- }
- #endif
- // Check the direct entry point to see if it is codegen thunk
- // if it is not, the background codegen thread has updated both original entry point and direct entry point
- // and they should still match, same as cases other then code gen
- return IsIntermediateCodeGenThunk(directEntryPoint) || originalEntryPoint == directEntryPoint
- #if ENABLE_PROFILE_INFO
- || (directEntryPoint == DynamicProfileInfo::EnsureDynamicProfileInfoThunk && body->IsNativeOriginalEntryPoint())
- #endif
- ;
- }
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- bool FunctionProxy::HasValidProfileEntryPoint() const
- {
- JavascriptMethod directEntryPoint = this->GetDefaultEntryPointInfo()->jsMethod;
- JavascriptMethod originalEntryPoint = this->GetOriginalEntryPoint_Unchecked();
- if (originalEntryPoint == DefaultDeferredParsingThunk)
- {
- return directEntryPoint == ProfileDeferredParsingThunk;
- }
- if (originalEntryPoint == DefaultDeferredDeserializeThunk)
- {
- return directEntryPoint == ProfileDeferredDeserializeThunk;
- }
- if (!this->IsFunctionBody())
- {
- return false;
- }
- #if ENABLE_PROFILE_INFO
- FunctionBody * functionBody = this->GetFunctionBody();
- if (functionBody->IsInterpreterThunk() || functionBody->IsSimpleJitOriginalEntryPoint())
- {
- return directEntryPoint == ProfileEntryThunk || IsIntermediateCodeGenThunk(directEntryPoint);
- }
- #if ENABLE_NATIVE_CODEGEN
- // In the profiler mode, the EnsureDynamicProfileInfoThunk is valid as we would be assigning to appropriate thunk when that thunk called.
- return functionBody->IsNativeOriginalEntryPoint() &&
- (directEntryPoint == DynamicProfileInfo::EnsureDynamicProfileInfoThunk || directEntryPoint == ProfileEntryThunk);
- #endif
- #else
- return true;
- #endif
- }
- #endif
- bool FunctionProxy::HasValidEntryPoint() const
- {
- if (this->IsWasmFunction() ||
- (!m_scriptContext->HadProfiled() &&
- !(this->m_scriptContext->IsScriptContextInDebugMode() && m_scriptContext->IsExceptionWrapperForBuiltInsEnabled())))
- {
- return this->HasValidNonProfileEntryPoint();
- }
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- if (m_scriptContext->IsProfiling())
- {
- return this->HasValidProfileEntryPoint();
- }
- return this->HasValidNonProfileEntryPoint() || this->HasValidProfileEntryPoint();
- #else
- return this->HasValidNonProfileEntryPoint();
- #endif
- }
- #endif
- void ParseableFunctionInfo::SetDeferredParsingEntryPoint()
- {
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- Assert(m_scriptContext->DeferredParsingThunk == ProfileDeferredParsingThunk
- || m_scriptContext->DeferredParsingThunk == DefaultDeferredParsingThunk);
- #else
- Assert(m_scriptContext->DeferredParsingThunk == DefaultDeferredParsingThunk);
- #endif
- this->SetEntryPoint(this->GetDefaultEntryPointInfo(), m_scriptContext->DeferredParsingThunk);
- this->SetOriginalEntryPoint(DefaultDeferredParsingThunk);
- }
- void ParseableFunctionInfo::SetInitialDefaultEntryPoint()
- {
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- Assert(m_scriptContext->CurrentThunk == ProfileEntryThunk || m_scriptContext->CurrentThunk == DefaultEntryThunk);
- Assert(this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredParsingThunk ||
- this->GetOriginalEntryPoint_Unchecked() == ProfileDeferredParsingThunk ||
- this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredDeserializeThunk ||
- this->GetOriginalEntryPoint_Unchecked() == ProfileDeferredDeserializeThunk ||
- this->GetOriginalEntryPoint_Unchecked() == DefaultEntryThunk ||
- this->GetOriginalEntryPoint_Unchecked() == ProfileEntryThunk);
- #else
- Assert(m_scriptContext->CurrentThunk == DefaultEntryThunk);
- Assert(this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredParsingThunk ||
- this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredDeserializeThunk ||
- this->GetOriginalEntryPoint_Unchecked() == DefaultEntryThunk);
- #endif
- Assert(this->m_defaultEntryPointInfo != nullptr);
- // CONSIDER: we can optimize this to generate the dynamic interpreter thunk up front
- // If we know that we are in the defer parsing thunk already
- this->SetEntryPoint(this->GetDefaultEntryPointInfo(), m_scriptContext->CurrentThunk);
- this->SetOriginalEntryPoint(DefaultEntryThunk);
- }
- #if ENABLE_NATIVE_CODEGEN
- void FunctionBody::SetCheckCodeGenEntryPoint(FunctionEntryPointInfo* entryPointInfo, JavascriptMethod entryPoint)
- {
- Assert(IsIntermediateCodeGenThunk(entryPoint));
- Assert(
- this->GetEntryPoint(entryPointInfo) == m_scriptContext->CurrentThunk ||
- (entryPointInfo == this->m_defaultEntryPointInfo && this->IsInterpreterThunk()) ||
- (
- GetSimpleJitEntryPointInfo() &&
- GetEntryPoint(entryPointInfo) == GetSimpleJitEntryPointInfo()->GetNativeEntrypoint()
- ));
- this->SetEntryPoint(entryPointInfo, entryPoint);
- }
- #endif
- #if DYNAMIC_INTERPRETER_THUNK
- void FunctionBody::GenerateDynamicInterpreterThunk()
- {
- AssertOrFailFastMsg(!m_isWasmFunction || GetByteCodeCount() > 0, "The wasm function should have been parsed before generating the dynamic interpreter thunk");
- if (this->m_dynamicInterpreterThunk == nullptr)
- {
- // NOTE: Etw rundown thread may be reading this->dynamicInterpreterThunk concurrently. We don't need to synchronize
- // access as it is ok for etw rundown to get either null or updated new value.
- if (m_isAsmJsFunction)
- {
- this->SetOriginalEntryPoint(this->m_scriptContext->GetNextDynamicAsmJsInterpreterThunk(&this->m_dynamicInterpreterThunk));
- }
- else
- {
- this->SetOriginalEntryPoint(this->m_scriptContext->GetNextDynamicInterpreterThunk(&this->m_dynamicInterpreterThunk));
- }
- if (this->m_dynamicInterpreterThunk != nullptr)
- {
- JS_ETW(EtwTrace::LogMethodInterpreterThunkLoadEvent(this));
- }
- }
- else
- {
- this->SetOriginalEntryPoint((JavascriptMethod)InterpreterThunkEmitter::ConvertToEntryPoint(this->m_dynamicInterpreterThunk));
- }
- #if DBG
- if (GetScriptContext()->GetThreadContext()->NoDynamicThunks())
- {
- Assert(this->m_dynamicInterpreterThunk == nullptr);
- #ifdef ASMJS_PLAT
- if (m_isAsmJsFunction)
- {
- Assert(this->GetOriginalEntryPoint_Unchecked() == (JavascriptMethod)&Js::InterpreterStackFrame::StaticInterpreterAsmThunk);
- }
- else
- #endif
- {
- Assert(this->GetOriginalEntryPoint_Unchecked() == (JavascriptMethod)&Js::InterpreterStackFrame::StaticInterpreterThunk);
- }
- }
- #endif
- }
- JavascriptMethod FunctionBody::EnsureDynamicInterpreterThunk(FunctionEntryPointInfo* entryPointInfo)
- {
- // This may be first call to the function, make sure we have dynamic profile info
- //
- // We need to ensure dynamic profile info even if we didn't generate a dynamic interpreter thunk
- // This happens when we go through CheckCodeGen thunk, to DelayDynamicInterpreterThunk, to here
- // but the background codegen thread updated the entry point with the native entry point.
- this->EnsureDynamicProfileInfo();
- Assert(HasValidEntryPoint());
- AssertMsg(!m_isWasmFunction || GetByteCodeCount() > 0, "Wasm function should be parsed by this point");
- if (InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetEntryPoint(entryPointInfo)))
- {
- // We are not doing code gen on this function, just change the entry point directly
- Assert(InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetOriginalEntryPoint_Unchecked()));
- GenerateDynamicInterpreterThunk();
- this->SetEntryPoint(entryPointInfo, this->GetOriginalEntryPoint_Unchecked());
- }
- else if (this->GetEntryPoint(entryPointInfo) == ProfileEntryThunk)
- {
- // We are not doing codegen on this function, just change the entry point directly
- // Don't replace the profile entry thunk
- Assert(InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetOriginalEntryPoint_Unchecked()));
- GenerateDynamicInterpreterThunk();
- }
- else if (InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetOriginalEntryPoint_Unchecked()))
- {
- JsUtil::JobProcessor * jobProcessor = this->GetScriptContext()->GetThreadContext()->GetJobProcessor();
- if (jobProcessor->ProcessesInBackground())
- {
- JsUtil::BackgroundJobProcessor * backgroundJobProcessor = static_cast<JsUtil::BackgroundJobProcessor *>(jobProcessor);
- AutoCriticalSection autocs(backgroundJobProcessor->GetCriticalSection());
- // Check again under lock
- if (InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetOriginalEntryPoint_Unchecked()))
- {
- // If the original entry point is DelayDynamicInterpreterThunk then there must be a version of this
- // function being codegen'd.
- Assert(IsIntermediateCodeGenThunk((JavascriptMethod)this->GetEntryPoint(this->GetDefaultEntryPointInfo())) || IsAsmJsCodeGenThunk((JavascriptMethod)this->GetEntryPoint(this->GetDefaultEntryPointInfo())));
- GenerateDynamicInterpreterThunk();
- }
- }
- else
- {
- // If the original entry point is DelayDynamicInterpreterThunk then there must be a version of this
- // function being codegen'd.
- Assert(IsIntermediateCodeGenThunk((JavascriptMethod)this->GetEntryPoint(this->GetDefaultEntryPointInfo())) || IsAsmJsCodeGenThunk((JavascriptMethod)this->GetEntryPoint(this->GetDefaultEntryPointInfo())));
- GenerateDynamicInterpreterThunk();
- }
- }
- return this->GetOriginalEntryPoint_Unchecked();
- }
- #endif
- #if ENABLE_NATIVE_CODEGEN
- void FunctionBody::SetNativeEntryPoint(FunctionEntryPointInfo* entryPointInfo, JavascriptMethod originalEntryPoint, JavascriptMethod directEntryPoint)
- {
- if (entryPointInfo->IsNativeEntryPointProcessed())
- {
- return;
- }
- bool isAsmJs = this->GetIsAsmjsMode();
- Assert(IsIntermediateCodeGenThunk(entryPointInfo->jsMethod) || CONFIG_FLAG(Prejit) || this->m_isFromNativeCodeModule || isAsmJs);
- entryPointInfo->EnsureIsReadyToCall();
- // keep originalEntryPoint updated with the latest known good native entry point
- if (entryPointInfo == this->GetDefaultEntryPointInfo())
- {
- this->SetOriginalEntryPoint(originalEntryPoint);
- }
- if (entryPointInfo->entryPointIndex == 0 && this->NeedEnsureDynamicProfileInfo())
- {
- entryPointInfo->jsMethod = DynamicProfileInfo::EnsureDynamicProfileInfoThunk;
- }
- else
- {
- entryPointInfo->jsMethod = directEntryPoint;
- }
- this->CaptureDynamicProfileState(entryPointInfo);
- if(entryPointInfo->GetJitMode() == ExecutionMode::SimpleJit)
- {
- Assert(GetExecutionMode() == ExecutionMode::SimpleJit);
- SetSimpleJitEntryPointInfo(entryPointInfo);
- ResetSimpleJitCallCount();
- }
- else
- {
- Assert(entryPointInfo->GetJitMode() == ExecutionMode::FullJit);
- Assert(isAsmJs || GetExecutionMode() == ExecutionMode::FullJit);
- entryPointInfo->callsCount =
- static_cast<uint8>(
- min(
- static_cast<uint>(static_cast<uint8>(CONFIG_FLAG(MinBailOutsBeforeRejit))) *
- (Js::FunctionEntryPointInfo::GetDecrCallCountPerBailout() - 1),
- 0xffu));
- }
- TraceExecutionMode();
- JS_ETW(EtwTrace::LogMethodNativeLoadEvent(this, entryPointInfo));
- #ifdef VTUNE_PROFILING
- VTuneChakraProfile::LogMethodNativeLoadEvent(this, entryPointInfo);
- #endif
- #ifdef _M_ARM
- // For ARM we need to make sure that pipeline is synchronized with memory/cache for newly jitted code.
- _InstructionSynchronizationBarrier();
- #endif
- entryPointInfo->SetNativeEntryPointProcessed();
- }
- void FunctionBody::DefaultSetNativeEntryPoint(FunctionEntryPointInfo* entryPointInfo, FunctionBody * functionBody, JavascriptMethod entryPoint)
- {
- Assert(functionBody->m_scriptContext->CurrentThunk == DefaultEntryThunk);
- functionBody->SetNativeEntryPoint(entryPointInfo, entryPoint, entryPoint);
- }
- void FunctionBody::ProfileSetNativeEntryPoint(FunctionEntryPointInfo* entryPointInfo, FunctionBody * functionBody, JavascriptMethod entryPoint)
- {
- #ifdef ENABLE_WASM
- // Do not profile WebAssembly functions
- if (functionBody->IsWasmFunction())
- {
- functionBody->SetNativeEntryPoint(entryPointInfo, entryPoint, entryPoint);
- return;
- }
- #endif
- Assert(functionBody->m_scriptContext->CurrentThunk == ProfileEntryThunk);
- functionBody->SetNativeEntryPoint(entryPointInfo, entryPoint, ProfileEntryThunk);
- }
- Js::JavascriptMethod FunctionBody::GetLoopBodyEntryPoint(Js::LoopHeader * loopHeader, int entryPointIndex)
- {
- #if DBG
- this->GetLoopNumber(loopHeader);
- #endif
- return loopHeader->GetEntryPointInfo(entryPointIndex)->jsMethod;
- }
- void FunctionBody::SetLoopBodyEntryPoint(Js::LoopHeader * loopHeader, EntryPointInfo* entryPointInfo, Js::JavascriptMethod entryPoint, uint loopNum)
- {
- #if DBG_DUMP
- if (PHASE_TRACE1(Js::JITLoopBodyPhase))
- {
- DumpFunctionId(true);
- Output::Print(_u(": %-20s LoopBody EntryPt Loop: %2d Address : %x\n"), GetDisplayName(), loopNum, entryPoint);
- Output::Flush();
- }
- #endif
- Assert(((LoopEntryPointInfo*)entryPointInfo)->loopHeader == loopHeader);
- Assert(reinterpret_cast<void*>(entryPointInfo->jsMethod) == nullptr);
- entryPointInfo->jsMethod = entryPoint;
- ((Js::LoopEntryPointInfo*)entryPointInfo)->totalJittedLoopIterations =
- static_cast<uint8>(
- min(
- static_cast<uint>(static_cast<uint8>(CONFIG_FLAG(MinBailOutsBeforeRejitForLoops))) *
- (Js::LoopEntryPointInfo::GetDecrLoopCountPerBailout() - 1),
- 0xffu));
- // reset the counter to 1 less than the threshold for TJLoopBody
- if (loopHeader->GetCurrentEntryPointInfo()->GetIsAsmJSFunction())
- {
- loopHeader->interpretCount = entryPointInfo->GetFunctionBody()->GetLoopInterpretCount(loopHeader) - 1;
- }
- JS_ETW(EtwTrace::LogLoopBodyLoadEvent(this, ((LoopEntryPointInfo*)entryPointInfo), ((uint16)loopNum)));
- #ifdef VTUNE_PROFILING
- VTuneChakraProfile::LogLoopBodyLoadEvent(this, ((LoopEntryPointInfo*)entryPointInfo), ((uint16)loopNum));
- #endif
- }
- #endif
- void FunctionBody::MarkScript(ByteBlock *byteCodeBlock, ByteBlock* auxBlock, ByteBlock* auxContextBlock,
- uint byteCodeCount, uint byteCodeInLoopCount, uint byteCodeWithoutLDACount)
- {
- CheckNotExecuting();
- CheckEmpty();
- #ifdef PERF_COUNTERS
- DWORD byteCodeSize = byteCodeBlock->GetLength()
- + (auxBlock? auxBlock->GetLength() : 0)
- + (auxContextBlock? auxContextBlock->GetLength() : 0);
- PERF_COUNTER_ADD(Code, DynamicByteCodeSize, byteCodeSize);
- #endif
- SetByteCodeCount(byteCodeCount);
- SetByteCodeInLoopCount(byteCodeInLoopCount);
- SetByteCodeWithoutLDACount(byteCodeWithoutLDACount);
- executionState.InitializeExecutionModeAndLimits(this);
- this->SetAuxiliaryData(auxBlock);
- this->SetAuxiliaryContextData(auxContextBlock);
- // Memory barrier needed here to make sure the background codegen thread's inliner
- // gets all the assignment before it sees that the function has been parse
- MemoryBarrier();
- this->byteCodeBlock = byteCodeBlock;
- PERF_COUNTER_ADD(Code, TotalByteCodeSize, byteCodeSize);
- // If this is a defer parse function body, we would not have registered it
- // on the function bodies list so we should register it now
- if (!this->m_isFuncRegistered)
- {
- this->GetUtf8SourceInfo()->SetFunctionBody(this);
- }
- }
- uint
- FunctionBody::GetLoopNumber(LoopHeader const * loopHeader) const
- {
- LoopHeader* loopHeaderArray = this->GetLoopHeaderArray();
- Assert(loopHeader >= loopHeaderArray);
- uint loopNum = (uint)(loopHeader - loopHeaderArray);
- Assert(loopNum < GetLoopCount());
- return loopNum;
- }
- uint
- FunctionBody::GetLoopNumberWithLock(LoopHeader const * loopHeader) const
- {
- LoopHeader* loopHeaderArray = this->GetLoopHeaderArrayWithLock();
- Assert(loopHeader >= loopHeaderArray);
- uint loopNum = (uint)(loopHeader - loopHeaderArray);
- Assert(loopNum < GetLoopCount());
- return loopNum;
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- bool FunctionBody::InstallProbe(int offset)
- {
- if (offset < 0 || ((uint)offset + 1) >= byteCodeBlock->GetLength())
- {
- return false;
- }
- byte* pbyteCodeBlockBuffer = this->byteCodeBlock->GetBuffer();
- if(!GetProbeBackingBlock())
- {
- // The probe backing block is set on a different thread than the main thread
- // The recycler doesn't like allocations from a different thread, so we allocate
- // the backing byte code block in the arena
- ArenaAllocator *pArena = m_scriptContext->AllocatorForDiagnostics();
- Assert(pArena);
- ByteBlock* probeBackingBlock = ByteBlock::NewFromArena(pArena, pbyteCodeBlockBuffer, byteCodeBlock->GetLength());
- SetProbeBackingBlock(probeBackingBlock);
- }
- // Make sure Break opcode only need one byte
- Assert(OpCodeUtil::IsSmallEncodedOpcode(OpCode::Break));
- #if ENABLE_NATIVE_CODEGEN
- Assert(!OpCodeAttr::HasMultiSizeLayout(OpCode::Break));
- #endif
- *(byte *)(pbyteCodeBlockBuffer + offset) = (byte)OpCode::Break;
- ++m_sourceInfo.m_probeCount;
- return true;
- }
- bool FunctionBody::UninstallProbe(int offset)
- {
- if (offset < 0 || ((uint)offset + 1) >= byteCodeBlock->GetLength())
- {
- return false;
- }
- byte* pbyteCodeBlockBuffer = byteCodeBlock->GetBuffer();
- Js::OpCode originalOpCode = ByteCodeReader::PeekByteOp(GetProbeBackingBlock()->GetBuffer() + offset);
- *(pbyteCodeBlockBuffer + offset) = (byte)originalOpCode;
- --m_sourceInfo.m_probeCount;
- AssertMsg(m_sourceInfo.m_probeCount >= 0, "Probe (Break Point) count became negative!");
- return true;
- }
- bool FunctionBody::ProbeAtOffset(int offset, OpCode* pOriginalOpcode)
- {
- if (!GetProbeBackingBlock())
- {
- return false;
- }
- if (offset < 0 || ((uint)offset + 1) >= this->byteCodeBlock->GetLength())
- {
- AssertMsg(false, "ProbeAtOffset called with out of bounds offset");
- return false;
- }
- Js::OpCode runningOpCode = ByteCodeReader::PeekByteOp(this->byteCodeBlock->GetBuffer() + offset);
- Js::OpCode originalOpcode = ByteCodeReader::PeekByteOp(GetProbeBackingBlock()->GetBuffer() + offset);
- if ( runningOpCode != originalOpcode)
- {
- *pOriginalOpcode = originalOpcode;
- return true;
- }
- else
- {
- // e.g. inline break or a step hit and is checking for a bp
- return false;
- }
- }
- #endif
- void FunctionBody::SetStackNestedFuncParent(FunctionInfo * parentFunctionInfo)
- {
- FunctionBody * parentFunctionBody = parentFunctionInfo->GetFunctionBody();
- RecyclerWeakReference<FunctionInfo>* parent = this->GetStackNestedFuncParent();
- if (parent != nullptr)
- {
- Assert(parent->Get() == parentFunctionInfo);
- return;
- }
- // Redeferral invalidates this assertion, as we may be recompiling with a different view of nested functions and
- // thus making different stack-nested-function decisions. I'm inclined to allow this, since things that have been
- // re-deferred will likely not be executed again, so it makes sense to exclude them from our analysis.
- // Assert(CanDoStackNestedFunc());
- Assert(parentFunctionBody->DoStackNestedFunc());
- this->SetAuxPtr<AuxPointerType::StackNestedFuncParent>(this->GetScriptContext()->GetRecycler()->CreateWeakReferenceHandle(parentFunctionInfo));
- }
- FunctionInfo * FunctionBody::GetStackNestedFuncParentStrongRef()
- {
- Assert(this->GetStackNestedFuncParent() != nullptr);
- return this->GetStackNestedFuncParent()->Get();
- }
- RecyclerWeakReference<FunctionInfo> * FunctionBody::GetStackNestedFuncParent()
- {
- return this->GetAuxPtr<AuxPointerType::StackNestedFuncParent>();
- }
- FunctionInfo * FunctionBody::GetAndClearStackNestedFuncParent()
- {
- if (this->GetAuxPtr<AuxPointerType::StackNestedFuncParent>())
- {
- FunctionInfo * parentFunctionInfo = GetStackNestedFuncParentStrongRef();
- ClearStackNestedFuncParent();
- return parentFunctionInfo;
- }
- return nullptr;
- }
- void FunctionBody::ClearStackNestedFuncParent()
- {
- this->SetAuxPtr<AuxPointerType::StackNestedFuncParent>(nullptr);
- }
- void FunctionBody::CreateCacheIdToPropertyIdMap(uint rootObjectLoadInlineCacheStart, uint rootObjectLoadMethodInlineCacheStart,
- uint rootObjectStoreInlineCacheStart,
- uint totalFieldAccessInlineCacheCount, uint isInstInlineCacheCount)
- {
- Assert(this->GetRootObjectLoadInlineCacheStart() == 0);
- Assert(this->GetRootObjectLoadMethodInlineCacheStart() == 0);
- Assert(this->GetRootObjectStoreInlineCacheStart() == 0);
- Assert(this->GetInlineCacheCount() == 0);
- Assert(this->GetIsInstInlineCacheCount() == 0);
- this->SetRootObjectLoadInlineCacheStart(rootObjectLoadInlineCacheStart);
- this->SetRootObjectLoadMethodInlineCacheStart(rootObjectLoadMethodInlineCacheStart);
- this->SetRootObjectStoreInlineCacheStart(rootObjectStoreInlineCacheStart);
- this->SetInlineCacheCount(totalFieldAccessInlineCacheCount);
- this->SetIsInstInlineCacheCount(isInstInlineCacheCount);
- this->CreateCacheIdToPropertyIdMap();
- }
- void FunctionBody::CreateCacheIdToPropertyIdMap()
- {
- Assert(this->cacheIdToPropertyIdMap == nullptr);
- Assert(this->inlineCaches == nullptr);
- uint count = this->GetInlineCacheCount() ;
- if (count!= 0)
- {
- this->cacheIdToPropertyIdMap =
- RecyclerNewArrayLeaf(this->m_scriptContext->GetRecycler(), PropertyId, count);
- #if DBG
- for (uint i = 0; i < count; i++)
- {
- this->cacheIdToPropertyIdMap[i] = Js::Constants::NoProperty;
- }
- #endif
- }
- }
- #if DBG
- void FunctionBody::VerifyCacheIdToPropertyIdMap()
- {
- uint count = this->GetInlineCacheCount();
- for (uint i = 0; i < count; i++)
- {
- Assert(this->cacheIdToPropertyIdMap[i] != Js::Constants::NoProperty);
- }
- }
- #endif
- void FunctionBody::SetPropertyIdForCacheId(uint cacheId, PropertyId propertyId)
- {
- Assert(this->cacheIdToPropertyIdMap != nullptr);
- Assert(cacheId < this->GetInlineCacheCount());
- Assert(this->cacheIdToPropertyIdMap[cacheId] == Js::Constants::NoProperty);
- this->cacheIdToPropertyIdMap[cacheId] = propertyId;
- }
- void FunctionBody::CreateReferencedPropertyIdMap(uint referencedPropertyIdCount)
- {
- this->SetReferencedPropertyIdCount(referencedPropertyIdCount);
- this->CreateReferencedPropertyIdMap();
- }
- void FunctionBody::CreateReferencedPropertyIdMap()
- {
- Assert(this->GetReferencedPropertyIdMap() == nullptr);
- uint count = this->GetReferencedPropertyIdCount();
- if (count!= 0)
- {
- this->SetReferencedPropertyIdMap(RecyclerNewArrayLeaf(this->m_scriptContext->GetRecycler(), PropertyId, count));
- #if DBG
- for (uint i = 0; i < count; i++)
- {
- this->GetReferencedPropertyIdMap()[i] = Js::Constants::NoProperty;
- }
- #endif
- }
- }
- #if DBG
- void FunctionBody::VerifyReferencedPropertyIdMap()
- {
- uint count = this->GetReferencedPropertyIdCount();
- for (uint i = 0; i < count; i++)
- {
- Assert(this->GetReferencedPropertyIdMap()[i] != Js::Constants::NoProperty);
- }
- }
- #endif
- PropertyId FunctionBody::GetReferencedPropertyId(uint index)
- {
- if (index < (uint)TotalNumberOfBuiltInProperties)
- {
- return index;
- }
- uint mapIndex = index - TotalNumberOfBuiltInProperties;
- return GetReferencedPropertyIdWithMapIndex(mapIndex);
- }
- PropertyId FunctionBody::GetReferencedPropertyIdWithLock(uint index)
- {
- if (index < (uint)TotalNumberOfBuiltInProperties)
- {
- return index;
- }
- uint mapIndex = index - TotalNumberOfBuiltInProperties;
- return GetReferencedPropertyIdWithMapIndexWithLock(mapIndex);
- }
- PropertyId FunctionBody::GetReferencedPropertyIdWithMapIndex(uint mapIndex)
- {
- Assert(this->GetReferencedPropertyIdMap());
- Assert(mapIndex < this->GetReferencedPropertyIdCount());
- return this->GetReferencedPropertyIdMap()[mapIndex];
- }
- PropertyId FunctionBody::GetReferencedPropertyIdWithMapIndexWithLock(uint mapIndex)
- {
- Assert(this->GetReferencedPropertyIdMapWithLock());
- Assert(mapIndex < this->GetReferencedPropertyIdCount());
- return this->GetReferencedPropertyIdMapWithLock()[mapIndex];
- }
- void FunctionBody::SetReferencedPropertyIdWithMapIndex(uint mapIndex, PropertyId propertyId)
- {
- Assert(propertyId >= TotalNumberOfBuiltInProperties);
- Assert(mapIndex < this->GetReferencedPropertyIdCount());
- Assert(this->GetReferencedPropertyIdMap() != nullptr);
- Assert(this->GetReferencedPropertyIdMap()[mapIndex] == Js::Constants::NoProperty);
- this->GetReferencedPropertyIdMap()[mapIndex] = propertyId;
- }
- void FunctionBody::CreateConstantTable()
- {
- Assert(this->GetConstTable() == nullptr);
- Assert(GetConstantCount() > FirstRegSlot);
- this->SetConstTable(RecyclerNewArrayZ(this->m_scriptContext->GetRecycler(), Field(Var), GetConstantCount()));
- // Initialize with the root object, which will always be recorded here.
- Js::RootObjectBase * rootObject = this->LoadRootObject();
- if (rootObject)
- {
- this->RecordConstant(RootObjectRegSlot, rootObject);
- }
- else
- {
- Assert(false);
- this->RecordConstant(RootObjectRegSlot, this->m_scriptContext->GetLibrary()->GetUndefined());
- }
- }
- void FunctionBody::RecordConstant(RegSlot location, Var var)
- {
- Assert(location < GetConstantCount());
- Assert(this->GetConstTable());
- Assert(var != nullptr);
- Assert(this->GetConstTable()[location - FunctionBody::FirstRegSlot] == nullptr);
- this->GetConstTable()[location - FunctionBody::FirstRegSlot] = var;
- }
- void FunctionBody::RecordNullObject(RegSlot location)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var nullObject = JavascriptOperators::OP_LdNull(scriptContext);
- this->RecordConstant(location, nullObject);
- }
- void FunctionBody::RecordUndefinedObject(RegSlot location)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var undefObject = JavascriptOperators::OP_LdUndef(scriptContext);
- this->RecordConstant(location, undefObject);
- }
- void FunctionBody::RecordTrueObject(RegSlot location)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var trueObject = JavascriptBoolean::OP_LdTrue(scriptContext);
- this->RecordConstant(location, trueObject);
- }
- void FunctionBody::RecordFalseObject(RegSlot location)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var falseObject = JavascriptBoolean::OP_LdFalse(scriptContext);
- this->RecordConstant(location, falseObject);
- }
- void FunctionBody::RecordIntConstant(RegSlot location, unsigned int val)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var intConst = JavascriptNumber::ToVar((int32)val, scriptContext);
- this->RecordConstant(location, intConst);
- }
- void FunctionBody::RecordStrConstant(RegSlot location, LPCOLESTR psz, uint32 cch, bool forcePropertyString)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- PropertyRecord const * propertyRecord;
- if (forcePropertyString)
- {
- scriptContext->GetOrAddPropertyRecord(psz, cch, &propertyRecord);
- }
- else
- {
- scriptContext->FindPropertyRecord(psz, cch, &propertyRecord);
- }
- Var str;
- if (propertyRecord == nullptr)
- {
- str = JavascriptString::NewCopyBuffer(psz, cch, scriptContext);
- }
- else
- {
- // If a particular string constant already has a propertyId, just create a property string for it
- // as it might be likely that it is used for a property lookup
- str = scriptContext->GetPropertyString(propertyRecord->GetPropertyId());
- }
- this->RecordConstant(location, str);
- }
- void FunctionBody::RecordBigIntConstant(RegSlot location, LPCOLESTR psz, uint32 cch, bool isNegative)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var bigintConst = JavascriptBigInt::Create(psz, cch, isNegative, scriptContext);
- this->RecordConstant(location, bigintConst);
- }
- void FunctionBody::RecordFloatConstant(RegSlot location, double d)
- {
- ScriptContext *scriptContext = this->GetScriptContext();
- Var floatConst = JavascriptNumber::ToVarIntCheck(d, scriptContext);
- this->RecordConstant(location, floatConst);
- }
- void FunctionBody::RecordNullDisplayConstant(RegSlot location)
- {
- this->RecordConstant(location, (Js::Var)&Js::NullFrameDisplay);
- }
- void FunctionBody::RecordStrictNullDisplayConstant(RegSlot location)
- {
- this->RecordConstant(location, (Js::Var)&Js::StrictNullFrameDisplay);
- }
- void FunctionBody::InitConstantSlots(Var *dstSlots)
- {
- // Initialize the given slots from the constant table.
- uint32 constCount = GetConstantCount();
- Assert(constCount > FunctionBody::FirstRegSlot);
- js_memcpy_s(dstSlots, (constCount - FunctionBody::FirstRegSlot) * sizeof(Var),
- this->GetConstTable(), (constCount - FunctionBody::FirstRegSlot) * sizeof(Var));
- }
- Var FunctionBody::GetConstantVar(RegSlot location)
- {
- Assert(this->GetConstTable());
- Assert(location < GetConstantCount());
- Assert(location != 0);
- return this->GetConstTable()[location - FunctionBody::FirstRegSlot];
- }
- #if DBG_DUMP
- void FunctionBody::Dump()
- {
- Js::ByteCodeDumper::Dump(this);
- }
- void FunctionBody::DumpScopes()
- {
- if(this->GetScopeObjectChain())
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("%s (%s) :\n"), this->GetDisplayName(), this->GetDebugNumberSet(debugStringBuffer));
- this->GetScopeObjectChain()->pScopeChain->Map( [=] (uint index, DebuggerScope* scope )
- {
- scope->Dump();
- });
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- void EntryPointInfo::DumpNativeOffsetMaps()
- {
- // Native Offsets
- if (this->HasNativeEntryPointData())
- {
- auto& nativeOffsetMaps = this->GetNativeEntryPointData()->GetNativeOffsetMaps();
- if (nativeOffsetMaps.Count() > 0)
- {
- Output::Print(_u("Native Map: baseAddr: 0x%0Ix, size: 0x%0Ix\nstatementId, offset range, address range\n"),
- this->GetNativeAddress(),
- this->GetCodeSize());
- int count = nativeOffsetMaps.Count();
- for (int i = 0; i < count; i++)
- {
- const NativeEntryPointData::NativeOffsetMap* map = &nativeOffsetMaps.Item(i);
- Output::Print(_u("S%4d, (%5d, %5d) (0x%012Ix, 0x%012Ix)\n"), map->statementIndex,
- map->nativeOffsetSpan.begin,
- map->nativeOffsetSpan.end,
- map->nativeOffsetSpan.begin + this->GetNativeAddress(),
- map->nativeOffsetSpan.end + this->GetNativeAddress());
- }
- }
- }
- }
- #endif
- void FunctionBody::DumpStatementMaps()
- {
- // Source Map to ByteCode
- StatementMapList * pStatementMaps = this->GetStatementMaps();
- if (pStatementMaps)
- {
- Output::Print(_u("Statement Map:\nstatementId, SourceSpan, ByteCodeSpan\n"));
- int count = pStatementMaps->Count();
- for(int i = 0; i < count; i++)
- {
- StatementMap* map = pStatementMaps->Item(i);
- Output::Print(_u("S%4d, (C%5d, C%5d) (B%5d, B%5d) Inner=%d\n"), i,
- map->sourceSpan.begin,
- map->sourceSpan.end,
- map->byteCodeSpan.begin,
- map->byteCodeSpan.end,
- map->isSubexpression);
- }
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- void EntryPointInfo::DumpNativeThrowSpanSequence()
- {
- // Native Throw Map
- SmallSpanSequence * nativeThrowSpanSequence = this->GetNativeEntryPointData()->GetNativeThrowSpanSequence();
- if (nativeThrowSpanSequence)
- {
- Output::Print(_u("Native Throw Map: baseAddr: 0x%0Ix, size: 0x%Ix\nstatementId, offset range, address range\n"),
- this->GetNativeAddress(),
- this->GetCodeSize());
- int count = nativeThrowSpanSequence->Count();
- SmallSpanSequenceIter iter;
- for (int i = 0; i < count; i++)
- {
- StatementData data;
- if (nativeThrowSpanSequence->Item(i, iter, data))
- {
- Output::Print(_u("S%4d, (%5d -----) (0x%012Ix --------)\n"), data.sourceBegin, // statementIndex
- data.bytecodeBegin, // nativeOffset
- data.bytecodeBegin + this->GetNativeAddress());
- }
- }
- }
- }
- #endif
- void FunctionBody::PrintStatementSourceLine(uint statementIndex)
- {
- if (m_isWasmFunction || this->GetUtf8SourceInfo()->GetIsLibraryCode())
- {
- // currently no source view support for wasm
- return;
- }
- const uint startOffset = GetStatementStartOffset(statementIndex);
- // startOffset should only be 0 if statementIndex is 0, otherwise it is EOF and we should skip printing anything
- if (startOffset != 0 || statementIndex == 0)
- {
- PrintStatementSourceLineFromStartOffset(startOffset);
- }
- }
- void FunctionBody::PrintStatementSourceLineFromStartOffset(uint cchStartOffset)
- {
- ULONG line;
- LONG col;
- LPCUTF8 source = GetStartOfDocument(_u("FunctionBody::PrintStatementSourceLineFromStartOffset"));
- Utf8SourceInfo* sourceInfo = this->GetUtf8SourceInfo();
- Assert(sourceInfo != nullptr);
- LPCUTF8 sourceInfoSrc = sourceInfo->GetSource(_u("FunctionBody::PrintStatementSourceLineFromStartOffset"));
- if(!sourceInfoSrc)
- {
- Assert(sourceInfo->GetIsLibraryCode());
- return;
- }
- if( source != sourceInfoSrc )
- {
- Output::Print(_u("\nDETECTED MISMATCH:\n"));
- Output::Print(_u("GetUtf8SourceInfo()->GetSource(): 0x%08X: %.*s ...\n"), sourceInfo, 16, sourceInfo);
- Output::Print(_u("GetStartOfDocument(): 0x%08X: %.*s ...\n"), source, 16, source);
- AssertMsg(false, "Non-matching start of document");
- }
- GetLineCharOffsetFromStartChar(cchStartOffset, &line, &col, false /*canAllocateLineCache*/);
- if (sourceInfo->GetSourceHolder() != ISourceHolder::GetEmptySourceHolder())
- {
- WORD color = 0;
- if (Js::Configuration::Global.flags.DumpLineNoInColor)
- {
- color = Output::SetConsoleForeground(12);
- }
- Output::Print(_u("\n\n Line %3d: "), line + 1);
- // Need to match up cchStartOffset to appropriate cbStartOffset given function's cbStartOffset and cchStartOffset
- size_t utf8SrcStartIdx = utf8::CharacterIndexToByteIndex(source, sourceInfo->GetCbLength(), cchStartOffset, this->m_cbStartOffset, this->m_cchStartOffset);
- size_t utf8SrcEndIdx = StartOffset() + LengthInBytes();
- char16* utf16Buf = HeapNewArray(char16, utf8SrcEndIdx - utf8SrcStartIdx + 2);
- size_t utf16BufSz = utf8::DecodeUnitsIntoAndNullTerminateNoAdvance(utf16Buf, source + utf8SrcStartIdx, source + utf8SrcEndIdx, utf8::DecodeOptions::doDefault);
- Assert(utf16BufSz <= utf8SrcEndIdx - utf8SrcStartIdx);
- for (size_t i = 0; i < utf16BufSz && utf16Buf[i] != _u('\n') && utf16Buf[i] != _u('\r'); i++)
- {
- Output::Print(_u("%lc"), utf16Buf[i]);
- }
- HeapDeleteArray(utf8SrcEndIdx - utf8SrcStartIdx + 2, utf16Buf);
- Output::Print(_u("\n"));
- Output::Print(_u(" Col %4d:%s^\n"), col + 1, ((col+1)<10000) ? _u(" ") : _u(""));
- if (color != 0)
- {
- Output::SetConsoleForeground(color);
- }
- }
- }
- #endif // DBG_DUMP
- /**
- * Get the source code offset for the given <statementIndex>.
- */
- uint FunctionBody::GetStatementStartOffset(const uint statementIndex)
- {
- uint startOffset = 0;
- if (statementIndex != Js::Constants::NoStatementIndex)
- {
- const Js::FunctionBody::SourceInfo * sourceInfo = &(this->m_sourceInfo);
- if (sourceInfo->pSpanSequence != nullptr)
- {
- Js::SmallSpanSequenceIter iter;
- sourceInfo->pSpanSequence->Reset(iter);
- Js::StatementData data;
- sourceInfo->pSpanSequence->Item(statementIndex, iter, data);
- startOffset = data.sourceBegin;
- }
- else
- {
- int index = statementIndex;
- Js::FunctionBody::StatementMap * statementMap = GetNextNonSubexpressionStatementMap(GetStatementMaps(), index);
- startOffset = statementMap->sourceSpan.Begin();
- }
- }
- return startOffset;
- }
- #ifdef IR_VIEWER
- /* BEGIN potentially reusable code */
- /*
- This code could be reused for locating source code in a debugger or to
- retrieve the text of source statements.
- Currently this code is used to retrieve the text of a source code statement
- in the IR_VIEWER feature.
- */
- /**
- * Given a statement's starting offset in the source code, calculate the beginning and end of a statement,
- * as well as the line and column number where the statement appears.
- *
- * @param startOffset (input) The offset into the source code where this statement begins.
- * @param sourceBegin (output) The beginning of the statement in the source string.
- * @param sourceEnd (output) The end of the statement in the source string.
- * @param line (output) The line number where the statement appeared in the source.
- * @param col (output) The column number where the statement appeared in the source.
- */
- void FunctionBody::GetSourceLineFromStartOffset(const uint startOffset, LPCUTF8 *sourceBegin, LPCUTF8 *sourceEnd,
- ULONG * line, LONG * col)
- {
- //
- // get source info
- //
- LPCUTF8 source = GetStartOfDocument(_u("IR Viewer FunctionBody::GetSourceLineFromStartOffset"));
- Utf8SourceInfo* sourceInfo = this->GetUtf8SourceInfo();
- Assert(sourceInfo != nullptr);
- LPCUTF8 sourceInfoSrc = sourceInfo->GetSource(_u("IR Viewer FunctionBody::GetSourceLineFromStartOffset"));
- if (!sourceInfoSrc)
- {
- Assert(sourceInfo->GetIsLibraryCode());
- return;
- }
- if (source != sourceInfoSrc)
- {
- Output::Print(_u("\nDETECTED MISMATCH:\n"));
- Output::Print(_u("GetUtf8SourceInfo()->GetSource(): 0x%08X: %.*s ...\n"), sourceInfo, 16, sourceInfo);
- Output::Print(_u("GetStartOfDocument(): 0x%08X: %.*s ...\n"), source, 16, source);
- AssertMsg(false, "Non-matching start of document");
- }
- //
- // calculate source line info
- //
- size_t cbStartOffset = utf8::CharacterIndexToByteIndex(source, sourceInfo->GetCbLength(), (const charcount_t)startOffset, (size_t)this->m_cbStartOffset, (charcount_t)this->m_cchStartOffset);
- GetLineCharOffsetFromStartChar(startOffset, line, col);
- size_t lastOffset = StartOffset() + LengthInBytes();
- size_t i = 0;
- for (i = cbStartOffset; i < lastOffset && source[i] != '\n' && source[i] != '\r'; i++)
- {
- // do nothing; scan until end of statement
- }
- size_t cbEndOffset = i;
- //
- // return
- //
- *sourceBegin = &source[cbStartOffset];
- *sourceEnd = &source[cbEndOffset];
- }
- /**
- * Given a statement index and output parameters, calculate the beginning and end of a statement,
- * as well as the line and column number where the statement appears.
- *
- * @param statementIndex (input) The statement's index (as used by the StatementBoundary pragma).
- * @param sourceBegin (output) The beginning of the statement in the source string.
- * @param sourceEnd (output) The end of the statement in the source string.
- * @param line (output) The line number where the statement appeared in the source.
- * @param col (output) The column number where the statement appeared in the source.
- */
- void FunctionBody::GetStatementSourceInfo(const uint statementIndex, LPCUTF8 *sourceBegin, LPCUTF8 *sourceEnd,
- ULONG * line, LONG * col)
- {
- const size_t startOffset = GetStatementStartOffset(statementIndex);
- // startOffset should only be 0 if statementIndex is 0, otherwise it is EOF and we should return empty string
- if (startOffset != 0 || statementIndex == 0)
- {
- GetSourceLineFromStartOffset(startOffset, sourceBegin, sourceEnd, line, col);
- }
- else
- {
- *sourceBegin = nullptr;
- *sourceEnd = nullptr;
- *line = 0;
- *col = 0;
- return;
- }
- }
- /* END potentially reusable code */
- #endif /* IR_VIEWER */
- #if ENABLE_TTD
- void FunctionBody::GetSourceLineFromStartOffset_TTD(const uint startOffset, ULONG* line, LONG* col)
- {
- GetLineCharOffsetFromStartChar(startOffset, line, col);
- }
- #endif
- #ifdef IR_VIEWER
- Js::DynamicObject * FunctionBody::GetIRDumpBaseObject()
- {
- if (!this->m_irDumpBaseObject)
- {
- this->m_irDumpBaseObject = this->m_scriptContext->GetLibrary()->CreateObject();
- }
- return this->m_irDumpBaseObject;
- }
- #endif /* IR_VIEWER */
- #if ENABLE_NATIVE_CODEGEN
- #ifdef VTUNE_PROFILING
- #include "jitprofiling.h"
- int EntryPointInfo::GetNativeOffsetMapCount() const
- {
- return this->GetNativeEntryPointData()->GetNativeOffsetMaps().Count();
- }
- uint EntryPointInfo::PopulateLineInfo(void* pInfo, FunctionBody* body)
- {
- auto& nativeOffsetMaps = this->GetNativeEntryPointData()->GetNativeOffsetMaps();
- LineNumberInfo* pLineInfo = (LineNumberInfo*)pInfo;
- ULONG functionLineNumber = body->GetLineNumber();
- pLineInfo[0].Offset = 0;
- pLineInfo[0].LineNumber = functionLineNumber;
- int lineNumber = 0;
- int j = 1; // start with 1 since offset 0 has already been populated with function line number
- int count = nativeOffsetMaps.Count();
- for(int i = 0; i < count; i++)
- {
- const NativeEntryPointData::NativeOffsetMap* map = &nativeOffsetMaps.Item(i);
- uint32 statementIndex = map->statementIndex;
- if (statementIndex == 0)
- {
- // statementIndex is 0, first line in the function, populate with function line number
- pLineInfo[j].Offset = map->nativeOffsetSpan.begin;
- pLineInfo[j].LineNumber = functionLineNumber;
- j++;
- }
- lineNumber = body->GetSourceLineNumber(statementIndex);
- if (lineNumber != 0)
- {
- pLineInfo[j].Offset = map->nativeOffsetSpan.end;
- pLineInfo[j].LineNumber = lineNumber;
- j++;
- }
- }
- return j;
- }
- ULONG FunctionBody::GetSourceLineNumber(uint statementIndex)
- {
- ULONG line = 0;
- if (statementIndex != Js::Constants::NoStatementIndex)
- {
- uint startOffset = GetStartOffset(statementIndex);
- if (startOffset != 0 || statementIndex == 0)
- {
- GetLineCharOffsetFromStartChar(startOffset, &line, nullptr, false /*canAllocateLineCache*/);
- line = line + 1;
- }
- }
- return line;
- }
- uint FunctionBody::GetStartOffset(uint statementIndex) const
- {
- uint startOffset = 0;
- const Js::FunctionBody::SourceInfo * sourceInfo = &this->m_sourceInfo;
- if (sourceInfo->pSpanSequence != nullptr)
- {
- Js::SmallSpanSequenceIter iter;
- sourceInfo->pSpanSequence->Reset(iter);
- Js::StatementData data;
- sourceInfo->pSpanSequence->Item(statementIndex, iter, data);
- startOffset = data.sourceBegin;
- }
- else
- {
- int index = statementIndex;
- Js::FunctionBody::StatementMap * statementMap = GetNextNonSubexpressionStatementMap(GetStatementMaps(), index);
- startOffset = statementMap->sourceSpan.Begin();
- }
- return startOffset;
- }
- #endif
- #endif
- void ParseableFunctionInfo::SetIsNonUserCode(bool set)
- {
- // Mark current function as a non-user code, so that we can distinguish cases where exceptions are
- // caught in non-user code (see ProbeContainer::HasAllowedForException).
- SetFlags(set, Flags_NonUserCode);
- // Propagate setting for all functions in this scope (nested).
- this->ForEachNestedFunc([&](FunctionProxy* proxy, uint32 index)
- {
- ParseableFunctionInfo * pBody = proxy->GetParseableFunctionInfo();
- if (pBody != nullptr)
- {
- pBody->SetIsNonUserCode(set);
- }
- return true;
- });
- }
- void FunctionBody::InsertSymbolToRegSlotList(JsUtil::CharacterBuffer<WCHAR> const& propName, RegSlot reg, RegSlot totalRegsCount)
- {
- if (totalRegsCount > 0)
- {
- PropertyId propertyId = GetOrAddPropertyIdTracked(propName);
- InsertSymbolToRegSlotList(reg, propertyId, totalRegsCount);
- }
- }
- void FunctionBody::InsertSymbolToRegSlotList(RegSlot reg, PropertyId propertyId, RegSlot totalRegsCount)
- {
- if (totalRegsCount > 0)
- {
- if (this->GetPropertyIdOnRegSlotsContainer() == nullptr)
- {
- this->SetPropertyIdOnRegSlotsContainer(PropertyIdOnRegSlotsContainer::New(m_scriptContext->GetRecycler()));
- }
- if (this->GetPropertyIdOnRegSlotsContainer()->propertyIdsForRegSlots == nullptr)
- {
- this->GetPropertyIdOnRegSlotsContainer()->CreateRegSlotsArray(m_scriptContext->GetRecycler(), totalRegsCount);
- }
- Assert(this->GetPropertyIdOnRegSlotsContainer() != nullptr);
- this->GetPropertyIdOnRegSlotsContainer()->Insert(reg, propertyId);
- }
- }
- void FunctionBody::SetPropertyIdsOfFormals(PropertyIdArray * formalArgs)
- {
- Assert(formalArgs);
- if (this->GetPropertyIdOnRegSlotsContainer() == nullptr)
- {
- this->SetPropertyIdOnRegSlotsContainer(PropertyIdOnRegSlotsContainer::New(m_scriptContext->GetRecycler()));
- }
- this->GetPropertyIdOnRegSlotsContainer()->SetFormalArgs(formalArgs);
- }
- #ifdef ENABLE_SCRIPT_PROFILING
- HRESULT FunctionBody::RegisterFunction(BOOL fChangeMode, BOOL fOnlyCurrent)
- {
- if (!this->IsFunctionParsed())
- {
- return S_OK;
- }
- HRESULT hr = this->ReportFunctionCompiled();
- if (FAILED(hr))
- {
- return hr;
- }
- if (fChangeMode)
- {
- this->SetEntryToProfileMode();
- }
- if (!fOnlyCurrent)
- {
- for (uint uIndex = 0; uIndex < this->GetNestedCount(); uIndex++)
- {
- Js::ParseableFunctionInfo * pBody = this->GetNestedFunctionForExecution(uIndex);
- if (pBody == nullptr || !pBody->IsFunctionParsed())
- {
- continue;
- }
- hr = pBody->GetFunctionBody()->RegisterFunction(fChangeMode);
- if (FAILED(hr))
- {
- break;
- }
- }
- }
- return hr;
- }
- HRESULT FunctionBody::ReportScriptCompiled()
- {
- AssertMsg(m_scriptContext != nullptr, "Script Context is null when reporting function information");
- PROFILER_SCRIPT_TYPE type = IsDynamicScript() ? PROFILER_SCRIPT_TYPE_DYNAMIC : PROFILER_SCRIPT_TYPE_USER;
- IDebugDocumentContext *pDebugDocumentContext = nullptr;
- this->m_scriptContext->GetDocumentContext(this, &pDebugDocumentContext);
- HRESULT hr = m_scriptContext->OnScriptCompiled((PROFILER_TOKEN) this->GetUtf8SourceInfo()->GetSourceInfoId(), type, pDebugDocumentContext);
- RELEASEPTR(pDebugDocumentContext);
- return hr;
- }
- HRESULT FunctionBody::ReportFunctionCompiled()
- {
- // Some assumptions by Logger interface.
- // to send NULL as a name in case the name is anonymous and hint is anonymous code.
- const char16 *pwszName = GetExternalDisplayName();
- IDebugDocumentContext *pDebugDocumentContext = nullptr;
- this->m_scriptContext->GetDocumentContext(this, &pDebugDocumentContext);
- SetHasFunctionCompiledSent(true);
- HRESULT hr = m_scriptContext->OnFunctionCompiled(m_functionNumber, (PROFILER_TOKEN) this->GetUtf8SourceInfo()->GetSourceInfoId(), pwszName, nullptr, pDebugDocumentContext);
- RELEASEPTR(pDebugDocumentContext);
- #if DBG
- if (m_iProfileSession >= m_scriptContext->GetProfileSession())
- {
- OUTPUT_TRACE_DEBUGONLY(Js::ScriptProfilerPhase, _u("FunctionBody::ReportFunctionCompiled, Duplicate compile event (%d < %d) for FunctionNumber : %d\n"),
- m_iProfileSession, m_scriptContext->GetProfileSession(), m_functionNumber);
- }
- AssertMsg(m_iProfileSession < m_scriptContext->GetProfileSession(), "Duplicate compile event sent");
- m_iProfileSession = m_scriptContext->GetProfileSession();
- #endif
- return hr;
- }
- void FunctionBody::SetEntryToProfileMode()
- {
- #if ENABLE_NATIVE_CODEGEN
- AssertMsg(this->m_scriptContext->CurrentThunk == ProfileEntryThunk, "ScriptContext not in profile mode");
- #if DBG
- AssertMsg(m_iProfileSession == m_scriptContext->GetProfileSession(), "Changing mode to profile for function that didn't send compile event");
- #endif
- // This is always done when bg thread is paused hence we don't need any kind of thread-synchronization at this point.
- // Change entry points to Profile Thunk
- // If the entrypoint is CodeGenOnDemand or CodeGen - then we don't change the entry points
- ProxyEntryPointInfo* defaultEntryPointInfo = this->GetDefaultEntryPointInfo();
- if (!IsIntermediateCodeGenThunk(defaultEntryPointInfo->jsMethod)
- && defaultEntryPointInfo->jsMethod != DynamicProfileInfo::EnsureDynamicProfileInfoThunk)
- {
- if (this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredParsingThunk)
- {
- defaultEntryPointInfo->jsMethod = ProfileDeferredParsingThunk;
- }
- else if (this->GetOriginalEntryPoint_Unchecked() == DefaultDeferredDeserializeThunk)
- {
- defaultEntryPointInfo->jsMethod = ProfileDeferredDeserializeThunk;
- }
- else
- {
- defaultEntryPointInfo->jsMethod = ProfileEntryThunk;
- }
- }
- // Update old entry points on the deferred prototype type so that they match current defaultEntryPointInfo.
- // to make sure that new JavascriptFunction instances use profile thunk.
- if (this->deferredPrototypeType)
- {
- this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
- this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
- }
- if (this->undeferredFunctionType)
- {
- this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
- this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
- }
- #if DBG
- if (!this->HasValidEntryPoint())
- {
- OUTPUT_TRACE_DEBUGONLY(Js::ScriptProfilerPhase, _u("FunctionBody::SetEntryToProfileMode, Assert due to HasValidEntryPoint(), directEntrypoint : 0x%0IX, originalentrypoint : 0x%0IX\n"),
- this->GetDefaultEntryPointInfo()->jsMethod, this->GetOriginalEntryPoint());
- AssertMsg(false, "Not a valid EntryPoint");
- }
- #endif
- #endif //ENABLE_NATIVE_CODEGEN
- }
- #endif // ENABLE_SCRIPT_PROFILING
- #if DBG
- void FunctionBody::MustBeInDebugMode()
- {
- Assert(GetUtf8SourceInfo()->IsInDebugMode());
- Assert(m_sourceInfo.pSpanSequence == nullptr);
- Assert(this->GetStatementMaps() != nullptr);
- }
- #endif
- void ParseableFunctionInfo::CleanupToReparse()
- {
- #if DBG
- if (this->IsFunctionBody())
- {
- GetFunctionBody()->UnlockCounters();
- }
- #endif
- // The current function is already compiled. In order to prep this function to ready for debug mode, most of the previous information need to be thrown away.
- // Clean up the nested functions
- this->ForEachNestedFunc([&](FunctionProxy* proxy, uint32 index)
- {
- // Note: redeferred functions may have fully compiled children. If we find a redeferred function, keep walking.
- if (proxy && ((proxy->CanBeDeferred() && proxy->GetFunctionInfo()->GetCompileCount() > 0) || proxy->IsFunctionBody()))
- {
- proxy->GetParseableFunctionInfo()->CleanupToReparse();
- }
- return true;
- });
- this->CleanupToReparseHelper();
- if (this->IsFunctionBody())
- {
- this->GetFunctionBody()->CleanupToReparseHelper();
- }
- }
- void FunctionBody::CleanupToReparseHelper()
- {
- this->CleanupRecyclerData(/* isShutdown */ false, true /* capture entry point cleanup stack trace */);
- this->entryPoints->ClearAndZero();
- // Store the originalEntryPoint to restore it back immediately.
- this->CreateNewDefaultEntryPoint();
- this->SetOriginalEntryPoint(this->GetScriptContext()->CurrentThunk);
- if (this->m_defaultEntryPointInfo)
- {
- this->GetDefaultFunctionEntryPointInfo()->entryPointIndex = 0;
- }
- this->SetAuxiliaryData(nullptr);
- this->SetAuxiliaryContextData(nullptr);
- AssertMsg(!this->byteCodeBlock || !this->IsWasmFunction(), "We should never reset the bytecode block for Wasm");
- this->byteCodeBlock = nullptr;
- this->SetLoopHeaderArray(nullptr);
- this->SetConstTable(nullptr);
- this->SetCodeGenRuntimeData(nullptr);
- this->cacheIdToPropertyIdMap = nullptr;
- this->SetFormalsPropIdArray(nullptr);
- this->SetReferencedPropertyIdMap(nullptr);
- this->SetLiteralRegexs(nullptr);
- this->SetSlotIdInCachedScopeToNestedIndexArray(nullptr);
- this->SetStatementMaps(nullptr);
- this->SetCodeGenGetSetRuntimeData(nullptr);
- this->SetPropertyIdOnRegSlotsContainer(nullptr);
- this->profiledLdLenCount = 0;
- this->profiledLdElemCount = 0;
- this->profiledStElemCount = 0;
- this->profiledCallSiteCount = 0;
- this->profiledArrayCallSiteCount = 0;
- this->profiledDivOrRemCount = 0;
- this->profiledSwitchCount = 0;
- this->profiledReturnTypeCount = 0;
- this->profiledSlotCount = 0;
- this->SetLoopCount(0);
- this->m_envDepth = (uint16)-1;
- this->SetByteCodeCount(0);
- this->SetByteCodeWithoutLDACount(0);
- this->SetByteCodeInLoopCount(0);
- #if ENABLE_PROFILE_INFO
- if (this->dynamicProfileInfo != nullptr)
- {
- SourceContextInfo * sourceContextInfo = GetSourceContextInfo();
- if(sourceContextInfo && sourceContextInfo->sourceDynamicProfileManager)
- {
- sourceContextInfo->sourceDynamicProfileManager->RemoveDynamicProfileInfo(GetFunctionInfo()->GetLocalFunctionId());
- }
- #ifdef DYNAMIC_PROFILE_STORAGE
- DynamicProfileInfoList * profileInfoList = GetScriptContext()->GetProfileInfoList();
- if (profileInfoList)
- {
- FOREACH_SLISTBASE_ENTRY_EDITING(Field(DynamicProfileInfo*), info, profileInfoList, iter)
- {
- if (info->HasFunctionBody() && info->GetFunctionBody() == this)
- {
- iter.UnlinkCurrent();
- break;
- }
- }
- NEXT_SLISTBASE_ENTRY_EDITING;
- }
- #endif
- this->dynamicProfileInfo = nullptr;
- }
- #endif
- this->hasExecutionDynamicProfileInfo = false;
- this->SetFirstTmpRegister(Constants::NoRegister);
- this->SetVarCount(0);
- this->SetConstantCount(0);
- this->SetLocalClosureRegister(Constants::NoRegister);
- this->SetParamClosureRegister(Constants::NoRegister);
- this->SetLocalFrameDisplayRegister(Constants::NoRegister);
- this->SetEnvRegister(Constants::NoRegister);
- this->SetThisRegisterForEventHandler(Constants::NoRegister);
- this->SetFirstInnerScopeRegister(Constants::NoRegister);
- this->SetFuncExprScopeRegister(Constants::NoRegister);
- this->SetInnerScopeCount(0);
- this->hasCachedScopePropIds = false;
- this->ResetObjectLiteralTypes();
- this->SetInlineCacheCount(0);
- this->SetRootObjectLoadInlineCacheStart(0);
- this->SetRootObjectLoadMethodInlineCacheStart(0);
- this->SetRootObjectStoreInlineCacheStart(0);
- this->SetIsInstInlineCacheCount(0);
- this->m_inlineCachesOnFunctionObject = false;
- this->SetReferencedPropertyIdCount(0);
- #if ENABLE_PROFILE_INFO
- this->SetPolymorphicCallSiteInfoHead(nullptr);
- #endif
- this->executionState.SetInterpretedCount(0);
- this->m_hasDoneAllNonLocalReferenced = false;
- this->SetDebuggerScopeIndex(0);
- this->m_isAsmJsScheduledForFullJIT = false;
- this->m_asmJsTotalLoopCount = 0;
- recentlyBailedOutOfJittedLoopBody = false;
- SetLoopInterpreterLimit(CONFIG_FLAG(LoopInterpretCount));
- ReinitializeExecutionModeAndLimits();
- Assert(this->m_sourceInfo.m_probeCount == 0);
- this->m_sourceInfo.m_probeBackingBlock = nullptr;
- #if DBG
- // This could be non-zero if the function threw exception before. Reset it.
- this->m_DEBUG_executionCount = 0;
- #endif
- if (this->m_sourceInfo.pSpanSequence != nullptr)
- {
- HeapDelete(this->m_sourceInfo.pSpanSequence);
- this->m_sourceInfo.pSpanSequence = nullptr;
- }
- if (this->m_sourceInfo.m_auxStatementData != nullptr)
- {
- // This must be consistent with how we allocate the data for this and inner structures.
- // We are using recycler, thus it's enough just to set to NULL.
- Assert(m_scriptContext->GetRecycler()->IsValidObject(m_sourceInfo.m_auxStatementData));
- m_sourceInfo.m_auxStatementData = nullptr;
- }
- }
- void ParseableFunctionInfo::CleanupToReparseHelper()
- {
- #if DYNAMIC_INTERPRETER_THUNK
- if (m_isAsmJsFunction && m_dynamicInterpreterThunk)
- {
- m_scriptContext->ReleaseDynamicAsmJsInterpreterThunk((BYTE*)this->m_dynamicInterpreterThunk, true);
- this->m_dynamicInterpreterThunk = nullptr;
- }
- #endif
- this->SetScopeInfo(nullptr);
- this->SetPropertyIdsForScopeSlotArray(nullptr, 0);
- this->GetUtf8SourceInfo()->DeleteLineOffsetCache();
- // Reset to default.
- this->flags = this->IsClassConstructor() ? Flags_None : Flags_HasNoExplicitReturnValue;
- ResetInParams();
- this->m_isAsmjsMode = false;
- this->m_isAsmJsFunction = false;
- }
- #ifdef ENABLE_SCRIPT_DEBUGGING
- void FunctionBody::SetEntryToDeferParseForDebugger()
- {
- ProxyEntryPointInfo* defaultEntryPointInfo = this->GetDefaultEntryPointInfo();
- if (defaultEntryPointInfo->jsMethod != DefaultDeferredParsingThunk
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- && defaultEntryPointInfo->jsMethod != ProfileDeferredParsingThunk
- #endif
- )
- {
- #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
- // Just change the thunk, the cleanup will be done once the function gets called.
- if (this->m_scriptContext->CurrentThunk == ProfileEntryThunk)
- {
- defaultEntryPointInfo->jsMethod = ProfileDeferredParsingThunk;
- }
- else
- #endif
- {
- defaultEntryPointInfo->jsMethod = DefaultDeferredParsingThunk;
- }
- this->SetOriginalEntryPoint(DefaultDeferredParsingThunk);
- this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
- }
- // Set other state back to before parse as well
- this->SetStackNestedFunc(false);
- this->SetAuxPtr<AuxPointerType::StackNestedFuncParent>(nullptr);
- this->SetReparsed(true);
- #if DBG
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- OUTPUT_VERBOSE_TRACE(Js::DebuggerPhase, _u("Regenerate Due To Debug Mode: function %s (%s) from script context %p\n"),
- this->GetDisplayName(), this->GetDebugNumberSet(debugStringBuffer), m_scriptContext);
- this->UnlockCounters(); // asuming background jit is stopped and allow the counter setters access again
- #endif
- }
- #endif
- void FunctionBody::ClearEntryPoints()
- {
- if (this->entryPoints)
- {
- this->MapEntryPoints([] (int index, FunctionEntryPointInfo* entryPoint)
- {
- if (nullptr != entryPoint)
- {
- // Finalize = Free up work item if it hasn't been released yet + entry point clean up
- // isShutdown is false because cleanup is called only in the !isShutdown case
- entryPoint->Finalize(/*isShutdown*/ false);
- }
- });
- this->MapLoopHeaders([] (uint loopNumber, LoopHeader* header)
- {
- header->MapEntryPoints([] (int index, LoopEntryPointInfo* entryPoint)
- {
- entryPoint->Cleanup(/*isShutdown*/ false, true /* capture cleanup stack */);
- });
- });
- }
- this->entryPoints->ClearAndZero();
- }
- //
- // For library code all references to jitted entry points need to be removed
- //
- void FunctionBody::ResetEntryPoint()
- {
- ClearEntryPoints();
- this->CreateNewDefaultEntryPoint();
- this->SetOriginalEntryPoint(DefaultEntryThunk);
- m_defaultEntryPointInfo->jsMethod = m_scriptContext->CurrentThunk;
- if (this->deferredPrototypeType)
- {
- // Update old entry points on the deferred prototype type,
- // as they may point to old native code gen regions which age gone now.
- this->deferredPrototypeType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
- this->deferredPrototypeType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
- }
- if (this->undeferredFunctionType)
- {
- this->undeferredFunctionType->SetEntryPoint(this->GetDefaultEntryPointInfo()->jsMethod);
- this->undeferredFunctionType->SetEntryPointInfo(this->GetDefaultEntryPointInfo());
- }
- ReinitializeExecutionModeAndLimits();
- }
- void FunctionBody::AddDeferParseAttribute()
- {
- this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() | FunctionInfo::Attributes::DeferredParse));
- }
- void FunctionBody::RemoveDeferParseAttribute()
- {
- this->SetAttributes((FunctionInfo::Attributes) (this->GetAttributes() & (~FunctionInfo::Attributes::DeferredParse)));
- }
- Js::DebuggerScope * FunctionBody::GetDiagCatchScopeObjectAt(int byteCodeOffset)
- {
- if (GetScopeObjectChain())
- {
- for (int i = 0; i < GetScopeObjectChain()->pScopeChain->Count(); i++)
- {
- Js::DebuggerScope *debuggerScope = GetScopeObjectChain()->pScopeChain->Item(i);
- Assert(debuggerScope);
- if (debuggerScope->IsCatchScope() && debuggerScope->IsOffsetInScope(byteCodeOffset))
- {
- return debuggerScope;
- }
- }
- }
- return nullptr;
- }
- ushort SmallSpanSequence::GetDiff(int current, int prev)
- {
- int diff = current - prev;
- if ((diff) < SHRT_MIN || (diff) >= SHRT_MAX)
- {
- diff = SHRT_MAX;
- if (!this->pActualOffsetList)
- {
- this->pActualOffsetList = JsUtil::GrowingUint32HeapArray::Create(4);
- }
- this->pActualOffsetList->Add(current);
- }
- return (ushort)diff;
- }
- // Get Values of the beginning of the statement at particular index.
- BOOL SmallSpanSequence::GetRangeAt(int index, SmallSpanSequenceIter &iter, int * pCountOfMissed, StatementData & data)
- {
- Assert((uint32)index < pStatementBuffer->Count());
- SmallSpan span(pStatementBuffer->ItemInBuffer((uint32)index));
- int countOfMissed = 0;
- if ((short)span.sourceBegin == SHRT_MAX)
- {
- // Look in ActualOffset store
- Assert(this->pActualOffsetList);
- Assert(this->pActualOffsetList->Count() > 0);
- Assert(this->pActualOffsetList->Count() > (uint32)iter.indexOfActualOffset);
- data.sourceBegin = this->pActualOffsetList->ItemInBuffer((uint32)iter.indexOfActualOffset);
- countOfMissed++;
- }
- else
- {
- data.sourceBegin = iter.accumulatedSourceBegin + (short)span.sourceBegin;
- }
- if (span.bytecodeBegin == SHRT_MAX)
- {
- // Look in ActualOffset store
- Assert(this->pActualOffsetList);
- Assert(this->pActualOffsetList->Count() > 0);
- Assert(this->pActualOffsetList->Count() > (uint32)(iter.indexOfActualOffset + countOfMissed));
- data.bytecodeBegin = this->pActualOffsetList->ItemInBuffer((uint32)iter.indexOfActualOffset + countOfMissed);
- countOfMissed++;
- }
- else
- {
- data.bytecodeBegin = iter.accumulatedBytecodeBegin + span.bytecodeBegin;
- }
- if (pCountOfMissed)
- {
- *pCountOfMissed = countOfMissed;
- }
- return TRUE;
- }
- void SmallSpanSequence::Reset(SmallSpanSequenceIter &iter)
- {
- iter.accumulatedIndex = 0;
- iter.accumulatedSourceBegin = baseValue;
- iter.accumulatedBytecodeBegin = 0;
- iter.indexOfActualOffset = 0;
- }
- BOOL SmallSpanSequence::GetMatchingStatementFromBytecode(int bytecode, SmallSpanSequenceIter &iter, StatementData & data)
- {
- if (Count() > 0 && bytecode >= 0)
- {
- // Support only in forward direction
- if (bytecode < iter.accumulatedBytecodeBegin
- || iter.accumulatedIndex <= 0 || (uint32)iter.accumulatedIndex >= Count())
- {
- // re-initialize the accumulators
- Reset(iter);
- }
- while ((uint32)iter.accumulatedIndex < Count())
- {
- int countOfMissed = 0;
- if (!GetRangeAt(iter.accumulatedIndex, iter, &countOfMissed, data))
- {
- Assert(FALSE);
- break;
- }
- if (data.bytecodeBegin >= bytecode)
- {
- if (data.bytecodeBegin > bytecode)
- {
- // Not exactly at the current bytecode, so it falls in between previous statement.
- data.sourceBegin = iter.accumulatedSourceBegin;
- data.bytecodeBegin = iter.accumulatedBytecodeBegin;
- }
- return TRUE;
- }
- // Look for the next
- iter.accumulatedSourceBegin = data.sourceBegin;
- iter.accumulatedBytecodeBegin = data.bytecodeBegin;
- iter.accumulatedIndex++;
- if (countOfMissed)
- {
- iter.indexOfActualOffset += countOfMissed;
- }
- }
- if (iter.accumulatedIndex != -1)
- {
- // Give the last one.
- Assert(data.bytecodeBegin < bytecode);
- return TRUE;
- }
- }
- // Failed to give the correct one, init to default
- iter.accumulatedIndex = -1;
- return FALSE;
- }
- BOOL SmallSpanSequence::Item(int index, SmallSpanSequenceIter &iter, StatementData & data)
- {
- if (!pStatementBuffer || (uint32)index >= pStatementBuffer->Count())
- {
- return FALSE;
- }
- if (iter.accumulatedIndex <= 0 || iter.accumulatedIndex > index)
- {
- Reset(iter);
- }
- while (iter.accumulatedIndex <= index)
- {
- Assert((uint32)iter.accumulatedIndex < pStatementBuffer->Count());
- int countOfMissed = 0;
- if (!GetRangeAt(iter.accumulatedIndex, iter, &countOfMissed, data))
- {
- Assert(FALSE);
- break;
- }
- // We store the next index
- iter.accumulatedSourceBegin = data.sourceBegin;
- iter.accumulatedBytecodeBegin = data.bytecodeBegin;
- iter.accumulatedIndex++;
- if (countOfMissed)
- {
- iter.indexOfActualOffset += countOfMissed;
- }
- if ((iter.accumulatedIndex - 1) == index)
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- BOOL SmallSpanSequence::Seek(int index, StatementData & data)
- {
- // This method will not alter any state of the variables, so this will just do plain search
- // from the beginning to look for that index.
- SmallSpanSequenceIter iter;
- Reset(iter);
- return Item(index, iter, data);
- }
- PropertyIdOnRegSlotsContainer * PropertyIdOnRegSlotsContainer::New(Recycler * recycler)
- {
- return RecyclerNew(recycler, PropertyIdOnRegSlotsContainer);
- }
- PropertyIdOnRegSlotsContainer::PropertyIdOnRegSlotsContainer()
- : propertyIdsForRegSlots(nullptr), length(0), propertyIdsForFormalArgs(nullptr), formalsUpperBound(Js::Constants::NoRegister)
- {
- }
- void PropertyIdOnRegSlotsContainer::CreateRegSlotsArray(Recycler * recycler, uint _length)
- {
- Assert(propertyIdsForRegSlots == nullptr);
- propertyIdsForRegSlots = RecyclerNewArrayLeafZ(recycler, PropertyId, _length);
- length = _length;
- }
- void PropertyIdOnRegSlotsContainer::SetFormalArgs(PropertyIdArray * formalArgs)
- {
- propertyIdsForFormalArgs = formalArgs;
- }
- //
- // Helper methods for PropertyIdOnRegSlotsContainer
- void PropertyIdOnRegSlotsContainer::Insert(RegSlot reg, PropertyId propId)
- {
- //
- // Reg is being used as an index;
- Assert(propertyIdsForRegSlots);
- Assert(reg < length);
- //
- // the current reg is unaccounted for const reg count. while fetching calculate the actual regslot value.
- Assert(propertyIdsForRegSlots[reg] == 0 || propertyIdsForRegSlots[reg] == propId);
- propertyIdsForRegSlots[reg] = propId;
- }
- void PropertyIdOnRegSlotsContainer::FetchItemAt(uint index, FunctionBody *pFuncBody, __out PropertyId *pPropId, __out RegSlot *pRegSlot)
- {
- Assert(index < length);
- Assert(pPropId);
- Assert(pRegSlot);
- Assert(pFuncBody);
- *pPropId = propertyIdsForRegSlots[index];
- *pRegSlot = pFuncBody->MapRegSlot(index);
- }
- bool PropertyIdOnRegSlotsContainer::IsRegSlotFormal(RegSlot reg)
- {
- if (propertyIdsForFormalArgs != nullptr && reg < length)
- {
- PropertyId propId = propertyIdsForRegSlots[reg];
- for (uint32 i = 0; i < propertyIdsForFormalArgs->count; i++)
- {
- if (propertyIdsForFormalArgs->elements[i] == propId)
- {
- return true;
- }
- }
- }
- return false;
- }
- ScopeType FrameDisplay::GetScopeType(void* scope)
- {
- if(Js::VarIs<Js::ActivationObject>(scope))
- {
- return ScopeType_ActivationObject;
- }
- if(Js::ScopeSlots::Is(scope))
- {
- return ScopeType_SlotArray;
- }
- return ScopeType_WithScope;
- }
- // ScopeSlots
- bool ScopeSlots::IsDebuggerScopeSlotArray()
- {
- return DebuggerScope::Is(slotArray[ScopeMetadataSlotIndex]);
- }
- // DebuggerScope
- bool DebuggerScope::Is(void* ptr)
- {
- if (!ptr)
- {
- return false;
- }
- return VirtualTableInfo<DebuggerScope>::HasVirtualTable(ptr);
- }
- // Get the sibling for the current debugger scope.
- DebuggerScope * DebuggerScope::GetSiblingScope(RegSlot location, FunctionBody *functionBody)
- {
- bool isBlockSlotOrObject = scopeType == Js::DiagExtraScopesType::DiagBlockScopeInSlot || scopeType == Js::DiagExtraScopesType::DiagBlockScopeInObject;
- bool isCatchSlotOrObject = scopeType == Js::DiagExtraScopesType::DiagCatchScopeInSlot || scopeType == Js::DiagExtraScopesType::DiagCatchScopeInObject;
- // This is expected to be called only when the current scope is either slot or activation object.
- Assert(isBlockSlotOrObject || isCatchSlotOrObject);
- if (siblingScope == nullptr)
- {
- // If the sibling isn't there, attempt to retrieve it if we're reparsing or create it anew if this is the first parse.
- siblingScope = functionBody->RecordStartScopeObject(isBlockSlotOrObject ? Js::DiagExtraScopesType::DiagBlockScopeDirect : Js::DiagExtraScopesType::DiagCatchScopeDirect, GetStart(), location);
- }
- return siblingScope;
- }
- // Adds a new property to be tracked in the debugger scope.
- // location - The slot array index or register slot location of where the property is stored.
- // propertyId - The property ID of the property.
- // flags - Flags that help describe the property.
- void DebuggerScope::AddProperty(RegSlot location, Js::PropertyId propertyId, DebuggerScopePropertyFlags flags)
- {
- DebuggerScopeProperty scopeProperty;
- scopeProperty.location = location;
- scopeProperty.propId = propertyId;
- // This offset is uninitialized until the property is initialized (with a ld opcode, for example).
- scopeProperty.byteCodeInitializationOffset = Constants::InvalidByteCodeOffset;
- scopeProperty.flags = flags;
- // Delay allocate the property list so we don't take up memory if there are no properties in this scope.
- // Scopes are created during non-debug mode as well so we want to keep them as small as possible.
- this->EnsurePropertyListIsAllocated();
- // The property doesn't exist yet, so add it.
- this->scopeProperties->Add(scopeProperty);
- }
- bool DebuggerScope::HasProperty(Js::PropertyId propertyId)
- {
- int i = -1;
- return GetPropertyIndex(propertyId, i);
- }
- bool DebuggerScope::GetPropertyIndex(Js::PropertyId propertyId, int& index)
- {
- if (!this->HasProperties())
- {
- index = -1;
- return false;
- }
- bool found = this->scopeProperties->MapUntil( [&](int i, const DebuggerScopeProperty& scopeProperty) {
- if(scopeProperty.propId == propertyId)
- {
- index = scopeProperty.location;
- return true;
- }
- return false;
- });
- if(!found)
- {
- return false;
- }
- return true;
- }
- #if DBG
- void DebuggerScope::Dump()
- {
- int indent = (GetScopeDepth() - 1) * 4;
- Output::Print(indent, _u("Begin scope: Address: %p Type: %s Location: %d Sibling: %p Range: [%d, %d]\n "), this, GetDebuggerScopeTypeString(scopeType), scopeLocation, PointerValue(this->siblingScope), range.begin, range.end);
- if (this->HasProperties())
- {
- this->scopeProperties->Map( [=] (int i, Js::DebuggerScopeProperty& scopeProperty) {
- Output::Print(indent, _u("%s(%d) Location: %d Const: %s Initialized: %d\n"), ThreadContext::GetContextForCurrentThread()->GetPropertyName(scopeProperty.propId)->GetBuffer(),
- scopeProperty.propId, scopeProperty.location, scopeProperty.IsConst() ? _u("true"): _u("false"), scopeProperty.byteCodeInitializationOffset);
- });
- }
- Output::Print(_u("\n"));
- }
- // Returns the debugger scope type in string format.
- PCWSTR DebuggerScope::GetDebuggerScopeTypeString(DiagExtraScopesType scopeType)
- {
- switch (scopeType)
- {
- case DiagExtraScopesType::DiagBlockScopeDirect:
- return _u("DiagBlockScopeDirect");
- case DiagExtraScopesType::DiagBlockScopeInObject:
- return _u("DiagBlockScopeInObject");
- case DiagExtraScopesType::DiagBlockScopeInSlot:
- return _u("DiagBlockScopeInSlot");
- case DiagExtraScopesType::DiagBlockScopeRangeEnd:
- return _u("DiagBlockScopeRangeEnd");
- case DiagExtraScopesType::DiagCatchScopeDirect:
- return _u("DiagCatchScopeDirect");
- case DiagExtraScopesType::DiagCatchScopeInObject:
- return _u("DiagCatchScopeInObject");
- case DiagExtraScopesType::DiagCatchScopeInSlot:
- return _u("DiagCatchScopeInSlot");
- case DiagExtraScopesType::DiagUnknownScope:
- return _u("DiagUnknownScope");
- case DiagExtraScopesType::DiagWithScope:
- return _u("DiagWithScope");
- case DiagExtraScopesType::DiagParamScope:
- return _u("DiagParamScope");
- case DiagExtraScopesType::DiagParamScopeInObject:
- return _u("DiagParamScopeInObject");
- default:
- AssertMsg(false, "Missing a debug scope type.");
- return _u("");
- }
- }
- #endif
- #if ENABLE_TTD
- Js::PropertyId DebuggerScope::GetPropertyIdForSlotIndex_TTD(uint32 slotIndex) const
- {
- const Js::DebuggerScopeProperty& scopeProperty = this->scopeProperties->Item(slotIndex);
- return scopeProperty.propId;
- }
- #endif
- // Updates the current offset of where the property is first initialized. This is used to
- // detect whether or not a property is in a dead zone when broken in the debugger.
- // location - The slot array index or register slot location of where the property is stored.
- // propertyId - The property ID of the property.
- // byteCodeOffset - The offset to set the initialization point at.
- // isFunctionDeclaration - Whether or not the property is a function declaration or not. Used for verification.
- // <returns> - True if the property was found and updated for the current scope, else false.
- bool DebuggerScope::UpdatePropertyInitializationOffset(
- RegSlot location,
- Js::PropertyId propertyId,
- int byteCodeOffset,
- bool isFunctionDeclaration /*= false*/)
- {
- if (UpdatePropertyInitializationOffsetInternal(location, propertyId, byteCodeOffset, isFunctionDeclaration))
- {
- return true;
- }
- if (siblingScope != nullptr && siblingScope->UpdatePropertyInitializationOffsetInternal(location, propertyId, byteCodeOffset, isFunctionDeclaration))
- {
- return true;
- }
- return false;
- }
- bool DebuggerScope::UpdatePropertyInitializationOffsetInternal(
- RegSlot location,
- Js::PropertyId propertyId,
- int byteCodeOffset,
- bool isFunctionDeclaration /*= false*/)
- {
- if (scopeProperties == nullptr)
- {
- return false;
- }
- for (int i = 0; i < scopeProperties->Count(); ++i)
- {
- DebuggerScopeProperty propertyItem = scopeProperties->Item(i);
- if (propertyItem.propId == propertyId && propertyItem.location == location)
- {
- if (propertyItem.byteCodeInitializationOffset == Constants::InvalidByteCodeOffset)
- {
- propertyItem.byteCodeInitializationOffset = byteCodeOffset;
- scopeProperties->SetExistingItem(i, propertyItem);
- }
- #if DBG
- else
- {
- // If the bytecode initialization offset is not Constants::InvalidByteCodeOffset,
- // it means we have two or more functions declared in the same scope with the same name
- // and one has already been marked. We track each location with a property entry
- // on the debugging side (when calling DebuggerScope::AddProperty()) as opposed to scanning
- // and checking if the property already exists each time we add in order to avoid duplicates.
- AssertMsg(isFunctionDeclaration, "Only function declarations can be defined more than once in the same scope with the same name.");
- AssertMsg(propertyItem.byteCodeInitializationOffset == byteCodeOffset, "The bytecode offset for all function declarations should be identical for this scope.");
- }
- #endif // DBG
- return true;
- }
- }
- return false;
- }
- // Updates the debugger scopes fields due to a regeneration of bytecode (happens during debugger attach or detach, for
- // example).
- void DebuggerScope::UpdateDueToByteCodeRegeneration(DiagExtraScopesType scopeType, int start, RegSlot scopeLocation)
- {
- #if DBG
- if (this->scopeType != Js::DiagUnknownScope)
- {
- // If the scope is unknown, it was deserialized without a scope type. Otherwise, it should not have changed.
- // The scope type can change on a re-parse in certain scenarios related to eval detection in legacy mode -> Winblue: 272122
- AssertMsg(this->scopeType == scopeType, "The debugger scope type should not have changed when generating bytecode again.");
- }
- #endif // DBG
- this->scopeType = scopeType;
- this->SetBegin(start);
- if(this->scopeProperties)
- {
- this->scopeProperties->Clear();
- }
- // Reset the scope location as it may have changed during bytecode generation from the last run.
- this->SetScopeLocation(scopeLocation);
- if (siblingScope)
- {
- // If we had a sibling scope during initial parsing, clear it now so that it will be reset
- // when it is retrieved during this bytecode generation pass, in GetSiblingScope().
- // GetSiblingScope() will ensure that the FunctionBody currentDebuggerScopeIndex value is
- // updated accordingly to account for future scopes coming after the sibling.
- // Calling of GetSiblingScope() will happen when register properties are added to this scope
- // via TrackRegisterPropertyForDebugger().
- siblingScope = nullptr;
- }
- }
- void DebuggerScope::UpdatePropertiesInForInOrOfCollectionScope()
- {
- if (this->scopeProperties != nullptr)
- {
- this->scopeProperties->All([&](Js::DebuggerScopeProperty& propertyItem)
- {
- propertyItem.flags |= DebuggerScopePropertyFlags_ForInOrOfCollection;
- return true;
- });
- }
- }
- void DebuggerScope::EnsurePropertyListIsAllocated()
- {
- if (this->scopeProperties == nullptr)
- {
- this->scopeProperties = RecyclerNew(this->recycler, DebuggerScopePropertyList, this->recycler);
- }
- }
- // Checks if the passed in ByteCodeGenerator offset is in this scope's being/end range.
- bool DebuggerScope::IsOffsetInScope(int offset) const
- {
- Assert(this->range.end != -1);
- return this->range.Includes(offset);
- }
- // Determines if the DebuggerScope contains a property with the passed in ID and
- // location in the internal property list.
- // propertyId - The ID of the property to search for.
- // location - The slot array index or register to search for.
- // outScopeProperty - Optional parameter that will return the property, if found.
- bool DebuggerScope::Contains(Js::PropertyId propertyId, RegSlot location) const
- {
- DebuggerScopeProperty tempProperty;
- return TryGetProperty(propertyId, location, &tempProperty);
- }
- // Gets whether or not the scope is a block scope (non-catch or with).
- bool DebuggerScope::IsBlockScope() const
- {
- AssertMsg(this->scopeType != Js::DiagBlockScopeRangeEnd, "Debugger scope type should never be set to range end - only reserved for marking the end of a scope (not persisted).");
- return this->scopeType == Js::DiagBlockScopeDirect
- || this->scopeType == Js::DiagBlockScopeInObject
- || this->scopeType == Js::DiagBlockScopeInSlot
- || this->scopeType == Js::DiagBlockScopeRangeEnd;
- }
- // Gets whether or not the scope is a catch block scope.
- bool DebuggerScope::IsCatchScope() const
- {
- return this->scopeType == Js::DiagCatchScopeDirect
- || this->scopeType == Js::DiagCatchScopeInObject
- || this->scopeType == Js::DiagCatchScopeInSlot;
- }
- // Gets whether or not the scope is a with block scope.
- bool DebuggerScope::IsWithScope() const
- {
- return this->scopeType == Js::DiagWithScope;
- }
- // Gets whether or not the scope is a slot array scope.
- bool DebuggerScope::IsSlotScope() const
- {
- return this->scopeType == Js::DiagBlockScopeInSlot
- || this->scopeType == Js::DiagCatchScopeInSlot;
- }
- bool DebuggerScope::IsParamScope() const
- {
- return this->scopeType == Js::DiagParamScope
- || this->scopeType == Js::DiagParamScopeInObject;
- }
- // Gets whether or not the scope has any properties in it.
- bool DebuggerScope::HasProperties() const
- {
- return this->scopeProperties && this->scopeProperties->Count() > 0;
- }
- // Checks if this scope is an ancestor of the passed in scope.
- bool DebuggerScope::IsAncestorOf(const DebuggerScope* potentialChildScope)
- {
- if (potentialChildScope == nullptr)
- {
- // If the child scope is null, it represents the global scope which
- // cannot be a child of anything.
- return false;
- }
- const DebuggerScope* currentScope = potentialChildScope;
- while (currentScope)
- {
- if (currentScope->GetParentScope() == this)
- {
- return true;
- }
- currentScope = currentScope->GetParentScope();
- }
- return false;
- }
- // Checks if all properties of the scope are currently in a dead zone given the specified offset.
- bool DebuggerScope::AreAllPropertiesInDeadZone(int byteCodeOffset) const
- {
- if (!this->HasProperties())
- {
- return false;
- }
- return this->scopeProperties->All([&](Js::DebuggerScopeProperty& propertyItem)
- {
- return propertyItem.IsInDeadZone(byteCodeOffset);
- });
- }
- // Attempts to get the specified property. Returns true if the property was copied to the structure; false otherwise.
- bool DebuggerScope::TryGetProperty(Js::PropertyId propertyId, RegSlot location, DebuggerScopeProperty* outScopeProperty) const
- {
- Assert(outScopeProperty);
- if (scopeProperties == nullptr)
- {
- return false;
- }
- for (int i = 0; i < scopeProperties->Count(); ++i)
- {
- DebuggerScopeProperty propertyItem = scopeProperties->Item(i);
- if (propertyItem.propId == propertyId && propertyItem.location == location)
- {
- *outScopeProperty = propertyItem;
- return true;
- }
- }
- return false;
- }
- bool DebuggerScope::TryGetValidProperty(Js::PropertyId propertyId, RegSlot location, int offset, DebuggerScopeProperty* outScopeProperty, bool* isInDeadZone) const
- {
- if (TryGetProperty(propertyId, location, outScopeProperty))
- {
- if (IsOffsetInScope(offset))
- {
- if (isInDeadZone != nullptr)
- {
- *isInDeadZone = outScopeProperty->IsInDeadZone(offset);
- }
- return true;
- }
- }
- return false;
- }
- void DebuggerScope::SetBegin(int begin)
- {
- range.begin = begin;
- if (siblingScope != nullptr)
- {
- siblingScope->SetBegin(begin);
- }
- }
- void DebuggerScope::SetEnd(int end)
- {
- range.end = end;
- if (siblingScope != nullptr)
- {
- siblingScope->SetEnd(end);
- }
- }
- // Finds the common ancestor scope between this scope and the passed in scope.
- // Returns nullptr if the scopes are part of different trees.
- DebuggerScope* DebuggerScope::FindCommonAncestor(DebuggerScope* debuggerScope)
- {
- AnalysisAssert(debuggerScope);
- if (this == debuggerScope)
- {
- return debuggerScope;
- }
- if (this->IsAncestorOf(debuggerScope))
- {
- return this;
- }
- if (debuggerScope->IsAncestorOf(this))
- {
- return debuggerScope;
- }
- DebuggerScope* firstNode = this;
- DebuggerScope* secondNode = debuggerScope;
- int firstDepth = firstNode->GetScopeDepth();
- int secondDepth = secondNode->GetScopeDepth();
- // Calculate the depth difference in order to bring the deep node up to the sibling
- // level of the shorter node.
- int depthDifference = abs(firstDepth - secondDepth);
- DebuggerScope*& nodeToBringUp = firstDepth > secondDepth ? firstNode : secondNode;
- while (depthDifference > 0)
- {
- AnalysisAssert(nodeToBringUp);
- nodeToBringUp = nodeToBringUp->GetParentScope();
- --depthDifference;
- }
- // Move up the tree and see where the nodes meet.
- while (firstNode && secondNode)
- {
- if (firstNode == secondNode)
- {
- return firstNode;
- }
- firstNode = firstNode->GetParentScope();
- secondNode = secondNode->GetParentScope();
- }
- // The nodes are not part of the same scope tree.
- return nullptr;
- }
- // Gets the depth of the scope in the parent link tree.
- int DebuggerScope::GetScopeDepth() const
- {
- int depth = 0;
- const DebuggerScope* currentDebuggerScope = this;
- while (currentDebuggerScope)
- {
- currentDebuggerScope = currentDebuggerScope->GetParentScope();
- ++depth;
- }
- return depth;
- }
- bool ScopeObjectChain::TryGetDebuggerScopePropertyInfo(PropertyId propertyId, RegSlot location, int offset, bool* isPropertyInDebuggerScope, bool *isConst, bool* isInDeadZone)
- {
- Assert(pScopeChain);
- Assert(isPropertyInDebuggerScope);
- Assert(isConst);
- *isPropertyInDebuggerScope = false;
- *isConst = false;
- // Search through each block scope until we find the current scope. If the register was found
- // in any of the scopes going down until we reach the scope of the debug break, then it's in scope.
- // if found but not in the scope, the out param will be updated (since it is actually a let or const), so that caller can make a call accordingly.
- for (int i = 0; i < pScopeChain->Count(); i++)
- {
- Js::DebuggerScope *debuggerScope = pScopeChain->Item(i);
- DebuggerScopeProperty debuggerScopeProperty;
- if (!debuggerScope->IsParamScope() && debuggerScope->TryGetProperty(propertyId, location, &debuggerScopeProperty))
- {
- bool isOffsetInScope = debuggerScope->IsOffsetInScope(offset);
- // For the Object scope, all the properties will have the same location (-1) so they can match. Use further check below to determine the propertyInDebuggerScope
- *isPropertyInDebuggerScope = isOffsetInScope || !debuggerScope->IsBlockObjectScope();
- if (isOffsetInScope)
- {
- if (isInDeadZone != nullptr)
- {
- *isInDeadZone = debuggerScopeProperty.IsInDeadZone(offset);
- }
- *isConst = debuggerScopeProperty.IsConst();
- return true;
- }
- }
- }
- return false;
- }
- void FunctionBody::AllocateForInCache()
- {
- uint profiledForInLoopCount = this->GetProfiledForInLoopCount();
- if (profiledForInLoopCount == 0)
- {
- return;
- }
- this->SetAuxPtr<AuxPointerType::ForInCacheArray>(AllocatorNewArrayZ(CacheAllocator, this->GetScriptContext()->GetEnumeratorAllocator(), EnumeratorCache, profiledForInLoopCount));
- }
- EnumeratorCache * FunctionBody::GetForInCache(uint index)
- {
- Assert(index < this->GetProfiledForInLoopCount());
- return &this->GetAuxPtr<AuxPointerType::ForInCacheArray>()[index];
- }
- EnumeratorCache * FunctionBody::GetForInCacheArray()
- {
- return this->GetAuxPtrWithLock<AuxPointerType::ForInCacheArray>();
- }
- void FunctionBody::CleanUpForInCache(bool isShutdown)
- {
- uint profiledForInLoopCount = this->GetProfiledForInLoopCount();
- if (profiledForInLoopCount == 0)
- {
- return;
- }
- EnumeratorCache * forInCacheArray = this->GetAuxPtr<AuxPointerType::ForInCacheArray>();
- if (forInCacheArray)
- {
- if (isShutdown)
- {
- memset(forInCacheArray, 0, sizeof(EnumeratorCache) * profiledForInLoopCount);
- }
- else
- {
- AllocatorDeleteArray(CacheAllocator, this->GetScriptContext()->GetEnumeratorAllocator(), profiledForInLoopCount, forInCacheArray);
- this->SetAuxPtr<AuxPointerType::ForInCacheArray>(nullptr);
- }
- }
- }
- void FunctionBody::AllocateInlineCache()
- {
- Assert(this->inlineCaches == nullptr);
- uint isInstInlineCacheStart = this->GetInlineCacheCount();
- uint totalCacheCount = isInstInlineCacheStart + GetIsInstInlineCacheCount();
- if (totalCacheCount != 0)
- {
- // Root object inline cache are not leaf
- void ** inlineCaches = RecyclerNewArrayZ(this->m_scriptContext->GetRecycler(),
- void*, totalCacheCount);
- #if DBG
- this->m_inlineCacheTypes = RecyclerNewArrayLeafZ(this->m_scriptContext->GetRecycler(),
- byte, totalCacheCount);
- #endif
- uint i = 0;
- uint plainInlineCacheEnd = GetRootObjectLoadInlineCacheStart();
- __analysis_assume(plainInlineCacheEnd <= totalCacheCount);
- for (; i < plainInlineCacheEnd; i++)
- {
- inlineCaches[i] = AllocatorNewZ(InlineCacheAllocator,
- this->m_scriptContext->GetInlineCacheAllocator(), InlineCache);
- }
- Js::RootObjectBase * rootObject = this->GetRootObject();
- ThreadContext * threadContext = this->GetScriptContext()->GetThreadContext();
- uint rootObjectLoadInlineCacheEnd = GetRootObjectLoadMethodInlineCacheStart();
- __analysis_assume(rootObjectLoadInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectLoadInlineCacheEnd; i++)
- {
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(this->GetPropertyIdFromCacheId(i)), false, false);
- }
- uint rootObjectLoadMethodInlineCacheEnd = GetRootObjectStoreInlineCacheStart();
- __analysis_assume(rootObjectLoadMethodInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectLoadMethodInlineCacheEnd; i++)
- {
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(this->GetPropertyIdFromCacheId(i)), true, false);
- }
- uint rootObjectStoreInlineCacheEnd = isInstInlineCacheStart;
- __analysis_assume(rootObjectStoreInlineCacheEnd <= totalCacheCount);
- for (; i < rootObjectStoreInlineCacheEnd; i++)
- {
- #pragma prefast(suppress:6386, "The analysis assume didn't help prefast figure out this is in range")
- inlineCaches[i] = rootObject->GetInlineCache(
- threadContext->GetPropertyName(this->GetPropertyIdFromCacheId(i)), false, true);
- }
- for (; i < totalCacheCount; i++)
- {
- inlineCaches[i] = AllocatorNewStructZ(CacheAllocator,
- this->m_scriptContext->GetIsInstInlineCacheAllocator(), IsInstInlineCache);
- }
- #if DBG
- this->m_inlineCacheTypes = RecyclerNewArrayLeafZ(this->m_scriptContext->GetRecycler(),
- byte, totalCacheCount);
- #endif
- this->inlineCaches = inlineCaches;
- }
- }
- InlineCache *FunctionBody::GetInlineCache(uint index)
- {
- Assert(this->inlineCaches != nullptr);
- Assert(index < this->GetInlineCacheCount());
- #if DBG
- Assert(this->m_inlineCacheTypes[index] == InlineCacheTypeNone ||
- this->m_inlineCacheTypes[index] == InlineCacheTypeInlineCache);
- this->m_inlineCacheTypes[index] = InlineCacheTypeInlineCache;
- #endif
- return reinterpret_cast<InlineCache *>(this->inlineCaches[index]);
- }
- bool FunctionBody::CanFunctionObjectHaveInlineCaches()
- {
- if (this->DoStackNestedFunc() || this->IsCoroutine())
- {
- return false;
- }
- uint totalCacheCount = this->GetInlineCacheCount() + this->GetIsInstInlineCacheCount();
- if (PHASE_FORCE(Js::ScriptFunctionWithInlineCachePhase, this) && totalCacheCount > 0)
- {
- return true;
- }
- // Only have inline caches on function object for possible inlining candidates.
- // Since we don't know the size of the top function, check against the maximum possible inline threshold
- // Negative inline byte code size threshold will disable inline cache on function object.
- const int byteCodeSizeThreshold = CONFIG_FLAG(InlineThreshold) + CONFIG_FLAG(InlineThresholdAdjustCountInSmallFunction);
- if (byteCodeSizeThreshold < 0 || this->GetByteCodeWithoutLDACount() > (uint)byteCodeSizeThreshold)
- {
- return false;
- }
- // Negative FuncObjectInlineCacheThreshold will disable inline cache on function object.
- if (CONFIG_FLAG(FuncObjectInlineCacheThreshold) < 0 || totalCacheCount > (uint)CONFIG_FLAG(FuncObjectInlineCacheThreshold) || totalCacheCount == 0)
- {
- return false;
- }
- return true;
- }
- void** FunctionBody::GetInlineCaches()
- {
- return this->inlineCaches;
- }
- #if DBG
- byte* FunctionBody::GetInlineCacheTypes()
- {
- return this->m_inlineCacheTypes;
- }
- #endif
- IsInstInlineCache *FunctionBody::GetIsInstInlineCache(uint index)
- {
- Assert(this->inlineCaches != nullptr);
- Assert(index < GetIsInstInlineCacheCount());
- index += this->GetInlineCacheCount();
- #if DBG
- Assert(this->m_inlineCacheTypes[index] == InlineCacheTypeNone ||
- this->m_inlineCacheTypes[index] == InlineCacheTypeIsInst);
- this->m_inlineCacheTypes[index] = InlineCacheTypeIsInst;
- #endif
- return reinterpret_cast<IsInstInlineCache *>(this->inlineCaches[index]);
- }
- PolymorphicInlineCache * FunctionBody::GetPolymorphicInlineCache(uint index)
- {
- return this->polymorphicInlineCaches.GetInlineCache(this, index);
- }
- PolymorphicInlineCache * FunctionBody::CreateNewPolymorphicInlineCache(uint index, PropertyId propertyId, InlineCache * inlineCache)
- {
- Assert(GetPolymorphicInlineCache(index) == nullptr);
- // Only create polymorphic inline caches for non-root inline cache indexes
- if (index < GetRootObjectLoadInlineCacheStart()
- #if DBG
- && !PHASE_OFF1(Js::PolymorphicInlineCachePhase)
- #endif
- )
- {
- PolymorphicInlineCache * polymorphicInlineCache = CreatePolymorphicInlineCache(index, MinPolymorphicInlineCacheSize);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_VERBOSE_TRACE1(Js::PolymorphicInlineCachePhase))
- {
- this->DumpFullFunctionName();
- Output::Print(_u(": New PIC, index = %d, size = %d\n"), index, MinPolymorphicInlineCacheSize);
- }
- #endif
- #if PHASE_PRINT_INTRUSIVE_TESTTRACE1
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- PHASE_PRINT_INTRUSIVE_TESTTRACE1(
- Js::PolymorphicInlineCachePhase,
- _u("TestTrace PIC: New, Function %s (%s), 0x%x, index = %d, size = %d\n"), this->GetDisplayName(), this->GetDebugNumberSet(debugStringBuffer), polymorphicInlineCache, index, MinPolymorphicInlineCacheSize);
- uint indexInPolymorphicCache = polymorphicInlineCache->GetInlineCacheIndexForType(inlineCache->GetType());
- inlineCache->CopyTo(propertyId, m_scriptContext, &(polymorphicInlineCache->GetInlineCaches()[indexInPolymorphicCache]));
- polymorphicInlineCache->UpdateInlineCachesFillInfo(indexInPolymorphicCache, true /*set*/);
- return polymorphicInlineCache;
- }
- return nullptr;
- }
- PolymorphicInlineCache * FunctionBody::CreateBiggerPolymorphicInlineCache(uint index, PropertyId propertyId)
- {
- PolymorphicInlineCache * polymorphicInlineCache = GetPolymorphicInlineCache(index);
- Assert(polymorphicInlineCache && polymorphicInlineCache->CanAllocateBigger());
- uint16 polymorphicInlineCacheSize = polymorphicInlineCache->GetSize();
- uint16 newPolymorphicInlineCacheSize = PolymorphicInlineCache::GetNextSize(polymorphicInlineCacheSize);
- Assert(newPolymorphicInlineCacheSize > polymorphicInlineCacheSize);
- PolymorphicInlineCache * newPolymorphicInlineCache = CreatePolymorphicInlineCache(index, newPolymorphicInlineCacheSize);
- polymorphicInlineCache->CopyTo(propertyId, m_scriptContext, newPolymorphicInlineCache);
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- if (PHASE_VERBOSE_TRACE1(Js::PolymorphicInlineCachePhase))
- {
- this->DumpFullFunctionName();
- Output::Print(_u(": Bigger PIC, index = %d, oldSize = %d, newSize = %d\n"), index, polymorphicInlineCacheSize, newPolymorphicInlineCacheSize);
- }
- #endif
- #if PHASE_PRINT_INTRUSIVE_TESTTRACE1
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- PHASE_PRINT_INTRUSIVE_TESTTRACE1(
- Js::PolymorphicInlineCachePhase,
- _u("TestTrace PIC: Bigger, Function %s (%s), 0x%x, index = %d, size = %d\n"), this->GetDisplayName(), this->GetDebugNumberSet(debugStringBuffer), newPolymorphicInlineCache, index, newPolymorphicInlineCacheSize);
- return newPolymorphicInlineCache;
- }
- void FunctionBody::ResetInlineCaches()
- {
- SetInlineCacheCount(0);
- SetRootObjectLoadInlineCacheStart(0);
- SetRootObjectStoreInlineCacheStart(0);
- SetIsInstInlineCacheCount(0);
- this->inlineCaches = nullptr;
- this->polymorphicInlineCaches.Reset();
- }
- PolymorphicInlineCache * FunctionBody::CreatePolymorphicInlineCache(uint index, uint16 size)
- {
- Recycler * recycler = this->m_scriptContext->GetRecycler();
- PolymorphicInlineCache * newPolymorphicInlineCache = FunctionBodyPolymorphicInlineCache::New(size, this);
- this->polymorphicInlineCaches.SetInlineCache(recycler, this, index, newPolymorphicInlineCache);
- return newPolymorphicInlineCache;
- }
- uint FunctionBody::NewObjectLiteral()
- {
- Assert(this->GetObjectLiteralTypes() == nullptr);
- return IncObjLiteralCount();
- }
- Field(DynamicType*)* FunctionBody::GetObjectLiteralTypeRef(uint index)
- {
- Assert(index < GetObjLiteralCount());
- auto literalTypes = this->GetObjectLiteralTypes();
- Assert(literalTypes != nullptr);
- return literalTypes + index;
- }
- Field(DynamicType*)* FunctionBody::GetObjectLiteralTypeRefWithLock(uint index)
- {
- Assert(index < GetObjLiteralCount());
- auto literalTypes = this->GetObjectLiteralTypesWithLock();
- Assert(literalTypes != nullptr);
- return literalTypes + index;
- }
- void FunctionBody::AllocateObjectLiteralTypeArray()
- {
- Assert(this->GetObjectLiteralTypes() == nullptr);
- uint objLiteralCount = GetObjLiteralCount();
- if (objLiteralCount == 0)
- {
- return;
- }
- this->SetObjectLiteralTypes(RecyclerNewArrayZ(this->GetScriptContext()->GetRecycler(), DynamicType *, objLiteralCount));
- }
- uint FunctionBody::NewLiteralRegex()
- {
- if (this->GetLiteralRegexes() != nullptr)
- {
- // This is a function nested in a redeferred function, so we won't regenerate byte code and won't make use of the index.
- // The regex count is already correct, so don't increment it.
- return 0;
- }
- return IncLiteralRegexCount();
- }
- void FunctionBody::AllocateLiteralRegexArray()
- {
- Assert(!this->GetLiteralRegexes());
- uint32 literalRegexCount = GetLiteralRegexCount();
- if (literalRegexCount == 0)
- {
- return;
- }
- this->SetLiteralRegexs(RecyclerNewArrayZ(m_scriptContext->GetRecycler(), UnifiedRegex::RegexPattern *, literalRegexCount));
- }
- #ifdef ASMJS_PLAT
- AsmJsFunctionInfo* FunctionBody::AllocateAsmJsFunctionInfo()
- {
- Assert( !this->GetAsmJsFunctionInfo() );
- this->SetAuxPtr<AuxPointerType::AsmJsFunctionInfo>(RecyclerNew( m_scriptContext->GetRecycler(), AsmJsFunctionInfo));
- return this->GetAsmJsFunctionInfo();
- }
- AsmJsModuleInfo* FunctionBody::AllocateAsmJsModuleInfo()
- {
- Assert( !this->GetAsmJsModuleInfo() );
- Recycler* rec = m_scriptContext->GetRecycler();
- this->SetAuxPtr<AuxPointerType::AsmJsModuleInfo>(RecyclerNew(rec, AsmJsModuleInfo, rec));
- return this->GetAsmJsModuleInfo();
- }
- #endif
- PropertyIdArray * FunctionBody::AllocatePropertyIdArrayForFormals(uint32 size, uint32 count, byte extraSlots)
- {
- //TODO: saravind: Should the allocation be a Leaf Allocation?
- PropertyIdArray * formalsPropIdArray = RecyclerNewPlus(GetScriptContext()->GetRecycler(), size, Js::PropertyIdArray, count, extraSlots);
- SetFormalsPropIdArray(formalsPropIdArray);
- return formalsPropIdArray;
- }
- UnifiedRegex::RegexPattern *FunctionBody::GetLiteralRegex(const uint index)
- {
- Assert(index < GetLiteralRegexCount());
- Assert(this->GetLiteralRegexes());
- return this->GetLiteralRegexes()[index];
- }
- UnifiedRegex::RegexPattern *FunctionBody::GetLiteralRegexWithLock(const uint index)
- {
- Assert(index < GetLiteralRegexCount());
- Assert(this->GetLiteralRegexesWithLock());
- return this->GetLiteralRegexesWithLock()[index];
- }
- void FunctionBody::SetLiteralRegex(const uint index, UnifiedRegex::RegexPattern *const pattern)
- {
- Assert(index < GetLiteralRegexCount());
- Assert(this->GetLiteralRegexes());
- auto literalRegexes = this->GetLiteralRegexes();
- if (literalRegexes[index] && literalRegexes[index] == pattern)
- {
- return;
- }
- Assert(!literalRegexes[index]);
- literalRegexes[index] = pattern;
- }
- void FunctionBody::ResetObjectLiteralTypes()
- {
- this->SetObjectLiteralTypes(nullptr);
- this->SetObjLiteralCount(0);
- }
- void FunctionBody::ResetLiteralRegexes()
- {
- SetLiteralRegexCount(0);
- this->SetLiteralRegexs(nullptr);
- }
- Js::AuxArray<uint32> * FunctionBody::AllocateSlotIdInCachedScopeToNestedIndexArray(uint32 slotCount)
- {
- Js::AuxArray<uint32> * slotIdToNestedIndexArray = RecyclerNewPlusLeaf(GetScriptContext()->GetRecycler(), slotCount * sizeof(uint32), Js::AuxArray<uint32>, slotCount);
- SetSlotIdInCachedScopeToNestedIndexArray(slotIdToNestedIndexArray);
- return slotIdToNestedIndexArray;
- }
- void FunctionBody::ResetProfileIds()
- {
- #if ENABLE_PROFILE_INFO
- Assert(!HasDynamicProfileInfo()); // profile data relies on the profile ID counts; it should not have been created yet
- Assert(!this->GetCodeGenRuntimeData()); // relies on 'profiledCallSiteCount'
- profiledCallSiteCount = 0;
- profiledArrayCallSiteCount = 0;
- profiledReturnTypeCount = 0;
- profiledSlotCount = 0;
- profiledLdLenCount = 0;
- profiledLdElemCount = 0;
- profiledStElemCount = 0;
- #endif
- }
- void FunctionBody::ResetByteCodeGenState()
- {
- // Byte code generation failed for this function. Revert any intermediate state being tracked in the function body, in
- // case byte code generation is attempted again for this function body.
- DebugOnly(this->UnlockCounters());
- ResetInlineCaches();
- ResetObjectLiteralTypes();
- ResetLiteralRegexes();
- ResetLoops();
- ResetProfileIds();
- ResetSlotIdInCachedScopeToNestedIndexArray();
- SetFirstTmpRegister(Constants::NoRegister);
- SetLocalClosureRegister(Constants::NoRegister);
- SetParamClosureRegister(Constants::NoRegister);
- SetLocalFrameDisplayRegister(Constants::NoRegister);
- SetEnvRegister(Constants::NoRegister);
- SetThisRegisterForEventHandler(Constants::NoRegister);
- SetFirstInnerScopeRegister(Constants::NoRegister);
- SetFuncExprScopeRegister(Constants::NoRegister);
- SetInnerScopeCount(0);
- hasCachedScopePropIds = false;
- this->SetConstantCount(0);
- this->SetConstTable(nullptr);
- AssertMsg(!this->byteCodeBlock || !this->IsWasmFunction(), "We should never reset the bytecode block for Wasm");
- this->byteCodeBlock = nullptr;
- // Also, remove the function body from the source info to prevent any further processing
- // of the function such as attempts to set breakpoints.
- if (GetIsFuncRegistered())
- {
- this->GetUtf8SourceInfo()->RemoveFunctionBody(this);
- }
- // There is other state that is set by the byte code generator but the state should be the same each time byte code
- // generation is done for the function, so it doesn't need to be reverted
- }
- void FunctionBody::ResetByteCodeGenVisitState()
- {
- // This function body is about to be visited by the byte code generator after defer-parsing it. Since the previous visit
- // pass may have failed, we need to restore state that is tracked on the function body by the visit pass.
- // Note: do not reset literal regexes if the function has already been compiled (e.g., is a parsed function enclosed by a
- // redeferred function) as we will not use the count of literals anyway, and the counters may be accessed by the background thread.
- DebugOnly(this->UnlockCounters());
- if (this->byteCodeBlock == nullptr)
- {
- ResetLiteralRegexes();
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- const FunctionCodeGenRuntimeData *FunctionBody::GetInlineeCodeGenRuntimeData(const ProfileId profiledCallSiteId) const
- {
- Assert(profiledCallSiteId < profiledCallSiteCount);
- auto codeGenRuntimeData = this->GetCodeGenRuntimeDataWithLock();
- return codeGenRuntimeData ? codeGenRuntimeData[profiledCallSiteId] : nullptr;
- }
- const FunctionCodeGenRuntimeData *FunctionBody::GetInlineeCodeGenRuntimeDataForTargetInlinee(const ProfileId profiledCallSiteId, Js::FunctionBody *inlineeFuncBody) const
- {
- Assert(profiledCallSiteId < profiledCallSiteCount);
- auto codeGenRuntimeData = this->GetCodeGenRuntimeDataWithLock();
- if (!codeGenRuntimeData)
- {
- return nullptr;
- }
- const FunctionCodeGenRuntimeData *runtimeData = codeGenRuntimeData[profiledCallSiteId];
- while (runtimeData && runtimeData->GetFunctionBody() != inlineeFuncBody)
- {
- runtimeData = runtimeData->GetNext();
- }
- return runtimeData;
- }
- template<Js::FunctionProxy::AuxPointerType auxType>
- FunctionCodeGenRuntimeData * FunctionBody::EnsureCodeGenRuntimeDataCommon(
- Recycler *const recycler,
- __in_range(0, profiledCallSiteCount - 1) const ProfileId profiledCallSiteId,
- FunctionBody *const inlinee)
- {
- Assert(recycler);
- Assert(profiledCallSiteId < profiledCallSiteCount);
- Assert(inlinee);
- if (this->GetAuxPtr(auxType) == nullptr)
- {
- FunctionCodeGenRuntimeData ** codeGenRuntimeData = RecyclerNewArrayZ(recycler, FunctionCodeGenRuntimeData *, profiledCallSiteCount);
- this->SetAuxPtr(auxType, codeGenRuntimeData);
- }
- Field(FunctionCodeGenRuntimeData *)* codeGenRuntimeData = this->GetAuxPtr<auxType>();
- Field(FunctionCodeGenRuntimeData *) const inlineeData = codeGenRuntimeData[profiledCallSiteId];
- if (inlineeData == nullptr)
- {
- FunctionCodeGenRuntimeData * runtimeData = RecyclerNew(recycler, FunctionCodeGenRuntimeData, inlinee);
- codeGenRuntimeData[profiledCallSiteId] = runtimeData;
- return runtimeData;
- }
- // Find the right code gen runtime data
- FunctionCodeGenRuntimeData * next = inlineeData;
- while (next && (next->GetFunctionBody() != inlinee))
- {
- next = next->GetNext();
- }
- if (next)
- {
- return next;
- }
- FunctionCodeGenRuntimeData * runtimeData = RecyclerNew(recycler, FunctionCodeGenRuntimeData, inlinee);
- runtimeData->SetupRuntimeDataChain(inlineeData);
- codeGenRuntimeData[profiledCallSiteId] = runtimeData;
- return runtimeData;
- }
- FunctionCodeGenRuntimeData *FunctionBody::EnsureInlineeCodeGenRuntimeData(
- Recycler *const recycler,
- __in_range(0, profiledCallSiteCount - 1) const ProfileId profiledCallSiteId,
- FunctionBody *const inlinee)
- {
- return EnsureCodeGenRuntimeDataCommon<AuxPointerType::CodeGenRuntimeData>(recycler, profiledCallSiteId, inlinee);
- }
- FunctionCodeGenRuntimeData * FunctionBody::EnsureCallbackInlineeCodeGenRuntimeData(
- Recycler *const recycler,
- __in_range(0, profiledCallSiteCount - 1) const ProfileId profiledCallSiteId,
- FunctionBody *const inlinee)
- {
- return EnsureCodeGenRuntimeDataCommon<AuxPointerType::CodeGenCallbackRuntimeData>(recycler, profiledCallSiteId, inlinee);
- }
- const FunctionCodeGenRuntimeData *FunctionBody::GetLdFldInlineeCodeGenRuntimeData(const InlineCacheIndex inlineCacheIndex) const
- {
- Assert(inlineCacheIndex < this->GetInlineCacheCount());
- auto data = this->GetCodeGenGetSetRuntimeDataWithLock();
- return (data != nullptr) ? data[inlineCacheIndex] : nullptr;
- }
- FunctionCodeGenRuntimeData *FunctionBody::EnsureLdFldInlineeCodeGenRuntimeData(
- Recycler *const recycler,
- const InlineCacheIndex inlineCacheIndex,
- FunctionBody *const inlinee)
- {
- Assert(recycler);
- Assert(inlineCacheIndex < this->GetInlineCacheCount());
- Assert(inlinee);
- if (this->GetCodeGenGetSetRuntimeData() == nullptr)
- {
- const auto codeGenRuntimeData = RecyclerNewArrayZ(recycler, FunctionCodeGenRuntimeData *, this->GetInlineCacheCount());
- this->SetCodeGenGetSetRuntimeData(codeGenRuntimeData);
- }
- auto codeGenGetSetRuntimeData = this->GetCodeGenGetSetRuntimeData();
- const auto inlineeData = codeGenGetSetRuntimeData[inlineCacheIndex];
- if (inlineeData)
- {
- return inlineeData;
- }
- return codeGenGetSetRuntimeData[inlineCacheIndex] = RecyclerNew(recycler, FunctionCodeGenRuntimeData, inlinee);
- }
- const FunctionCodeGenRuntimeData * FunctionBody::GetCallbackInlineeCodeGenRuntimeData(const ProfileId profiledCallSiteId) const
- {
- Assert(profiledCallSiteId < profiledCallSiteCount);
- Field(FunctionCodeGenRuntimeData*)* codeGenRuntimeData = this->GetCodeGenCallbackRuntimeDataWithLock();
- return codeGenRuntimeData ? codeGenRuntimeData[profiledCallSiteId] : nullptr;
- }
- #endif
- void FunctionBody::AllocateLoopHeaders()
- {
- Assert(this->GetLoopHeaderArray() == nullptr);
- uint loopCount = GetLoopCount();
- if (loopCount != 0)
- {
- this->SetLoopHeaderArray(RecyclerNewArrayZ(this->m_scriptContext->GetRecycler(), LoopHeader, loopCount));
- auto loopHeaderArray = this->GetLoopHeaderArray();
- for (uint i = 0; i < loopCount; i++)
- {
- loopHeaderArray[i].Init(this);
- }
- }
- }
- void FunctionBody::ReleaseLoopHeaders()
- {
- #if ENABLE_NATIVE_CODEGEN
- this->MapLoopHeaders([](uint loopNumber, LoopHeader * loopHeader)
- {
- loopHeader->ReleaseEntryPoints();
- });
- #endif
- }
- void FunctionBody::ResetLoops()
- {
- SetLoopCount(0);
- this->SetLoopHeaderArray(nullptr);
- }
- void FunctionBody::RestoreOldDefaultEntryPoint(FunctionEntryPointInfo* oldEntryPointInfo,
- JavascriptMethod oldOriginalEntryPoint,
- FunctionEntryPointInfo* newEntryPointInfo)
- {
- Assert(newEntryPointInfo);
- this->SetDefaultFunctionEntryPointInfo(oldEntryPointInfo, oldOriginalEntryPoint);
- this->entryPoints->RemoveAt(newEntryPointInfo->entryPointIndex);
- }
- FunctionEntryPointInfo* FunctionBody::CreateNewDefaultEntryPoint()
- {
- Recycler *const recycler = this->m_scriptContext->GetRecycler();
- const JavascriptMethod currentThunk = m_scriptContext->CurrentThunk;
- FunctionEntryPointInfo *const entryPointInfo =
- RecyclerNewFinalized(
- recycler,
- FunctionEntryPointInfo,
- this,
- currentThunk,
- m_scriptContext->GetThreadContext());
- AddEntryPointToEntryPointList(entryPointInfo);
- {
- // Allocations in this region may trigger expiry and cause unexpected changes to state
- AUTO_NO_EXCEPTION_REGION;
- Js::JavascriptMethod originalEntryPoint, directEntryPoint;
- #if ENABLE_NATIVE_CODEGEN
- FunctionEntryPointInfo *const simpleJitEntryPointInfo = GetSimpleJitEntryPointInfo();
- if(simpleJitEntryPointInfo && GetExecutionMode() == ExecutionMode::FullJit)
- {
- directEntryPoint =
- originalEntryPoint = simpleJitEntryPointInfo->GetNativeEntrypoint();
- }
- else
- #endif
- {
- #if DYNAMIC_INTERPRETER_THUNK
- // If the dynamic interpreter thunk hasn't been created yet, then the entry point can be set to
- // the default entry point. Otherwise, since the new default entry point is being created to
- // move back to the interpreter, the original entry point is going to be the dynamic interpreter thunk
- originalEntryPoint =
- m_dynamicInterpreterThunk
- ? reinterpret_cast<JavascriptMethod>(InterpreterThunkEmitter::ConvertToEntryPoint(m_dynamicInterpreterThunk))
- : DefaultEntryThunk;
- #else
- originalEntryPoint = DefaultEntryThunk;
- #endif
- directEntryPoint = currentThunk == DefaultEntryThunk ? originalEntryPoint : currentThunk;
- }
- entryPointInfo->jsMethod = directEntryPoint;
- SetDefaultFunctionEntryPointInfo(entryPointInfo, originalEntryPoint);
- }
- return entryPointInfo;
- }
- LoopHeader *FunctionBody::GetLoopHeader(uint index) const
- {
- Assert(this->GetLoopHeaderArray() != nullptr);
- Assert(index < GetLoopCount());
- return &this->GetLoopHeaderArray()[index];
- }
- LoopHeader *FunctionBody::GetLoopHeaderWithLock(uint index) const
- {
- Assert(this->GetLoopHeaderArrayWithLock() != nullptr);
- Assert(index < GetLoopCount());
- return &this->GetLoopHeaderArrayWithLock()[index];
- }
- FunctionEntryPointInfo *FunctionBody::GetSimpleJitEntryPointInfo() const
- {
- return this->GetAuxPtr<AuxPointerType::SimpleJitEntryPointInfo>();
- }
- void FunctionBody::SetSimpleJitEntryPointInfo(FunctionEntryPointInfo *const entryPointInfo)
- {
- this->SetAuxPtr<AuxPointerType::SimpleJitEntryPointInfo>(entryPointInfo);
- }
- uint32 FunctionBody::GetInterpretedCount() const
- {
- return executionState.GetInterpretedCount();
- }
- uint32 FunctionBody::IncreaseInterpretedCount()
- {
- return executionState.IncreaseInterpretedCount();
- }
- void FunctionBody::SetAsmJsExecutionMode()
- {
- executionState.SetAsmJsExecutionMode();
- }
- void FunctionBody::SetDefaultInterpreterExecutionMode()
- {
- executionState.SetDefaultInterpreterExecutionMode();
- }
- ExecutionMode FunctionBody::GetExecutionMode() const
- {
- return executionState.GetExecutionMode();
- }
- ExecutionMode FunctionBody::GetInterpreterExecutionMode(const bool isPostBailout)
- {
- return executionState.GetInterpreterExecutionMode(isPostBailout);
- }
- bool FunctionBody::IsInterpreterExecutionMode() const
- {
- return GetExecutionMode() <= ExecutionMode::ProfilingInterpreter;
- }
- bool FunctionBody::TryTransitionToNextExecutionMode()
- {
- return executionState.TryTransitionToNextExecutionMode();
- }
- void FunctionBody::TryTransitionToNextInterpreterExecutionMode()
- {
- executionState.TryTransitionToNextInterpreterExecutionMode();
- }
- void FunctionBody::SetIsSpeculativeJitCandidate()
- {
- executionState.SetIsSpeculativeJitCandidate();
- }
- bool FunctionBody::TryTransitionToJitExecutionMode()
- {
- return executionState.TryTransitionToJitExecutionMode();
- }
- void FunctionBody::TransitionToSimpleJitExecutionMode()
- {
- executionState.TransitionToSimpleJitExecutionMode();
- }
- void FunctionBody::TransitionToFullJitExecutionMode()
- {
- executionState.TransitionToFullJitExecutionMode();
- }
- void FunctionBody::ReinitializeExecutionModeAndLimits()
- {
- // Do not remove wasCalledFromLoop
- wasCalledFromLoop = false;
- executionState.ReinitializeExecutionModeAndLimits(this);
- }
- void FunctionBody::ResetSimpleJitLimitAndCallCount()
- {
- Assert(GetDefaultFunctionEntryPointInfo() == GetSimpleJitEntryPointInfo());
- executionState.ResetSimpleJitLimit();
- ResetSimpleJitCallCount();
- }
- void FunctionBody::SetSimpleJitCallCount(const uint16 simpleJitLimit) const
- {
- Assert(GetExecutionMode() == ExecutionMode::SimpleJit);
- Assert(GetDefaultFunctionEntryPointInfo() == GetSimpleJitEntryPointInfo());
- // Simple JIT counts down and transitions on overflow
- const uint8 limit = static_cast<uint8>(min(0xffui16, simpleJitLimit));
- GetSimpleJitEntryPointInfo()->callsCount = limit == 0 ? 0 : limit - 1;
- }
- void FunctionBody::ResetSimpleJitCallCount()
- {
- uint32 interpretedCount = GetInterpretedCount();
- uint16 simpleJitLimit = static_cast<uint16>(executionState.GetSimpleJitLimit());
- SetSimpleJitCallCount(
- simpleJitLimit > interpretedCount
- ? simpleJitLimit - static_cast<uint16>(interpretedCount)
- : 0ui16);
- }
- uint16 FunctionBody::GetProfiledIterations() const
- {
- return executionState.GetProfiledIterations();
- }
- void FunctionBody::OnFullJitDequeued(const FunctionEntryPointInfo *const entryPointInfo)
- {
- executionState.AssertIsInitialized();
- Assert(GetExecutionMode() == ExecutionMode::FullJit);
- Assert(entryPointInfo);
- if(entryPointInfo != GetDefaultFunctionEntryPointInfo())
- {
- return;
- }
- // Re-queue the full JIT work item after this many iterations
- executionState.SetFullJitRequeueThreshold(static_cast<uint16>(DEFAULT_CONFIG_FullJitRequeueThreshold));
- }
- void FunctionBody::TraceExecutionMode(const char *const eventDescription) const
- {
- executionState.AssertIsInitialized();
- if(PHASE_TRACE(Phase::ExecutionModePhase, this))
- {
- DoTraceExecutionMode(eventDescription);
- }
- }
- void FunctionBody::TraceInterpreterExecutionMode() const
- {
- executionState.AssertIsInitialized();
- if(!PHASE_TRACE(Phase::ExecutionModePhase, this))
- {
- return;
- }
- switch(GetExecutionMode())
- {
- case ExecutionMode::Interpreter:
- case ExecutionMode::AutoProfilingInterpreter:
- case ExecutionMode::ProfilingInterpreter:
- DoTraceExecutionMode(nullptr);
- break;
- }
- }
- void FunctionBody::DoTraceExecutionMode(const char *const eventDescription) const
- {
- Assert(PHASE_TRACE(Phase::ExecutionModePhase, this));
- executionState.AssertIsInitialized();
- char16 functionIdString[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(
- _u("ExecutionMode - ")
- _u("function: %s (%s), ")
- _u("mode: %S, ")
- _u("size: %u, "),
- GetDisplayName(),
- GetDebugNumberSet(functionIdString),
- ExecutionModeName(executionState.GetExecutionMode()),
- GetByteCodeCount());
- executionState.PrintLimits();
- if(eventDescription)
- {
- Output::Print(_u(", event: %S"), eventDescription);
- }
- Output::Print(_u("\n"));
- Output::Flush();
- }
- bool FunctionBody::DoSimpleJit() const
- {
- return
- !PHASE_OFF(Js::SimpleJitPhase, this) &&
- #ifdef ASMJS_PLAT
- !GetIsAsmjsMode() &&
- #endif
- !GetScriptContext()->GetConfig()->IsNoNative() &&
- !GetScriptContext()->IsScriptContextInDebugMode() &&
- DoInterpreterProfile() &&
- #pragma warning(suppress: 6235) // (<non-zero constant> || <expression>) is always a non-zero constant.
- (!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile()) &&
- !IsCoroutine(); // Generator JIT requires bailout which SimpleJit cannot do since it skips GlobOpt
- }
- bool FunctionBody::DoSimpleJitWithLock() const
- {
- return
- !PHASE_OFF(Js::SimpleJitPhase, this) &&
- #ifdef ASMJS_PLAT
- !GetIsAsmjsMode() &&
- #endif
- !GetScriptContext()->GetConfig()->IsNoNative() &&
- !this->IsInDebugMode() &&
- DoInterpreterProfileWithLock() &&
- #pragma warning(suppress: 6235) // (<non-zero constant> || <expression>) is always a non-zero constant.
- (!CONFIG_FLAG(NewSimpleJit) || DoInterpreterAutoProfile()) &&
- !IsCoroutine(); // Generator JIT requires bailout which SimpleJit cannot do since it skips GlobOpt
- }
- bool FunctionBody::DoSimpleJitDynamicProfile() const
- {
- Assert(DoSimpleJitWithLock());
- return !PHASE_OFF(Js::SimpleJitDynamicProfilePhase, this) && !CONFIG_FLAG(NewSimpleJit);
- }
- bool FunctionBody::DoInterpreterProfile() const
- {
- #if ENABLE_PROFILE_INFO
- return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this);
- #else
- return false;
- #endif
- }
- bool FunctionBody::DoInterpreterProfileWithLock() const
- {
- #if ENABLE_PROFILE_INFO
- return !PHASE_OFF(InterpreterProfilePhase, this) && DynamicProfileInfo::IsEnabled(this);
- #else
- return false;
- #endif
- }
- bool FunctionBody::DoInterpreterAutoProfile() const
- {
- Assert(DoInterpreterProfile());
- #ifdef ASMJS_PLAT
- if (this->GetIsAsmjsMode()) return false;
- #endif
- return !PHASE_OFF(InterpreterAutoProfilePhase, this) && !this->IsInDebugMode();
- }
- bool FunctionBody::WasCalledFromLoop() const
- {
- return wasCalledFromLoop;
- }
- void FunctionBody::SetWasCalledFromLoop()
- {
- if(wasCalledFromLoop)
- {
- return;
- }
- wasCalledFromLoop = true;
- if(Configuration::Global.flags.EnforceExecutionModeLimits)
- {
- if(PHASE_TRACE(Phase::ExecutionModePhase, this))
- {
- executionState.CommitExecutedIterations();
- TraceExecutionMode("WasCalledFromLoop (before)");
- }
- }
- else
- {
- // This function is likely going to be called frequently since it's called from a loop. Reduce the full JIT
- // threshold to realize the full JIT perf benefit sooner.
- executionState.CommitExecutedIterations();
- TraceExecutionMode("WasCalledFromLoop (before)");
- uint16 fullJitThreshold = executionState.GetFullJitThreshold();
- if(fullJitThreshold > 1)
- {
- executionState.SetFullJitThreshold(fullJitThreshold / 2, !CONFIG_FLAG(NewSimpleJit));
- }
- }
- {
- // Reduce the loop interpreter limit too, for the same reasons as above
- const uint oldLoopInterpreterLimit = GetLoopInterpreterLimit();
- const uint newLoopInterpreterLimit = GetReducedLoopInterpretCount();
- Assert(newLoopInterpreterLimit <= oldLoopInterpreterLimit);
- SetLoopInterpreterLimit(newLoopInterpreterLimit);
- // Adjust loop headers' interpret counts to ensure that loops will still be profiled a number of times before
- // loop bodies are jitted
- const uint oldLoopProfileThreshold = GetLoopProfileThreshold(oldLoopInterpreterLimit);
- const uint newLoopProfileThreshold = GetLoopProfileThreshold(newLoopInterpreterLimit);
- MapLoopHeaders([=](const uint index, LoopHeader *const loopHeader)
- {
- const uint interpretedCount = loopHeader->interpretCount;
- if(interpretedCount <= newLoopProfileThreshold || interpretedCount >= oldLoopInterpreterLimit)
- {
- // The loop hasn't been profiled yet and wouldn't have started profiling even with the new profile
- // threshold, or it has already been profiled the necessary minimum number of times based on the old limit
- return;
- }
- if(interpretedCount <= oldLoopProfileThreshold)
- {
- // The loop hasn't been profiled yet, but would have started profiling with the new profile threshold. Start
- // profiling on the next iteration.
- loopHeader->interpretCount = newLoopProfileThreshold;
- return;
- }
- // The loop has been profiled some already. Preserve the number of profiled iterations.
- loopHeader->interpretCount = newLoopProfileThreshold + (interpretedCount - oldLoopProfileThreshold);
- });
- }
- TraceExecutionMode("WasCalledFromLoop");
- }
- bool FunctionBody::RecentlyBailedOutOfJittedLoopBody() const
- {
- return recentlyBailedOutOfJittedLoopBody;
- }
- void FunctionBody::SetRecentlyBailedOutOfJittedLoopBody(const bool value)
- {
- recentlyBailedOutOfJittedLoopBody = value;
- }
- uint16 FunctionBody::GetMinProfileIterations()
- {
- return
- static_cast<uint>(
- CONFIG_FLAG(NewSimpleJit)
- ? DEFAULT_CONFIG_MinProfileIterations
- : DEFAULT_CONFIG_MinProfileIterations_OldSimpleJit);
- }
- uint16 FunctionBody::GetMinFunctionProfileIterations()
- {
- return GetMinProfileIterations();
- }
- uint FunctionBody::GetMinLoopProfileIterations(const uint loopInterpreterLimit)
- {
- return min(static_cast<uint>(GetMinProfileIterations()), loopInterpreterLimit);
- }
- uint FunctionBody::GetLoopProfileThreshold(const uint loopInterpreterLimit) const
- {
- return
- DoInterpreterProfile()
- ? DoInterpreterAutoProfile()
- ? loopInterpreterLimit - GetMinLoopProfileIterations(loopInterpreterLimit)
- : 0
- : static_cast<uint>(-1);
- }
- uint FunctionBody::GetReducedLoopInterpretCount()
- {
- const uint loopInterpretCount = CONFIG_FLAG(LoopInterpretCount);
- if(CONFIG_ISENABLED(LoopInterpretCountFlag))
- {
- return loopInterpretCount;
- }
- return max(loopInterpretCount / 3, GetMinLoopProfileIterations(loopInterpretCount));
- }
- uint FunctionBody::GetLoopInterpretCount(LoopHeader* loopHeader) const
- {
- if(loopHeader->isNested)
- {
- Assert(GetLoopInterpreterLimit() >= GetReducedLoopInterpretCount());
- return GetReducedLoopInterpretCount();
- }
- return GetLoopInterpreterLimit();
- }
- bool FunctionBody::DoObjectHeaderInlining()
- {
- return !PHASE_OFF1(ObjectHeaderInliningPhase);
- }
- bool FunctionBody::DoObjectHeaderInliningForConstructors()
- {
- return !PHASE_OFF1(ObjectHeaderInliningForConstructorsPhase) && DoObjectHeaderInlining();
- }
- bool FunctionBody::DoObjectHeaderInliningForConstructor(const uint32 inlineSlotCapacity)
- {
- return inlineSlotCapacity == 0 ? DoObjectHeaderInliningForEmptyObjects() : DoObjectHeaderInliningForConstructors();
- }
- bool FunctionBody::DoObjectHeaderInliningForObjectLiterals()
- {
- return !PHASE_OFF1(ObjectHeaderInliningForObjectLiteralsPhase) && DoObjectHeaderInlining();
- }
- bool FunctionBody::DoObjectHeaderInliningForObjectLiteral(const uint32 inlineSlotCapacity)
- {
- return
- inlineSlotCapacity == 0
- ? DoObjectHeaderInliningForEmptyObjects()
- : DoObjectHeaderInliningForObjectLiterals() &&
- inlineSlotCapacity <= static_cast<uint32>(MaxPreInitializedObjectHeaderInlinedTypeInlineSlotCount);
- }
- bool FunctionBody::DoObjectHeaderInliningForObjectLiteral(
- const PropertyIdArray *const propIds)
- {
- Assert(propIds);
- return
- DoObjectHeaderInliningForObjectLiteral(propIds->count) &&
- PathTypeHandlerBase::UsePathTypeHandlerForObjectLiteral(propIds);
- }
- bool FunctionBody::DoObjectHeaderInliningForEmptyObjects()
- {
- #pragma prefast(suppress:6237, "(<zero> && <expression>) is always zero. <expression> is never evaluated and might have side effects.")
- return PHASE_ON1(ObjectHeaderInliningForEmptyObjectsPhase) && DoObjectHeaderInlining();
- }
- void FunctionBody::Finalize(bool isShutdown)
- {
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- if (Js::Configuration::Global.flags.Instrument.IsEnabled(Js::LinearScanPhase, this->GetSourceContextId(), this->GetLocalFunctionId()))
- {
- this->DumpRegStats(this);
- }
- #endif
- this->Cleanup(isShutdown);
- this->CleanupSourceInfo(isShutdown);
- this->CleanupFunctionProxyCounters();
- }
- void FunctionBody::OnMark()
- {
- this->m_hasActiveReference = true;
- }
- void FunctionBody::CleanupSourceInfo(bool isScriptContextClosing)
- {
- Assert(this->cleanedUp);
- if (!sourceInfoCleanedUp)
- {
- if (GetIsFuncRegistered() && !isScriptContextClosing)
- {
- // If our function is registered, then there must
- // be a Utf8SourceInfo pinned by it.
- Assert(this->m_utf8SourceInfo);
- this->GetUtf8SourceInfo()->RemoveFunctionBody(this);
- }
- if (this->m_sourceInfo.pSpanSequence != nullptr)
- {
- HeapDelete(this->m_sourceInfo.pSpanSequence);
- this->m_sourceInfo.pSpanSequence = nullptr;
- }
- sourceInfoCleanedUp = true;
- }
- }
- template<bool IsScriptContextShutdown>
- void FunctionBody::CleanUpInlineCaches()
- {
- uint unregisteredInlineCacheCount = 0;
- if (nullptr != this->inlineCaches)
- {
- // Inline caches are in this order
- // plain inline cache
- // root object load inline cache
- // root object store inline cache
- // isInst inline cache
- // The inlineCacheCount includes all but isInst inline cache
- uint i = 0;
- uint plainInlineCacheEnd = GetRootObjectLoadInlineCacheStart();
- for (; i < plainInlineCacheEnd; i++)
- {
- if (nullptr != this->inlineCaches[i])
- {
- InlineCache* inlineCache = (InlineCache*)this->inlineCaches[i];
- if (IsScriptContextShutdown)
- {
- inlineCache->Clear();
- }
- else
- {
- if (inlineCache->RemoveFromInvalidationList())
- {
- unregisteredInlineCacheCount++;
- }
- AllocatorDelete(InlineCacheAllocator, this->m_scriptContext->GetInlineCacheAllocator(), inlineCache);
- }
- }
- }
- RootObjectBase * rootObjectBase = this->GetRootObject();
- uint rootObjectLoadInlineCacheEnd = GetRootObjectLoadMethodInlineCacheStart();
- for (; i < rootObjectLoadInlineCacheEnd; i++)
- {
- if (nullptr != this->inlineCaches[i])
- {
- if (IsScriptContextShutdown)
- {
- ((InlineCache*)this->inlineCaches[i])->Clear();
- }
- else
- {
- // A single root object inline caches for a given property is shared by all functions. It is ref counted
- // and doesn't get released to the allocator until there are no more outstanding references. Thus we don't need
- // to (and, in fact, cannot) remove it from the invalidation list here. Instead, we'll do it in ReleaseInlineCache
- // when there are no more outstanding references.
- unregisteredInlineCacheCount += rootObjectBase->ReleaseInlineCache(this->GetPropertyIdFromCacheId(i), false, false, IsScriptContextShutdown);
- }
- }
- }
- uint rootObjectLoadMethodInlineCacheEnd = GetRootObjectStoreInlineCacheStart();
- for (; i < rootObjectLoadMethodInlineCacheEnd; i++)
- {
- if (nullptr != this->inlineCaches[i])
- {
- if (IsScriptContextShutdown)
- {
- ((InlineCache*)this->inlineCaches[i])->Clear();
- }
- else
- {
- // A single root object inline caches for a given property is shared by all functions. It is ref counted
- // and doesn't get released to the allocator until there are no more outstanding references. Thus we don't need
- // to (and, in fact, cannot) remove it from the invalidation list here. Instead, we'll do it in ReleaseInlineCache
- // when there are no more outstanding references.
- unregisteredInlineCacheCount += rootObjectBase->ReleaseInlineCache(this->GetPropertyIdFromCacheId(i), true, false, IsScriptContextShutdown);
- }
- }
- }
- uint rootObjectStoreInlineCacheEnd = this->GetInlineCacheCount();
- for (; i < rootObjectStoreInlineCacheEnd; i++)
- {
- if (nullptr != this->inlineCaches[i])
- {
- if (IsScriptContextShutdown)
- {
- ((InlineCache*)this->inlineCaches[i])->Clear();
- }
- else
- {
- // A single root object inline caches for a given property is shared by all functions. It is ref counted
- // and doesn't get released to the allocator until there are no more outstanding references. Thus we don't need
- // to (and, in fact, cannot) remove it from the invalidation list here. Instead, we'll do it in ReleaseInlineCache
- // when there are no more outstanding references.
- unregisteredInlineCacheCount += rootObjectBase->ReleaseInlineCache(this->GetPropertyIdFromCacheId(i), false, true, IsScriptContextShutdown);
- }
- }
- }
- uint totalCacheCount = GetInlineCacheCount() + GetIsInstInlineCacheCount();
- for (; i < totalCacheCount; i++)
- {
- if (nullptr != this->inlineCaches[i])
- {
- IsInstInlineCache* inlineCache = (IsInstInlineCache*)this->inlineCaches[i];
- if (IsScriptContextShutdown)
- {
- inlineCache->Clear();
- }
- else
- {
- inlineCache->Unregister(this->m_scriptContext);
- AllocatorDelete(CacheAllocator, this->m_scriptContext->GetIsInstInlineCacheAllocator(), inlineCache);
- }
- }
- }
- this->inlineCaches = nullptr;
- }
- auto codeGenRuntimeData = this->GetCodeGenRuntimeData();
- if (nullptr != codeGenRuntimeData)
- {
- for (ProfileId i = 0; i < this->profiledCallSiteCount; i++)
- {
- const FunctionCodeGenRuntimeData* runtimeData = codeGenRuntimeData[i];
- if (nullptr != runtimeData)
- {
- runtimeData->MapInlineCaches([&](InlineCache* inlineCache)
- {
- if (nullptr != inlineCache)
- {
- if (IsScriptContextShutdown)
- {
- inlineCache->Clear();
- }
- else
- {
- if (inlineCache->RemoveFromInvalidationList())
- {
- unregisteredInlineCacheCount++;
- }
- AllocatorDelete(InlineCacheAllocator, this->m_scriptContext->GetInlineCacheAllocator(), inlineCache);
- }
- }
- });
- }
- }
- }
- auto codeGenGetSetRuntimeData = this->GetCodeGenGetSetRuntimeData();
- if (codeGenGetSetRuntimeData != nullptr)
- {
- for (uint i = 0; i < this->GetInlineCacheCount(); i++)
- {
- auto runtimeData = codeGenGetSetRuntimeData[i];
- if (nullptr != runtimeData)
- {
- runtimeData->MapInlineCaches([&](InlineCache* inlineCache)
- {
- if (nullptr != inlineCache)
- {
- if (IsScriptContextShutdown)
- {
- inlineCache->Clear();
- }
- else
- {
- if (inlineCache->RemoveFromInvalidationList())
- {
- unregisteredInlineCacheCount++;
- }
- AllocatorDelete(InlineCacheAllocator, this->m_scriptContext->GetInlineCacheAllocator(), inlineCache);
- }
- }
- });
- }
- }
- }
- if (unregisteredInlineCacheCount > 0)
- {
- AssertMsg(!IsScriptContextShutdown, "Unregistration of inlineCache should only be done if this is not scriptContext shutdown.");
- ThreadContext* threadContext = this->m_scriptContext->GetThreadContext();
- threadContext->NotifyInlineCacheBatchUnregistered(unregisteredInlineCacheCount);
- }
- while (this->GetPolymorphicInlineCachesHead())
- {
- this->GetPolymorphicInlineCachesHead()->Finalize(IsScriptContextShutdown);
- }
- polymorphicInlineCaches.Reset();
- }
- void FunctionBody::CleanupRecyclerData(bool isShutdown, bool doEntryPointCleanupCaptureStack)
- {
- // If we're not shutting down (i.e closing the script context), we need to remove our inline caches from
- // thread context's invalidation lists, and release memory back to the arena. During script context shutdown,
- // we leave everything in place, because the inline cache arena will stay alive until script context is destroyed
- // (i.e it's destructor has been called) and thus the invalidation lists are safe to keep references to caches from this
- // script context. We will, however, zero all inline caches so that we don't have to process them on subsequent
- // collections, which may still happen from other script contexts.
- if (isShutdown)
- {
- CleanUpInlineCaches<true>();
- }
- else
- {
- CleanUpInlineCaches<false>();
- }
- if (this->entryPoints)
- {
- #if defined(ENABLE_DEBUG_CONFIG_OPTIONS) && !(DBG)
- // On fretest builds, capture the stack only if the FreTestDiagMode switch is on
- doEntryPointCleanupCaptureStack = doEntryPointCleanupCaptureStack && Js::Configuration::Global.flags.FreTestDiagMode;
- #endif
- this->MapEntryPoints([=](int index, FunctionEntryPointInfo* entryPoint)
- {
- if (nullptr != entryPoint)
- {
- // Finalize = Free up work item if it hasn't been released yet + entry point clean up
- // isShutdown is false because cleanup is called only in the !isShutdown case
- entryPoint->Finalize(isShutdown);
- #if ENABLE_ENTRYPOINT_CLEANUP_TRACE
- #if ENABLE_DEBUG_STACK_BACK_TRACE
- // Do this separately since calling EntryPoint::Finalize doesn't capture the stack trace
- // and in some calls to CleanupRecyclerData, we do want the stack trace captured.
- if (doEntryPointCleanupCaptureStack)
- {
- entryPoint->CaptureCleanupStackTrace();
- }
- #endif
- #endif
- }
- });
- this->MapLoopHeaders([=](uint loopNumber, LoopHeader* header)
- {
- bool shuttingDown = isShutdown;
- header->MapEntryPoints([=](int index, LoopEntryPointInfo* entryPoint)
- {
- entryPoint->Cleanup(shuttingDown, doEntryPointCleanupCaptureStack);
- });
- });
- }
- #ifdef PERF_COUNTERS
- this->CleanupPerfCounter();
- #endif
- }
- //
- // Removes all references of the function body and causes clean up of entry points.
- // If the cleanup has already occurred before this would be a no-op.
- //
- void FunctionBody::Cleanup(bool isScriptContextClosing)
- {
- if (cleanedUp)
- {
- return;
- }
- DebugOnly(this->SetIsClosing());
- CleanupRecyclerData(isScriptContextClosing, false /* capture entry point cleanup stack trace */);
- CleanUpForInCache(isScriptContextClosing);
- this->SetObjLiteralCount(0);
- this->SetScopeSlotArraySizes(0, 0);
- // Manually clear these values to break any circular references
- // that might prevent the script context from being disposed
- this->auxPtrs = nullptr;
- AssertMsg(isScriptContextClosing || !m_hasActiveReference || !this->byteCodeBlock || !this->IsWasmFunction(), "We should never reset the bytecode block for Wasm when still referenced");
- this->byteCodeBlock = nullptr;
- this->entryPoints = nullptr;
- this->inlineCaches = nullptr;
- this->cacheIdToPropertyIdMap = nullptr;
- this->polymorphicInlineCaches.Reset();
- this->SetConstTable(nullptr);
- #if DYNAMIC_INTERPRETER_THUNK
- if (this->HasInterpreterThunkGenerated())
- {
- JS_ETW(EtwTrace::LogMethodInterpreterThunkUnloadEvent(this));
- if (!isScriptContextClosing)
- {
- if (m_isAsmJsFunction)
- {
- m_scriptContext->ReleaseDynamicAsmJsInterpreterThunk((BYTE*)this->m_dynamicInterpreterThunk, /*addtoFreeList*/ true);
- }
- else
- {
- m_scriptContext->ReleaseDynamicInterpreterThunk((BYTE*)this->m_dynamicInterpreterThunk, /*addtoFreeList*/ true);
- }
- }
- }
- #endif
- this->cleanedUp = true;
- }
- #ifdef PERF_COUNTERS
- void FunctionBody::CleanupPerfCounter()
- {
- // We might not have the byte code block yet if we defer parsed.
- DWORD byteCodeSize = (this->byteCodeBlock? this->byteCodeBlock->GetLength() : 0)
- + (this->GetAuxiliaryData() ? this->GetAuxiliaryData()->GetLength() : 0)
- + (this->GetAuxiliaryContextData() ? this->GetAuxiliaryContextData()->GetLength() : 0);
- PERF_COUNTER_SUB(Code, DynamicByteCodeSize, byteCodeSize);
- if (this->m_isDeserializedFunction)
- {
- PERF_COUNTER_DEC(Code, DeserializedFunctionBody);
- }
- PERF_COUNTER_SUB(Code, TotalByteCodeSize, byteCodeSize);
- }
- #endif
- void FunctionBody::CaptureDynamicProfileState(FunctionEntryPointInfo* entryPointInfo)
- {
- // DisableJIT-TODO: Move this to be under if DYNAMIC_PROFILE
- #if ENABLE_NATIVE_CODEGEN
- // (See also the FunctionBody member written in CaptureDynamicProfileState.)
- this->SetSavedPolymorphicCacheState(entryPointInfo->GetNativeEntryPointData()->GetPendingPolymorphicCacheState());
- this->savedInlinerVersion = entryPointInfo->GetNativeEntryPointData()->GetPendingInlinerVersion();
- this->savedImplicitCallsFlags = entryPointInfo->GetNativeEntryPointData()->GetPendingImplicitCallFlags();
- #endif
- }
- #if ENABLE_NATIVE_CODEGEN
- BYTE FunctionBody::GetSavedInlinerVersion() const
- {
- Assert(this->dynamicProfileInfo != nullptr);
- return this->savedInlinerVersion;
- }
- uint32 FunctionBody::GetSavedPolymorphicCacheState() const
- {
- Assert(this->dynamicProfileInfo != nullptr);
- return this->savedPolymorphicCacheState;
- }
- void FunctionBody::SetSavedPolymorphicCacheState(uint32 state)
- {
- this->savedPolymorphicCacheState = state;
- }
- #endif
- void FunctionBody::SetHasHotLoop()
- {
- if(hasHotLoop)
- {
- return;
- }
- hasHotLoop = true;
- if(Configuration::Global.flags.EnforceExecutionModeLimits)
- {
- return;
- }
- executionState.CommitExecutedIterations();
- TraceExecutionMode("HasHotLoop (before)");
- if(executionState.GetFullJitThreshold() > 1)
- {
- executionState.SetFullJitThreshold(1, true);
- }
- TraceExecutionMode("HasHotLoop");
- }
- bool FunctionBody::IsInlineApplyDisabled()
- {
- return this->disableInlineApply;
- }
- void FunctionBody::SetDisableInlineApply(bool set)
- {
- this->disableInlineApply = set;
- }
- void FunctionBody::InitDisableInlineApply()
- {
- SetDisableInlineApply(
- (this->GetLocalFunctionId() != Js::Constants::NoFunctionId && PHASE_OFF(Js::InlinePhase, this)) ||
- PHASE_OFF(Js::InlineApplyPhase, this));
- }
- bool FunctionBody::CheckCalleeContextForInlining(FunctionProxy* calleeFunctionProxy)
- {
- return this->GetScriptContext() == calleeFunctionProxy->GetScriptContext() &&
- this->GetSecondaryHostSourceContext() == calleeFunctionProxy->GetSecondaryHostSourceContext();
- }
- #if ENABLE_NATIVE_CODEGEN
- ImplicitCallFlags FunctionBody::GetSavedImplicitCallsFlags() const
- {
- Assert(this->dynamicProfileInfo != nullptr);
- return this->savedImplicitCallsFlags;
- }
- bool FunctionBody::HasNonBuiltInCallee()
- {
- for (ProfileId i = 0; i < profiledCallSiteCount; i++)
- {
- Assert(HasDynamicProfileInfo());
- if (dynamicProfileInfo->MayHaveNonBuiltinCallee(i))
- {
- return true;
- }
- }
- return false;
- }
- #endif
- #ifdef ENABLE_SCRIPT_DEBUGGING
- void FunctionBody::CheckAndRegisterFuncToDiag(ScriptContext *scriptContext)
- {
- // We will register function if, this is not host managed and it was not registered before.
- if (GetHostSourceContext() == Js::Constants::NoHostSourceContext
- && !m_isFuncRegisteredToDiag
- && !scriptContext->GetDebugContext()->GetProbeContainer()->IsContextRegistered(GetSecondaryHostSourceContext()))
- {
- FunctionBody *pFunc = scriptContext->GetDebugContext()->GetProbeContainer()->GetGlobalFunc(scriptContext, GetSecondaryHostSourceContext());
- if (pFunc)
- {
- // Existing behavior here is to ignore the OOM and since RegisterFuncToDiag
- // can throw now, we simply ignore the OOM here
- try
- {
- // Register the function to the PDM as eval code (the debugger app will show file as 'eval code')
- pFunc->RegisterFuncToDiag(scriptContext, Constants::EvalCode);
- }
- catch (Js::OutOfMemoryException)
- {
- }
- scriptContext->GetDebugContext()->GetProbeContainer()->RegisterContextToDiag(GetSecondaryHostSourceContext(), scriptContext->AllocatorForDiagnostics());
- m_isFuncRegisteredToDiag = true;
- }
- }
- else
- {
- m_isFuncRegisteredToDiag = true;
- }
- }
- #endif
- DebuggerScope* FunctionBody::RecordStartScopeObject(DiagExtraScopesType scopeType, int start, RegSlot scopeLocation, int* index)
- {
- Recycler* recycler = m_scriptContext->GetRecycler();
- if (!GetScopeObjectChain())
- {
- SetScopeObjectChain(RecyclerNew(recycler, ScopeObjectChain, recycler));
- }
- // Check if we need to create the scope object or if it already exists from a previous bytecode
- // generator pass.
- DebuggerScope* debuggerScope = nullptr;
- int currentDebuggerScopeIndex = this->GetNextDebuggerScopeIndex();
- if (!this->TryGetDebuggerScopeAt(currentDebuggerScopeIndex, debuggerScope))
- {
- // Create a new debugger scope.
- debuggerScope = AddScopeObject(scopeType, start, scopeLocation);
- }
- else
- {
- debuggerScope->UpdateDueToByteCodeRegeneration(scopeType, start, scopeLocation);
- }
- if(index)
- {
- *index = currentDebuggerScopeIndex;
- }
- return debuggerScope;
- }
- void FunctionBody::RecordEndScopeObject(DebuggerScope* currentScope, int end)
- {
- AssertMsg(currentScope, "No current debugger scope passed in.");
- currentScope->SetEnd(end);
- }
- DebuggerScope * FunctionBody::AddScopeObject(DiagExtraScopesType scopeType, int start, RegSlot scopeLocation)
- {
- Assert(GetScopeObjectChain());
- DebuggerScope *scopeObject = RecyclerNew(m_scriptContext->GetRecycler(), DebuggerScope, m_scriptContext->GetRecycler(), scopeType, scopeLocation, start);
- GetScopeObjectChain()->pScopeChain->Add(scopeObject);
- return scopeObject;
- }
- // Tries to retrieve the debugger scope at the specified index. If the index is out of range, nullptr
- // is returned.
- bool FunctionBody::TryGetDebuggerScopeAt(int index, DebuggerScope*& debuggerScope)
- {
- AssertMsg(this->GetScopeObjectChain(), "TryGetDebuggerScopeAt should only be called with a valid scope chain in place.");
- Assert(index >= 0);
- const Js::ScopeObjectChain::ScopeObjectChainList* scopeChain = this->GetScopeObjectChain()->pScopeChain;
- if (index < scopeChain->Count())
- {
- debuggerScope = scopeChain->Item(index);
- return true;
- }
- return false;
- }
- #if DYNAMIC_INTERPRETER_THUNK
- DWORD FunctionBody::GetDynamicInterpreterThunkSize() const
- {
- return InterpreterThunkEmitter::ThunkSize;
- }
- #endif
- #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
- void
- FunctionBody::DumpFullFunctionName()
- {
- char16 debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(_u("Function %s (%s)"), this->GetDisplayName(), this->GetDebugNumberSet(debugStringBuffer));
- }
- void FunctionBody::DumpFunctionId(bool pad)
- {
- uint sourceContextId = this->GetSourceContextInfo()->sourceContextId;
- if (sourceContextId == Js::Constants::NoSourceContext)
- {
- if (this->IsDynamicScript())
- {
- Output::Print(pad? _u("Dy.%-3d") : _u("Dyn#%d"), this->GetLocalFunctionId());
- }
- else
- {
- // Function from LoadFile
- Output::Print(pad? _u("%-5d") : _u("#%d"), this->GetLocalFunctionId());
- }
- }
- else
- {
- Output::Print(pad? _u("%2d.%-3d") : _u("#%d.%d"), sourceContextId, this->GetLocalFunctionId());
- }
- }
- #endif
- void FunctionBody::EnsureAuxStatementData()
- {
- if (m_sourceInfo.m_auxStatementData == nullptr)
- {
- Recycler* recycler = m_scriptContext->GetRecycler();
- // Note: allocating must be consistent with clean up in CleanupToReparse.
- m_sourceInfo.m_auxStatementData = RecyclerNew(recycler, AuxStatementData);
- }
- }
- /*static*/
- void FunctionBody::GetShortNameFromUrl(__in LPCWSTR pchUrl, _Out_writes_z_(cchBuffer) LPWSTR pchShortName, __in size_t cchBuffer)
- {
- LPCWSTR pchFile = wcsrchr(pchUrl, _u('/'));
- if (pchFile == nullptr)
- {
- pchFile = wcsrchr(pchUrl, _u('\\'));
- }
- LPCWSTR pchToCopy = pchUrl;
- if (pchFile != nullptr)
- {
- pchToCopy = pchFile + 1;
- }
- wcscpy_s(pchShortName, cchBuffer, pchToCopy);
- }
- FunctionBody::StatementAdjustmentRecordList* FunctionBody::GetStatementAdjustmentRecords()
- {
- if (m_sourceInfo.m_auxStatementData)
- {
- return m_sourceInfo.m_auxStatementData->m_statementAdjustmentRecords;
- }
- return nullptr;
- }
- FunctionBody::CrossFrameEntryExitRecordList* FunctionBody::GetCrossFrameEntryExitRecords()
- {
- if (m_sourceInfo.m_auxStatementData)
- {
- return m_sourceInfo.m_auxStatementData->m_crossFrameBlockEntryExisRecords;
- }
- return nullptr;
- }
- void FunctionBody::RecordCrossFrameEntryExitRecord(uint byteCodeOffset, bool isEnterBlock)
- {
- this->EnsureAuxStatementData();
- Recycler* recycler = this->m_scriptContext->GetRecycler();
- if (this->GetCrossFrameEntryExitRecords() == nullptr)
- {
- m_sourceInfo.m_auxStatementData->m_crossFrameBlockEntryExisRecords = RecyclerNew(recycler, CrossFrameEntryExitRecordList, recycler);
- }
- Assert(this->GetCrossFrameEntryExitRecords());
- CrossFrameEntryExitRecord record(byteCodeOffset, isEnterBlock);
- this->GetCrossFrameEntryExitRecords()->Add(record); // Will copy stack value and put the copy into the container.
- }
- FunctionBody::AuxStatementData::AuxStatementData() : m_statementAdjustmentRecords(nullptr), m_crossFrameBlockEntryExisRecords(nullptr)
- {
- }
- FunctionBody::StatementAdjustmentRecord::StatementAdjustmentRecord() :
- m_byteCodeOffset((uint)Constants::InvalidOffset), m_adjustmentType(SAT_None)
- {
- }
- FunctionBody::StatementAdjustmentRecord::StatementAdjustmentRecord(StatementAdjustmentType type, int byteCodeOffset) :
- m_adjustmentType(type), m_byteCodeOffset(byteCodeOffset)
- {
- Assert(SAT_None <= type && type <= SAT_All);
- }
- FunctionBody::StatementAdjustmentRecord::StatementAdjustmentRecord(const StatementAdjustmentRecord& other) :
- m_byteCodeOffset(other.m_byteCodeOffset), m_adjustmentType(other.m_adjustmentType)
- {
- }
- uint FunctionBody::StatementAdjustmentRecord::GetByteCodeOffset()
- {
- Assert(m_byteCodeOffset != Constants::InvalidOffset);
- return m_byteCodeOffset;
- }
- FunctionBody::StatementAdjustmentType FunctionBody::StatementAdjustmentRecord::GetAdjustmentType()
- {
- Assert(this->m_adjustmentType != SAT_None);
- return m_adjustmentType;
- }
- FunctionBody::CrossFrameEntryExitRecord::CrossFrameEntryExitRecord() :
- m_byteCodeOffset((uint)Constants::InvalidOffset), m_isEnterBlock(false)
- {
- }
- FunctionBody::CrossFrameEntryExitRecord::CrossFrameEntryExitRecord(uint byteCodeOffset, bool isEnterBlock) :
- m_byteCodeOffset(byteCodeOffset), m_isEnterBlock(isEnterBlock)
- {
- }
- FunctionBody::CrossFrameEntryExitRecord::CrossFrameEntryExitRecord(const CrossFrameEntryExitRecord& other) :
- m_byteCodeOffset(other.m_byteCodeOffset), m_isEnterBlock(other.m_isEnterBlock)
- {
- }
- uint FunctionBody::CrossFrameEntryExitRecord::GetByteCodeOffset() const
- {
- Assert(m_byteCodeOffset != Constants::InvalidOffset);
- return m_byteCodeOffset;
- }
- bool FunctionBody::CrossFrameEntryExitRecord::GetIsEnterBlock()
- {
- return m_isEnterBlock;
- }
- EntryPointPolymorphicInlineCacheInfo::EntryPointPolymorphicInlineCacheInfo(FunctionBody * functionBody) :
- selfInfo(functionBody),
- inlineeInfo(functionBody->GetRecycler())
- {
- }
- PolymorphicInlineCacheInfo * EntryPointPolymorphicInlineCacheInfo::GetInlineeInfo(FunctionBody * inlineeFunctionBody)
- {
- SListCounted<PolymorphicInlineCacheInfo*, Recycler>::Iterator iter(&inlineeInfo);
- while (iter.Next())
- {
- PolymorphicInlineCacheInfo * info = iter.Data();
- if (info->GetFunctionBody() == inlineeFunctionBody)
- {
- return info;
- }
- }
- return nullptr;
- }
- PolymorphicInlineCacheInfo * EntryPointPolymorphicInlineCacheInfo::EnsureInlineeInfo(Recycler * recycler, FunctionBody * inlineeFunctionBody)
- {
- PolymorphicInlineCacheInfo * info = GetInlineeInfo(inlineeFunctionBody);
- if (!info)
- {
- info = RecyclerNew(recycler, PolymorphicInlineCacheInfo, inlineeFunctionBody);
- inlineeInfo.Prepend(info);
- }
- return info;
- }
- void EntryPointPolymorphicInlineCacheInfo::SetPolymorphicInlineCache(FunctionBody * functionBody, uint index, PolymorphicInlineCache * polymorphicInlineCache, bool isInlinee, byte polyCacheUtil)
- {
- if (!isInlinee)
- {
- SetPolymorphicInlineCache(&selfInfo, functionBody, index, polymorphicInlineCache, polyCacheUtil);
- Assert(functionBody == selfInfo.GetFunctionBody());
- }
- else
- {
- SetPolymorphicInlineCache(EnsureInlineeInfo(functionBody->GetScriptContext()->GetRecycler(), functionBody), functionBody, index, polymorphicInlineCache, polyCacheUtil);
- Assert(functionBody == GetInlineeInfo(functionBody)->GetFunctionBody());
- }
- }
- void EntryPointPolymorphicInlineCacheInfo::SetPolymorphicInlineCache(PolymorphicInlineCacheInfo * polymorphicInlineCacheInfo, FunctionBody * functionBody, uint index, PolymorphicInlineCache * polymorphicInlineCache, byte polyCacheUtil)
- {
- polymorphicInlineCacheInfo->GetPolymorphicInlineCaches()->SetInlineCache(functionBody->GetScriptContext()->GetRecycler(), functionBody, index, polymorphicInlineCache);
- polymorphicInlineCacheInfo->GetUtilArray()->SetUtil(functionBody, index, polyCacheUtil);
- }
- void PolymorphicCacheUtilizationArray::SetUtil(Js::FunctionBody* functionBody, uint index, byte util)
- {
- Assert(functionBody);
- Assert(index < functionBody->GetInlineCacheCount());
- EnsureUtilArray(functionBody->GetScriptContext()->GetRecycler(), functionBody);
- this->utilArray[index] = util;
- }
- byte PolymorphicCacheUtilizationArray::GetUtil(Js::FunctionBody* functionBody, uint index)
- {
- Assert(index < functionBody->GetInlineCacheCount());
- return this->utilArray[index];
- }
- void PolymorphicCacheUtilizationArray::EnsureUtilArray(Recycler * const recycler, Js::FunctionBody * functionBody)
- {
- Assert(recycler);
- Assert(functionBody);
- Assert(functionBody->GetInlineCacheCount() != 0);
- if(this->utilArray)
- {
- return;
- }
- this->utilArray = RecyclerNewArrayLeafZ(recycler, byte, functionBody->GetInlineCacheCount());
- }
- #if ENABLE_NATIVE_CODEGEN
- DWORD_PTR EntryPointInfo::GetNativeAddress() const
- {
- // need the assert to skip for asmjsFunction as nativeAddress can be interpreter too for asmjs
- Assert(this->GetState() == CodeGenRecorded || this->GetState() == CodeGenDone || this->isAsmJsFunction);
- // !! this is illegal, however (by design) `IsInNativeAddressRange` needs it
- return reinterpret_cast<DWORD_PTR>(this->GetNativeEntryPointData()->GetNativeAddress());
- }
- Js::JavascriptMethod EntryPointInfo::GetThunkAddress() const
- {
- Assert(this->GetState() == CodeGenRecorded || this->GetState() == CodeGenDone);
- return this->GetNativeEntryPointData()->GetThunkAddress();
- }
- Js::JavascriptMethod EntryPointInfo::GetNativeEntrypoint() const
- {
- Assert(this->GetState() == CodeGenRecorded || this->GetState() == CodeGenDone || this->isAsmJsFunction);
- Js::JavascriptMethod thunkAddress = this->GetNativeEntryPointData()->GetThunkAddress();
- return thunkAddress ? thunkAddress : this->GetNativeEntryPointData()->GetNativeAddress();
- }
- ptrdiff_t EntryPointInfo::GetCodeSize() const
- {
- Assert(this->GetState() == CodeGenRecorded || this->GetState() == CodeGenDone);
- return this->GetNativeEntryPointData()->GetCodeSize();
- }
- JitTransferData * EntryPointInfo::GetJitTransferData()
- {
- return this->GetNativeEntryPointData()->GetJitTransferData();
- }
- SmallSpanSequence* EntryPointInfo::GetNativeThrowSpanSequence() const
- {
- Assert(this->GetState() != NotScheduled);
- Assert(this->GetState() != CleanedUp);
- return this->GetNativeEntryPointData()->GetNativeThrowSpanSequence();
- }
- void EntryPointInfo::SetNativeThrowSpanSequence(SmallSpanSequence* seq)
- {
- Assert(this->GetState() == CodeGenQueued);
- Assert(this->GetNativeEntryPointData()->GetNativeThrowSpanSequence() == nullptr);
- this->GetNativeEntryPointData()->SetNativeThrowSpanSequence(seq);
- }
- uint EntryPointInfo::GetFrameHeight()
- {
- return this->GetNativeEntryPointData()->GetFrameHeight();
- }
- bool EntryPointInfo::HasInlinees()
- {
- return this->GetFrameHeight() > 0;
- }
- NativeEntryPointData * EntryPointInfo::EnsureNativeEntryPointData()
- {
- NativeEntryPointData * data = this->nativeEntryPointData;
- if (data == nullptr)
- {
- #if ENABLE_OOP_NATIVE_CODEGEN
- if (JITManager::GetJITManager()->IsOOPJITEnabled())
- {
- data = RecyclerNew(this->GetScriptContext()->GetRecycler(), OOPNativeEntryPointData);
- }
- else
- #endif
- {
- data = RecyclerNew(this->GetScriptContext()->GetRecycler(), InProcNativeEntryPointData);
- }
- this->nativeEntryPointData = data;
- }
- return data;
- }
- bool EntryPointInfo::HasNativeEntryPointData() const
- {
- return this->nativeEntryPointData != nullptr;
- }
- NativeEntryPointData * EntryPointInfo::GetNativeEntryPointData() const
- {
- Assert(this->HasNativeEntryPointData());
- return this->nativeEntryPointData;
- }
- InProcNativeEntryPointData * EntryPointInfo::GetInProcNativeEntryPointData()
- {
- return static_cast<InProcNativeEntryPointData *>(this->GetNativeEntryPointData());
- }
- #if ENABLE_OOP_NATIVE_CODEGEN
- OOPNativeEntryPointData * EntryPointInfo::GetOOPNativeEntryPointData()
- {
- return static_cast<OOPNativeEntryPointData *>(this->GetNativeEntryPointData());
- }
- #endif
- void EntryPointInfo::EnsureIsReadyToCall()
- {
- ProcessJitTransferData();
- #if ENABLE_OOP_NATIVE_CODEGEN
- #if !FLOATVAR
- if (JITManager::GetJITManager()->IsOOPJITEnabled())
- {
- this->GetOOPNativeEntryPointData()->ProcessNumberPageSegments(GetScriptContext());
- }
- #endif
- #endif
- }
- void EntryPointInfo::SetCodeGenRecorded(Js::JavascriptMethod thunkAddress, Js::JavascriptMethod nativeAddress, ptrdiff_t codeSize, void * validationCookie)
- {
- Assert(this->GetState() == CodeGenQueued);
- Assert(codeSize > 0);
- this->GetNativeEntryPointData()->RecordNativeCode(thunkAddress, nativeAddress, codeSize, validationCookie);
- this->state = CodeGenRecorded;
- #ifdef PERF_COUNTERS
- this->OnRecorded();
- #endif
- }
- void EntryPointInfo::SetCodeGenDone()
- {
- Assert(this->GetState() == CodeGenRecorded);
- this->state = CodeGenDone;
- this->workItem = nullptr;
- if (this->IsLoopBody())
- {
- this->GetFunctionBody()->SetHasDoneLoopBodyCodeGen(true);
- }
- }
- void EntryPointInfo::ProcessJitTransferData()
- {
- Assert(!IsCleanedUp());
- auto jitTransferData = GetJitTransferData();
- if (jitTransferData == nullptr)
- {
- return;
- }
- class AutoCleanup
- {
- EntryPointInfo *entryPointInfo;
- public:
- AutoCleanup(EntryPointInfo *entryPointInfo) : entryPointInfo(entryPointInfo)
- {
- }
- void Done()
- {
- entryPointInfo = nullptr;
- }
- ~AutoCleanup()
- {
- if (entryPointInfo)
- {
- entryPointInfo->OnNativeCodeInstallFailure();
- }
- }
- } autoCleanup(this);
- ScriptContext* scriptContext = GetScriptContext();
- if (jitTransferData->GetIsReady())
- {
- PinTypeRefs(scriptContext);
- InstallGuards(scriptContext);
- }
- FreeJitTransferData();
- autoCleanup.Done();
- }
- void EntryPointInfo::OnNativeCodeInstallFailure()
- {
- // If more data is transferred from the background thread to the main thread in ProcessJitTransferData,
- // corresponding fields on the entryPointInfo should be rolled back here.
- this->nativeEntryPointData->ClearTypeRefsAndGuards(GetScriptContext());
- this->ResetOnNativeCodeInstallFailure();
- }
- #ifdef FIELD_ACCESS_STATS
- FieldAccessStats* EntryPointInfo::EnsureFieldAccessStats(Recycler* recycler)
- {
- if (this->fieldAccessStats == nullptr)
- {
- this->fieldAccessStats = RecyclerNew(recycler, FieldAccessStats);
- }
- return this->fieldAccessStats;
- }
- #endif
- void EntryPointInfo::PinTypeRefs(ScriptContext* scriptContext)
- {
- NativeEntryPointData * nativeEntryPointData = this->GetNativeEntryPointData();
- JitTransferData * jitTransferData = nativeEntryPointData->GetJitTransferData();
- Assert(jitTransferData != nullptr && jitTransferData->GetIsReady());
- Recycler* recycler = scriptContext->GetRecycler();
- if (jitTransferData->GetRuntimeTypeRefs() != nullptr)
- {
- // Copy pinned types from a heap allocated array created on the background thread
- // to a recycler allocated array which will live as long as this EntryPointInfo.
- // The original heap allocated array will be freed at the end of NativeCodeGenerator::CheckCodeGenDone
- void** jitPinnedTypeRefs = jitTransferData->GetRuntimeTypeRefs();
- size_t jitPinnedTypeRefCount = jitTransferData->GetRuntimeTypeRefCount();
- nativeEntryPointData->PinTypeRefs(recycler, jitPinnedTypeRefCount, jitPinnedTypeRefs);
- }
- }
- void EntryPointInfo::InstallGuards(ScriptContext* scriptContext)
- {
- NativeEntryPointData * nativeEntryPointData = this->GetNativeEntryPointData();
- JitTransferData * jitTransferData = this->GetNativeEntryPointData()->GetJitTransferData();
- Assert(jitTransferData != nullptr && jitTransferData->GetIsReady());
- for (int i = 0; i < jitTransferData->lazyBailoutPropertyCount; i++)
- {
- Assert(jitTransferData->lazyBailoutProperties != nullptr);
- Js::PropertyId propertyId = jitTransferData->lazyBailoutProperties[i];
- Js::PropertyGuard* sharedPropertyGuard = nullptr;
- bool hasSharedPropertyGuard = nativeEntryPointData->TryGetSharedPropertyGuard(propertyId, sharedPropertyGuard);
- Assert(hasSharedPropertyGuard);
- bool isValid = hasSharedPropertyGuard ? sharedPropertyGuard->IsValid() : false;
- if (isValid)
- {
- scriptContext->GetThreadContext()->RegisterLazyBailout(propertyId, this);
- }
- else
- {
- OUTPUT_TRACE2(Js::LazyBailoutPhase, this->GetFunctionBody(), _u("Lazy bailout - Invalidation due to property: %s \n"), scriptContext->GetPropertyName(propertyId)->GetBuffer());
- this->Invalidate(true);
- return;
- }
- }
- // in-proc JIT
- if (jitTransferData->equivalentTypeGuardCount > 0)
- {
- Assert(jitTransferData->equivalentTypeGuardOffsets == nullptr);
- Assert(jitTransferData->equivalentTypeGuards != nullptr);
- int guardCount = jitTransferData->equivalentTypeGuardCount;
- JitEquivalentTypeGuard** guards = jitTransferData->equivalentTypeGuards;
- EquivalentTypeCache * cache = nativeEntryPointData->EnsureEquivalentTypeCache(guardCount, scriptContext, this);
- for (JitEquivalentTypeGuard** guard = guards; guard < guards + guardCount; guard++)
- {
- EquivalentTypeCache* oldCache = (*guard)->GetCache();
- // Copy the contents of the heap-allocated cache to the recycler-allocated version to make sure the types are
- // kept alive. Allow the properties pointer to refer to the heap-allocated arrays. It will stay alive as long
- // as the entry point is alive, and property entries contain no pointers to other recycler allocated objects.
- (*cache) = (*oldCache);
- // Set the recycler-allocated cache on the (heap-allocated) guard.
- (*guard)->SetCache(cache);
- for (uint i = 0; i < EQUIVALENT_TYPE_CACHE_SIZE; i++)
- {
- if((*cache).types[i] != nullptr)
- {
- (*cache).types[i]->SetHasBeenCached();
- }
- else
- {
- #ifdef DEBUG
- for (uint __i = i; __i < EQUIVALENT_TYPE_CACHE_SIZE; __i++)
- { Assert((*cache).types[__i] == nullptr); }
- #endif
- break; // type array must be shrinked.
- }
- }
- cache++;
- }
- }
- #if ENABLE_OOP_NATIVE_CODEGEN
- if (jitTransferData->equivalentTypeGuardOffsets)
- {
- // InstallGuards
- int guardCount = jitTransferData->equivalentTypeGuardOffsets->count;
- EquivalentTypeCache* cache = this->nativeEntryPointData->EnsureEquivalentTypeCache(guardCount, scriptContext, this);
- char * nativeDataBuffer = this->GetOOPNativeEntryPointData()->GetNativeDataBuffer();
- for (int i = 0; i < guardCount; i++)
- {
- auto& cacheIDL = jitTransferData->equivalentTypeGuardOffsets->guards[i].cache;
- auto guardOffset = jitTransferData->equivalentTypeGuardOffsets->guards[i].offset;
- JitEquivalentTypeGuard* guard = (JitEquivalentTypeGuard*)(nativeDataBuffer + guardOffset);
- cache[i].guard = guard;
- cache[i].hasFixedValue = cacheIDL.hasFixedValue != 0;
- cache[i].isLoadedFromProto = cacheIDL.isLoadedFromProto != 0;
- cache[i].nextEvictionVictim = cacheIDL.nextEvictionVictim;
- cache[i].record.propertyCount = cacheIDL.record.propertyCount;
- cache[i].record.properties = (EquivalentPropertyEntry*)(nativeDataBuffer + cacheIDL.record.propertyOffset);
- for (int j = 0; j < EQUIVALENT_TYPE_CACHE_SIZE; j++)
- {
- cache[i].types[j] = (Js::Type*)cacheIDL.types[j];
- }
- guard->SetCache(&cache[i]);
- }
- }
- // OOP JIT
- if (jitTransferData->typeGuardTransferData.entries != nullptr)
- {
- int propertyGuardCount = jitTransferData->typeGuardTransferData.propertyGuardCount;
- Field(FakePropertyGuardWeakReference*) * propertyGuardWeakRefs = this->nativeEntryPointData->EnsurePropertyGuardWeakRefs(propertyGuardCount, scriptContext->GetRecycler());
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- auto next = &jitTransferData->typeGuardTransferData.entries;
- while (*next)
- {
- Js::PropertyId propertyId = (*next)->propId;
- Js::PropertyGuard* sharedPropertyGuard = nullptr;
- // We use the shared guard created during work item creation to ensure that the condition we assumed didn't change while
- // we were JIT-ing. If we don't have a shared property guard for this property then we must not need to protect it,
- // because it exists on the instance. Unfortunately, this means that if we have a bug and fail to create a shared
- // guard for some property during work item creation, we won't find out about it here.
- bool isNeeded = nativeEntryPointData->TryGetSharedPropertyGuard(propertyId, sharedPropertyGuard);
- bool isValid = isNeeded ? sharedPropertyGuard->IsValid() : false;
- if (isNeeded)
- {
- char * nativeDataBuffer = this->GetOOPNativeEntryPointData()->GetNativeDataBuffer();
- for (unsigned int i = 0; i < (*next)->guardsCount; i++)
- {
- Js::JitIndexedPropertyGuard* guard = (Js::JitIndexedPropertyGuard*)(nativeDataBuffer + (*next)->guardOffsets[i]);
- int guardIndex = guard->GetIndex();
- Assert(guardIndex >= 0 && guardIndex < propertyGuardCount);
- // We use the shared guard here to make sure the conditions we assumed didn't change while we were JIT-ing.
- // If they did, we proactively invalidate the guard here, so that we bail out if we try to call this code.
- if (isValid)
- {
- auto propertyGuardWeakRef = propertyGuardWeakRefs[guardIndex];
- if (propertyGuardWeakRef == nullptr)
- {
- propertyGuardWeakRef = Js::FakePropertyGuardWeakReference::New(scriptContext->GetRecycler(), guard);
- propertyGuardWeakRefs[guardIndex] = propertyGuardWeakRef;
- }
- Assert(propertyGuardWeakRef->Get() == guard);
- threadContext->RegisterUniquePropertyGuard(propertyId, propertyGuardWeakRef);
- }
- else
- {
- guard->Invalidate();
- }
- }
- }
- *next = (*next)->next;
- }
- }
- #endif
- // in-proc JIT
- // The propertyGuardsByPropertyId structure is temporary and serves only to register the type guards for the correct
- // properties. If we've done code gen for this EntryPointInfo, typePropertyGuardsByPropertyId will have been used and nulled out.
- if (jitTransferData->propertyGuardsByPropertyId != nullptr)
- {
- int propertyGuardCount = jitTransferData->propertyGuardCount;
- Field(FakePropertyGuardWeakReference*) * propertyGuardWeakRefs = nativeEntryPointData->EnsurePropertyGuardWeakRefs(propertyGuardCount, scriptContext->GetRecycler());
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- Js::TypeGuardTransferEntry* entry = jitTransferData->propertyGuardsByPropertyId;
- while (entry->propertyId != Js::Constants::NoProperty)
- {
- Js::PropertyId propertyId = entry->propertyId;
- Js::PropertyGuard* sharedPropertyGuard = nullptr;
- // We use the shared guard created during work item creation to ensure that the condition we assumed didn't change while
- // we were JIT-ing. If we don't have a shared property guard for this property then we must not need to protect it,
- // because it exists on the instance. Unfortunately, this means that if we have a bug and fail to create a shared
- // guard for some property during work item creation, we won't find out about it here.
- bool isNeeded = nativeEntryPointData->TryGetSharedPropertyGuard(propertyId, sharedPropertyGuard);
- bool isValid = isNeeded ? sharedPropertyGuard->IsValid() : false;
- int entryGuardIndex = 0;
- while (entry->guards[entryGuardIndex] != nullptr)
- {
- if (isNeeded)
- {
- Js::JitIndexedPropertyGuard* guard = entry->guards[entryGuardIndex];
- int guardIndex = guard->GetIndex();
- Assert(guardIndex >= 0 && guardIndex < propertyGuardCount);
- // We use the shared guard here to make sure the conditions we assumed didn't change while we were JIT-ing.
- // If they did, we proactively invalidate the guard here, so that we bail out if we try to call this code.
- if (isValid)
- {
- auto propertyGuardWeakRef = propertyGuardWeakRefs[guardIndex];
- if (propertyGuardWeakRef == nullptr)
- {
- propertyGuardWeakRef = Js::FakePropertyGuardWeakReference::New(scriptContext->GetRecycler(), guard);
- propertyGuardWeakRefs[guardIndex] = propertyGuardWeakRef;
- }
- Assert(propertyGuardWeakRef->Get() == guard);
- threadContext->RegisterUniquePropertyGuard(propertyId, propertyGuardWeakRef);
- }
- else
- {
- guard->Invalidate();
- }
- }
- entryGuardIndex++;
- }
- entry = reinterpret_cast<Js::TypeGuardTransferEntry*>(&entry->guards[++entryGuardIndex]);
- }
- }
- // The ctorCacheGuardsByPropertyId structure is temporary and serves only to register the constructor cache guards for the correct
- // properties. If we've done code gen for this EntryPointInfo, ctorCacheGuardsByPropertyId will have been used and nulled out.
- // Unlike type property guards, constructor cache guards use the live constructor caches associated with function objects. These are
- // recycler allocated and are kept alive by the constructorCaches field, where they were inserted during work item creation.
- // OOP JIT
- if (jitTransferData->ctorCacheTransferData.entries != nullptr)
- {
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- CtorCacheTransferEntryIDL ** entries = jitTransferData->ctorCacheTransferData.entries;
- for (uint i = 0; i < jitTransferData->ctorCacheTransferData.ctorCachesCount; ++i)
- {
- Js::PropertyId propertyId = entries[i]->propId;
- Js::PropertyGuard* sharedPropertyGuard = nullptr;
- // We use the shared guard created during work item creation to ensure that the condition we assumed didn't change while
- // we were JIT-ing. If we don't have a shared property guard for this property then we must not need to protect it,
- // because it exists on the instance. Unfortunately, this means that if we have a bug and fail to create a shared
- // guard for some property during work item creation, we won't find out about it here.
- bool isNeeded = nativeEntryPointData->TryGetSharedPropertyGuard(propertyId, sharedPropertyGuard);
- bool isValid = isNeeded ? sharedPropertyGuard->IsValid() : false;
- if (isNeeded)
- {
- for (uint j = 0; j < entries[i]->cacheCount; ++j)
- {
- Js::ConstructorCache* cache = (Js::ConstructorCache*)(entries[i]->caches[j]);
- // We use the shared cache here to make sure the conditions we assumed didn't change while we were JIT-ing.
- // If they did, we proactively invalidate the cache here, so that we bail out if we try to call this code.
- if (isValid)
- {
- threadContext->RegisterConstructorCache(propertyId, cache);
- }
- else
- {
- cache->InvalidateAsGuard();
- }
- }
- }
- }
- }
- if (jitTransferData->ctorCacheGuardsByPropertyId != nullptr)
- {
- ThreadContext* threadContext = scriptContext->GetThreadContext();
- Js::CtorCacheGuardTransferEntry* entry = jitTransferData->ctorCacheGuardsByPropertyId;
- while (entry->propertyId != Js::Constants::NoProperty)
- {
- Js::PropertyId propertyId = entry->propertyId;
- Js::PropertyGuard* sharedPropertyGuard = nullptr;
- // We use the shared guard created during work item creation to ensure that the condition we assumed didn't change while
- // we were JIT-ing. If we don't have a shared property guard for this property then we must not need to protect it,
- // because it exists on the instance. Unfortunately, this means that if we have a bug and fail to create a shared
- // guard for some property during work item creation, we won't find out about it here.
- bool isNeeded = nativeEntryPointData->TryGetSharedPropertyGuard(propertyId, sharedPropertyGuard);
- bool isValid = isNeeded ? sharedPropertyGuard->IsValid() : false;
- int entryCacheIndex = 0;
- while (entry->caches[entryCacheIndex] != 0)
- {
- if (isNeeded)
- {
- Js::ConstructorCache* cache = (Js::ConstructorCache*)(entry->caches[entryCacheIndex]);
- // We use the shared cache here to make sure the conditions we assumed didn't change while we were JIT-ing.
- // If they did, we proactively invalidate the cache here, so that we bail out if we try to call this code.
- if (isValid)
- {
- threadContext->RegisterConstructorCache(propertyId, cache);
- }
- else
- {
- cache->InvalidateAsGuard();
- }
- }
- entryCacheIndex++;
- }
- entry = reinterpret_cast<Js::CtorCacheGuardTransferEntry*>(&entry->caches[++entryCacheIndex]);
- }
- }
- if (PHASE_ON(Js::FailNativeCodeInstallPhase, this->GetFunctionBody()))
- {
- Js::Throw::OutOfMemory();
- }
- }
- InlineeFrameRecord* EntryPointInfo::FindInlineeFrame(void* returnAddress)
- {
- #if ENABLE_OOP_NATIVE_CODEGEN
- if (JITManager::GetJITManager()->IsOOPJITEnabled()) // OOP JIT
- {
- OOPNativeEntryPointData * oopNativeEntryPointData = this->GetOOPNativeEntryPointData();
- char * nativeDataBuffer = oopNativeEntryPointData->GetNativeDataBuffer();
- NativeOffsetInlineeFrameRecordOffset* offsets = (NativeOffsetInlineeFrameRecordOffset*)(nativeDataBuffer + oopNativeEntryPointData->GetInlineeFrameOffsetArrayOffset());
- size_t offset = (size_t)((BYTE*)returnAddress - (BYTE*)this->GetNativeAddress());
- uint inlineeFrameOffsetArrayCount = oopNativeEntryPointData->GetInlineeFrameOffsetArrayCount();
- if (inlineeFrameOffsetArrayCount == 0)
- {
- return nullptr;
- }
- uint fromIndex = 0;
- uint toIndex = inlineeFrameOffsetArrayCount - 1;
- while (fromIndex <= toIndex)
- {
- uint midIndex = fromIndex + (toIndex - fromIndex) / 2;
- auto item = offsets[midIndex];
- if (item.offset >= offset)
- {
- if (midIndex == 0 || (midIndex > 0 && offsets[midIndex - 1].offset < offset))
- {
- if (offsets[midIndex].recordOffset == NativeOffsetInlineeFrameRecordOffset::InvalidRecordOffset)
- {
- return nullptr;
- }
- else
- {
- return (InlineeFrameRecord*)(nativeDataBuffer + offsets[midIndex].recordOffset);
- }
- }
- else
- {
- toIndex = midIndex - 1;
- }
- }
- else
- {
- fromIndex = midIndex + 1;
- }
- }
- return nullptr;
- }
- else
- #endif
- // in-proc JIT
- {
- InlineeFrameMap * inlineeFrameMap = this->GetInProcNativeEntryPointData()->GetInlineeFrameMap();
- if (inlineeFrameMap == nullptr)
- {
- return nullptr;
- }
- size_t offset = (size_t)((BYTE*)returnAddress - (BYTE*)this->GetNativeAddress());
- int index = inlineeFrameMap->BinarySearch([=](const NativeOffsetInlineeFramePair& pair, int index) {
- if (pair.offset >= offset)
- {
- if (index == 0 || (index > 0 && inlineeFrameMap->Item(index - 1).offset < offset))
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- return -1;
- });
- if (index == -1)
- {
- return nullptr;
- }
- return inlineeFrameMap->Item(index).record;
- }
- }
- void EntryPointInfo::DoLazyBailout(BYTE** addressOfInstructionPointer, Js::FunctionBody* functionBody, const PropertyRecord* propertyRecord)
- {
- BYTE* instructionPointer = *addressOfInstructionPointer;
- NativeEntryPointData * nativeEntryPointData = this->GetNativeEntryPointData();
- Js::JavascriptMethod nativeAddress = nativeEntryPointData->GetNativeAddress();
- ptrdiff_t codeSize = nativeEntryPointData->GetCodeSize();
- Assert(instructionPointer > (BYTE*)nativeAddress && instructionPointer < ((BYTE*)nativeAddress + codeSize));
- size_t offset = instructionPointer - (BYTE*)nativeAddress;
- BailOutRecordMap * bailoutRecordMap = this->GetInProcNativeEntryPointData()->GetBailOutRecordMap();
- int found = bailoutRecordMap->BinarySearch([=](const LazyBailOutRecord& record, int index)
- {
- // find the closest entry which is greater than the current offset.
- if (record.offset >= offset)
- {
- if (index == 0 || (index > 0 && bailoutRecordMap->Item(index - 1).offset < offset))
- {
- return 0;
- }
- else
- {
- return 1;
- }
- }
- return -1;
- });
- if (found != -1)
- {
- LazyBailOutRecord& record = bailoutRecordMap->Item(found);
- *addressOfInstructionPointer = record.instructionPointer;
- record.SetBailOutKind();
- if (PHASE_TRACE1(Js::LazyBailoutPhase))
- {
- Output::Print(_u("On stack lazy bailout. Property: %s Old IP: 0x%x New IP: 0x%x "), propertyRecord->GetBuffer(), instructionPointer, record.instructionPointer);
- #if DBG
- record.Dump(functionBody);
- #endif
- Output::Print(_u("\n"));
- }
- }
- else
- {
- AssertMsg(false, "Lazy Bailout address mapping missing");
- }
- }
- void EntryPointInfo::FreeJitTransferData()
- {
- if (this->HasNativeEntryPointData())
- {
- this->GetNativeEntryPointData()->FreeJitTransferData();;
- }
- }
- bool EntryPointInfo::ClearEquivalentTypeCaches()
- {
- return this->GetNativeEntryPointData()->ClearEquivalentTypeCaches(GetScriptContext()->GetRecycler());
- }
- bool EquivalentTypeCache::ClearUnusedTypes(Recycler *recycler)
- {
- bool isAnyTypeLive = false;
- Assert(this->guard);
- if (this->guard->IsValid())
- {
- if (this->guard->IsPoly())
- {
- JitPolyEquivalentTypeGuard * polyGuard = this->guard->AsPolyTypeCheckGuard();
- for (uint8 i = 0; i < polyGuard->GetSize(); i++)
- {
- intptr_t value = polyGuard->GetPolyValue(i);
- if (value != PropertyGuard::GuardValue::Uninitialized && value != PropertyGuard::GuardValue::Invalidated_DuringSweep)
- {
- Type *type = reinterpret_cast<Type*>(value);
- if (!recycler->IsObjectMarked(type))
- {
- polyGuard->InvalidateDuringSweep(i);
- }
- else
- {
- isAnyTypeLive = true;
- }
- }
- }
- }
- else
- {
- Type *type = reinterpret_cast<Type*>(this->guard->GetValue());
- if (!recycler->IsObjectMarked(type))
- {
- this->guard->InvalidateDuringSweep();
- }
- else
- {
- isAnyTypeLive = true;
- }
- }
- }
- uint16 nonNullIndex = 0;
- #if DBG
- bool isGuardValuePresent = false;
- #endif
- for (int i = 0; i < EQUIVALENT_TYPE_CACHE_SIZE; i++)
- {
- Type *type = this->types[i];
- if (type != nullptr)
- {
- this->types[i] = nullptr;
- if (recycler->IsObjectMarked(type))
- {
- // compact the types array by moving non-null types
- // to the beginning.
- this->types[nonNullIndex++] = type;
- #if DBG
- if (guard->IsPoly())
- {
- isGuardValuePresent = true;
- }
- else if (this->guard->GetValue() == reinterpret_cast<intptr_t>(type))
- {
- isGuardValuePresent = true;
- }
- #endif
- }
- }
- else
- {
- #ifdef DEBUG
- for (int __i = i; __i < EQUIVALENT_TYPE_CACHE_SIZE; __i++)
- { Assert(this->types[__i] == nullptr); }
- #endif
- break; // array must be shrinked already
- }
- }
- if (nonNullIndex > 0)
- {
- isAnyTypeLive = true;
- }
- else if (guard->IsPoly())
- {
- if (!isAnyTypeLive)
- {
- guard->Invalidate();
- }
- }
- else if (guard->IsInvalidatedDuringSweep())
- {
- // just mark this as actual invalidated since there are no types
- // present
- guard->Invalidate();
- }
- // verify if guard value is valid, it is present in one of the types
- AssertMsg(!this->guard->IsValid() || isGuardValuePresent || nonNullIndex == 0,
- "After ClearUnusedTypes, valid guard value should be one of the cached equivalent types.");
- return isAnyTypeLive;
- }
- #endif
- #if ENABLE_ENTRYPOINT_CLEANUP_TRACE
- #if ENABLE_DEBUG_STACK_BACK_TRACE
- void EntryPointInfo::CaptureCleanupStackTrace()
- {
- if (this->cleanupStack != nullptr)
- {
- this->cleanupStack->Delete(&NoCheckHeapAllocator::Instance);
- this->cleanupStack = nullptr;
- }
- this->cleanupStack = StackBackTrace::Capture(&NoCheckHeapAllocator::Instance);
- }
- #endif
- #endif
- void EntryPointInfo::Finalize(bool isShutdown)
- {
- __super::Finalize(isShutdown);
- if (!isShutdown)
- {
- ReleasePendingWorkItem();
- }
- #if ENABLE_ENTRYPOINT_CLEANUP_TRACE
- this->SetCleanupReason(CleanupReason::CleanUpForFinalize);
- #endif
- this->Cleanup(isShutdown, false);
- #if ENABLE_ENTRYPOINT_CLEANUP_TRACE
- #if ENABLE_DEBUG_STACK_BACK_TRACE
- if (this->cleanupStack != nullptr)
- {
- this->cleanupStack->Delete(&NoCheckHeapAllocator::Instance);
- this->cleanupStack = nullptr;
- }
- #endif
- #endif
- this->library = nullptr;
- }
- void EntryPointInfo::Cleanup(bool isShutdown, bool captureCleanupStack)
- {
- if (this->GetState() != CleanedUp)
- {
- #if ENABLE_NATIVE_CODEGEN
- this->OnCleanup(isShutdown);
- if (this->nativeEntryPointData)
- {
- this->nativeEntryPointData->Cleanup(GetScriptContext(), isShutdown, false);
- this->nativeEntryPointData = nullptr;
- }
- #endif // ENABLE_NATIVE_CODEGEN
- // This is how we set the CleanedUp state
- this->workItem = nullptr;
- this->library = nullptr;
- this->state = CleanedUp;
- #if ENABLE_ENTRYPOINT_CLEANUP_TRACE
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- #if !DBG
- captureCleanupStack = captureCleanupStack && Js::Configuration::Global.flags.FreTestDiagMode;
- #endif
- #if ENABLE_DEBUG_STACK_BACK_TRACE
- if (captureCleanupStack)
- {
- this->CaptureCleanupStackTrace();
- }
- #endif
- #endif
- #endif
- #if DEBUG
- const unsigned char* rpcData = serializedRpcData;
- HeapDeleteArray(serializedRpcDataSize, rpcData);
- serializedRpcDataSize = 0;
- serializedRpcData = nullptr;
- #endif
- }
- }
- void EntryPointInfo::Reset(bool resetStateToNotScheduled)
- {
- Assert(this->GetState() != CleanedUp);
- this->workItem = nullptr;
- #if ENABLE_NATIVE_CODEGEN
- if (this->nativeEntryPointData)
- {
- this->nativeEntryPointData->Cleanup(GetScriptContext(), false, true);
- this->nativeEntryPointData = nullptr;
- }
- #endif
- // Set the state to NotScheduled only if the call to Reset is not because of JIT cap being reached
- if (resetStateToNotScheduled)
- {
- this->state = NotScheduled;
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- // This function needs review when we enable lazy bailouts-
- // Is calling Reset enough? Does Reset sufficiently resets the state of the entryPointInfo?
- void EntryPointInfo::ResetOnLazyBailoutFailure()
- {
- Assert(PHASE_ON1(Js::LazyBailoutPhase));
- // Reset the entry point upon a lazy bailout.
- this->Reset(true);
- this->jsMethod = nullptr;
- }
- #endif
- #ifdef ASMJS_PLAT
- void EntryPointInfo::SetIsTJMode(bool value)
- {
- Assert(this->GetIsAsmJSFunction());
- mIsTemplatizedJitMode = value;
- }
- bool EntryPointInfo::GetIsTJMode()const
- {
- return mIsTemplatizedJitMode;
- };
- // set code size, used by TJ to set the code size
- void EntryPointInfo::SetTJCodeSize(ptrdiff_t size)
- {
- Assert(isAsmJsFunction);
- // TODO: We don't need the whole NativeEntryPointData to just hold just the code and size for TJ mode
- this->EnsureNativeEntryPointData()->SetTJCodeSize(size);
- }
- void EntryPointInfo::SetTJNativeAddress(Js::JavascriptMethod address, void * validationCookie)
- {
- Assert(isAsmJsFunction);
- // TODO: We don't need the whole NativeEntryPointData to just hold just the code and size for TJ mode
- this->EnsureNativeEntryPointData()->SetTJNativeAddress(address, validationCookie);
- }
- #endif
- //End AsmJS Support
- #ifdef PERF_COUNTERS
- void FunctionEntryPointInfo::OnRecorded()
- {
- PERF_COUNTER_ADD(Code, TotalNativeCodeSize, GetCodeSize());
- PERF_COUNTER_ADD(Code, FunctionNativeCodeSize, GetCodeSize());
- PERF_COUNTER_ADD(Code, DynamicNativeCodeSize, GetCodeSize());
- }
- #endif
- FunctionEntryPointInfo::FunctionEntryPointInfo(FunctionProxy * functionProxy, Js::JavascriptMethod method, ThreadContext* context) :
- EntryPointInfo(method, functionProxy->GetScriptContext()->GetLibrary(), context),
- localVarSlotsOffset(Js::Constants::InvalidOffset),
- localVarChangedOffset(Js::Constants::InvalidOffset),
- callsCount(0),
- jitMode(ExecutionMode::Interpreter),
- functionProxy(functionProxy),
- nextEntryPoint(nullptr)
- {
- }
- #if ENABLE_NATIVE_CODEGEN
- ExecutionMode FunctionEntryPointInfo::GetJitMode() const
- {
- return jitMode;
- }
- void FunctionEntryPointInfo::SetJitMode(const ExecutionMode jitMode)
- {
- Assert(jitMode == ExecutionMode::SimpleJit || jitMode == ExecutionMode::FullJit);
- this->jitMode = jitMode;
- }
- #endif
- bool FunctionEntryPointInfo::ExecutedSinceCallCountCollection() const
- {
- return this->callsCount != this->lastCallsCount;
- }
- void FunctionEntryPointInfo::CollectCallCounts()
- {
- this->lastCallsCount = this->callsCount;
- }
- void FunctionEntryPointInfo::ReleasePendingWorkItem()
- {
- // Do this outside of Cleanup since cleanup can be called from the background thread
- // We remove any work items corresponding to the function body being reclaimed
- // so that the background thread doesn't try to use them. ScriptContext != null => this
- // is a function entry point
- // In general this is not needed for loop bodies since loop bodies aren't in the low priority
- // queue, they should be jitted before the entry point is finalized
- if (!this->IsNotScheduled() && !this->IsCleanedUp())
- {
- #if defined(_M_ARM32_OR_ARM64)
- // On ARM machines, order of writes is not guaranteed while reading data from another processor
- // So we need to have a memory barrier here in order to make sure that the work item is consistent
- MemoryBarrier();
- #endif
- CodeGenWorkItem* workItem = this->GetWorkItem();
- if (workItem != nullptr)
- {
- Assert(this->library != nullptr);
- #if ENABLE_NATIVE_CODEGEN
- TryReleaseNonHiPriWorkItem(this->library->GetScriptContext(), workItem);
- #endif
- }
- }
- }
- FunctionBody *FunctionEntryPointInfo::GetFunctionBody() const
- {
- return functionProxy->GetFunctionBody();
- }
- #if ENABLE_NATIVE_CODEGEN
- void EntryPointInfo::CleanupNativeCode(ScriptContext * scriptContext)
- {
- if (this->jsMethod == this->GetNativeEntrypoint())
- {
- #if DBG
- // tag the jsMethod in case the native address is reused in recycler and create a false positive
- // not checking validationCookie because this can happen while debugger attaching, native address
- // are batch freed through deleting NativeCodeGenerator
- this->jsMethod = (Js::JavascriptMethod)((intptr_t)this->jsMethod | 1);
- #else
- this->jsMethod = nullptr;
- #endif
- }
- }
- #endif
- void FunctionEntryPointInfo::OnCleanup(bool isShutdown)
- {
- if (this->IsCodeGenDone())
- {
- Assert(this->functionProxy->GetFunctionInfo()->HasBody());
- #if ENABLE_NATIVE_CODEGEN
- if (this->IsNativeEntryPointProcessed())
- {
- JS_ETW(EtwTrace::LogMethodNativeUnloadEvent(this->functionProxy->GetFunctionBody(), this));
- }
- #endif
- FunctionBody* functionBody = this->functionProxy->GetFunctionBody();
- #ifdef ASMJS_PLAT
- if (this->GetIsTJMode())
- {
- // release LoopHeaders here if the entrypointInfo is TJ
- this->GetFunctionBody()->ReleaseLoopHeaders();
- }
- #endif
- if(functionBody->GetSimpleJitEntryPointInfo() == this)
- {
- functionBody->SetSimpleJitEntryPointInfo(nullptr);
- }
- #if ENABLE_NATIVE_CODEGEN
- CleanupNativeCode(this->functionProxy->GetScriptContext());
- #endif
- }
- this->functionProxy = nullptr;
- }
- #if ENABLE_NATIVE_CODEGEN
- void FunctionEntryPointInfo::ResetOnNativeCodeInstallFailure()
- {
- this->functionProxy->MapFunctionObjectTypes([&](ScriptFunctionType* functionType)
- {
- Assert(functionType->GetTypeId() == TypeIds_Function);
- if (functionType->GetEntryPointInfo() == this)
- {
- if (!this->GetIsAsmJSFunction())
- {
- functionType->SetEntryPoint(GetCheckCodeGenThunk());
- }
- #ifdef ASMJS_PLAT
- else
- {
- functionType->SetEntryPoint(GetCheckAsmJsCodeGenThunk());
- }
- #endif
- }
- });
- }
- void FunctionEntryPointInfo::EnterExpirableCollectMode()
- {
- this->lastCallsCount = this->callsCount;
- // For code that is not jitted yet we don't want to expire since there is nothing to free here
- if (this->IsCodeGenPending())
- {
- this->SetIsObjectUsed();
- }
- }
- void FunctionEntryPointInfo::Invalidate(bool prolongEntryPoint)
- {
- Assert(!this->functionProxy->IsDeferred());
- FunctionBody* functionBody = this->functionProxy->GetFunctionBody();
- Assert(this != functionBody->GetSimpleJitEntryPointInfo());
- // We may have got here following OOM in ProcessJitTransferData. Free any data we have
- // to reduce the chance of another OOM below.
- this->FreeJitTransferData();
- FunctionEntryPointInfo* entryPoint = functionBody->GetDefaultFunctionEntryPointInfo();
- if (entryPoint->IsCodeGenPending())
- {
- OUTPUT_TRACE(Js::LazyBailoutPhase, _u("Skipping creating new entrypoint as one is already pending\n"));
- }
- else
- {
- class AutoCleanup
- {
- EntryPointInfo *entryPointInfo;
- public:
- AutoCleanup(EntryPointInfo *entryPointInfo) : entryPointInfo(entryPointInfo)
- {
- }
- void Done()
- {
- entryPointInfo = nullptr;
- }
- ~AutoCleanup()
- {
- if (entryPointInfo)
- {
- entryPointInfo->ResetOnLazyBailoutFailure();
- }
- }
- } autoCleanup(this);
- entryPoint = functionBody->CreateNewDefaultEntryPoint();
- GenerateFunction(functionBody->GetScriptContext()->GetNativeCodeGenerator(), functionBody, /*function*/ nullptr);
- autoCleanup.Done();
- }
- this->functionProxy->MapFunctionObjectTypes([&](ScriptFunctionType* functionType)
- {
- Assert(functionType->GetTypeId() == TypeIds_Function);
- if (functionType->GetEntryPointInfo() == this)
- {
- functionType->SetEntryPointInfo(entryPoint);
- functionType->SetEntryPoint(this->functionProxy->GetDirectEntryPoint(entryPoint));
- }
- });
- if (!prolongEntryPoint)
- {
- ThreadContext* threadContext = this->functionProxy->GetScriptContext()->GetThreadContext();
- threadContext->QueueFreeOldEntryPointInfoIfInScript(this);
- }
- }
- void FunctionEntryPointInfo::Expire()
- {
- if (this->lastCallsCount != this->callsCount || !this->IsNativeEntryPointProcessed() || this->IsCleanedUp())
- {
- return;
- }
- ThreadContext* threadContext = this->functionProxy->GetScriptContext()->GetThreadContext();
- Assert(!this->functionProxy->IsDeferred());
- FunctionBody* functionBody = this->functionProxy->GetFunctionBody();
- FunctionEntryPointInfo *simpleJitEntryPointInfo = functionBody->GetSimpleJitEntryPointInfo();
- const bool expiringSimpleJitEntryPointInfo = simpleJitEntryPointInfo == this;
- if(expiringSimpleJitEntryPointInfo)
- {
- if(functionBody->GetExecutionMode() != ExecutionMode::FullJit)
- {
- // Don't expire simple JIT code until the transition to full JIT
- return;
- }
- simpleJitEntryPointInfo = nullptr;
- functionBody->SetSimpleJitEntryPointInfo(nullptr);
- }
- try
- {
- AUTO_NESTED_HANDLED_EXCEPTION_TYPE(ExceptionType_OutOfMemory);
- FunctionEntryPointInfo* newEntryPoint = nullptr;
- FunctionEntryPointInfo *const defaultEntryPointInfo = functionBody->GetDefaultFunctionEntryPointInfo();
- if(this == defaultEntryPointInfo)
- {
- if(simpleJitEntryPointInfo)
- {
- newEntryPoint = simpleJitEntryPointInfo;
- functionBody->SetDefaultFunctionEntryPointInfo(simpleJitEntryPointInfo, newEntryPoint->GetNativeEntrypoint());
- functionBody->ResetSimpleJitLimitAndCallCount();
- }
- #ifdef ASMJS_PLAT
- else if (functionBody->GetIsAsmJsFunction())
- {
- // the new entrypoint will be set to interpreter
- newEntryPoint = functionBody->CreateNewDefaultEntryPoint();
- newEntryPoint->SetIsAsmJSFunction(true);
- newEntryPoint->jsMethod = AsmJsDefaultEntryThunk;
- functionBody->SetIsAsmJsFullJitScheduled(false);
- functionBody->SetDefaultInterpreterExecutionMode();
- this->functionProxy->SetOriginalEntryPoint(AsmJsDefaultEntryThunk);
- }
- #endif
- else
- {
- newEntryPoint = functionBody->CreateNewDefaultEntryPoint();
- functionBody->ReinitializeExecutionModeAndLimits();
- #if ENABLE_NATIVE_CODEGEN
- // In order for the function to ever get JIT again, we need to call GenerateFunction now
- if (!PHASE_OFF(Js::BackEndPhase, functionBody) && !functionBody->GetScriptContext()->GetConfig()->IsNoNative())
- {
- GenerateFunction(functionBody->GetScriptContext()->GetNativeCodeGenerator(), functionBody);
- }
- #endif
- }
- functionBody->TraceExecutionMode("JitCodeExpired");
- }
- else
- {
- newEntryPoint = defaultEntryPointInfo;
- }
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Expiring 0x%p\n"), this);
- this->functionProxy->MapFunctionObjectTypes([&] (ScriptFunctionType* functionType)
- {
- Assert(functionType->GetTypeId() == TypeIds_Function);
- if (functionType->GetEntryPointInfo() == this)
- {
- OUTPUT_TRACE(Js::ExpirableCollectPhase, _u("Type 0x%p uses this entry point- switching to default entry point\n"), this);
- functionType->SetEntryPointInfo(newEntryPoint);
- // we are allowed to replace the entry point on the type only if it's
- // directly using the jitted code or a type is referencing this entry point
- // but the entry point hasn't been called since the codegen thunk was installed on it
- if (functionType->GetEntryPoint() == functionProxy->GetDirectEntryPoint(this) || IsIntermediateCodeGenThunk(functionType->GetEntryPoint()))
- {
- functionType->SetEntryPoint(this->functionProxy->GetDirectEntryPoint(newEntryPoint));
- }
- }
- else
- {
- Assert(!functionType->GetEntryPointInfo()->IsFunctionEntryPointInfo() ||
- ((FunctionEntryPointInfo*)functionType->GetEntryPointInfo())->IsCleanedUp()
- || functionType->GetEntryPoint() != this->GetNativeEntrypoint());
- }
- });
- if(expiringSimpleJitEntryPointInfo)
- {
- // We could have just created a new entry point info that is using the simple JIT code. An allocation may have
- // triggered shortly after, resulting in expiring the simple JIT entry point info. Update any entry point infos
- // that are using the simple JIT code, and update the original entry point as necessary as well.
- const JavascriptMethod newOriginalEntryPoint =
- functionBody->GetDynamicInterpreterEntryPoint()
- ? reinterpret_cast<JavascriptMethod>(
- InterpreterThunkEmitter::ConvertToEntryPoint(functionBody->GetDynamicInterpreterEntryPoint()))
- : DefaultEntryThunk;
- const JavascriptMethod currentThunk = functionBody->GetScriptContext()->CurrentThunk;
- const JavascriptMethod newDirectEntryPoint =
- currentThunk == DefaultEntryThunk ? newOriginalEntryPoint : currentThunk;
- const JavascriptMethod simpleJitNativeAddress = GetNativeEntrypoint();
- functionBody->MapEntryPoints([&](const int entryPointIndex, FunctionEntryPointInfo *const entryPointInfo)
- {
- if(entryPointInfo != this && entryPointInfo->jsMethod == simpleJitNativeAddress)
- {
- entryPointInfo->jsMethod = newDirectEntryPoint;
- }
- });
- if(functionBody->GetOriginalEntryPoint_Unchecked() == simpleJitNativeAddress)
- {
- functionBody->SetOriginalEntryPoint(newOriginalEntryPoint);
- functionBody->VerifyOriginalEntryPoint();
- }
- }
- threadContext->QueueFreeOldEntryPointInfoIfInScript(this);
- }
- catch (Js::OutOfMemoryException)
- {
- // If we can't allocate a new entry point, skip expiring this object
- if(expiringSimpleJitEntryPointInfo)
- {
- simpleJitEntryPointInfo = this;
- functionBody->SetSimpleJitEntryPointInfo(this);
- }
- }
- }
- #endif
- #ifdef PERF_COUNTERS
- void LoopEntryPointInfo::OnRecorded()
- {
- PERF_COUNTER_ADD(Code, TotalNativeCodeSize, GetCodeSize());
- PERF_COUNTER_ADD(Code, LoopNativeCodeSize, GetCodeSize());
- PERF_COUNTER_ADD(Code, DynamicNativeCodeSize, GetCodeSize());
- }
- #endif
- FunctionBody *LoopEntryPointInfo::GetFunctionBody() const
- {
- return loopHeader->functionBody;
- }
- //End AsmJs Support
- void LoopEntryPointInfo::OnCleanup(bool isShutdown)
- {
- #ifdef ASMJS_PLAT
- if (this->IsCodeGenDone() && !this->GetIsTJMode())
- #else
- if (this->IsCodeGenDone())
- #endif
- {
- JS_ETW(EtwTrace::LogLoopBodyUnloadEvent(this->loopHeader->functionBody, this,
- this->loopHeader->functionBody->GetLoopNumber(this->loopHeader)));
- #if ENABLE_NATIVE_CODEGEN
- this->CleanupNativeCode(this->loopHeader->functionBody->GetScriptContext());
- #endif
- }
- }
- #if ENABLE_NATIVE_CODEGEN
- void LoopEntryPointInfo::ResetOnNativeCodeInstallFailure()
- {
- // Since we call the address on the entryPointInfo for loop bodies, all we need to do is to roll back
- // the fields on the entryPointInfo related to transferring data from jit thread to main thread (already
- // being done in EntryPointInfo::OnNativeCodeInstallFailure). On the next loop iteration, the interpreter
- // will call EntryPointInfo::EnsureIsReadyToCall and we'll try to process jit transfer data again.
- }
- #endif
- void LoopHeader::Init( FunctionBody * functionBody )
- {
- // DisableJIT-TODO: Should this entire class be ifdefed out?
- #if ENABLE_NATIVE_CODEGEN
- this->functionBody = functionBody;
- Recycler* recycler = functionBody->GetScriptContext()->GetRecycler();
- // Sync entryPoints changes to etw rundown lock
- auto syncObj = functionBody->GetScriptContext()->GetThreadContext()->GetFunctionBodyLock();
- this->entryPoints = RecyclerNew(recycler, LoopEntryPointList, recycler, syncObj);
- this->CreateEntryPoint();
- #endif
- }
- #if ENABLE_NATIVE_CODEGEN
- int LoopHeader::CreateEntryPoint()
- {
- ScriptContext* scriptContext = this->functionBody->GetScriptContext();
- Recycler* recycler = scriptContext->GetRecycler();
- LoopEntryPointInfo* entryPoint = RecyclerNew(recycler, LoopEntryPointInfo, this, scriptContext->GetLibrary());
- return this->entryPoints->Add(entryPoint);
- }
- void LoopHeader::ReleaseEntryPoints()
- {
- for (int iEntryPoint = 0; iEntryPoint < this->entryPoints->Count(); iEntryPoint++)
- {
- LoopEntryPointInfo * entryPoint = this->entryPoints->Item(iEntryPoint);
- if (entryPoint != nullptr && entryPoint->IsCodeGenDone())
- {
- // ReleaseEntryPoints is not called during recycler shutdown scenarios
- // We also don't capture the cleanup stack since we've not seen cleanup bugs affect
- // loop entry points so far. We can pass true here if this is no longer the case.
- entryPoint->Cleanup(false /* isShutdown */, false /* capture cleanup stack */);
- this->entryPoints->Item(iEntryPoint, nullptr);
- }
- }
- }
- #endif
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- void FunctionBody::DumpRegStats(FunctionBody *funcBody)
- {
- if (funcBody->callCountStats == 0)
- {
- return;
- }
- uint loads = funcBody->regAllocLoadCount;
- uint stores = funcBody->regAllocStoreCount;
- if (Js::Configuration::Global.flags.NormalizeStats)
- {
- loads /= this->callCountStats;
- stores /= this->callCountStats;
- }
- funcBody->DumpFullFunctionName();
- Output::SkipToColumn(55);
- Output::Print(_u("Calls:%6d Loads:%9d Stores:%9d Total refs:%9d\n"), this->callCountStats,
- loads, stores, loads + stores);
- }
- #endif
- Js::RegSlot FunctionBody::GetRestParamRegSlot()
- {
- Js::RegSlot dstRegSlot = GetConstantCount();
- if (GetHasImplicitArgIns())
- {
- dstRegSlot += GetInParamsCount() - 1;
- }
- return dstRegSlot;
- }
- uint FunctionBody::GetNumberOfRecursiveCallSites()
- {
- uint recursiveInlineSpan = 0;
- uint recursiveCallSiteInlineInfo = 0;
- #if ENABLE_PROFILE_INFO
- if (this->HasDynamicProfileInfo())
- {
- recursiveCallSiteInlineInfo = this->dynamicProfileInfo->GetRecursiveInlineInfo();
- }
- #endif
- while (recursiveCallSiteInlineInfo)
- {
- recursiveInlineSpan += (recursiveCallSiteInlineInfo & 1);
- recursiveCallSiteInlineInfo >>= 1;
- }
- return recursiveInlineSpan;
- }
- bool FunctionBody::CanInlineRecursively(uint depth, bool tryAggressive)
- {
- uint recursiveInlineSpan = this->GetNumberOfRecursiveCallSites();
- uint minRecursiveInlineDepth = (uint)CONFIG_FLAG(RecursiveInlineDepthMin);
- if (recursiveInlineSpan != this->GetProfiledCallSiteCount() || tryAggressive == false)
- {
- return depth < minRecursiveInlineDepth;
- }
- uint maxRecursiveInlineDepth = (uint)CONFIG_FLAG(RecursiveInlineDepthMax);
- uint maxRecursiveBytecodeBudget = (uint)CONFIG_FLAG(RecursiveInlineThreshold);
- uint numberOfAllowedFuncs = maxRecursiveBytecodeBudget / this->GetByteCodeWithoutLDACount();
- uint maxDepth;
- if (recursiveInlineSpan == 1)
- {
- maxDepth = numberOfAllowedFuncs;
- }
- else
- {
- maxDepth = (uint)ceil(log((double)((double)numberOfAllowedFuncs) / log((double)recursiveInlineSpan)));
- }
- maxDepth = maxDepth < minRecursiveInlineDepth ? minRecursiveInlineDepth : maxDepth;
- maxDepth = maxDepth < maxRecursiveInlineDepth ? maxDepth : maxRecursiveInlineDepth;
- return depth < maxDepth;
- }
- static const char16 LoopWStr[] = _u("Loop");
- size_t FunctionBody::GetLoopBodyName(uint loopNumber, _Out_writes_opt_z_(sizeInChars) WCHAR* displayName, _In_ size_t sizeInChars)
- {
- const char16* functionName = this->GetExternalDisplayName();
- size_t length = wcslen(functionName) + /*length of largest int32*/ 10 + _countof(LoopWStr) + /*null*/ 1;
- if (sizeInChars < length || displayName == nullptr)
- {
- return length;
- }
- int charsWritten = swprintf_s(displayName, length, _u("%s%s%u"), functionName, LoopWStr, loopNumber + 1);
- Assert(charsWritten != -1);
- return charsWritten + /*nullptr*/ 1;
- }
- void FunctionBody::MapAndSetEnvRegister(RegSlot reg)
- {
- Assert(!m_hasEnvRegister);
- SetEnvRegister(this->MapRegSlot(reg));
- }
- void FunctionBody::SetEnvRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasEnvRegister = false;
- }
- else
- {
- m_hasEnvRegister = true;
- SetCountField(CounterFields::EnvRegister, reg);
- }
- }
- RegSlot FunctionBody::GetEnvRegister() const
- {
- return m_hasEnvRegister ? GetCountField(CounterFields::EnvRegister) : Constants::NoRegister;
- }
- void FunctionBody::MapAndSetThisRegisterForEventHandler(RegSlot reg)
- {
- Assert(!m_hasThisRegisterForEventHandler);
- SetThisRegisterForEventHandler(this->MapRegSlot(reg));
- }
- void FunctionBody::SetThisRegisterForEventHandler(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasThisRegisterForEventHandler = false;
- }
- else
- {
- m_hasThisRegisterForEventHandler = true;
- SetCountField(CounterFields::ThisRegisterForEventHandler, reg);
- }
- }
- RegSlot FunctionBody::GetThisRegisterForEventHandler() const
- {
- return m_hasThisRegisterForEventHandler ? GetCountField(CounterFields::ThisRegisterForEventHandler) : Constants::NoRegister;
- }
- void FunctionBody::SetLocalClosureRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasLocalClosureRegister = false;
- }
- else
- {
- m_hasLocalClosureRegister = true;
- SetCountField(CounterFields::LocalClosureRegister, reg);
- }
- }
- void FunctionBody::MapAndSetLocalClosureRegister(RegSlot reg)
- {
- Assert(!m_hasLocalClosureRegister);
- SetLocalClosureRegister(this->MapRegSlot(reg));
- }
- RegSlot FunctionBody::GetLocalClosureRegister() const
- {
- return m_hasLocalClosureRegister ? GetCountField(CounterFields::LocalClosureRegister) : Constants::NoRegister;
- }
- void FunctionBody::SetParamClosureRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasParamClosureRegister = false;
- }
- else
- {
- m_hasParamClosureRegister = true;
- SetCountField(CounterFields::ParamClosureRegister, reg);
- }
- }
- void FunctionBody::MapAndSetParamClosureRegister(RegSlot reg)
- {
- Assert(!m_hasParamClosureRegister);
- SetParamClosureRegister(this->MapRegSlot(reg));
- }
- RegSlot FunctionBody::GetParamClosureRegister() const
- {
- return m_hasParamClosureRegister ? GetCountField(CounterFields::ParamClosureRegister) : Constants::NoRegister;
- }
- void FunctionBody::MapAndSetLocalFrameDisplayRegister(RegSlot reg)
- {
- Assert(!m_hasLocalFrameDisplayRegister);
- SetLocalFrameDisplayRegister(this->MapRegSlot(reg));
- }
- void FunctionBody::SetLocalFrameDisplayRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasLocalFrameDisplayRegister = false;
- }
- else
- {
- m_hasLocalFrameDisplayRegister = true;
- SetCountField(CounterFields::LocalFrameDisplayRegister, reg);
- }
- }
- RegSlot FunctionBody::GetLocalFrameDisplayRegister() const
- {
- return m_hasLocalFrameDisplayRegister ? GetCountField(CounterFields::LocalFrameDisplayRegister) : Constants::NoRegister;
- }
- void FunctionBody::MapAndSetFirstInnerScopeRegister(RegSlot reg)
- {
- Assert(!m_hasFirstInnerScopeRegister);
- SetFirstInnerScopeRegister(this->MapRegSlot(reg));
- }
- void FunctionBody::SetFirstInnerScopeRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasFirstInnerScopeRegister = false;
- }
- else
- {
- m_hasFirstInnerScopeRegister = true;
- SetCountField(CounterFields::FirstInnerScopeRegister, reg);
- }
- }
- RegSlot FunctionBody::GetFirstInnerScopeRegister() const
- {
- return m_hasFirstInnerScopeRegister ? GetCountField(CounterFields::FirstInnerScopeRegister) : Constants::NoRegister;
- }
- void FunctionBody::MapAndSetFuncExprScopeRegister(RegSlot reg)
- {
- Assert(!m_hasFuncExprScopeRegister);
- SetFuncExprScopeRegister(this->MapRegSlot(reg));
- }
- void FunctionBody::SetFuncExprScopeRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasFuncExprScopeRegister = false;
- }
- else
- {
- m_hasFuncExprScopeRegister = true;
- SetCountField(CounterFields::FuncExprScopeRegister, reg);
- }
- }
- RegSlot FunctionBody::GetFuncExprScopeRegister() const
- {
- return m_hasFuncExprScopeRegister ? GetCountField(CounterFields::FuncExprScopeRegister) : Constants::NoRegister;
- }
- void FunctionBody::SetFirstTmpRegister(RegSlot reg)
- {
- if (reg == Constants::NoRegister)
- {
- m_hasFirstTmpRegister = false;
- }
- else
- {
- m_hasFirstTmpRegister = true;
- SetCountField(CounterFields::FirstTmpRegister, reg);
- }
- }
- RegSlot FunctionBody::GetFirstTmpRegister() const
- {
- return m_hasFirstTmpRegister ? this->GetCountField(CounterFields::FirstTmpRegister) : Constants::NoRegister;
- }
- #if DBG && defined(ENABLE_SCRIPT_DEBUGGING)
- Js::DebuggerMode FunctionBody::GetDebuggerMode()
- {
- return this->GetScriptContext()->GetDebugContext()->GetDebuggerMode();
- }
- #endif
- }
- #if !DBG
- // Don't grow these data structure unless absolutely necessary
- CompileAssert(sizeof(Js::EntryPointInfo) <= 56);
- CompileAssert(sizeof(Js::FunctionEntryPointInfo) <= 96);
- #endif
|