| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152 |
- //-------------------------------------------------------------------------------------------------------
- // Copyright (C) Microsoft. All rights reserved.
- // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
- //-------------------------------------------------------------------------------------------------------
- #include "RuntimeByteCodePch.h"
- #include "FormalsUtil.h"
- #include "Library\StackScriptFunction.h"
- void PreVisitBlock(ParseNode *pnodeBlock, ByteCodeGenerator *byteCodeGenerator);
- void PostVisitBlock(ParseNode *pnodeBlock, ByteCodeGenerator *byteCodeGenerator);
- bool IsCallOfConstants(ParseNode *pnode)
- {
- return pnode->sxCall.callOfConstants && pnode->sxCall.argCount > ByteCodeGenerator::MinArgumentsForCallOptimization;
- }
- template <class PrefixFn, class PostfixFn>
- void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn prefix, PostfixFn postfix, ParseNode * pnodeParent = nullptr);
- template<class TContext>
- void VisitIndirect(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, TContext* context,
- void (*prefix)(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, TContext* context),
- void (*postfix)(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, TContext* context),
- ParseNode *pnodeParent = nullptr)
- {
- Visit(pnode, byteCodeGenerator,
- [context, prefix](ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator)
- {
- prefix(pnode, byteCodeGenerator, context);
- },
- [context, postfix](ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator)
- {
- if (postfix)
- {
- postfix(pnode, byteCodeGenerator, context);
- }
- }, pnodeParent);
- }
- template <class PrefixFn, class PostfixFn>
- void VisitList(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn prefix, PostfixFn postfix)
- {
- Assert(pnode != nullptr);
- Assert(pnode->nop == knopList);
- do
- {
- ParseNode * pnode1 = pnode->sxBin.pnode1;
- Visit(pnode1, byteCodeGenerator, prefix, postfix);
- pnode = pnode->sxBin.pnode2;
- }
- while (pnode->nop == knopList);
- Visit(pnode, byteCodeGenerator, prefix, postfix);
- }
- template <class PrefixFn, class PostfixFn>
- void VisitWithStmt(ParseNode *pnode, Js::RegSlot loc, ByteCodeGenerator* byteCodeGenerator, PrefixFn prefix, PostfixFn postfix, ParseNode *pnodeParent)
- {
- // Note the fact that we're visiting the body of a with statement. This allows us to optimize register assignment
- // in the normal case of calls not requiring that their "this" objects be found dynamically.
- Scope *scope = pnode->sxWith.scope;
- byteCodeGenerator->PushScope(scope);
- Visit(pnode->sxWith.pnodeBody, byteCodeGenerator, prefix, postfix, pnodeParent);
- scope->SetIsObject();
- scope->SetMustInstantiate(true);
- byteCodeGenerator->PopScope();
- }
- bool BlockHasOwnScope(ParseNode* pnodeBlock, ByteCodeGenerator *byteCodeGenerator)
- {
- Assert(pnodeBlock->nop == knopBlock);
- return pnodeBlock->sxBlock.scope != nullptr &&
- (!(pnodeBlock->grfpn & fpnSyntheticNode) ||
- (pnodeBlock->sxBlock.blockType == PnodeBlockType::Global && byteCodeGenerator->IsEvalWithBlockScopingNoParentScopeInfo()));
- }
- void BeginVisitBlock(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- if (BlockHasOwnScope(pnode, byteCodeGenerator))
- {
- Scope *scope = pnode->sxBlock.scope;
- FuncInfo *func = scope->GetFunc();
- if (scope->IsInnerScope())
- {
- // Give this scope an index so its slots can be accessed via the index in the byte code,
- // not a register.
- scope->SetInnerScopeIndex(func->AcquireInnerScopeIndex());
- }
- byteCodeGenerator->PushBlock(pnode);
- byteCodeGenerator->PushScope(pnode->sxBlock.scope);
- }
- }
- void EndVisitBlock(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- if (BlockHasOwnScope(pnode, byteCodeGenerator))
- {
- Scope *scope = pnode->sxBlock.scope;
- FuncInfo *func = scope->GetFunc();
- if (!byteCodeGenerator->IsInDebugMode() &&
- scope->HasInnerScopeIndex())
- {
- // In debug mode, don't release the current index, as we're giving each scope a unique index, regardless
- // of nesting.
- Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
- func->ReleaseInnerScopeIndex();
- }
- Assert(byteCodeGenerator->GetCurrentScope() == scope);
- byteCodeGenerator->PopScope();
- byteCodeGenerator->PopBlock();
- }
- }
- void BeginVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- Scope *scope = pnode->sxCatch.scope;
- FuncInfo *func = scope->GetFunc();
- if (func->GetCallsEval() || func->GetChildCallsEval() ||
- (byteCodeGenerator->GetFlags() & (fscrEval | fscrImplicitThis | fscrImplicitParents)))
- {
- scope->SetIsObject();
- }
- // Give this scope an index so its slots can be accessed via the index in the byte code,
- // not a register.
- scope->SetInnerScopeIndex(func->AcquireInnerScopeIndex());
- byteCodeGenerator->PushScope(pnode->sxCatch.scope);
- }
- void EndVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- Scope *scope = pnode->sxCatch.scope;
- if (scope->HasInnerScopeIndex() && !byteCodeGenerator->IsInDebugMode())
- {
- // In debug mode, don't release the current index, as we're giving each scope a unique index,
- // regardless of nesting.
- FuncInfo *func = scope->GetFunc();
- Assert(scope->GetInnerScopeIndex() == func->CurrentInnerScopeIndex());
- func->ReleaseInnerScopeIndex();
- }
- byteCodeGenerator->PopScope();
- }
- bool CreateNativeArrays(ByteCodeGenerator *byteCodeGenerator, FuncInfo *funcInfo)
- {
- #if ENABLE_PROFILE_INFO
- Js::FunctionBody *functionBody = funcInfo ? funcInfo->GetParsedFunctionBody() : nullptr;
- return
- !PHASE_OFF_OPTFUNC(Js::NativeArrayPhase, functionBody) &&
- !byteCodeGenerator->IsInDebugMode() &&
- (
- functionBody
- ? Js::DynamicProfileInfo::IsEnabled(Js::NativeArrayPhase, functionBody)
- : Js::DynamicProfileInfo::IsEnabledForAtLeastOneFunction(
- Js::NativeArrayPhase,
- byteCodeGenerator->GetScriptContext())
- );
- #else
- return false;
- #endif
- }
- bool EmitAsConstantArray(ParseNode *pnodeArr, ByteCodeGenerator *byteCodeGenerator)
- {
- Assert(pnodeArr && pnodeArr->nop == knopArray);
- // TODO: We shouldn't have to handle an empty funcinfo stack here, but there seem to be issues
- // with the stack involved nested deferral. Remove this null check when those are resolved.
- if (CreateNativeArrays(byteCodeGenerator, byteCodeGenerator->TopFuncInfo()))
- {
- return pnodeArr->sxArrLit.arrayOfNumbers;
- }
- return pnodeArr->sxArrLit.arrayOfTaggedInts && pnodeArr->sxArrLit.count > 1;
- }
- void PropagateFlags(ParseNode *pnodeChild, ParseNode *pnodeParent);
- template<class PrefixFn, class PostfixFn>
- void Visit(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn prefix, PostfixFn postfix, ParseNode *pnodeParent)
- {
- if (pnode == nullptr)
- {
- return;
- }
- ThreadContext::ProbeCurrentStackNoDispose(Js::Constants::MinStackByteCodeVisitor, byteCodeGenerator->GetScriptContext());
- prefix(pnode, byteCodeGenerator);
- switch (pnode->nop)
- {
- default:
- {
- uint flags = ParseNode::Grfnop(pnode->nop);
- if (flags&fnopUni)
- {
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- }
- else if (flags&fnopBin)
- {
- Visit(pnode->sxBin.pnode1, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxBin.pnode2, byteCodeGenerator, prefix, postfix);
- }
- break;
- }
- case knopParamPattern:
- Visit(pnode->sxParamPattern.pnode1, byteCodeGenerator, prefix, postfix);
- break;
- case knopArrayPattern:
- if (!byteCodeGenerator->InDestructuredPattern())
- {
- byteCodeGenerator->SetInDestructuredPattern(true);
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- byteCodeGenerator->SetInDestructuredPattern(false);
- }
- else
- {
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- }
- break;
- case knopCall:
- Visit(pnode->sxCall.pnodeTarget, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxCall.pnodeArgs, byteCodeGenerator, prefix, postfix);
- break;
- case knopNew:
- {
- Visit(pnode->sxCall.pnodeTarget, byteCodeGenerator, prefix, postfix);
- if (!IsCallOfConstants(pnode))
- {
- Visit(pnode->sxCall.pnodeArgs, byteCodeGenerator, prefix, postfix);
- }
- break;
- }
- case knopQmark:
- Visit(pnode->sxTri.pnode1, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxTri.pnode2, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxTri.pnode3, byteCodeGenerator, prefix, postfix);
- break;
- case knopList:
- VisitList(pnode, byteCodeGenerator, prefix, postfix);
- break;
- // PTNODE(knopVarDecl , "varDcl" ,None ,Var ,fnopNone)
- case knopVarDecl:
- case knopConstDecl:
- case knopLetDecl:
- if (pnode->sxVar.pnodeInit != nullptr)
- Visit(pnode->sxVar.pnodeInit, byteCodeGenerator, prefix, postfix);
- break;
- // PTNODE(knopFncDecl , "fncDcl" ,None ,Fnc ,fnopLeaf)
- case knopFncDecl:
- {
- // Inner function declarations are visited before anything else in the scope.
- // (See VisitFunctionsInScope.)
- break;
- }
- case knopClassDecl:
- {
- // Visit the extends expression first, since it's bound outside the scope containing the class name.
- Visit(pnode->sxClass.pnodeExtends, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxClass.pnodeDeclName, byteCodeGenerator, prefix, postfix);
- // Now visit the class name and methods.
- BeginVisitBlock(pnode->sxClass.pnodeBlock, byteCodeGenerator);
- Visit(pnode->sxClass.pnodeName, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxClass.pnodeStaticMembers, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxClass.pnodeConstructor, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxClass.pnodeMembers, byteCodeGenerator, prefix, postfix);
- EndVisitBlock(pnode->sxClass.pnodeBlock, byteCodeGenerator);
- break;
- }
- case knopStrTemplate:
- {
- // Visit the string node lists only if we do not have a tagged template.
- // We never need to visit the raw strings as they are not used in non-tagged templates and
- // tagged templates will register them as part of the callsite constant object.
- if (!pnode->sxStrTemplate.isTaggedTemplate)
- {
- Visit(pnode->sxStrTemplate.pnodeStringLiterals, byteCodeGenerator, prefix, postfix);
- }
- Visit(pnode->sxStrTemplate.pnodeSubstitutionExpressions, byteCodeGenerator, prefix, postfix);
- break;
- }
- // PTNODE(knopProg , "program" ,None ,Fnc ,fnopNone)
- case knopProg:
- {
- // We expect that the global statements have been generated (meaning that the pnodeFncs
- // field is a real pointer, not an enumeration).
- Assert(pnode->sxFnc.pnodeBody);
- uint i = 0;
- VisitNestedScopes(pnode->sxFnc.pnodeScopes, pnode, byteCodeGenerator, prefix, postfix, &i);
- // Visiting global code: track the last value statement.
- BeginVisitBlock(pnode->sxFnc.pnodeScopes, byteCodeGenerator);
- pnode->sxProg.pnodeLastValStmt = VisitBlock(pnode->sxFnc.pnodeBody, byteCodeGenerator, prefix, postfix);
- EndVisitBlock(pnode->sxFnc.pnodeScopes, byteCodeGenerator);
- break;
- }
- case knopFor:
- BeginVisitBlock(pnode->sxFor.pnodeBlock, byteCodeGenerator);
- Visit(pnode->sxFor.pnodeInit, byteCodeGenerator, prefix, postfix);
- byteCodeGenerator->EnterLoop();
- Visit(pnode->sxFor.pnodeCond, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxFor.pnodeIncr, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxFor.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- byteCodeGenerator->ExitLoop();
- EndVisitBlock(pnode->sxFor.pnodeBlock, byteCodeGenerator);
- break;
- // PTNODE(knopIf , "if" ,None ,If ,fnopNone)
- case knopIf:
- Visit(pnode->sxIf.pnodeCond, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxIf.pnodeTrue, byteCodeGenerator, prefix, postfix, pnode);
- if (pnode->sxIf.pnodeFalse != nullptr)
- {
- Visit(pnode->sxIf.pnodeFalse, byteCodeGenerator, prefix, postfix, pnode);
- }
- break;
- // PTNODE(knopWhile , "while" ,None ,While,fnopBreak|fnopContinue)
- // PTNODE(knopDoWhile , "do-while" ,None ,While,fnopBreak|fnopContinue)
- case knopDoWhile:
- case knopWhile:
- byteCodeGenerator->EnterLoop();
- Visit(pnode->sxWhile.pnodeCond, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxWhile.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- byteCodeGenerator->ExitLoop();
- break;
- // PTNODE(knopForIn , "for in" ,None ,ForIn,fnopBreak|fnopContinue|fnopCleanup)
- case knopForIn:
- case knopForOf:
- BeginVisitBlock(pnode->sxForInOrForOf.pnodeBlock, byteCodeGenerator);
- Visit(pnode->sxForInOrForOf.pnodeLval, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxForInOrForOf.pnodeObj, byteCodeGenerator, prefix, postfix);
- byteCodeGenerator->EnterLoop();
- Visit(pnode->sxForInOrForOf.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- byteCodeGenerator->ExitLoop();
- EndVisitBlock(pnode->sxForInOrForOf.pnodeBlock, byteCodeGenerator);
- break;
- // PTNODE(knopReturn , "return" ,None ,Uni ,fnopNone)
- case knopReturn:
- if (pnode->sxReturn.pnodeExpr != nullptr)
- Visit(pnode->sxReturn.pnodeExpr, byteCodeGenerator, prefix, postfix);
- break;
- // PTNODE(knopBlock , "{}" ,None ,Block,fnopNone)
- case knopBlock:
- {
- if (pnode->sxBlock.pnodeStmt != nullptr)
- {
- BeginVisitBlock(pnode, byteCodeGenerator);
- pnode->sxBlock.pnodeLastValStmt = VisitBlock(pnode->sxBlock.pnodeStmt, byteCodeGenerator, prefix, postfix, pnode);
- EndVisitBlock(pnode, byteCodeGenerator);
- }
- else
- {
- pnode->sxBlock.pnodeLastValStmt = nullptr;
- }
- break;
- }
- // PTNODE(knopWith , "with" ,None ,With ,fnopCleanup)
- case knopWith:
- Visit(pnode->sxWith.pnodeObj, byteCodeGenerator, prefix, postfix);
- VisitWithStmt(pnode, pnode->sxWith.pnodeObj->location, byteCodeGenerator, prefix, postfix, pnode);
- break;
- // PTNODE(knopBreak , "break" ,None ,Jump ,fnopNone)
- case knopBreak:
- // TODO: some representation of target
- break;
- // PTNODE(knopContinue , "continue" ,None ,Jump ,fnopNone)
- case knopContinue:
- // TODO: some representation of target
- break;
- // PTNODE(knopLabel , "label" ,None ,Label,fnopNone)
- case knopLabel:
- // TODO: print labeled statement
- break;
- // PTNODE(knopSwitch , "switch" ,None ,Switch,fnopBreak)
- case knopSwitch:
- Visit(pnode->sxSwitch.pnodeVal, byteCodeGenerator, prefix, postfix);
- BeginVisitBlock(pnode->sxSwitch.pnodeBlock, byteCodeGenerator);
- for (ParseNode *pnodeT = pnode->sxSwitch.pnodeCases; nullptr != pnodeT; pnodeT = pnodeT->sxCase.pnodeNext)
- {
- Visit(pnodeT, byteCodeGenerator, prefix, postfix, pnode);
- }
- Visit(pnode->sxSwitch.pnodeBlock, byteCodeGenerator, prefix, postfix);
- EndVisitBlock(pnode->sxSwitch.pnodeBlock, byteCodeGenerator);
- break;
- // PTNODE(knopCase , "case" ,None ,Case ,fnopNone)
- case knopCase:
- Visit(pnode->sxCase.pnodeExpr, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxCase.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- break;
- case knopTypeof:
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- break;
- // PTNODE(knopTryCatchFinally,"try-catch-finally",None,TryCatchFinally,fnopCleanup)
- case knopTryFinally:
- Visit(pnode->sxTryFinally.pnodeTry, byteCodeGenerator, prefix, postfix, pnode);
- Visit(pnode->sxTryFinally.pnodeFinally, byteCodeGenerator, prefix, postfix, pnode);
- break;
- // PTNODE(knopTryCatch , "try-catch" ,None ,TryCatch ,fnopCleanup)
- case knopTryCatch:
- Visit(pnode->sxTryCatch.pnodeTry, byteCodeGenerator, prefix, postfix, pnode);
- Visit(pnode->sxTryCatch.pnodeCatch, byteCodeGenerator, prefix, postfix, pnode);
- break;
- // PTNODE(knopTry , "try" ,None ,Try ,fnopCleanup)
- case knopTry:
- Visit(pnode->sxTry.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- break;
- case knopCatch:
- BeginVisitCatch(pnode, byteCodeGenerator);
- Visit(pnode->sxCatch.pnodeParam, byteCodeGenerator, prefix, postfix);
- Visit(pnode->sxCatch.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- EndVisitCatch(pnode, byteCodeGenerator);
- break;
- case knopFinally:
- Visit(pnode->sxFinally.pnodeBody, byteCodeGenerator, prefix, postfix, pnode);
- break;
- // PTNODE(knopThrow , "throw" ,None ,Uni ,fnopNone)
- case knopThrow:
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- break;
- case knopArray:
- {
- bool arrayLitOpt = EmitAsConstantArray(pnode, byteCodeGenerator);
- if (!arrayLitOpt)
- {
- Visit(pnode->sxUni.pnode1, byteCodeGenerator, prefix, postfix);
- }
- break;
- }
- case knopComma:
- {
- ParseNode *pnode1 = pnode->sxBin.pnode1;
- if (pnode1->nop == knopComma)
- {
- // Spot-fix to avoid recursion on very large comma expressions.
- ArenaAllocator *alloc = byteCodeGenerator->GetAllocator();
- SList<ParseNode*> rhsStack(alloc);
- do
- {
- rhsStack.Push(pnode1->sxBin.pnode2);
- pnode1 = pnode1->sxBin.pnode1;
- }
- while (pnode1->nop == knopComma);
- Visit(pnode1, byteCodeGenerator, prefix, postfix);
- while (!rhsStack.Empty())
- {
- ParseNode *pnodeRhs = rhsStack.Pop();
- Visit(pnodeRhs, byteCodeGenerator, prefix, postfix);
- }
- }
- else
- {
- Visit(pnode1, byteCodeGenerator, prefix, postfix);
- }
- Visit(pnode->sxBin.pnode2, byteCodeGenerator, prefix, postfix);
- }
- break;
- }
- if (pnodeParent)
- {
- PropagateFlags(pnode, pnodeParent);
- }
- postfix(pnode, byteCodeGenerator);
- }
- bool IsJump(ParseNode *pnode)
- {
- switch (pnode->nop)
- {
- case knopBreak:
- case knopContinue:
- case knopThrow:
- case knopReturn:
- return true;
- case knopBlock:
- case knopDoWhile:
- case knopWhile:
- case knopWith:
- case knopIf:
- case knopForIn:
- case knopForOf:
- case knopFor:
- case knopSwitch:
- case knopCase:
- case knopTryFinally:
- case knopTryCatch:
- case knopTry:
- case knopCatch:
- case knopFinally:
- return (pnode->sxStmt.grfnop & fnopJump) != 0;
- default:
- return false;
- }
- }
- void PropagateFlags(ParseNode *pnodeChild, ParseNode *pnodeParent)
- {
- if (IsJump(pnodeChild))
- {
- pnodeParent->sxStmt.grfnop |= fnopJump;
- }
- }
- void Bind(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator);
- void BindReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator);
- void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator);
- // TODO[ianhall]: This should be in a shared AST Utility header or source file
- bool IsExpressionStatement(ParseNode* stmt, const Js::ScriptContext *const scriptContext)
- {
- if (stmt->nop == knopFncDecl)
- {
- // 'knopFncDecl' is used for both function declarations and function expressions. In a program, a function expression
- // produces the function object that is created for the function expression as its value for the program. A function
- // declaration does not produce a value for the program.
- return !stmt->sxFnc.IsDeclaration();
- }
- if ((stmt->nop >= 0) && (stmt->nop<knopLim))
- {
- return (ParseNode::Grfnop(stmt->nop) & fnopNotExprStmt) == 0;
- }
- return false;
- }
- bool MustProduceValue(ParseNode *pnode, const Js::ScriptContext *const scriptContext)
- {
- // Determine whether the current statement is guaranteed to produce a value.
- if (IsExpressionStatement(pnode, scriptContext))
- {
- // These are trivially true.
- return true;
- }
- for (;;)
- {
- switch (pnode->nop)
- {
- case knopFor:
- // Check the common "for (;;)" case.
- if (pnode->sxFor.pnodeCond != nullptr ||
- pnode->sxFor.pnodeBody == nullptr)
- {
- return false;
- }
- // Loop body is always executed. Look at the loop body next.
- pnode = pnode->sxFor.pnodeBody;
- break;
- case knopIf:
- // True only if both "if" and "else" exist, and both produce values.
- if (pnode->sxIf.pnodeTrue == nullptr ||
- pnode->sxIf.pnodeFalse == nullptr)
- {
- return false;
- }
- if (!MustProduceValue(pnode->sxIf.pnodeFalse, scriptContext))
- {
- return false;
- }
- pnode = pnode->sxIf.pnodeTrue;
- break;
- case knopWhile:
- // Check the common "while (1)" case.
- if (pnode->sxWhile.pnodeBody == nullptr ||
- (pnode->sxWhile.pnodeCond &&
- (pnode->sxWhile.pnodeCond->nop != knopInt ||
- pnode->sxWhile.pnodeCond->sxInt.lw == 0)))
- {
- return false;
- }
- // Loop body is always executed. Look at the loop body next.
- pnode = pnode->sxWhile.pnodeBody;
- break;
- case knopDoWhile:
- if (pnode->sxWhile.pnodeBody == nullptr)
- {
- return false;
- }
- // Loop body is always executed. Look at the loop body next.
- pnode = pnode->sxWhile.pnodeBody;
- break;
- case knopBlock:
- return pnode->sxBlock.pnodeLastValStmt != nullptr;
- case knopWith:
- if (pnode->sxWith.pnodeBody == nullptr)
- {
- return false;
- }
- pnode = pnode->sxWith.pnodeBody;
- break;
- case knopSwitch:
- {
- // This is potentially the most inefficient case. We could consider adding a flag to the PnSwitch
- // struct and computing it when we visit the switch, but:
- // a. switch statements at global scope shouldn't be that common;
- // b. switch statements with many arms shouldn't be that common;
- // c. switches without default cases can be trivially skipped.
- if (pnode->sxSwitch.pnodeDefault == nullptr)
- {
- // Can't guarantee that any code is executed.
- return false;
- }
- ParseNode *pnodeCase;
- for (pnodeCase = pnode->sxSwitch.pnodeCases; pnodeCase; pnodeCase = pnodeCase->sxCase.pnodeNext)
- {
- if (pnodeCase->sxCase.pnodeBody == nullptr)
- {
- if (pnodeCase->sxCase.pnodeNext == nullptr)
- {
- // Last case has no code to execute.
- return false;
- }
- // Fall through to the next case.
- }
- else
- {
- if (!MustProduceValue(pnodeCase->sxCase.pnodeBody, scriptContext))
- {
- return false;
- }
- }
- }
- return true;
- }
- case knopTryCatch:
- // True only if both try and catch produce a value.
- if (pnode->sxTryCatch.pnodeTry->sxTry.pnodeBody == nullptr ||
- pnode->sxTryCatch.pnodeCatch->sxCatch.pnodeBody == nullptr)
- {
- return false;
- }
- if (!MustProduceValue(pnode->sxTryCatch.pnodeCatch->sxCatch.pnodeBody, scriptContext))
- {
- return false;
- }
- pnode = pnode->sxTryCatch.pnodeTry->sxTry.pnodeBody;
- break;
- case knopTryFinally:
- if (pnode->sxTryFinally.pnodeFinally->sxFinally.pnodeBody == nullptr)
- {
- // No finally body: look at the try body.
- if (pnode->sxTryFinally.pnodeTry->sxTry.pnodeBody == nullptr)
- {
- return false;
- }
- pnode = pnode->sxTryFinally.pnodeTry->sxTry.pnodeBody;
- break;
- }
- // Skip the try body, since the finally body will always follow it.
- pnode = pnode->sxTryFinally.pnodeFinally->sxFinally.pnodeBody;
- break;
- default:
- return false;
- }
- }
- }
- ByteCodeGenerator::ByteCodeGenerator(Js::ScriptContext* scriptContext, Js::ScopeInfo* parentScopeInfo) :
- alloc(nullptr),
- scriptContext(scriptContext),
- flags(0),
- funcInfoStack(nullptr),
- pRootFunc(nullptr),
- pCurrentFunction(nullptr),
- globalScope(nullptr),
- currentScope(nullptr),
- parentScopeInfo(parentScopeInfo),
- dynamicScopeCount(0),
- isBinding(false),
- propertyRecords(nullptr),
- inDestructuredPattern(false)
- {
- m_writer.Create();
- }
- /* static */
- bool ByteCodeGenerator::IsFalse(ParseNode* node)
- {
- return (node->nop == knopInt && node->sxInt.lw == 0) || node->nop == knopFalse;
- }
- bool ByteCodeGenerator::UseParserBindings() const
- {
- return IsInNonDebugMode() && !PHASE_OFF1(Js::ParserBindPhase);
- }
- bool ByteCodeGenerator::IsES6DestructuringEnabled() const
- {
- return scriptContext->GetConfig()->IsES6DestructuringEnabled();
- }
- bool ByteCodeGenerator::IsES6ForLoopSemanticsEnabled() const
- {
- return scriptContext->GetConfig()->IsES6ForLoopSemanticsEnabled();
- }
- // ByteCodeGenerator debug mode means we are generating debug mode user-code. Library code is always in non-debug mode.
- bool ByteCodeGenerator::IsInDebugMode() const
- {
- return scriptContext->IsInDebugMode() && !m_utf8SourceInfo->GetIsLibraryCode();
- }
- // ByteCodeGenerator non-debug mode means we are not debugging, or we are generating library code which is always in non-debug mode.
- bool ByteCodeGenerator::IsInNonDebugMode() const
- {
- return scriptContext->IsInNonDebugMode() || m_utf8SourceInfo->GetIsLibraryCode();
- }
- bool ByteCodeGenerator::ShouldTrackDebuggerMetadata() const
- {
- return (IsInDebugMode())
- #if DBG_DUMP
- || (Js::Configuration::Global.flags.Debug)
- #endif
- ;
- }
- void ByteCodeGenerator::SetRootFuncInfo(FuncInfo* func)
- {
- Assert(pRootFunc == nullptr || pRootFunc == func->byteCodeFunction || !IsInNonDebugMode());
- if (this->flags & (fscrImplicitThis | fscrImplicitParents))
- {
- // Mark a top-level event handler, since it will need to construct the "this" pointer's
- // namespace hierarchy to access globals.
- Assert(!func->IsGlobalFunction());
- func->SetIsEventHandler(true);
- }
- if (pRootFunc)
- {
- return;
- }
- this->pRootFunc = func->byteCodeFunction->GetParseableFunctionInfo();
- }
- Js::RegSlot ByteCodeGenerator::NextVarRegister()
- {
- return funcInfoStack->Top()->NextVarRegister();
- }
- Js::RegSlot ByteCodeGenerator::NextConstRegister()
- {
- return funcInfoStack->Top()->NextConstRegister();
- }
- FuncInfo * ByteCodeGenerator::TopFuncInfo() const
- {
- return funcInfoStack->Empty() ? nullptr : funcInfoStack->Top();
- }
- void ByteCodeGenerator::EnterLoop()
- {
- if (this->TopFuncInfo())
- {
- this->TopFuncInfo()->hasLoop = true;
- }
- loopDepth++;
- }
- void ByteCodeGenerator::SetHasTry(bool has)
- {
- TopFuncInfo()->GetParsedFunctionBody()->SetHasTry(has);
- }
- void ByteCodeGenerator::SetHasFinally(bool has)
- {
- TopFuncInfo()->GetParsedFunctionBody()->SetHasFinally(has);
- }
- // TODO: per-function register assignment for env and global symbols
- void ByteCodeGenerator::AssignRegister(Symbol *sym)
- {
- AssertMsg(sym->GetDecl() == nullptr || sym->GetDecl()->nop != knopConstDecl || sym->GetDecl()->nop != knopLetDecl,
- "const and let should get only temporary register, assigned during emit stage");
- if (sym->GetLocation() == Js::Constants::NoRegister)
- {
- sym->SetLocation(NextVarRegister());
- }
- }
- void ByteCodeGenerator::AddTargetStmt(ParseNode *pnodeStmt)
- {
- FuncInfo *top = funcInfoStack->Top();
- top->AddTargetStmt(pnodeStmt);
- }
- Js::RegSlot ByteCodeGenerator::AssignNullConstRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignNullConstRegister();
- }
- Js::RegSlot ByteCodeGenerator::AssignUndefinedConstRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignUndefinedConstRegister();
- }
- Js::RegSlot ByteCodeGenerator::AssignTrueConstRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignTrueConstRegister();
- }
- Js::RegSlot ByteCodeGenerator::AssignFalseConstRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignFalseConstRegister();
- }
- Js::RegSlot ByteCodeGenerator::AssignThisRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignThisRegister();
- }
- Js::RegSlot ByteCodeGenerator::AssignNewTargetRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- return top->AssignNewTargetRegister();
- }
- void ByteCodeGenerator::SetNeedEnvRegister()
- {
- FuncInfo *top = funcInfoStack->Top();
- top->SetNeedEnvRegister();
- }
- void ByteCodeGenerator::AssignFrameObjRegister()
- {
- FuncInfo* top = funcInfoStack->Top();
- if (top->frameObjRegister == Js::Constants::NoRegister)
- {
- top->frameObjRegister = top->NextVarRegister();
- }
- }
- void ByteCodeGenerator::AssignFrameDisplayRegister()
- {
- FuncInfo* top = funcInfoStack->Top();
- if (top->frameDisplayRegister == Js::Constants::NoRegister)
- {
- top->frameDisplayRegister = top->NextVarRegister();
- }
- }
- void ByteCodeGenerator::AssignFrameSlotsRegister()
- {
- FuncInfo* top = funcInfoStack->Top();
- if (top->frameSlotsRegister == Js::Constants::NoRegister)
- {
- top->frameSlotsRegister = NextVarRegister();
- }
- }
- void ByteCodeGenerator::SetNumberOfInArgs(Js::ArgSlot argCount)
- {
- FuncInfo *top = funcInfoStack->Top();
- top->inArgsCount = argCount;
- }
- Js::RegSlot ByteCodeGenerator::EnregisterConstant(unsigned int constant)
- {
- Js::RegSlot loc = Js::Constants::NoRegister;
- FuncInfo *top = funcInfoStack->Top();
- if (!top->constantToRegister.TryGetValue(constant, &loc))
- {
- loc = NextConstRegister();
- top->constantToRegister.Add(constant, loc);
- }
- return loc;
- }
- Js::RegSlot ByteCodeGenerator::EnregisterStringConstant(IdentPtr pid)
- {
- Js::RegSlot loc = Js::Constants::NoRegister;
- FuncInfo *top = funcInfoStack->Top();
- if (!top->stringToRegister.TryGetValue(pid, &loc))
- {
- loc = NextConstRegister();
- top->stringToRegister.Add(pid, loc);
- }
- return loc;
- }
- Js::RegSlot ByteCodeGenerator::EnregisterDoubleConstant(double d)
- {
- Js::RegSlot loc = Js::Constants::NoRegister;
- FuncInfo *top = funcInfoStack->Top();
- if (!top->TryGetDoubleLoc(d, &loc))
- {
- loc = NextConstRegister();
- top->AddDoubleConstant(d, loc);
- }
- return loc;
- }
- Js::RegSlot ByteCodeGenerator::EnregisterStringTemplateCallsiteConstant(ParseNode* pnode)
- {
- Assert(pnode->nop == knopStrTemplate);
- Assert(pnode->sxStrTemplate.isTaggedTemplate);
- Js::RegSlot loc = Js::Constants::NoRegister;
- FuncInfo* top = funcInfoStack->Top();
- if (!top->stringTemplateCallsiteRegisterMap.TryGetValue(pnode, &loc))
- {
- loc = NextConstRegister();
- top->stringTemplateCallsiteRegisterMap.Add(pnode, loc);
- }
- return loc;
- }
- //
- // Restore all outer func scope info when reparsing a deferred func.
- //
- void ByteCodeGenerator::RestoreScopeInfo(Js::FunctionBody* functionBody)
- {
- if (functionBody && functionBody->GetScopeInfo())
- {
- PROBE_STACK(scriptContext, Js::Constants::MinStackByteCodeVisitor);
- Js::ScopeInfo* scopeInfo = functionBody->GetScopeInfo();
- RestoreScopeInfo(scopeInfo->GetParent()); // Recursively restore outer func scope info
- Js::ScopeInfo* paramScopeInfo = scopeInfo->GetParamScopeInfo();
- Scope* paramScope = nullptr;
- if (paramScopeInfo != nullptr)
- {
- paramScope = paramScopeInfo->GetScope();
- Assert(paramScope || !UseParserBindings());
- if (paramScope == nullptr || !UseParserBindings())
- {
- paramScope = Anew(alloc, Scope, alloc, ScopeType_Parameter, true);
- }
- // We need the funcInfo before continuing the restoration of the param scope, so wait for the funcInfo to be created.
- }
- Scope* bodyScope = scopeInfo->GetScope();
- Assert(bodyScope || !UseParserBindings());
- if (bodyScope == nullptr || !UseParserBindings())
- {
- if (scopeInfo->IsGlobalEval())
- {
- bodyScope = Anew(alloc, Scope, alloc, ScopeType_GlobalEvalBlock, true);
- }
- else
- {
- bodyScope = Anew(alloc, Scope, alloc, ScopeType_FunctionBody, true);
- }
- }
- FuncInfo* func = Anew(alloc, FuncInfo, functionBody->GetDisplayName(), alloc, paramScope, bodyScope, nullptr, functionBody);
- if (paramScope != nullptr)
- {
- paramScope->SetFunc(func);
- paramScopeInfo->GetScopeInfo(nullptr, this, func, paramScope);
- }
- if (bodyScope->GetScopeType() == ScopeType_GlobalEvalBlock)
- {
- func->bodyScope = this->currentScope;
- }
- PushFuncInfo(L"RestoreScopeInfo", func);
- if (!functionBody->DoStackNestedFunc())
- {
- func->hasEscapedUseNestedFunc = true;
- }
- Js::ScopeInfo* funcExprScopeInfo = scopeInfo->GetFuncExprScopeInfo();
- if (funcExprScopeInfo)
- {
- Scope* funcExprScope = funcExprScopeInfo->GetScope();
- Assert(funcExprScope || !UseParserBindings());
- if (funcExprScope == nullptr || !UseParserBindings())
- {
- funcExprScope = Anew(alloc, Scope, alloc, ScopeType_FuncExpr, true);
- }
- funcExprScope->SetFunc(func);
- func->SetFuncExprScope(funcExprScope);
- funcExprScopeInfo->GetScopeInfo(nullptr, this, func, funcExprScope);
- }
- scopeInfo->GetScopeInfo(nullptr, this, func, bodyScope);
- }
- else
- {
- Assert(this->TopFuncInfo() == nullptr);
- // funcBody is glo
- currentScope = Anew(alloc, Scope, alloc, ScopeType_Global, !UseParserBindings());
- globalScope = currentScope;
- FuncInfo *func = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction,
- alloc, nullptr, currentScope, nullptr, functionBody);
- PushFuncInfo(L"RestoreScopeInfo", func);
- }
- }
- FuncInfo * ByteCodeGenerator::StartBindGlobalStatements(ParseNode *pnode)
- {
- if (parentScopeInfo)
- {
- Assert(CONFIG_FLAG(DeferNested));
- trackEnvDepth = true;
- RestoreScopeInfo(parentScopeInfo->GetParent());
- trackEnvDepth = false;
- // "currentScope" is the parentFunc scope. This ensures the deferred func declaration
- // symbol will bind to the func declaration symbol already available in parentFunc scope.
- }
- else
- {
- currentScope = pnode->sxProg.scope;
- Assert(currentScope);
- if (!currentScope || !UseParserBindings())
- {
- currentScope = Anew(alloc, Scope, alloc, ScopeType_Global, true);
- pnode->sxProg.scope = currentScope;
- }
- globalScope = currentScope;
- }
- Js::FunctionBody * byteCodeFunction;
- if (!IsInNonDebugMode() && this->pCurrentFunction != nullptr && this->pCurrentFunction->GetIsGlobalFunc() && !this->pCurrentFunction->IsFakeGlobalFunc(flags))
- {
- // we will re-use the global FunctionBody which was created before deferred parse.
- byteCodeFunction = this->pCurrentFunction;
- byteCodeFunction->RemoveDeferParseAttribute();
- byteCodeFunction->ResetByteCodeGenVisitState();
- if (byteCodeFunction->GetBoundPropertyRecords() == nullptr)
- {
- Assert(!IsInNonDebugMode());
- // This happens when we try to re-use the function body which was created due to serialized bytecode.
- byteCodeFunction->SetBoundPropertyRecords(EnsurePropertyRecordList());
- }
- }
- else if ((this->flags & fscrDeferredFnc))
- {
- byteCodeFunction = this->EnsureFakeGlobalFuncForUndefer(pnode);
- }
- else
- {
- byteCodeFunction = this->MakeGlobalFunctionBody(pnode);
- // Mark this global function to required for register script event
- byteCodeFunction->SetIsTopLevel(true);
- if (pnode->sxFnc.GetStrictMode() != 0)
- {
- byteCodeFunction->SetIsStrictMode();
- }
- }
- if (byteCodeFunction->IsReparsed())
- {
- byteCodeFunction->RestoreState(pnode);
- }
- else
- {
- byteCodeFunction->SaveState(pnode);
- }
- FuncInfo *funcInfo = Anew(alloc, FuncInfo, Js::Constants::GlobalFunction,
- alloc, nullptr, globalScope, pnode, byteCodeFunction);
- long currentAstSize = pnode->sxFnc.astSize;
- if (currentAstSize > this->maxAstSize)
- {
- this->maxAstSize = currentAstSize;
- }
- PushFuncInfo(L"StartBindGlobalStatements", funcInfo);
- return funcInfo;
- }
- void ByteCodeGenerator::AssignPropertyId(IdentPtr pid)
- {
- if (pid->GetPropertyId() == Js::Constants::NoProperty)
- {
- Js::PropertyId id = TopFuncInfo()->byteCodeFunction->GetOrAddPropertyIdTracked(SymbolName(pid->Psz(), pid->Cch()));
- pid->SetPropertyId(id);
- }
- }
- void ByteCodeGenerator::AssignPropertyId(Symbol *sym, Js::ParseableFunctionInfo* functionInfo)
- {
- sym->SetPosition(functionInfo->GetOrAddPropertyIdTracked(sym->GetName()));
- }
- template <class PrefixFn, class PostfixFn>
- ParseNode* VisitBlock(ParseNode *pnode, ByteCodeGenerator* byteCodeGenerator, PrefixFn prefix, PostfixFn postfix, ParseNode *pnodeParent = nullptr)
- {
- ParseNode *pnodeLastVal = nullptr;
- if (pnode != nullptr)
- {
- bool fTrackVal = byteCodeGenerator->IsBinding() &&
- (byteCodeGenerator->GetFlags() & fscrReturnExpression) &&
- byteCodeGenerator->TopFuncInfo()->IsGlobalFunction();
- while (pnode->nop == knopList)
- {
- Visit(pnode->sxBin.pnode1, byteCodeGenerator, prefix, postfix, pnodeParent);
- if (fTrackVal)
- {
- // If we're tracking values, find the last statement (if any) in the block that is
- // guaranteed to produce a value.
- if (MustProduceValue(pnode->sxBin.pnode1, byteCodeGenerator->GetScriptContext()))
- {
- pnodeLastVal = pnode->sxBin.pnode1;
- }
- if (IsJump(pnode->sxBin.pnode1))
- {
- // This is a jump out of the current block. The remaining instructions (if any)
- // will not be executed, so stop tracking them.
- fTrackVal = false;
- }
- }
- pnode = pnode->sxBin.pnode2;
- }
- Visit(pnode, byteCodeGenerator, prefix, postfix, pnodeParent);
- if (fTrackVal)
- {
- if (MustProduceValue(pnode, byteCodeGenerator->GetScriptContext()))
- {
- pnodeLastVal = pnode;
- }
- }
- }
- return pnodeLastVal;
- }
- FuncInfo * ByteCodeGenerator::StartBindFunction(const wchar_t *name, uint nameLength, uint shortNameOffset, bool* pfuncExprWithName, ParseNode *pnode)
- {
- bool funcExprWithName;
- union
- {
- Js::ParseableFunctionInfo* parseableFunctionInfo;
- Js::FunctionBody* parsedFunctionBody;
- };
- bool isDeferParsed = false;
- if (this->pCurrentFunction &&
- this->pCurrentFunction->IsFunctionParsed())
- {
- Assert(this->pCurrentFunction->StartInDocument() == pnode->ichMin);
- Assert(this->pCurrentFunction->LengthInChars() == pnode->LengthInCodepoints());
- // This is the root function for the current AST subtree, and it already has a FunctionBody
- // (created by a deferred parse) which we're now filling in.
- parsedFunctionBody = this->pCurrentFunction;
- parsedFunctionBody->RemoveDeferParseAttribute();
- if (parsedFunctionBody->GetBoundPropertyRecords() == nullptr)
- {
- // This happens when we try to re-use the function body which was created due to serialized bytecode.
- parsedFunctionBody->SetBoundPropertyRecords(EnsurePropertyRecordList());
- }
- Assert(!parsedFunctionBody->IsDeferredParseFunction() || parsedFunctionBody->IsReparsed());
- pnode->sxFnc.SetDeclaration(parsedFunctionBody->GetIsDeclaration());
- funcExprWithName =
- !(parsedFunctionBody->GetIsDeclaration() || pnode->sxFnc.IsMethod()) &&
- pnode->sxFnc.pnodeName != nullptr &&
- pnode->sxFnc.pnodeName->nop == knopVarDecl;
- *pfuncExprWithName = funcExprWithName;
- Assert(parsedFunctionBody->GetLocalFunctionId() == pnode->sxFnc.functionId || !IsInNonDebugMode());
- // Some state may be tracked on the function body during the visit pass. Since the previous visit pass may have failed,
- // we need to reset the state on the function body.
- parsedFunctionBody->ResetByteCodeGenVisitState();
- if (parsedFunctionBody->GetScopeInfo())
- {
- // Propagate flags from the (real) parent function.
- Js::ParseableFunctionInfo *parent = parsedFunctionBody->GetScopeInfo()->GetParent();
- if (parent)
- {
- Assert(parent->GetFunctionBody());
- if (parent->GetFunctionBody()->GetHasOrParentHasArguments())
- {
- parsedFunctionBody->SetHasOrParentHasArguments(true);
- }
- }
- }
- }
- else
- {
- funcExprWithName = *pfuncExprWithName;
- Js::LocalFunctionId functionId = pnode->sxFnc.functionId;
- isDeferParsed = (pnode->sxFnc.pnodeBody == nullptr);
- // Create a function body if:
- // 1. The parse node is not defer parsed
- // 2. Or creating function proxies is disallowed
- bool createFunctionBody = !isDeferParsed;
- if (!CONFIG_FLAG(CreateFunctionProxy)) createFunctionBody = true;
- Js::FunctionInfo::Attributes attributes = Js::FunctionInfo::Attributes::None;
- if (pnode->sxFnc.IsAsync())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ErrorOnNew | Js::FunctionInfo::Attributes::Async);
- }
- if (pnode->sxFnc.IsLambda())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ErrorOnNew | Js::FunctionInfo::Attributes::Lambda);
- }
- if (pnode->sxFnc.HasSuperReference())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::SuperReference);
- }
- if (pnode->sxFnc.IsClassMember())
- {
- if (pnode->sxFnc.IsClassConstructor())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ClassConstructor);
- }
- else
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ErrorOnNew | Js::FunctionInfo::Attributes::ClassMethod);
- }
- }
- if (pnode->sxFnc.IsGenerator())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::Generator);
- }
- if (pnode->sxFnc.IsAccessor())
- {
- attributes = (Js::FunctionInfo::Attributes)(attributes | Js::FunctionInfo::Attributes::ErrorOnNew);
- }
- if (createFunctionBody)
- {
- ENTER_PINNED_SCOPE(Js::PropertyRecordList, propertyRecordList);
- propertyRecordList = EnsurePropertyRecordList();
- parsedFunctionBody = Js::FunctionBody::NewFromRecycler(scriptContext, name, nameLength, shortNameOffset, pnode->sxFnc.nestedCount, m_utf8SourceInfo,
- m_utf8SourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId, functionId, propertyRecordList
- , attributes
- #ifdef PERF_COUNTERS
- , false /* is function from deferred deserialized proxy */
- #endif
- );
- LEAVE_PINNED_SCOPE();
- }
- else
- {
- ENTER_PINNED_SCOPE(Js::PropertyRecordList, propertyRecordList);
- propertyRecordList = nullptr;
- if (funcExprWithName)
- {
- propertyRecordList = EnsurePropertyRecordList();
- }
- parseableFunctionInfo = Js::ParseableFunctionInfo::New(scriptContext, pnode->sxFnc.nestedCount, functionId, m_utf8SourceInfo, name, nameLength, shortNameOffset, propertyRecordList, attributes);
- LEAVE_PINNED_SCOPE();
- }
- // In either case register the function reference
- scriptContext->RegisterDynamicFunctionReference(parseableFunctionInfo);
- #if DBG
- parseableFunctionInfo->deferredParseNextFunctionId = pnode->sxFnc.deferredParseNextFunctionId;
- #endif
- parseableFunctionInfo->SetIsDeclaration(pnode->sxFnc.IsDeclaration() != 0);
- parseableFunctionInfo->SetIsAccessor(pnode->sxFnc.IsAccessor() != 0);
- if (pnode->sxFnc.IsAccessor())
- {
- scriptContext->optimizationOverrides.SetSideEffects(Js::SideEffects_Accessor);
- }
- }
- Scope *funcExprScope = nullptr;
- if (funcExprWithName)
- {
- if (!UseParserBindings())
- {
- funcExprScope = Anew(alloc, Scope, alloc, ScopeType_FuncExpr, true);
- pnode->sxFnc.scope = funcExprScope;
- }
- else
- {
- funcExprScope = pnode->sxFnc.scope;
- Assert(funcExprScope);
- }
- PushScope(funcExprScope);
- Symbol *sym = AddSymbolToScope(funcExprScope, name, nameLength, pnode->sxFnc.pnodeName, STFunction);
- sym->SetFuncExpr(true);
- sym->SetPosition(parsedFunctionBody->GetOrAddPropertyIdTracked(sym->GetName()));
- pnode->sxFnc.SetFuncSymbol(sym);
- }
- Scope *paramScope = pnode->sxFnc.pnodeScopes ? pnode->sxFnc.pnodeScopes->sxBlock.scope : nullptr;
- Scope *bodyScope = pnode->sxFnc.pnodeBodyScope ? pnode->sxFnc.pnodeBodyScope->sxBlock.scope : nullptr;
- Assert(paramScope != nullptr || !pnode->sxFnc.pnodeScopes || !UseParserBindings());
- if (paramScope == nullptr || !UseParserBindings())
- {
- paramScope = Anew(alloc, Scope, alloc, ScopeType_Parameter, true);
- if (pnode->sxFnc.pnodeScopes)
- {
- pnode->sxFnc.pnodeScopes->sxBlock.scope = paramScope;
- }
- }
- if (bodyScope == nullptr || !UseParserBindings())
- {
- bodyScope = Anew(alloc, Scope, alloc, ScopeType_FunctionBody, true);
- if (pnode->sxFnc.pnodeBodyScope)
- {
- pnode->sxFnc.pnodeBodyScope->sxBlock.scope = bodyScope;
- }
- }
- AssertMsg(pnode->nop == knopFncDecl, "Non-function declaration trying to create function body");
- parseableFunctionInfo->SetIsGlobalFunc(false);
- if (pnode->sxFnc.GetStrictMode() != 0)
- {
- parseableFunctionInfo->SetIsStrictMode();
- }
- FuncInfo *funcInfo = Anew(alloc, FuncInfo, name, alloc, paramScope, bodyScope, pnode, parseableFunctionInfo);
- if (pnode->sxFnc.GetArgumentsObjectEscapes())
- {
- // If the parser detected that the arguments object escapes, then the function scope escapes
- // and cannot be cached.
- this->FuncEscapes(bodyScope);
- funcInfo->SetHasMaybeEscapedNestedFunc(DebugOnly(L"ArgumentsObjectEscapes"));
- }
- if (!isDeferParsed)
- {
- if (parsedFunctionBody->IsReparsed())
- {
- parsedFunctionBody->RestoreState(pnode);
- }
- else
- {
- parsedFunctionBody->SaveState(pnode);
- }
- }
- funcInfo->SetChildCallsEval(!!pnode->sxFnc.ChildCallsEval());
- if (pnode->sxFnc.CallsEval())
- {
- funcInfo->SetCallsEval(true);
- bodyScope->SetIsDynamic(true);
- bodyScope->SetIsObject();
- bodyScope->SetCapturesAll(true);
- bodyScope->SetMustInstantiate(true);
- paramScope->SetIsObject();
- paramScope->SetMustInstantiate(true);
- paramScope->SetCapturesAll(true);
- }
- PushFuncInfo(L"StartBindFunction", funcInfo);
- PushScope(paramScope);
- PushScope(bodyScope);
- if (funcExprScope)
- {
- funcExprScope->SetFunc(funcInfo);
- funcInfo->funcExprScope = funcExprScope;
- }
- long currentAstSize = pnode->sxFnc.astSize;
- if (currentAstSize > this->maxAstSize)
- {
- this->maxAstSize = currentAstSize;
- }
- return funcInfo;
- }
- void ByteCodeGenerator::EndBindFunction(bool funcExprWithName)
- {
- bool isGlobalScope = currentScope->GetScopeType() == ScopeType_Global;
- Assert(currentScope->GetScopeType() == ScopeType_FunctionBody || isGlobalScope);
- PopScope(); // function body
- if (isGlobalScope)
- {
- Assert(currentScope == nullptr);
- }
- else
- {
- Assert(currentScope->GetScopeType() == ScopeType_Parameter);
- PopScope(); // parameter scope
- }
- if (funcExprWithName)
- {
- Assert(currentScope->GetScopeType() == ScopeType_FuncExpr);
- PopScope();
- }
- funcInfoStack->Pop();
- }
- void ByteCodeGenerator::StartBindCatch(ParseNode *pnode)
- {
- Scope *scope = pnode->sxCatch.scope;
- Assert(scope);
- if (scope == nullptr || !UseParserBindings())
- {
- scope = Anew(alloc, Scope, alloc, (pnode->sxCatch.pnodeParam->nop == knopParamPattern) ? ScopeType_CatchParamPattern : ScopeType_Catch, true);
- pnode->sxCatch.scope = scope;
- }
- Assert(currentScope);
- scope->SetFunc(currentScope->GetFunc());
- PushScope(scope);
- }
- void ByteCodeGenerator::EndBindCatch()
- {
- PopScope();
- }
- void ByteCodeGenerator::PushScope(Scope *innerScope)
- {
- Assert(innerScope != nullptr);
- if (isBinding
- && currentScope != nullptr
- && currentScope->GetScopeType() == ScopeType_FunctionBody
- && innerScope->GetMustInstantiate())
- {
- // If the current scope is a function body, we don't expect another function body
- // without going through a function expression or parameter scope first. This may
- // not be the case in the emit phase, where we can merge the two scopes. This also
- // does not apply to incoming scopes marked as !mustInstantiate.
- Assert(innerScope->GetScopeType() != ScopeType_FunctionBody);
- }
- innerScope->SetEnclosingScope(currentScope);
- currentScope = innerScope;
- if (currentScope->GetIsDynamic())
- {
- this->dynamicScopeCount++;
- }
- if (this->trackEnvDepth && currentScope->GetMustInstantiate())
- {
- this->envDepth++;
- if (this->envDepth == 0)
- {
- Js::Throw::OutOfMemory();
- }
- }
- }
- void ByteCodeGenerator::PopScope()
- {
- Assert(currentScope != nullptr);
- if (this->trackEnvDepth && currentScope->GetMustInstantiate())
- {
- this->envDepth--;
- Assert(this->envDepth != (uint16)-1);
- }
- if (currentScope->GetIsDynamic())
- {
- this->dynamicScopeCount--;
- }
- currentScope = currentScope->GetEnclosingScope();
- }
- void ByteCodeGenerator::PushBlock(ParseNode *pnode)
- {
- pnode->sxBlock.SetEnclosingBlock(currentBlock);
- currentBlock = pnode;
- }
- void ByteCodeGenerator::PopBlock()
- {
- currentBlock = currentBlock->sxBlock.GetEnclosingBlock();
- }
- void ByteCodeGenerator::PushFuncInfo(wchar_t const * location, FuncInfo* funcInfo)
- {
- // We might have multiple global scope for deferparse.
- // Assert(!funcInfo->IsGlobalFunction() || this->TopFuncInfo() == nullptr || this->TopFuncInfo()->IsGlobalFunction());
- if (PHASE_TRACE1(Js::ByteCodePhase))
- {
- Output::Print(L"%s: PushFuncInfo: %s", location, funcInfo->name);
- if (this->TopFuncInfo())
- {
- Output::Print(L" Top: %s", this->TopFuncInfo()->name);
- }
- Output::Print(L"\n");
- Output::Flush();
- }
- funcInfoStack->Push(funcInfo);
- }
- void ByteCodeGenerator::PopFuncInfo(wchar_t const * location)
- {
- FuncInfo * funcInfo = funcInfoStack->Pop();
- // Assert(!funcInfo->IsGlobalFunction() || this->TopFuncInfo() == nullptr || this->TopFuncInfo()->IsGlobalFunction());
- if (PHASE_TRACE1(Js::ByteCodePhase))
- {
- Output::Print(L"%s: PopFuncInfo: %s", location, funcInfo->name);
- if (this->TopFuncInfo())
- {
- Output::Print(L" Top: %s", this->TopFuncInfo()->name);
- }
- Output::Print(L"\n");
- Output::Flush();
- }
- }
- Symbol * ByteCodeGenerator::FindSymbol(Symbol **symRef, IdentPtr pid, bool forReference)
- {
- const wchar_t *key = nullptr;
- int keyLength;
- Symbol *sym = nullptr;
- if (!UseParserBindings())
- {
- key = reinterpret_cast<const wchar_t*>(pid->Psz());
- keyLength = pid->Cch();
- sym = currentScope->FindSymbol(SymbolName(key, keyLength), STUnknown);
- if (symRef)
- {
- *symRef = sym;
- }
- }
- else
- {
- Assert(symRef);
- if (*symRef)
- {
- sym = *symRef;
- }
- else
- {
- this->AssignPropertyId(pid);
- return nullptr;
- }
- key = reinterpret_cast<const wchar_t*>(sym->GetPid()->Psz());
- }
- Scope *symScope = sym->GetScope();
- Assert(symScope);
- #if DBG_DUMP
- if (this->Trace())
- {
- if (sym != nullptr)
- {
- Output::Print(L"resolved %s to symbol of type %s: \n", key, sym->GetSymbolTypeName());
- }
- else
- {
- Output::Print(L"did not resolve %s\n", key);
- }
- }
- #endif
- if (!(sym->GetIsGlobal()))
- {
- FuncInfo *top = funcInfoStack->Top();
- bool nonLocalRef = symScope->GetFunc() != top;
- if (forReference)
- {
- Js::PropertyId i;
- Scope *scope = FindScopeForSym(symScope, nullptr, &i, top);
- // If we have a reference to a local within a with, we want to generate a closure represented by an object.
- if (scope != symScope && scope->GetIsDynamic())
- {
- nonLocalRef = true;
- symScope->SetIsObject();
- }
- }
- if (nonLocalRef)
- {
- // Symbol referenced through a closure. Mark it as such and give it a property ID.
- sym->SetHasNonLocalReference(true, this);
- sym->SetPosition(top->byteCodeFunction->GetOrAddPropertyIdTracked(sym->GetName()));
- // If this is var is local to a function (meaning that it belongs to the function's scope
- // *or* to scope that need not be instantiated, like a function expression scope, which we'll
- // merge with the function scope, then indicate that fact.
- symScope->SetHasLocalInClosure(true);
- if (symScope->GetFunc()->GetHasArguments() && sym->GetIsFormal())
- {
- // A formal is referenced non-locally. We need to allocate it on the heap, so
- // do the same for the whole arguments object.
- // Formal is referenced. So count of formals to function > 0.
- // So no need to check for inParams here.
- symScope->GetFunc()->SetHasHeapArguments(true);
- }
- if (symScope->GetFunc() != top)
- {
- top->SetHasClosureReference(true);
- }
- }
- else if (sym->GetHasNonLocalReference() && !sym->GetIsCommittedToSlot() && !sym->HasVisitedCapturingFunc())
- {
- sym->SetHasNonCommittedReference(true);
- }
- if (sym->GetFuncExpr())
- {
- symScope->GetFunc()->SetFuncExprNameReference(true);
- // If the func expr is captured by a closure, and if it's not getting its own scope,
- // we have to make the function's scope a dynamic object. That's because we have to init
- // the property as non-writable, and if we put it directly in the slots we can't protect it from being
- // overwritten.
- if (nonLocalRef && !symScope->GetFunc()->GetCallsEval() && !symScope->GetFunc()->GetChildCallsEval())
- {
- symScope->GetFunc()->GetParamScope()->SetIsObject();
- symScope->GetFunc()->GetBodyScope()->SetIsObject();
- }
- }
- }
- return sym;
- }
- Symbol * ByteCodeGenerator::AddSymbolToScope(Scope *scope, const wchar_t *key, int keyLength, ParseNode *varDecl, SymbolType symbolType)
- {
- Symbol *sym = nullptr;
- if (!UseParserBindings())
- {
- SymbolName const symName(key, keyLength);
- if (scope->GetScopeType() == ScopeType_FunctionBody)
- {
- sym = scope->GetFunc()->GetParamScope()->FindLocalSymbol(symName);
- }
- if (sym == nullptr)
- {
- sym = scope->FindLocalSymbol(symName);
- }
- if (sym == nullptr)
- {
- sym = Anew(alloc, Symbol, symName, varDecl, symbolType);
- scope->AddNewSymbol(sym);
- #if DBG_DUMP
- if (this->Trace())
- {
- Output::Print(L"added symbol %s of type %s to scope %x\n", key, sym->GetSymbolTypeName(), scope);
- }
- #endif
- }
- }
- else
- {
- switch (varDecl->nop)
- {
- case knopConstDecl:
- case knopLetDecl:
- case knopVarDecl:
- sym = varDecl->sxVar.sym/*New*/;
- break;
- case knopName:
- AnalysisAssert(varDecl->sxPid.symRef);
- sym = *varDecl->sxPid.symRef;
- break;
- default:
- AnalysisAssert(0);
- sym = nullptr;
- break;
- }
- if (sym->GetScope() != scope && sym->GetScope()->GetScopeType() != ScopeType_Parameter)
- {
- // This can happen when we have a function declared at global eval scope, and it has
- // references in deferred function bodies inside the eval. The BCG creates a new global scope
- // on such compiles, so we essentially have to migrate the symbol to the new scope.
- // We check fscrEvalCode, not fscrEval, because the same thing can happen in indirect eval,
- // when fscrEval is not set.
- Assert(((this->flags & fscrEvalCode) && sym->GetIsGlobal() && sym->GetSymbolType() == STFunction) || this->IsConsoleScopeEval());
- Assert(scope->GetScopeType() == ScopeType_Global);
- scope->AddNewSymbol(sym);
- }
- }
- Assert(sym && sym->GetScope() && (sym->GetScope() == scope || sym->GetScope()->GetScopeType() == ScopeType_Parameter));
- return sym;
- }
- Symbol * ByteCodeGenerator::AddSymbolToFunctionScope(const wchar_t *key, int keyLength, ParseNode *varDecl, SymbolType symbolType)
- {
- Scope* scope = currentScope->GetFunc()->GetBodyScope();
- return this->AddSymbolToScope(scope, key, keyLength, varDecl, symbolType);
- }
- FuncInfo *ByteCodeGenerator::FindEnclosingNonLambda()
- {
- for (Scope *scope = TopFuncInfo()->GetBodyScope(); scope; scope = scope->GetEnclosingScope())
- {
- if (!scope->GetFunc()->IsLambda())
- {
- return scope->GetFunc();
- }
- }
- Assert(0);
- return nullptr;
- }
- bool ByteCodeGenerator::CanStackNestedFunc(FuncInfo * funcInfo, bool trace)
- {
- #if ENABLE_DEBUG_CONFIG_OPTIONS
- wchar_t debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- #endif
- Assert(!funcInfo->IsGlobalFunction());
- bool const doStackNestedFunc = !funcInfo->HasMaybeEscapedNestedFunc() && !IsInDebugMode() && !funcInfo->byteCodeFunction->IsGenerator();
- if (!doStackNestedFunc)
- {
- return false;
- }
- bool callsEval = funcInfo->GetCallsEval() || funcInfo->GetChildCallsEval();
- if (callsEval)
- {
- if (trace)
- {
- PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, funcInfo->byteCodeFunction,
- L"HasMaybeEscapedNestedFunc (Eval): %s (function %s)\n",
- funcInfo->byteCodeFunction->GetDisplayName(),
- funcInfo->byteCodeFunction->GetDebugNumberSet(debugStringBuffer));
- }
- return false;
- }
- if (funcInfo->GetBodyScope()->GetIsObject() || funcInfo->GetParamScope()->GetIsObject())
- {
- if (trace)
- {
- PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, funcInfo->byteCodeFunction,
- L"HasMaybeEscapedNestedFunc (ObjectScope): %s (function %s)\n",
- funcInfo->byteCodeFunction->GetDisplayName(),
- funcInfo->byteCodeFunction->GetDebugNumberSet(debugStringBuffer));
- }
- return false;
- }
- if (trace && funcInfo->byteCodeFunction->GetNestedCount())
- {
- // Only print functions that actually have nested functions, although we will still mark
- // functions that don't have nested child functions as DoStackNestedFunc.
- PHASE_PRINT_TESTTRACE(Js::StackFuncPhase, funcInfo->byteCodeFunction,
- L"DoStackNestedFunc: %s (function %s)\n",
- funcInfo->byteCodeFunction->GetDisplayName(),
- funcInfo->byteCodeFunction->GetDebugNumberSet(debugStringBuffer));
- }
- return !PHASE_OFF(Js::StackFuncPhase, funcInfo->byteCodeFunction);
- }
- bool ByteCodeGenerator::NeedObjectAsFunctionScope(FuncInfo * funcInfo, ParseNode * pnodeFnc) const
- {
- return funcInfo->GetCallsEval()
- || funcInfo->GetChildCallsEval()
- || NeedScopeObjectForArguments(funcInfo, pnodeFnc)
- || (this->flags & (fscrEval | fscrImplicitThis | fscrImplicitParents));
- }
- Scope * ByteCodeGenerator::FindScopeForSym(Scope *symScope, Scope *scope, Js::PropertyId *envIndex, FuncInfo *funcInfo) const
- {
- for (scope = scope ? scope->GetEnclosingScope() : currentScope; scope; scope = scope->GetEnclosingScope())
- {
- if (scope->GetFunc() != funcInfo && scope->GetMustInstantiate() && scope != this->globalScope)
- {
- (*envIndex)++;
- }
- if (scope == symScope || scope->GetIsDynamic())
- {
- break;
- }
- }
- Assert(scope);
- return scope;
- }
- /* static */
- Js::OpCode ByteCodeGenerator::GetStFldOpCode(FuncInfo* funcInfo, bool isRoot, bool isLetDecl, bool isConstDecl, bool isClassMemberInit)
- {
- return GetStFldOpCode(funcInfo->GetIsStrictMode(), isRoot, isLetDecl, isConstDecl, isClassMemberInit);
- }
- /* static */
- Js::OpCode ByteCodeGenerator::GetScopedStFldOpCode(FuncInfo* funcInfo, bool isConsoleScopeLetConst)
- {
- if (isConsoleScopeLetConst)
- {
- return Js::OpCode::ConsoleScopedStFld;
- }
- return GetScopedStFldOpCode(funcInfo->GetIsStrictMode());
- }
- /* static */
- Js::OpCode ByteCodeGenerator::GetStElemIOpCode(FuncInfo* funcInfo)
- {
- return GetStElemIOpCode(funcInfo->GetIsStrictMode());
- }
- bool ByteCodeGenerator::DoJitLoopBodies(FuncInfo *funcInfo) const
- {
- // Never JIT loop bodies in a function with a try.
- // Otherwise, always JIT loop bodies under /forcejitloopbody.
- // Otherwise, JIT loop bodies unless we're in eval/"new Function" or feature is disabled.
- Assert(funcInfo->byteCodeFunction->IsFunctionParsed());
- Js::FunctionBody* functionBody = funcInfo->byteCodeFunction->GetFunctionBody();
- return functionBody->ForceJITLoopBody() || funcInfo->byteCodeFunction->IsJitLoopBodyPhaseEnabled();
- }
- void ByteCodeGenerator::Generate(__in ParseNode *pnode, ulong grfscr, __in ByteCodeGenerator* byteCodeGenerator,
- __inout Js::ParseableFunctionInfo ** ppRootFunc, __in uint sourceIndex,
- __in bool forceNoNative, __in Parser* parser, Js::ScriptFunction **functionRef)
- {
- Js::ScriptContext * scriptContext = byteCodeGenerator->scriptContext;
- #ifdef PROFILE_EXEC
- scriptContext->ProfileBegin(Js::ByteCodePhase);
- #endif
- JS_ETW(EventWriteJSCRIPT_BYTECODEGEN_START(scriptContext, 0));
- ThreadContext * threadContext = scriptContext->GetThreadContext();
- Js::Utf8SourceInfo * utf8SourceInfo = scriptContext->GetSource(sourceIndex);
- byteCodeGenerator->m_utf8SourceInfo = utf8SourceInfo;
- // For dynamic code, just provide a small number since that source info should have very few functions
- // For static code, the nextLocalFunctionId is a good guess of the initial size of the array to minimize reallocs
- SourceContextInfo * sourceContextInfo = utf8SourceInfo->GetSrcInfo()->sourceContextInfo;
- utf8SourceInfo->EnsureInitialized((grfscr & fscrDynamicCode) ? 4 : (sourceContextInfo->nextLocalFunctionId - pnode->sxFnc.functionId));
- sourceContextInfo->EnsureInitialized();
- ArenaAllocator localAlloc(L"ByteCode", threadContext->GetPageAllocator(), Js::Throw::OutOfMemory);
- byteCodeGenerator->parser = parser;
- byteCodeGenerator->SetCurrentSourceIndex(sourceIndex);
- byteCodeGenerator->Begin(&localAlloc, grfscr, *ppRootFunc);
- byteCodeGenerator->functionRef = functionRef;
- Visit(pnode, byteCodeGenerator, Bind, AssignRegisters);
- byteCodeGenerator->forceNoNative = forceNoNative;
- byteCodeGenerator->EmitProgram(pnode);
- if (byteCodeGenerator->flags & fscrEval)
- {
- // The eval caller's frame always escapes if eval refers to the caller's arguments.
- byteCodeGenerator->GetRootFunc()->GetFunctionBody()->SetFuncEscapes(
- byteCodeGenerator->funcEscapes || pnode->sxProg.m_UsesArgumentsAtGlobal);
- }
- #ifdef IR_VIEWER
- if (grfscr & fscrIrDumpEnable)
- {
- byteCodeGenerator->GetRootFunc()->GetFunctionBody()->SetIRDumpEnabled(true);
- }
- #endif /* IR_VIEWER */
- byteCodeGenerator->CheckDeferParseHasMaybeEscapedNestedFunc();
- #ifdef PROFILE_EXEC
- scriptContext->ProfileEnd(Js::ByteCodePhase);
- #endif
- JS_ETW(EventWriteJSCRIPT_BYTECODEGEN_STOP(scriptContext, 0));
- #if ENABLE_NATIVE_CODEGEN && defined(ENABLE_PREJIT)
- if (!byteCodeGenerator->forceNoNative && !scriptContext->GetConfig()->IsNoNative()
- && Js::Configuration::Global.flags.Prejit
- && (grfscr & fscrNoPreJit) == 0)
- {
- GenerateAllFunctions(scriptContext->GetNativeCodeGenerator(), byteCodeGenerator->GetRootFunc()->GetFunctionBody());
- }
- #endif
- if (ppRootFunc)
- {
- *ppRootFunc = byteCodeGenerator->GetRootFunc();
- }
- #ifdef PERF_COUNTERS
- PHASE_PRINT_TESTTRACE1(Js::DeferParsePhase, L"TestTrace: deferparse - # of func: %d # deferparsed: %d\n",
- PerfCounter::CodeCounterSet::GetTotalFunctionCounter().GetValue(), PerfCounter::CodeCounterSet::GetDeferredFunctionCounter().GetValue());
- #endif
- }
- void ByteCodeGenerator::CheckDeferParseHasMaybeEscapedNestedFunc()
- {
- if (!this->parentScopeInfo)
- {
- return;
- }
- Assert(CONFIG_FLAG(DeferNested));
- Assert(this->funcInfoStack && !this->funcInfoStack->Empty());
- // Box the stack nested function if we detected new may be escaped use function.
- SList<FuncInfo *>::Iterator i(this->funcInfoStack);
- bool succeed = i.Next();
- Assert(succeed);
- Assert(i.Data()->IsGlobalFunction()); // We always leave a glo on type when defer parsing.
- Assert(!i.Data()->IsRestored());
- succeed = i.Next();
- FuncInfo * top = i.Data();
- Assert(!top->IsGlobalFunction());
- Assert(top->IsRestored());
- Js::FunctionBody * rootFuncBody = this->GetRootFunc()->GetFunctionBody();
- if (!rootFuncBody->DoStackNestedFunc())
- {
- top->SetHasMaybeEscapedNestedFunc(DebugOnly(L"DeferredChild"));
- }
- else
- {
- // We have to wait until it is parsed before we populate the stack nested func parent.
- Js::FunctionBody * parentFunctionBody = nullptr;
- FuncInfo * parentFunc = top->GetBodyScope()->GetEnclosingFunc();
- if (!parentFunc->IsGlobalFunction())
- {
- parentFunctionBody = parentFunc->GetParsedFunctionBody();
- Assert(parentFunctionBody != rootFuncBody);
- if (parentFunctionBody->DoStackNestedFunc())
- {
- rootFuncBody->SetStackNestedFuncParent(parentFunctionBody);
- }
- }
- }
- do
- {
- FuncInfo * funcInfo = i.Data();
- Assert(funcInfo->IsRestored());
- Js::FunctionBody * functionBody = funcInfo->GetParsedFunctionBody();
- bool didStackNestedFunc = functionBody->DoStackNestedFunc();
- if (!didStackNestedFunc)
- {
- return;
- }
- if (funcInfo->HasMaybeEscapedNestedFunc())
- {
- // This should box the rest of the parent functions.
- if (PHASE_TESTTRACE(Js::StackFuncPhase, this->pCurrentFunction))
- {
- wchar_t debugStringBuffer[MAX_FUNCTION_BODY_DEBUG_STRING_SIZE];
- Output::Print(L"DeferParse: box and disable stack function: %s (function %s)\n",
- functionBody->GetDisplayName(), functionBody->GetDebugNumberSet(debugStringBuffer));
- Output::Flush();
- }
- // During the box workflow we reset all the parents of all nested functions and up. If a fault occurs when the stack function
- // is created this will cause further issues when trying to use the function object again. So failing faster seems to make more sense.
- try
- {
- Js::StackScriptFunction::Box(functionBody, functionRef);
- }
- catch (Js::OutOfMemoryException)
- {
- FailedToBox_OOM_fatal_error((ULONG_PTR)functionBody);
- }
- return;
- }
- }
- while (i.Next());
- }
- void ByteCodeGenerator::Begin(
- __in ArenaAllocator *alloc,
- __in ulong grfscr,
- __in Js::ParseableFunctionInfo* pRootFunc)
- {
- this->alloc = alloc;
- this->flags = grfscr;
- this->pRootFunc = pRootFunc;
- this->pCurrentFunction = pRootFunc ? pRootFunc->GetFunctionBody() : nullptr;
- if (this->pCurrentFunction && this->pCurrentFunction->GetIsGlobalFunc() && IsInNonDebugMode())
- {
- // This is the deferred parse case (not due to debug mode), in which case the global function will not be marked to compiled again.
- this->pCurrentFunction = nullptr;
- }
- this->globalScope = nullptr;
- this->currentScope = nullptr;
- this->currentBlock = nullptr;
- this->isBinding = true;
- this->inPrologue = false;
- this->funcEscapes = false;
- this->maxAstSize = 0;
- this->loopDepth = 0;
- this->envDepth = 0;
- this->trackEnvDepth = false;
- this->funcInfoStack = Anew(alloc, SList<FuncInfo*>, alloc);
- // If pRootFunc is not null, this is a deferred parse function
- // so reuse the property record list bound there since some of the symbols could have
- // been bound. If it's null, we need to create a new property record list
- if (pRootFunc != nullptr)
- {
- this->propertyRecords = pRootFunc->GetBoundPropertyRecords();
- }
- else
- {
- this->propertyRecords = nullptr;
- }
- Js::FunctionBody *fakeGlobalFunc = scriptContext->GetFakeGlobalFuncForUndefer();
- if (fakeGlobalFunc)
- {
- fakeGlobalFunc->ClearBoundPropertyRecords();
- }
- }
- HRESULT GenerateByteCode(__in ParseNode *pnode, __in ulong grfscr, __in Js::ScriptContext* scriptContext, __inout Js::ParseableFunctionInfo ** ppRootFunc,
- __in uint sourceIndex, __in bool forceNoNative, __in Parser* parser, __in CompileScriptException *pse, Js::ScopeInfo* parentScopeInfo,
- Js::ScriptFunction ** functionRef)
- {
- HRESULT hr = S_OK;
- ByteCodeGenerator byteCodeGenerator(scriptContext, parentScopeInfo);
- BEGIN_TRANSLATE_EXCEPTION_TO_HRESULT_NESTED
- {
- // Main code.
- ByteCodeGenerator::Generate(pnode, grfscr, &byteCodeGenerator, ppRootFunc, sourceIndex, forceNoNative, parser, functionRef);
- }
- END_TRANSLATE_EXCEPTION_TO_HRESULT(hr);
- if (FAILED(hr))
- {
- hr = pse->ProcessError(nullptr, hr, nullptr);
- }
- return hr;
- }
- void BindInstAndMember(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- Assert(pnode->nop == knopDot);
- BindReference(pnode, byteCodeGenerator);
- ParseNode *right = pnode->sxBin.pnode2;
- Assert(right->nop == knopName);
- byteCodeGenerator->AssignPropertyId(right->sxPid.pid);
- right->sxPid.sym = nullptr;
- right->sxPid.symRef = nullptr;
- right->grfpn |= fpnMemberReference;
- }
- void BindReference(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- // Do special reference-op binding so that we can, for instance, handle call from inside "with"
- // where the "this" instance must be found dynamically.
- bool isCallNode = false;
- bool funcEscapes = false;
- switch (pnode->nop)
- {
- case knopCall:
- isCallNode = true;
- pnode = pnode->sxCall.pnodeTarget;
- break;
- case knopDelete:
- case knopTypeof:
- pnode = pnode->sxUni.pnode1;
- break;
- case knopDot:
- case knopIndex:
- funcEscapes = true;
- // fall through
- case knopAsg:
- pnode = pnode->sxBin.pnode1;
- break;
- default:
- AssertMsg(0, "Unexpected opcode in BindReference");
- return;
- }
- if (pnode->nop == knopName)
- {
- pnode->sxPid.sym = byteCodeGenerator->FindSymbol(pnode->sxPid.symRef, pnode->sxPid.pid, isCallNode);
- if (funcEscapes &&
- pnode->sxPid.sym &&
- pnode->sxPid.sym->GetSymbolType() == STFunction &&
- (!pnode->sxPid.sym->GetIsGlobal() || (byteCodeGenerator->GetFlags() & fscrEval)))
- {
- // Dot, index, and scope ops can cause a local function on the LHS to escape.
- // Make sure scopes are not cached in this case.
- byteCodeGenerator->FuncEscapes(pnode->sxPid.sym->GetScope());
- }
- }
- }
- void MarkFormal(ByteCodeGenerator *byteCodeGenerator, Symbol *formal, bool assignLocation, bool needDeclaration)
- {
- if (assignLocation)
- {
- formal->SetLocation(byteCodeGenerator->NextVarRegister());
- }
- if (needDeclaration)
- {
- formal->SetNeedDeclaration(true);
- }
- }
- void AddArgsToScope(ParseNodePtr pnode, ByteCodeGenerator *byteCodeGenerator, bool assignLocation)
- {
- Assert(byteCodeGenerator->TopFuncInfo()->varRegsCount == 0);
- Js::ArgSlot pos = 1;
- bool isSimpleParameterList = pnode->sxFnc.IsSimpleParameterList();
- auto addArgToScope = [&](ParseNode *arg)
- {
- if (arg->IsVarLetOrConst())
- {
- Symbol *formal = byteCodeGenerator->AddSymbolToScope(byteCodeGenerator->TopFuncInfo()->GetParamScope(),
- reinterpret_cast<const wchar_t*>(arg->sxVar.pid->Psz()),
- arg->sxVar.pid->Cch(),
- arg,
- STFormal);
- #if DBG_DUMP
- if (byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared arg %s of type %s at position %d\n", arg->sxVar.pid->Psz(), formal->GetSymbolTypeName(), pos);
- }
- #endif
- if (!isSimpleParameterList)
- {
- formal->SetIsNonSimpleParameter(true);
- }
- arg->sxVar.sym = formal;
- MarkFormal(byteCodeGenerator, formal, assignLocation || !isSimpleParameterList, !isSimpleParameterList);
- }
- else if (arg->nop == knopParamPattern)
- {
- arg->sxParamPattern.location = byteCodeGenerator->NextVarRegister();
- }
- else
- {
- Assert(false);
- }
- UInt16Math::Inc(pos);
- };
- // We process rest separately because the number of in args needs to exclude rest.
- MapFormalsWithoutRest(pnode, addArgToScope);
- byteCodeGenerator->SetNumberOfInArgs(pos);
- MapFormalsFromPattern(pnode, addArgToScope);
- if (pnode->sxFnc.pnodeRest != nullptr)
- {
- // The rest parameter will always be in a register, regardless of whether it is in a scope slot.
- // We save the assignLocation value for the assert condition below.
- bool assignLocationSave = assignLocation;
- assignLocation = true;
- addArgToScope(pnode->sxFnc.pnodeRest);
- assignLocation = assignLocationSave;
- }
- Assert(!assignLocation || byteCodeGenerator->TopFuncInfo()->varRegsCount + 1 == pos);
- }
- void AddVarsToScope(ParseNode *vars, ByteCodeGenerator *byteCodeGenerator)
- {
- while (vars != nullptr)
- {
- Symbol *sym = nullptr;
- if (!byteCodeGenerator->UseParserBindings()
- && byteCodeGenerator->GetCurrentScope()->GetFunc()->GetParamScope() != nullptr)
- {
- SymbolName const symName(reinterpret_cast<const wchar_t*>(vars->sxVar.pid->Psz()), vars->sxVar.pid->Cch());
- // If we are not using parser bindings, we need to check that the sym is not in the parameter scope before adding it.
- // Fetch the sym we just added from the param scope.
- sym = byteCodeGenerator->GetCurrentScope()->GetFunc()->GetParamScope()->FindLocalSymbol(symName);
- // Arguments needs to be created at parameter scope.
- if (sym == nullptr && vars->grfpn & PNodeFlags::fpnArguments)
- {
- Scope* scope = byteCodeGenerator->GetCurrentScope()->GetFunc()->GetParamScope();
- sym = byteCodeGenerator->AddSymbolToScope(scope, reinterpret_cast<const wchar_t*>(vars->sxVar.pid->Psz()), vars->sxVar.pid->Cch(), vars, STVariable);
- }
- }
- if (sym == nullptr)
- {
- sym = byteCodeGenerator->AddSymbolToFunctionScope(reinterpret_cast<const wchar_t*>(vars->sxVar.pid->Psz()), vars->sxVar.pid->Cch(), vars, STVariable);
- }
- #if DBG_DUMP
- if (sym->GetSymbolType() == STVariable && byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared var %s of type %s\n",
- vars->sxVar.pid->Psz(), sym->GetSymbolTypeName());
- }
- #endif
- if (sym->GetIsArguments() || vars->sxVar.pnodeInit == nullptr)
- {
- // LHS's of var decls are usually bound to symbols later, during the Visit/Bind pass,
- // so that things like catch scopes can be taken into account.
- // The exception is "arguments", which always binds to the local scope.
- // We can also bind to the function scope symbol now if there's no init value
- // to assign.
- vars->sxVar.sym = sym;
- if (sym->GetIsArguments())
- {
- byteCodeGenerator->TopFuncInfo()->SetArgumentsSymbol(sym);
- }
- }
- else
- {
- vars->sxVar.sym = nullptr;
- }
- vars = vars->sxVar.pnodeNext;
- }
- }
- template <class Fn>
- void VisitFncDecls(ParseNode *fns, Fn action)
- {
- while (fns != nullptr)
- {
- switch (fns->nop)
- {
- case knopFncDecl:
- action(fns);
- fns = fns->sxFnc.pnodeNext;
- break;
- case knopBlock:
- fns = fns->sxBlock.pnodeNext;
- break;
- case knopCatch:
- fns = fns->sxCatch.pnodeNext;
- break;
- case knopWith:
- fns = fns->sxWith.pnodeNext;
- break;
- default:
- AssertMsg(false, "Unexpected opcode in tree of scopes");
- return;
- }
- }
- }
- FuncInfo* PreVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator)
- {
- // Do binding of function name(s), initialize function scope, propagate function-wide properties from
- // the parent (if any).
- FuncInfo* parentFunc = byteCodeGenerator->TopFuncInfo();
- // fIsRoot indicates that this is the root function to be returned to a ParseProcedureText/AddScriptLet/etc. call.
- // In such cases, the global function is just a wrapper around the root function's declaration.
- // We used to assert that this was the only top-level function body, but it's possible to trick
- // "new Function" into compiling more than one function (see WOOB 1121759).
- bool fIsRoot = (!(byteCodeGenerator->GetFlags() & fscrGlobalCode) &&
- parentFunc->IsGlobalFunction() &&
- parentFunc->root->sxFnc.GetTopLevelScope() == pnode);
- const wchar_t *funcName = Js::Constants::AnonymousFunction;
- uint funcNameLength = Js::Constants::AnonymousFunctionLength;
- uint functionNameOffset = 0;
- bool funcExprWithName = false;
- if (pnode->sxFnc.hint != nullptr)
- {
- funcName = reinterpret_cast<const wchar_t*>(pnode->sxFnc.hint);
- funcNameLength = pnode->sxFnc.hintLength;
- functionNameOffset = pnode->sxFnc.hintOffset;
- Assert(funcNameLength != 0 || funcNameLength == (int)wcslen(funcName));
- }
- if (pnode->sxFnc.IsDeclaration() || pnode->sxFnc.IsMethod())
- {
- // Class members have the fully qualified name stored in 'hint', no need to replace it.
- if (pnode->sxFnc.pid && !pnode->sxFnc.IsClassMember())
- {
- funcName = reinterpret_cast<const wchar_t*>(pnode->sxFnc.pid->Psz());
- funcNameLength = pnode->sxFnc.pid->Cch();
- functionNameOffset = 0;
- }
- }
- else if ((pnode->sxFnc.pnodeName != nullptr) &&
- (pnode->sxFnc.pnodeName->nop == knopVarDecl))
- {
- funcName = reinterpret_cast<const wchar_t*>(pnode->sxFnc.pnodeName->sxVar.pid->Psz());
- funcNameLength = pnode->sxFnc.pnodeName->sxVar.pid->Cch();
- functionNameOffset = 0;
- //
- // create the new scope for Function expression only in ES5 mode
- //
- funcExprWithName = true;
- }
- if (byteCodeGenerator->Trace())
- {
- Output::Print(L"function start %s\n", funcName);
- }
- Assert(pnode->sxFnc.funcInfo == nullptr);
- FuncInfo* funcInfo = pnode->sxFnc.funcInfo = byteCodeGenerator->StartBindFunction(funcName, funcNameLength, functionNameOffset, &funcExprWithName, pnode);
- funcInfo->byteCodeFunction->SetIsNamedFunctionExpression(funcExprWithName);
- funcInfo->byteCodeFunction->SetIsNameIdentifierRef(pnode->sxFnc.isNameIdentifierRef);
- if (fIsRoot)
- {
- byteCodeGenerator->SetRootFuncInfo(funcInfo);
- }
- if (pnode->sxFnc.pnodeBody == nullptr)
- {
- // This is a deferred byte code gen, so we're done.
- // Process the formal arguments, even if there's no AST for the body, to support Function.length.
- if (byteCodeGenerator->UseParserBindings())
- {
- Js::ArgSlot pos = 1;
- // We skip the rest parameter here because it is not counted towards the in arg count.
- MapFormalsWithoutRest(pnode, [&](ParseNode *pnode) { UInt16Math::Inc(pos); });
- byteCodeGenerator->SetNumberOfInArgs(pos);
- }
- else
- {
- AddArgsToScope(pnode, byteCodeGenerator, false);
- }
- return funcInfo;
- }
- if (pnode->sxFnc.HasReferenceableBuiltInArguments())
- {
- // The parser identified that there is a way to reference the built-in 'arguments' variable from this function. So, we
- // need to determine whether we need to create the variable or not. We need to create the variable iff:
- if (pnode->sxFnc.CallsEval())
- {
- // 1. eval is called.
- // 2. when the debugging is enabled, since user can seek arguments during breakpoint.
- funcInfo->SetHasArguments(true);
- funcInfo->SetHasHeapArguments(true);
- if (funcInfo->inArgsCount == 0)
- {
- // If no formals to function, no need to create the propertyid array
- byteCodeGenerator->AssignNullConstRegister();
- }
- }
- else if (pnode->sxFnc.UsesArguments())
- {
- // 3. the function directly references an 'arguments' identifier
- funcInfo->SetHasArguments(true);
- if (pnode->sxFnc.HasHeapArguments())
- {
- funcInfo->SetHasHeapArguments(true, !pnode->sxFnc.IsGenerator() /*= Optimize arguments in backend*/);
- if (funcInfo->inArgsCount == 0)
- {
- // If no formals to function, no need to create the propertyid array
- byteCodeGenerator->AssignNullConstRegister();
- }
- }
- }
- }
- Js::FunctionBody* parentFunctionBody = parentFunc->GetParsedFunctionBody();
- if (funcInfo->GetHasArguments() ||
- parentFunctionBody->GetHasOrParentHasArguments())
- {
- // The JIT uses this info, for instance, to narrow kills of array operations
- funcInfo->GetParsedFunctionBody()->SetHasOrParentHasArguments(true);
- }
- PreVisitBlock(pnode->sxFnc.pnodeScopes, byteCodeGenerator);
- // If we have arguments, we are going to need locations if the function is in strict mode or we have a non-simple parameter list. This is because we will not create a scope object.
- bool assignLocationForFormals = !(funcInfo->GetHasHeapArguments() && ByteCodeGenerator::NeedScopeObjectForArguments(funcInfo, funcInfo->root));
- AddArgsToScope(pnode, byteCodeGenerator, assignLocationForFormals);
- PreVisitBlock(pnode->sxFnc.pnodeBodyScope, byteCodeGenerator);
- AddVarsToScope(pnode->sxFnc.pnodeVars, byteCodeGenerator);
- return funcInfo;
- }
- void AssignFuncSymRegister(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * callee)
- {
- // register to hold the allocated function (in enclosing sequence of global statements)
- // TODO: Make the parser identify uses of function decls as RHS's of expressions.
- // Currently they're all marked as used, so they all get permanent (non-temp) registers.
- if (pnode->sxFnc.pnodeName == nullptr)
- {
- return;
- }
- Assert(pnode->sxFnc.pnodeName->nop == knopVarDecl);
- Symbol *sym = pnode->sxFnc.pnodeName->sxVar.sym;
- if (sym)
- {
- if (!sym->GetIsGlobal() && !(callee->funcExprScope && callee->funcExprScope->GetIsObject()))
- {
- // If the func decl is used, we have to give the expression a register to protect against:
- // x.x = function f() {...};
- // x.y = function f() {...};
- // If we let the value reside in the local slot for f, then both assignments will get the
- // second definition.
- if (!pnode->sxFnc.IsDeclaration())
- {
- // A named function expression's name belongs to the enclosing scope.
- // In ES5 mode, it is visible only inside the inner function.
- // Allocate a register for the 'name' symbol from an appropriate register namespace.
- if (callee->GetFuncExprNameReference())
- {
- // This is a function expression with a name, but probably doesn't have a use within
- // the function. If that is the case then allocate a register for LdFuncExpr inside the function
- // we just finished post-visiting.
- if (sym->GetLocation() == Js::Constants::NoRegister)
- {
- sym->SetLocation(callee->NextVarRegister());
- }
- }
- }
- else
- {
- // Function declaration
- byteCodeGenerator->AssignRegister(sym);
- pnode->location = sym->GetLocation();
- Assert(byteCodeGenerator->GetCurrentScope()->GetFunc() == sym->GetScope()->GetFunc());
- Symbol * functionScopeVarSym = sym->GetFuncScopeVarSym();
- if (functionScopeVarSym &&
- !functionScopeVarSym->GetIsGlobal() &&
- !functionScopeVarSym->IsInSlot(sym->GetScope()->GetFunc()))
- {
- Assert(byteCodeGenerator->GetScriptContext()->GetConfig()->IsBlockScopeEnabled());
- byteCodeGenerator->AssignRegister(functionScopeVarSym);
- }
- }
- }
- else if (!pnode->sxFnc.IsDeclaration())
- {
- if (sym->GetLocation() == Js::Constants::NoRegister)
- {
- // Here, we are assigning a register for the LdFuncExpr instruction inside the function we just finished
- // post-visiting. The symbol is given a register from the register pool for the function we just finished
- // post-visiting, rather than from the parent function's register pool.
- sym->SetLocation(callee->NextVarRegister());
- }
- }
- }
- }
- FuncInfo* PostVisitFunction(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator)
- {
- // Assign function-wide registers such as local frame object, closure environment, etc., based on
- // observed attributes. Propagate attributes to the parent function (if any).
- FuncInfo *top = byteCodeGenerator->TopFuncInfo();
- Symbol *sym = pnode->sxFnc.GetFuncSymbol();
- bool funcExprWithName = !top->IsGlobalFunction() && sym && sym->GetFuncExpr();
- if (top->IsLambda())
- {
- if (byteCodeGenerator->FindEnclosingNonLambda()->isThisLexicallyCaptured)
- {
- top->byteCodeFunction->SetCapturesThis();
- }
- }
- // If this is a named function expression and has deferred child, mark has non-local reference.
- if (funcExprWithName)
- {
- // If we are reparsing this function due to being in debug mode - we should restore the state of this from the earlier parse
- if (top->byteCodeFunction->IsFunctionParsed() && top->GetParsedFunctionBody()->HasFuncExprNameReference())
- {
- top->SetFuncExprNameReference(true);
- sym->SetHasNonLocalReference(true, byteCodeGenerator);
- top->SetHasLocalInClosure(true);
- }
- else if (top->HasDeferredChild())
- {
- // Before doing this, though, make sure there's no local symbol that hides the function name
- // from the nested functions. If a lookup starting at the current local scope finds some symbol
- // other than the func expr, then it's hidden. (See Win8 393618.)
- Assert(CONFIG_FLAG(DeferNested));
- sym->SetHasNonLocalReference(true, byteCodeGenerator);
- top->SetHasLocalInClosure(true);
- if (pnode->sxFnc.pnodeBody)
- {
- top->GetParsedFunctionBody()->SetAllNonLocalReferenced(true);
- }
- if (!top->root->sxFnc.NameIsHidden())
- {
- top->SetFuncExprNameReference(true);
- if (pnode->sxFnc.pnodeBody)
- {
- top->GetParsedFunctionBody()->SetFuncExprNameReference(true);
- }
- top->GetBodyScope()->SetIsObject();
- top->GetParamScope()->SetIsObject();
- if (pnode->sxFnc.pnodeBody)
- {
- top->GetParsedFunctionBody()->SetHasSetIsObject(true);
- }
- }
- }
- }
- if (pnode->nop != knopProg
- && !top->bodyScope->GetIsObject()
- && byteCodeGenerator->NeedObjectAsFunctionScope(top, pnode))
- {
- // Even if it wasn't determined during visiting this function that we need a scope object, we still have a few conditions that may require one.
- top->bodyScope->SetIsObject();
- }
- if (pnode->nop == knopProg
- && top->byteCodeFunction->GetIsStrictMode()
- && (byteCodeGenerator->GetFlags() & fscrEval))
- {
- // At global scope inside a strict mode eval, vars will not leak out and require a scope object (along with its parent.)
- top->bodyScope->SetIsObject();
- }
- if (pnode->sxFnc.pnodeBody)
- {
- if (!top->IsGlobalFunction())
- {
- PostVisitBlock(pnode->sxFnc.pnodeBodyScope, byteCodeGenerator);
- PostVisitBlock(pnode->sxFnc.pnodeScopes, byteCodeGenerator);
- }
- if ((byteCodeGenerator->GetFlags() & fscrEvalCode) && top->GetCallsEval())
- {
- // Must establish "this" in case nested eval refers to it.
- top->GetParsedFunctionBody()->SetHasThis(true);
- }
- // This function refers to the closure environment if:
- // 1. it has a child function (we'll pass the environment to the constructor when the child is created -
- // even if it's not needed, it's as cheap as loading "null" from the library);
- // 2. it calls eval (and will use the environment to construct the scope chain to pass to eval);
- // 3. it refers to a local defined in a parent function;
- // 4. it refers to a global and some parent calls eval (which might declare the "global" locally);
- // 5. it refers to a global and we're in an event handler;
- // 6. it refers to a global and the function was declared inside a "with";
- // 7. it refers to a global and we're in an eval expression.
- if (pnode->sxFnc.nestedCount != 0 ||
- top->GetCallsEval() ||
- top->GetHasClosureReference() ||
- ((top->GetHasGlobalRef() &&
- (byteCodeGenerator->InDynamicScope() ||
- (byteCodeGenerator->GetFlags() & (fscrImplicitThis | fscrImplicitParents | fscrEval))))))
- {
- byteCodeGenerator->SetNeedEnvRegister();
- if (top->GetIsEventHandler())
- {
- byteCodeGenerator->AssignThisRegister();
- }
- }
- // This function needs to construct a local frame on the heap if it is not the global function (even in eval) and:
- // 1. it calls eval, which may refer to or declare any locals in this frame;
- // 2. a child calls eval (which may refer to locals through a closure);
- // 3. it uses non-strict mode "arguments", so the arguments have to be put in a closure;
- // 4. it defines a local that is used by a child function (read from a closure).
- // 5. it is a main function that's wrapped in a function expression scope but has locals used through
- // a closure (used in forReference function call cases in a with for example).
- if (!top->IsGlobalFunction())
- {
- if (top->GetCallsEval() ||
- top->GetChildCallsEval() ||
- (top->GetHasArguments() && ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode) && pnode->sxFnc.pnodeArgs != nullptr) ||
- top->GetHasLocalInClosure() ||
- top->funcExprScope && top->funcExprScope->GetMustInstantiate())
- {
- if (!top->GetCallsEval())
- {
- byteCodeGenerator->AssignFrameSlotsRegister();
- }
- if (byteCodeGenerator->NeedObjectAsFunctionScope(top, top->root)
- || top->bodyScope->GetIsObject()
- || top->paramScope->GetIsObject())
- {
- byteCodeGenerator->AssignFrameObjRegister();
- }
- // The function also needs to construct a frame display if:
- // 1. it calls eval;
- // 2. it has a child function.
- // 3. When has arguments and in debug mode. So that frame display be there along with frame object register.
- if (top->GetCallsEval() ||
- pnode->sxFnc.nestedCount != 0
- || (top->GetHasArguments()
- && (pnode->sxFnc.pnodeArgs != nullptr)
- && byteCodeGenerator->IsInDebugMode()))
- {
- byteCodeGenerator->SetNeedEnvRegister(); // This to ensure that Env should be there when the FrameDisplay register is there.
- byteCodeGenerator->AssignFrameDisplayRegister();
- if (top->GetIsEventHandler())
- {
- byteCodeGenerator->AssignThisRegister();
- }
- }
- }
- if (top->GetHasArguments())
- {
- Symbol *argSym = top->GetArgumentsSymbol();
- Assert(argSym);
- if (argSym)
- {
- Assert(top->bodyScope->GetScopeSlotCount() == 0);
- Assert(top->sameNameArgsPlaceHolderSlotCount == 0);
- byteCodeGenerator->AssignRegister(argSym);
- uint i = 0;
- auto setArgScopeSlot = [&](ParseNode *pnodeArg)
- {
- if (pnodeArg->IsVarLetOrConst())
- {
- Symbol* sym = pnodeArg->sxVar.sym;
- if (sym->GetScopeSlot() != Js::Constants::NoProperty)
- {
- top->sameNameArgsPlaceHolderSlotCount++; // Same name args appeared before
- }
- sym->SetScopeSlot(i);
- i++;
- }
- };
- // We don't need to process the rest parameter here because it may not need a scope slot.
- if (ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode))
- {
- MapFormalsWithoutRest(pnode, setArgScopeSlot);
- MapFormalsFromPattern(pnode, setArgScopeSlot);
- }
- top->paramScope->SetScopeSlotCount(i);
- Assert(top->GetHasHeapArguments());
- if (ByteCodeGenerator::NeedScopeObjectForArguments(top, pnode)
- && pnode->sxFnc.IsSimpleParameterList())
- {
- top->byteCodeFunction->SetHasImplicitArgIns(false);
- }
- }
- }
- }
- else
- {
- Assert(top->IsGlobalFunction());
- // eval is called in strict mode
- bool newScopeForEval = (top->byteCodeFunction->GetIsStrictMode() && (byteCodeGenerator->GetFlags() & fscrEval));
- if (newScopeForEval)
- {
- byteCodeGenerator->SetNeedEnvRegister();
- byteCodeGenerator->AssignFrameObjRegister();
- byteCodeGenerator->AssignFrameDisplayRegister();
- }
- }
- Assert(!funcExprWithName || sym);
- if (funcExprWithName)
- {
- Assert(top->funcExprScope);
- // If the func expr may be accessed via eval, force the func expr scope into an object.
- if (top->GetCallsEval() || top->GetChildCallsEval())
- {
- top->funcExprScope->SetIsObject();
- }
- if (top->funcExprScope->GetIsObject())
- {
- top->funcExprScope->SetLocation(byteCodeGenerator->NextVarRegister());
- }
- }
- }
- byteCodeGenerator->EndBindFunction(funcExprWithName);
- // If the "child" is the global function, we're done.
- if (top->IsGlobalFunction())
- {
- return top;
- }
- FuncInfo* const parentFunc = byteCodeGenerator->TopFuncInfo();
- Js::FunctionBody * parentFunctionBody = parentFunc->byteCodeFunction->GetFunctionBody();
- Assert(parentFunctionBody != nullptr);
- bool const hasAnyDeferredChild = top->HasDeferredChild() || top->IsDeferred();
- bool setHasNonLocalReference = parentFunctionBody->HasAllNonLocalReferenced();
- // If we have any deferred child, we need to instantiate the fake global block scope if it is not empty
- if (parentFunc->IsGlobalFunction())
- {
- if (hasAnyDeferredChild && byteCodeGenerator->IsEvalWithBlockScopingNoParentScopeInfo())
- {
- Scope * globalEvalBlockScope = parentFunc->GetGlobalEvalBlockScope();
- if (globalEvalBlockScope->Count() != 0 || parentFunc->isThisLexicallyCaptured)
- {
- // We must instantiate the eval block scope if we have any deferred child
- // and force all symbol to have non local reference so it will be captured
- // by the scope.
- globalEvalBlockScope->SetMustInstantiate(true);
- globalEvalBlockScope->ForceAllSymbolNonLocalReference(byteCodeGenerator);
- parentFunc->SetHasDeferredChild();
- }
- }
- }
- else
- {
- if (setHasNonLocalReference)
- {
- // All locals are already marked as non-locals-referenced. Mark the parent as well.
- if (parentFunctionBody->HasSetIsObject())
- {
- // Updated the current function, as per the previous stored info.
- parentFunc->GetBodyScope()->SetIsObject();
- parentFunc->GetParamScope()->SetIsObject();
- }
- }
- // Propagate "hasDeferredChild" attribute back to parent.
- if (hasAnyDeferredChild)
- {
- Assert(CONFIG_FLAG(DeferNested));
- parentFunc->SetHasDeferredChild();
- // Anything in parent may have non-local reference from deferredChild.
- setHasNonLocalReference = true;
- parentFunctionBody->SetAllNonLocalReferenced(true);
- // If a deferred child has with, parent scopes may contain symbols called inside the with.
- // Current implementation needs the symScope isObject.
- if (top->ChildHasWith() || pnode->sxFnc.HasWithStmt())
- {
- parentFunc->SetChildHasWith();
- parentFunc->GetBodyScope()->SetIsObject();
- parentFunc->GetParamScope()->SetIsObject();
- // Record this for future use in the no-refresh debugging.
- parentFunctionBody->SetHasSetIsObject(true);
- }
- }
- // Propagate HasMaybeEscapedNestedFunc
- if (!byteCodeGenerator->CanStackNestedFunc(top, false) ||
- byteCodeGenerator->NeedObjectAsFunctionScope(top, pnode))
- {
- parentFunc->SetHasMaybeEscapedNestedFunc(DebugOnly(L"Child"));
- }
- }
- if (top->GetCallsEval() || top->GetChildCallsEval())
- {
- parentFunc->SetChildCallsEval(true);
- ParseNode *currentBlock = byteCodeGenerator->GetCurrentBlock();
- if (currentBlock)
- {
- Assert(currentBlock->nop == knopBlock);
- currentBlock->sxBlock.SetChildCallsEval(true);
- }
- parentFunc->SetHasHeapArguments(true);
- setHasNonLocalReference = true;
- parentFunctionBody->SetAllNonLocalReferenced(true);
- Scope * const funcExprScope = top->funcExprScope;
- if (funcExprScope)
- {
- // If we have the body scope as an object, the outer function expression scope also needs to be an object to propagate the name.
- funcExprScope->SetIsObject();
- }
- if (parentFunc->inArgsCount == 1)
- {
- // If no formals to function, no need to create the propertyid array
- byteCodeGenerator->AssignNullConstRegister();
- }
- }
- if (setHasNonLocalReference && !parentFunctionBody->HasDoneAllNonLocalReferenced())
- {
- parentFunc->GetBodyScope()->ForceAllSymbolNonLocalReference(byteCodeGenerator);
- if (!parentFunc->IsGlobalFunction())
- {
- parentFunc->GetParamScope()->ForceAllSymbolNonLocalReference(byteCodeGenerator);
- }
- parentFunctionBody->SetHasDoneAllNonLocalReferenced(true);
- }
- if (top->HasSuperReference())
- {
- top->AssignSuperRegister();
- }
- if (top->HasDirectSuper())
- {
- top->AssignSuperCtorRegister();
- }
- if (top->IsClassConstructor())
- {
- if (top->IsBaseClassConstructor())
- {
- // Base class constructor may not explicitly reference new.target but we always need to have it in order to construct the 'this' object.
- top->AssignNewTargetRegister();
- // Also must have a register to slot the 'this' object into.
- top->AssignThisRegister();
- }
- else
- {
- // Derived class constructors need to check undefined against explicit return statements.
- top->AssignUndefinedConstRegister();
- top->AssignNewTargetRegister();
- if (top->GetCallsEval() || top->GetChildCallsEval())
- {
- top->AssignThisRegister();
- top->SetIsThisLexicallyCaptured();
- top->SetIsNewTargetLexicallyCaptured();
- top->SetIsSuperLexicallyCaptured();
- top->SetIsSuperCtorLexicallyCaptured();
- top->SetHasLocalInClosure(true);
- top->SetHasClosureReference(true);
- top->SetHasCapturedThis();
- }
- }
- }
- AssignFuncSymRegister(pnode, byteCodeGenerator, top);
- return top;
- }
- void MarkInit(ParseNode* pnode)
- {
- if (pnode->nop == knopList)
- {
- do
- {
- MarkInit(pnode->sxBin.pnode1);
- pnode = pnode->sxBin.pnode2;
- }
- while (pnode->nop == knopList);
- MarkInit(pnode);
- }
- else
- {
- Symbol *sym = nullptr;
- ParseNode *pnodeInit = nullptr;
- if (pnode->nop == knopVarDecl)
- {
- sym = pnode->sxVar.sym;
- pnodeInit = pnode->sxVar.pnodeInit;
- }
- else if (pnode->nop == knopAsg && pnode->sxBin.pnode1->nop == knopName)
- {
- sym = pnode->sxBin.pnode1->sxPid.sym;
- pnodeInit = pnode->sxBin.pnode2;
- }
- if (sym && !sym->GetIsUsed() && pnodeInit)
- {
- sym->SetHasInit(true);
- if (sym->HasVisitedCapturingFunc())
- {
- sym->SetHasNonCommittedReference(false);
- }
- }
- }
- }
- void AddFunctionsToScope(ParseNodePtr scope, ByteCodeGenerator * byteCodeGenerator)
- {
- VisitFncDecls(scope, [byteCodeGenerator](ParseNode *fn)
- {
- ParseNode *pnodeName = fn->sxFnc.pnodeName;
- if (pnodeName && pnodeName->nop == knopVarDecl && fn->sxFnc.IsDeclaration())
- {
- const wchar_t *fnName = pnodeName->sxVar.pid->Psz();
- if (byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared function %s\n", fnName);
- }
- // In ES6, functions are scoped to the block, which will be the current scope.
- // Pre-ES6, function declarations are scoped to the function body, so get that scope.
- Symbol *sym;
- if (!byteCodeGenerator->GetCurrentScope()->IsGlobalEvalBlockScope())
- {
- sym = byteCodeGenerator->AddSymbolToScope(byteCodeGenerator->GetCurrentScope(), fnName, pnodeName->sxVar.pid->Cch(), pnodeName, STFunction);
- }
- else
- {
- sym = byteCodeGenerator->AddSymbolToFunctionScope(fnName, pnodeName->sxVar.pid->Cch(), pnodeName, STFunction);
- }
- pnodeName->sxVar.sym = sym;
- if (sym->GetIsGlobal())
- {
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- func->SetHasGlobalRef(true);
- }
- if (byteCodeGenerator->GetScriptContext()->GetConfig()->IsBlockScopeEnabled()
- && sym->GetScope() != sym->GetScope()->GetFunc()->GetBodyScope()
- && sym->GetScope() != sym->GetScope()->GetFunc()->GetParamScope())
- {
- sym->SetIsBlockVar(true);
- }
- }
- });
- }
- template <class PrefixFn, class PostfixFn>
- void VisitNestedScopes(ParseNode* pnodeScopeList, ParseNode* pnodeParent, ByteCodeGenerator* byteCodeGenerator,
- PrefixFn prefix, PostfixFn postfix, uint *pIndex)
- {
- // Visit all scopes nested in this scope before visiting this function's statements. This way we have all the
- // attributes of all the inner functions before we assign registers within this function.
- // All the attributes we need to propagate downward should already be recorded by the parser.
- // - call to "eval()"
- // - nested in "with"
- Js::ParseableFunctionInfo* parentFunc = pnodeParent->sxFnc.funcInfo->byteCodeFunction;
- ParseNode* pnodeScope;
- uint i = 0;
- // Cache to restore it back once we come out of current function.
- Js::FunctionBody * pLastReuseFunc = byteCodeGenerator->pCurrentFunction;
- for (pnodeScope = pnodeScopeList; pnodeScope;)
- {
- switch (pnodeScope->nop)
- {
- case knopFncDecl:
- if (pLastReuseFunc)
- {
- if (!byteCodeGenerator->IsInNonDebugMode())
- {
- // Here we are trying to match the inner sub-tree as well with already created inner function.
- if ((pLastReuseFunc->GetIsGlobalFunc() && parentFunc->GetIsGlobalFunc())
- || (!pLastReuseFunc->GetIsGlobalFunc() && !parentFunc->GetIsGlobalFunc()))
- {
- Assert(pLastReuseFunc->StartInDocument() == pnodeParent->ichMin);
- Assert(pLastReuseFunc->LengthInChars() == pnodeParent->LengthInCodepoints());
- Assert(pLastReuseFunc->GetNestedCount() == parentFunc->GetNestedCount());
- // If the current function is not parsed yet, its function body is not generated yet.
- // Reset pCurrentFunction to null so that it will not be able re-use anything.
- Js::FunctionProxy* proxy = pLastReuseFunc->GetNestedFunc((*pIndex));
- if (proxy && proxy->IsFunctionBody())
- {
- byteCodeGenerator->pCurrentFunction = proxy->GetFunctionBody();
- }
- else
- {
- byteCodeGenerator->pCurrentFunction = nullptr;
- }
- }
- }
- else if (!parentFunc->GetIsGlobalFunc())
- {
- // In the deferred parsing mode, we will be reusing the only one function (which is asked when on ::Begin) all inner function will be created.
- byteCodeGenerator->pCurrentFunction = nullptr;
- }
- }
- PreVisitFunction(pnodeScope, byteCodeGenerator);
- pnodeParent->sxFnc.funcInfo->OnStartVisitFunction(pnodeScope);
- if (pnodeScope->sxFnc.pnodeBody)
- {
- if (!byteCodeGenerator->IsInNonDebugMode() && pLastReuseFunc != nullptr && byteCodeGenerator->pCurrentFunction == nullptr)
- {
- // Patch current non-parsed function's FunctionBodyImpl with the new generated function body.
- // So that the function object (pointing to the old function body) can able to get to the new one.
- Js::FunctionProxy* proxy = pLastReuseFunc->GetNestedFunc((*pIndex));
- if (proxy && !proxy->IsFunctionBody())
- {
- proxy->UpdateFunctionBodyImpl(pnodeScope->sxFnc.funcInfo->byteCodeFunction->GetFunctionBody());
- }
- }
- BeginVisitBlock(pnodeScope->sxFnc.pnodeScopes, byteCodeGenerator);
- i = 0;
- ParseNodePtr containerScope = pnodeScope->sxFnc.pnodeScopes;
- VisitNestedScopes(containerScope, pnodeScope, byteCodeGenerator, prefix, postfix, &i);
- MapFormals(pnodeScope, [&](ParseNode *argNode) { Visit(argNode, byteCodeGenerator, prefix, postfix); });
- if (!pnodeScope->sxFnc.IsSimpleParameterList())
- {
- byteCodeGenerator->AssignUndefinedConstRegister();
- }
- BeginVisitBlock(pnodeScope->sxFnc.pnodeBodyScope, byteCodeGenerator);
- ParseNode* pnode = pnodeScope->sxFnc.pnodeBody;
- while (pnode->nop == knopList)
- {
- // Check to see whether initializations of locals to "undef" can be skipped.
- // The logic to do this is cheap - omit the init if we see an init with a value
- // on the RHS at the top statement level (i.e., not inside a block, try, loop, etc.)
- // before we see a use. The motivation is to help identify single-def locals in the BE.
- // Note that this can't be done for globals.
- byteCodeGenerator->SetCurrentTopStatement(pnode->sxBin.pnode1);
- Visit(pnode->sxBin.pnode1, byteCodeGenerator, prefix, postfix);
- if (!pnodeScope->sxFnc.funcInfo->GetCallsEval() &&
- !pnodeScope->sxFnc.funcInfo->GetChildCallsEval() &&
- // So that it will not be marked as init thus it will be added to the diagnostics symbols container.
- !(byteCodeGenerator->ShouldTrackDebuggerMetadata()))
- {
- MarkInit(pnode->sxBin.pnode1);
- }
- pnode = pnode->sxBin.pnode2;
- }
- byteCodeGenerator->SetCurrentTopStatement(pnode);
- Visit(pnode, byteCodeGenerator, prefix, postfix);
- EndVisitBlock(pnodeScope->sxFnc.pnodeBodyScope, byteCodeGenerator);
- EndVisitBlock(pnodeScope->sxFnc.pnodeScopes, byteCodeGenerator);
- }
- else if (pnodeScope->sxFnc.nestedCount)
- {
- // The nested function is deferred but has its own nested functions.
- // Make sure we at least zero-initialize its array in case, for instance, we get cloned
- // before the function is called and the array filled in.
- #ifdef RECYCLER_WRITE_BARRIER
- WriteBarrierPtr<Js::FunctionProxy>::ClearArray(pnodeScope->sxFnc.funcInfo->byteCodeFunction->GetNestedFuncArray(), pnodeScope->sxFnc.nestedCount);
- #else
- memset(pnodeScope->sxFnc.funcInfo->byteCodeFunction->GetNestedFuncArray(), 0, pnodeScope->sxFnc.nestedCount * sizeof(Js::FunctionBody*));
- #endif
- }
- pnodeScope->sxFnc.nestedIndex = *pIndex;
- parentFunc->SetNestedFunc(pnodeScope->sxFnc.funcInfo->byteCodeFunction, (*pIndex)++, byteCodeGenerator->GetFlags());
- Assert(parentFunc);
- pnodeParent->sxFnc.funcInfo->OnEndVisitFunction(pnodeScope);
- PostVisitFunction(pnodeScope, byteCodeGenerator);
- // Merge parameter and body scopes, unless we are deferring the function.
- // If we are deferring the function, we will need both scopes to do the proper binding when
- // the function is undeferred. After the function is undeferred, it is safe to merge the scopes.
- if (pnodeScope->sxFnc.funcInfo->paramScope != nullptr && pnodeScope->sxFnc.pnodeBody != nullptr)
- {
- Scope::MergeParamAndBodyScopes(pnodeScope, byteCodeGenerator);
- }
- pnodeScope = pnodeScope->sxFnc.pnodeNext;
- byteCodeGenerator->pCurrentFunction = pLastReuseFunc;
- break;
- case knopBlock:
- PreVisitBlock(pnodeScope, byteCodeGenerator);
- pnodeParent->sxFnc.funcInfo->OnStartVisitScope(pnodeScope->sxBlock.scope);
- VisitNestedScopes(pnodeScope->sxBlock.pnodeScopes, pnodeParent, byteCodeGenerator, prefix, postfix, pIndex);
- pnodeParent->sxFnc.funcInfo->OnEndVisitScope(pnodeScope->sxBlock.scope);
- PostVisitBlock(pnodeScope, byteCodeGenerator);
- pnodeScope = pnodeScope->sxBlock.pnodeNext;
- break;
- case knopCatch:
- PreVisitCatch(pnodeScope, byteCodeGenerator);
- Visit(pnodeScope->sxCatch.pnodeParam, byteCodeGenerator, prefix, postfix);
- if (pnodeScope->sxCatch.pnodeParam->nop == knopParamPattern)
- {
- if (pnodeScope->sxCatch.pnodeParam->sxParamPattern.location == Js::Constants::NoRegister)
- {
- pnodeScope->sxCatch.pnodeParam->sxParamPattern.location = byteCodeGenerator->NextVarRegister();
- }
- }
- pnodeParent->sxFnc.funcInfo->OnStartVisitScope(pnodeScope->sxCatch.scope);
- VisitNestedScopes(pnodeScope->sxCatch.pnodeScopes, pnodeParent, byteCodeGenerator, prefix, postfix, pIndex);
- pnodeParent->sxFnc.funcInfo->OnEndVisitScope(pnodeScope->sxCatch.scope);
- PostVisitCatch(pnodeScope, byteCodeGenerator);
- pnodeScope = pnodeScope->sxCatch.pnodeNext;
- break;
- case knopWith:
- PreVisitWith(pnodeScope, byteCodeGenerator);
- pnodeParent->sxFnc.funcInfo->OnStartVisitScope(pnodeScope->sxWith.scope);
- VisitNestedScopes(pnodeScope->sxWith.pnodeScopes, pnodeParent, byteCodeGenerator, prefix, postfix, pIndex);
- pnodeParent->sxFnc.funcInfo->OnEndVisitScope(pnodeScope->sxWith.scope);
- PostVisitWith(pnodeScope, byteCodeGenerator);
- pnodeScope = pnodeScope->sxWith.pnodeNext;
- break;
- default:
- AssertMsg(false, "Unexpected opcode in tree of scopes");
- return;
- }
- }
- }
- void PreVisitBlock(ParseNode *pnodeBlock, ByteCodeGenerator *byteCodeGenerator)
- {
- if (!pnodeBlock->sxBlock.scope &&
- !pnodeBlock->sxBlock.HasBlockScopedContent() &&
- !pnodeBlock->sxBlock.GetCallsEval())
- {
- // Do nothing here if the block doesn't declare anything or call eval (which may declare something).
- return;
- }
- bool isGlobalEvalBlockScope = false;
- FuncInfo *func = byteCodeGenerator->TopFuncInfo();
- if (func->IsGlobalFunction() &&
- func->root->sxFnc.pnodeScopes == pnodeBlock &&
- byteCodeGenerator->IsEvalWithBlockScopingNoParentScopeInfo())
- {
- isGlobalEvalBlockScope = true;
- }
- Assert(!byteCodeGenerator->UseParserBindings() ||
- !pnodeBlock->sxBlock.scope ||
- isGlobalEvalBlockScope == (pnodeBlock->sxBlock.scope->GetScopeType() == ScopeType_GlobalEvalBlock));
- ArenaAllocator *alloc = byteCodeGenerator->GetAllocator();
- Scope *scope;
- if ((pnodeBlock->sxBlock.blockType == PnodeBlockType::Global && !byteCodeGenerator->IsEvalWithBlockScopingNoParentScopeInfo()) || pnodeBlock->sxBlock.blockType == PnodeBlockType::Function)
- {
- scope = byteCodeGenerator->GetCurrentScope();
- if (pnodeBlock->sxBlock.blockType == PnodeBlockType::Function)
- {
- AnalysisAssert(pnodeBlock->sxBlock.scope);
- if (pnodeBlock->sxBlock.scope->GetScopeType() == ScopeType_Parameter
- && scope->GetScopeType() == ScopeType_FunctionBody)
- {
- scope = scope->GetEnclosingScope();
- }
- }
- pnodeBlock->sxBlock.scope = scope;
- }
- else if (!(pnodeBlock->grfpn & fpnSyntheticNode) || isGlobalEvalBlockScope)
- {
- Assert(byteCodeGenerator->GetScriptContext()->GetConfig()->IsBlockScopeEnabled());
- scope = pnodeBlock->sxBlock.scope;
- if (!scope || !byteCodeGenerator->UseParserBindings())
- {
- scope = Anew(alloc, Scope, alloc,
- isGlobalEvalBlockScope? ScopeType_GlobalEvalBlock : ScopeType_Block, true);
- pnodeBlock->sxBlock.scope = scope;
- }
- scope->SetFunc(byteCodeGenerator->TopFuncInfo());
- // For now, prevent block scope from being merged with enclosing function scope.
- // Consider optimizing this.
- scope->SetCanMerge(false);
- if (isGlobalEvalBlockScope)
- {
- scope->SetIsObject();
- }
- byteCodeGenerator->PushScope(scope);
- byteCodeGenerator->PushBlock(pnodeBlock);
- }
- else
- {
- return;
- }
- Assert(scope && scope == pnodeBlock->sxBlock.scope);
- bool isGlobalScope = (scope->GetEnclosingScope() == nullptr);
- Assert(!isGlobalScope || (pnodeBlock->grfpn & fpnSyntheticNode));
- // If it is the global eval block scope, we don't what function decl to be assigned in the block scope.
- // They should already declared in the global function's scope.
- if (!isGlobalEvalBlockScope && !isGlobalScope)
- {
- AddFunctionsToScope(pnodeBlock->sxBlock.pnodeScopes, byteCodeGenerator);
- }
- // We can skip this check by not creating the GlobalEvalBlock above and in Parser::Parse for console eval but that seems to break couple of places
- // as we heavily depend on BlockHasOwnScope function. Once we clean up the creation of GlobalEvalBlock for evals we can clean this as well.
- if (byteCodeGenerator->IsConsoleScopeEval() && isGlobalEvalBlockScope && !isGlobalScope)
- {
- AssertMsg(scope->GetEnclosingScope()->GetScopeType() == ScopeType_Global, "Additional scope between Global and GlobalEvalBlock?");
- scope = scope->GetEnclosingScope();
- isGlobalScope = true;
- }
- auto addSymbolToScope = [scope, byteCodeGenerator, isGlobalScope](ParseNode *pnode)
- {
- Symbol *sym = byteCodeGenerator->AddSymbolToScope(scope, reinterpret_cast<const wchar_t*>(pnode->sxVar.pid->Psz()), pnode->sxVar.pid->Cch(), pnode, STVariable);
- #if DBG_DUMP
- if (sym->GetSymbolType() == STVariable && byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared %s %s of type %s\n",
- sym->GetDecl()->nop == knopLetDecl ? L"let" : L"const",
- pnode->sxVar.pid->Psz(),
- sym->GetSymbolTypeName());
- }
- #endif
- sym->SetIsGlobal(isGlobalScope);
- sym->SetIsBlockVar(true);
- sym->SetNeedDeclaration(true);
- pnode->sxVar.sym = sym;
- };
- byteCodeGenerator->IterateBlockScopedVariables(pnodeBlock, addSymbolToScope);
- }
- void PostVisitBlock(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- if (!BlockHasOwnScope(pnode, byteCodeGenerator))
- {
- return;
- }
- if (pnode->sxBlock.GetCallsEval() || pnode->sxBlock.GetChildCallsEval() || (byteCodeGenerator->GetFlags() & (fscrEval | fscrImplicitThis | fscrImplicitParents)))
- {
- Scope *scope = pnode->sxBlock.scope;
- bool scopeIsEmpty = scope->IsEmpty();
- scope->SetIsObject();
- scope->SetCapturesAll(true);
- scope->SetMustInstantiate(!scopeIsEmpty);
- }
- byteCodeGenerator->PopScope();
- byteCodeGenerator->PopBlock();
- ParseNode *currentBlock = byteCodeGenerator->GetCurrentBlock();
- if (currentBlock && (pnode->sxBlock.GetCallsEval() || pnode->sxBlock.GetChildCallsEval()))
- {
- currentBlock->sxBlock.SetChildCallsEval(true);
- }
- }
- void PreVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- // Push the catch scope and add the catch expression to it.
- byteCodeGenerator->StartBindCatch(pnode);
- if (pnode->sxCatch.pnodeParam->nop == knopParamPattern)
- {
- Parser::MapBindIdentifier(pnode->sxCatch.pnodeParam->sxParamPattern.pnode1, [&](ParseNodePtr item)
- {
- Symbol *sym = item->sxVar.sym;
- if (!byteCodeGenerator->UseParserBindings())
- {
- sym = byteCodeGenerator->AddSymbolToScope(pnode->sxCatch.scope, reinterpret_cast<const wchar_t*>(item->sxVar.pid->Psz()), item->sxVar.pid->Cch(), item, STVariable);
- item->sxVar.sym = sym;
- }
- #if DBG_DUMP
- if (byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared catch var %s of type %s\n",
- item->sxVar.pid->Psz(), sym->GetSymbolTypeName());
- }
- #endif
- });
- }
- else
- {
- Symbol *sym;
- if (!byteCodeGenerator->UseParserBindings())
- {
- sym = byteCodeGenerator->AddSymbolToScope(pnode->sxCatch.scope, reinterpret_cast<const wchar_t*>(pnode->sxCatch.pnodeParam->sxPid.pid->Psz()), pnode->sxCatch.pnodeParam->sxPid.pid->Cch(), pnode->sxCatch.pnodeParam, STVariable);
- }
- else
- {
- sym = *pnode->sxCatch.pnodeParam->sxPid.symRef;
- }
- Assert(sym->GetScope() == pnode->sxCatch.scope);
- #if DBG_DUMP
- if (byteCodeGenerator->Trace())
- {
- Output::Print(L"current context has declared catch var %s of type %s\n",
- pnode->sxCatch.pnodeParam->sxPid.pid->Psz(), sym->GetSymbolTypeName());
- }
- #endif
- sym->SetIsCatch(true);
- pnode->sxCatch.pnodeParam->sxPid.sym = sym;
- }
- // This call will actually add the nested function symbols to the enclosing function scope (which is what we want).
- AddFunctionsToScope(pnode->sxCatch.pnodeScopes, byteCodeGenerator);
- }
- void PostVisitCatch(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- byteCodeGenerator->EndBindCatch();
- }
- void PreVisitWith(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- ArenaAllocator *alloc = byteCodeGenerator->GetAllocator();
- Scope *scope = Anew(alloc, Scope, alloc, ScopeType_With);
- scope->SetFunc(byteCodeGenerator->TopFuncInfo());
- scope->SetIsDynamic(true);
- pnode->sxWith.scope = scope;
- byteCodeGenerator->PushScope(scope);
- }
- void PostVisitWith(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- byteCodeGenerator->PopScope();
- }
- void BindFuncSymbol(ParseNode *pnodeFnc, ByteCodeGenerator *byteCodeGenerator)
- {
- if (pnodeFnc->sxFnc.pnodeName)
- {
- Assert(pnodeFnc->sxFnc.pnodeName->nop == knopVarDecl);
- Symbol *sym = pnodeFnc->sxFnc.pnodeName->sxVar.sym;
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- if (sym == nullptr || sym->GetIsGlobal())
- {
- func->SetHasGlobalRef(true);
- }
- }
- }
- bool IsMathLibraryId(Js::PropertyId propertyId)
- {
- return (propertyId >= Js::PropertyIds::abs) && (propertyId <= Js::PropertyIds::fround);
- }
- bool IsLibraryFunction(ParseNode* expr, Js::ScriptContext* scriptContext)
- {
- if (expr && expr->nop == knopDot)
- {
- ParseNode* lhs = expr->sxBin.pnode1;
- ParseNode* rhs = expr->sxBin.pnode2;
- if ((lhs != nullptr) && (rhs != nullptr) && (lhs->nop == knopName) && (rhs->nop == knopName))
- {
- Symbol* lsym = lhs->sxPid.sym;
- if ((lsym == nullptr || lsym->GetIsGlobal()) && lhs->sxPid.PropertyIdFromNameNode() == Js::PropertyIds::Math)
- {
- return IsMathLibraryId(rhs->sxPid.PropertyIdFromNameNode());
- }
- }
- }
- return false;
- }
- struct SymCheck
- {
- static const int kMaxInvertedSyms = 8;
- Symbol* syms[kMaxInvertedSyms];
- Symbol* permittedSym;
- int symCount;
- bool result;
- bool cond;
- bool AddSymbol(Symbol* sym)
- {
- if (symCount < kMaxInvertedSyms)
- {
- syms[symCount++] = sym;
- return true;
- }
- else
- {
- return false;
- }
- }
- bool MatchSymbol(Symbol* sym)
- {
- if (sym != permittedSym)
- {
- for (int i = 0; i < symCount; i++)
- {
- if (sym == syms[i])
- {
- return true;
- }
- }
- }
- return false;
- }
- void Init()
- {
- symCount = 0;
- result = true;
- }
- };
- void CheckInvertableExpr(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, SymCheck* symCheck)
- {
- if (symCheck->result)
- {
- switch (pnode->nop)
- {
- case knopName:
- if (symCheck->MatchSymbol(pnode->sxPid.sym))
- {
- symCheck->result = false;
- }
- break;
- case knopCall:
- {
- ParseNode* callTarget = pnode->sxBin.pnode1;
- if (callTarget != nullptr)
- {
- if (callTarget->nop == knopName)
- {
- Symbol* sym = callTarget->sxPid.sym;
- if (sym && sym->SingleDef())
- {
- ParseNode* decl = sym->GetDecl();
- if (decl == nullptr ||
- decl->nop != knopVarDecl ||
- !IsLibraryFunction(decl->sxVar.pnodeInit, byteCodeGenerator->GetScriptContext()))
- {
- symCheck->result = false;
- }
- }
- else
- {
- symCheck->result = false;
- }
- }
- else if (callTarget->nop == knopDot)
- {
- if (!IsLibraryFunction(callTarget, byteCodeGenerator->GetScriptContext()))
- {
- symCheck->result = false;
- }
- }
- }
- else
- {
- symCheck->result = false;
- }
- break;
- }
- case knopDot:
- if (!IsLibraryFunction(pnode, byteCodeGenerator->GetScriptContext()))
- {
- symCheck->result = false;
- }
- break;
- case knopTrue:
- case knopFalse:
- case knopAdd:
- case knopSub:
- case knopDiv:
- case knopMul:
- case knopExpo:
- case knopMod:
- case knopNeg:
- case knopInt:
- case knopFlt:
- case knopLt:
- case knopGt:
- case knopLe:
- case knopGe:
- case knopEq:
- case knopNe:
- break;
- default:
- symCheck->result = false;
- break;
- }
- }
- }
- bool InvertableExpr(SymCheck* symCheck, ParseNode* expr, ByteCodeGenerator* byteCodeGenerator)
- {
- symCheck->result = true;
- symCheck->cond = false;
- symCheck->permittedSym = nullptr;
- VisitIndirect<SymCheck>(expr, byteCodeGenerator, symCheck, &CheckInvertableExpr, nullptr);
- return symCheck->result;
- }
- bool InvertableExprPlus(SymCheck* symCheck, ParseNode* expr, ByteCodeGenerator* byteCodeGenerator, Symbol* permittedSym)
- {
- symCheck->result = true;
- symCheck->cond = true;
- symCheck->permittedSym = permittedSym;
- VisitIndirect<SymCheck>(expr, byteCodeGenerator, symCheck, &CheckInvertableExpr, nullptr);
- return symCheck->result;
- }
- void CheckLocalVarDef(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- Assert(pnode->nop == knopAsg);
- if (pnode->sxBin.pnode1 != nullptr)
- {
- ParseNode *lhs = pnode->sxBin.pnode1;
- if (lhs->nop == knopName)
- {
- Symbol *sym = lhs->sxPid.sym;
- if (sym != nullptr)
- {
- sym->RecordDef();
- }
- }
- }
- }
- ParseNode* ConstructInvertedStatement(ParseNode* stmt, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo,
- ParseNode** outerStmtRef)
- {
- if (stmt == nullptr)
- {
- return nullptr;
- }
- ParseNode* cStmt;
- if ((stmt->nop == knopAsg) || (stmt->nop == knopVarDecl))
- {
- ParseNode* rhs = nullptr;
- ParseNode* lhs = nullptr;
- if (stmt->nop == knopAsg)
- {
- rhs = stmt->sxBin.pnode2;
- lhs = stmt->sxBin.pnode1;
- }
- else if (stmt->nop == knopVarDecl)
- {
- rhs = stmt->sxVar.pnodeInit;
- }
- ArenaAllocator* alloc = byteCodeGenerator->GetAllocator();
- ParseNode* loopInvar = byteCodeGenerator->GetParser()->CreateTempNode(rhs);
- loopInvar->location = funcInfo->NextVarRegister();
- // Can't use a temp register here because the inversion happens at the parse tree level without generating
- // any bytecode yet. All local non-temp registers need to be initialized for jitted loop bodies, and since this is
- // not a user variable, track this register separately to have it be initialized at the top of the function.
- funcInfo->nonUserNonTempRegistersToInitialize.Add(loopInvar->location);
- // add temp node to list of initializers for new outer loop
- if ((*outerStmtRef)->sxBin.pnode1 == nullptr)
- {
- (*outerStmtRef)->sxBin.pnode1 = loopInvar;
- }
- else
- {
- ParseNode* listNode = Parser::StaticCreateBinNode(knopList, nullptr, nullptr, alloc);
- (*outerStmtRef)->sxBin.pnode2 = listNode;
- listNode->sxBin.pnode1 = loopInvar;
- *outerStmtRef = listNode;
- }
- ParseNode* tempName = byteCodeGenerator->GetParser()->CreateTempRef(loopInvar);
- if (lhs != nullptr)
- {
- cStmt = Parser::StaticCreateBinNode(knopAsg, lhs, tempName, alloc);
- }
- else
- {
- // Use AddVarDeclNode to add the var to the function.
- // Do not use CreateVarDeclNode which is meant to be used while parsing. It assumes that
- // parser's internal data structures (m_ppnodeVar in particular) is at the "current" location.
- cStmt = byteCodeGenerator->GetParser()->AddVarDeclNode(stmt->sxVar.pid, funcInfo->root);
- cStmt->sxVar.pnodeInit = tempName;
- cStmt->sxVar.sym = stmt->sxVar.sym;
- }
- }
- else
- {
- cStmt = byteCodeGenerator->GetParser()->CopyPnode(stmt);
- }
- return cStmt;
- }
- ParseNode* ConstructInvertedLoop(ParseNode* innerLoop, ParseNode* outerLoop, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- ArenaAllocator* alloc = byteCodeGenerator->GetAllocator();
- ParseNode* outerLoopC = Parser::StaticCreateNodeT<knopFor>(alloc);
- outerLoopC->sxFor.pnodeInit = innerLoop->sxFor.pnodeInit;
- outerLoopC->sxFor.pnodeCond = innerLoop->sxFor.pnodeCond;
- outerLoopC->sxFor.pnodeIncr = innerLoop->sxFor.pnodeIncr;
- outerLoopC->sxFor.pnodeBlock = innerLoop->sxFor.pnodeBlock;
- outerLoopC->sxFor.pnodeInverted = nullptr;
- ParseNode* innerLoopC = Parser::StaticCreateNodeT<knopFor>(alloc);
- innerLoopC->sxFor.pnodeInit = outerLoop->sxFor.pnodeInit;
- innerLoopC->sxFor.pnodeCond = outerLoop->sxFor.pnodeCond;
- innerLoopC->sxFor.pnodeIncr = outerLoop->sxFor.pnodeIncr;
- innerLoopC->sxFor.pnodeBlock = outerLoop->sxFor.pnodeBlock;
- innerLoopC->sxFor.pnodeInverted = nullptr;
- ParseNode* innerBod = Parser::StaticCreateBlockNode(alloc);
- innerLoopC->sxFor.pnodeBody = innerBod;
- innerBod->sxBlock.scope = innerLoop->sxFor.pnodeBody->sxBlock.scope;
- ParseNode* outerBod = Parser::StaticCreateBlockNode(alloc);
- outerLoopC->sxFor.pnodeBody = outerBod;
- outerBod->sxBlock.scope = outerLoop->sxFor.pnodeBody->sxBlock.scope;
- ParseNode* listNode = Parser::StaticCreateBinNode(knopList, nullptr, nullptr, alloc);
- outerBod->sxBlock.pnodeStmt = listNode;
- ParseNode* innerBodOriginal = innerLoop->sxFor.pnodeBody;
- ParseNode* origStmt = innerBodOriginal->sxBlock.pnodeStmt;
- if (origStmt->nop == knopList)
- {
- ParseNode* invertedStmt = nullptr;
- while (origStmt->nop == knopList)
- {
- ParseNode* invertedItem = ConstructInvertedStatement(origStmt->sxBin.pnode1, byteCodeGenerator, funcInfo, &listNode);
- if (invertedStmt != nullptr)
- {
- invertedStmt = invertedStmt->sxBin.pnode2 = byteCodeGenerator->GetParser()->CreateBinNode(knopList, invertedItem, nullptr);
- }
- else
- {
- invertedStmt = innerBod->sxBlock.pnodeStmt = byteCodeGenerator->GetParser()->CreateBinNode(knopList, invertedItem, nullptr);
- }
- origStmt = origStmt->sxBin.pnode2;
- }
- Assert(invertedStmt != nullptr);
- invertedStmt->sxBin.pnode2 = ConstructInvertedStatement(origStmt, byteCodeGenerator, funcInfo, &listNode);
- }
- else
- {
- innerBod->sxBlock.pnodeStmt = ConstructInvertedStatement(origStmt, byteCodeGenerator, funcInfo, &listNode);
- }
- if (listNode->sxBin.pnode1 == nullptr)
- {
- listNode->sxBin.pnode1 = byteCodeGenerator->GetParser()->CreateTempNode(nullptr);
- }
- listNode->sxBin.pnode2 = innerLoopC;
- return outerLoopC;
- }
- bool InvertableStmt(ParseNode* stmt, Symbol* outerVar, ParseNode* innerLoop, ParseNode* outerLoop, ByteCodeGenerator* byteCodeGenerator, SymCheck* symCheck)
- {
- if (stmt != nullptr)
- {
- ParseNode* lhs = nullptr;
- ParseNode* rhs = nullptr;
- if (stmt->nop == knopAsg)
- {
- lhs = stmt->sxBin.pnode1;
- rhs = stmt->sxBin.pnode2;
- }
- else if (stmt->nop == knopVarDecl)
- {
- rhs = stmt->sxVar.pnodeInit;
- }
- if (lhs != nullptr)
- {
- if (lhs->nop == knopDot)
- {
- return false;
- }
- if (lhs->nop == knopName)
- {
- if ((lhs->sxPid.sym != nullptr) && (lhs->sxPid.sym->GetIsGlobal()))
- {
- return false;
- }
- }
- else if (lhs->nop == knopIndex)
- {
- ParseNode* indexed = lhs->sxBin.pnode1;
- ParseNode* index = lhs->sxBin.pnode2;
- if ((index == nullptr) || (indexed == nullptr))
- {
- return false;
- }
- if ((indexed->nop != knopName) || (indexed->sxPid.sym == nullptr))
- {
- return false;
- }
- if (!InvertableExprPlus(symCheck, index, byteCodeGenerator, outerVar))
- {
- return false;
- }
- }
- }
- if (rhs != nullptr)
- {
- if (!InvertableExpr(symCheck, rhs, byteCodeGenerator))
- {
- return false;
- }
- }
- else
- {
- if (!InvertableExpr(symCheck, stmt, byteCodeGenerator))
- {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- bool GatherInversionSyms(ParseNode* stmt, Symbol* outerVar, ParseNode* innerLoop, ByteCodeGenerator* byteCodeGenerator, SymCheck* symCheck)
- {
- if (stmt != nullptr)
- {
- ParseNode* lhs = nullptr;
- Symbol* auxSym = nullptr;
- if (stmt->nop == knopAsg)
- {
- lhs = stmt->sxBin.pnode1;
- }
- else if (stmt->nop == knopVarDecl)
- {
- auxSym = stmt->sxVar.sym;
- }
- if (lhs != nullptr)
- {
- if (lhs->nop == knopDot)
- {
- return false;
- }
- if (lhs->nop == knopName)
- {
- if ((lhs->sxPid.sym == nullptr) || (lhs->sxPid.sym->GetIsGlobal()))
- {
- return false;
- }
- else
- {
- auxSym = lhs->sxPid.sym;
- }
- }
- }
- if (auxSym != nullptr)
- {
- return symCheck->AddSymbol(auxSym);
- }
- }
- return true;
- }
- bool InvertableBlock(ParseNode* block, Symbol* outerVar, ParseNode* innerLoop, ParseNode* outerLoop, ByteCodeGenerator* byteCodeGenerator,
- SymCheck* symCheck)
- {
- if (block == nullptr)
- {
- return false;
- }
- if (!symCheck->AddSymbol(outerVar))
- {
- return false;
- }
- if (innerLoop->sxFor.pnodeBody->nop == knopBlock && innerLoop->sxFor.pnodeBody->sxBlock.HasBlockScopedContent()
- || outerLoop->sxFor.pnodeBody->nop == knopBlock && outerLoop->sxFor.pnodeBody->sxBlock.HasBlockScopedContent())
- {
- // we can not invert loops if there are block scoped declarations inside
- return false;
- }
- if ((block != nullptr) && (block->nop == knopBlock))
- {
- ParseNode* stmt = block->sxBlock.pnodeStmt;
- while ((stmt != nullptr) && (stmt->nop == knopList))
- {
- if (!GatherInversionSyms(stmt->sxBin.pnode1, outerVar, innerLoop, byteCodeGenerator, symCheck))
- {
- return false;
- }
- stmt = stmt->sxBin.pnode2;
- }
- if (!GatherInversionSyms(stmt, outerVar, innerLoop, byteCodeGenerator, symCheck))
- {
- return false;
- }
- stmt = block->sxBlock.pnodeStmt;
- while ((stmt != nullptr) && (stmt->nop == knopList))
- {
- if (!InvertableStmt(stmt->sxBin.pnode1, outerVar, innerLoop, outerLoop, byteCodeGenerator, symCheck))
- {
- return false;
- }
- stmt = stmt->sxBin.pnode2;
- }
- if (!InvertableStmt(stmt, outerVar, innerLoop, outerLoop, byteCodeGenerator, symCheck))
- {
- return false;
- }
- return (InvertableExprPlus(symCheck, innerLoop->sxFor.pnodeCond, byteCodeGenerator, nullptr) &&
- InvertableExprPlus(symCheck, outerLoop->sxFor.pnodeCond, byteCodeGenerator, outerVar));
- }
- else
- {
- return false;
- }
- }
- // Start of invert loop optimization.
- // For now, find simple cases (only for loops around single assignment).
- // Returns new AST for inverted loop; also returns in out param
- // side effects level, if any that guards the new AST (old AST will be
- // used if guard fails).
- // Should only be called with loopNode representing top-level statement.
- ParseNode* InvertLoop(ParseNode* outerLoop, ByteCodeGenerator* byteCodeGenerator, FuncInfo* funcInfo)
- {
- if (byteCodeGenerator->GetScriptContext()->optimizationOverrides.GetSideEffects() != Js::SideEffects_None)
- {
- return nullptr;
- }
- SymCheck symCheck;
- symCheck.Init();
- if (outerLoop->nop == knopFor)
- {
- ParseNode* innerLoop = outerLoop->sxFor.pnodeBody;
- if ((innerLoop == nullptr) || (innerLoop->nop != knopBlock))
- {
- return nullptr;
- }
- else
- {
- innerLoop = innerLoop->sxBlock.pnodeStmt;
- }
- if ((innerLoop != nullptr) && (innerLoop->nop == knopFor))
- {
- if ((outerLoop->sxFor.pnodeInit != nullptr) &&
- (outerLoop->sxFor.pnodeInit->nop == knopVarDecl) &&
- (outerLoop->sxFor.pnodeInit->sxVar.pnodeInit != nullptr) &&
- (outerLoop->sxFor.pnodeInit->sxVar.pnodeInit->nop == knopInt) &&
- (outerLoop->sxFor.pnodeIncr != nullptr) &&
- ((outerLoop->sxFor.pnodeIncr->nop == knopIncPre) || (outerLoop->sxFor.pnodeIncr->nop == knopIncPost)) &&
- (outerLoop->sxFor.pnodeIncr->sxUni.pnode1->nop == knopName) &&
- (outerLoop->sxFor.pnodeInit->sxVar.pid == outerLoop->sxFor.pnodeIncr->sxUni.pnode1->sxPid.pid) &&
- (innerLoop->sxFor.pnodeIncr != nullptr) &&
- ((innerLoop->sxFor.pnodeIncr->nop == knopIncPre) || (innerLoop->sxFor.pnodeIncr->nop == knopIncPost)) &&
- (innerLoop->sxFor.pnodeInit != nullptr) &&
- (innerLoop->sxFor.pnodeInit->nop == knopVarDecl) &&
- (innerLoop->sxFor.pnodeInit->sxVar.pnodeInit != nullptr) &&
- (innerLoop->sxFor.pnodeInit->sxVar.pnodeInit->nop == knopInt) &&
- (innerLoop->sxFor.pnodeIncr->sxUni.pnode1->nop == knopName) &&
- (innerLoop->sxFor.pnodeInit->sxVar.pid == innerLoop->sxFor.pnodeIncr->sxUni.pnode1->sxPid.pid))
- {
- Symbol* outerVar = outerLoop->sxFor.pnodeInit->sxVar.sym;
- Symbol* innerVar = innerLoop->sxFor.pnodeInit->sxVar.sym;
- if ((outerVar != nullptr) && (innerVar != nullptr))
- {
- ParseNode* block = innerLoop->sxFor.pnodeBody;
- if (InvertableBlock(block, outerVar, innerLoop, outerLoop, byteCodeGenerator, &symCheck))
- {
- return ConstructInvertedLoop(innerLoop, outerLoop, byteCodeGenerator, funcInfo);
- }
- }
- }
- }
- }
- return nullptr;
- }
- void SetAdditionalBindInfoForVariables(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- Symbol *sym = pnode->sxVar.sym;
- if (sym == nullptr)
- {
- return;
- }
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- if (func->IsGlobalFunction())
- {
- func->SetHasGlobalRef(true);
- }
- if (!sym->GetIsGlobal() && !sym->GetIsArguments() &&
- (sym->GetScope() == func->GetBodyScope() || sym->GetScope() == func->GetParamScope() || sym->GetScope()->GetCanMerge()))
- {
- if (func->GetChildCallsEval())
- {
- func->SetHasLocalInClosure(true);
- }
- else
- {
- sym->RecordDef();
- }
- }
- // If this decl does an assignment inside a loop body, then there's a chance
- // that a jitted loop body will expect us to begin with a valid value in this var.
- // So mark the sym as used so that we guarantee the var will at least get "undefined".
- if (byteCodeGenerator->IsInLoop() &&
- pnode->sxVar.pnodeInit)
- {
- sym->SetIsUsed(true);
- }
- }
- // bind references to definitions (prefix pass)
- void Bind(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- if (pnode == nullptr)
- {
- return;
- }
- switch (pnode->nop)
- {
- case knopBreak:
- case knopContinue:
- byteCodeGenerator->AddTargetStmt(pnode->sxJump.pnodeTarget);
- break;
- case knopProg:
- {
- FuncInfo* globFuncInfo = byteCodeGenerator->StartBindGlobalStatements(pnode);
- pnode->sxFnc.funcInfo = globFuncInfo;
- AddFunctionsToScope(pnode->sxFnc.GetTopLevelScope(), byteCodeGenerator);
- AddVarsToScope(pnode->sxFnc.pnodeVars, byteCodeGenerator);
- // There are no args to add, but "eval" gets a this pointer.
- byteCodeGenerator->SetNumberOfInArgs(!!(byteCodeGenerator->GetFlags() & fscrEvalCode));
- if (!globFuncInfo->IsFakeGlobalFunction(byteCodeGenerator->GetFlags()))
- {
- // Global code: the root function is the global function.
- byteCodeGenerator->SetRootFuncInfo(globFuncInfo);
- }
- else if (globFuncInfo->byteCodeFunction)
- {
- // If the current global code wasn't marked to be treated as global code (e.g. from deferred parsing),
- // we don't need to send a register script event for it.
- globFuncInfo->byteCodeFunction->SetIsTopLevel(false);
- }
- if (pnode->sxFnc.CallsEval())
- {
- globFuncInfo->SetCallsEval(true);
- }
- break;
- }
- case knopFncDecl:
- // VisitFunctionsInScope has already done binding within the declared function. Here, just record the fact
- // that the parent function has a local/global declaration in it.
- BindFuncSymbol(pnode, byteCodeGenerator);
- if (pnode->sxFnc.IsGenerator())
- {
- // Always assume generator functions escape since tracking them requires tracking
- // the resulting generators in addition to the function.
- byteCodeGenerator->FuncEscapes(byteCodeGenerator->TopFuncInfo()->GetBodyScope());
- }
- if (!pnode->sxFnc.IsDeclaration())
- {
- FuncInfo *funcInfo = byteCodeGenerator->TopFuncInfo();
- if (!funcInfo->IsGlobalFunction() || (byteCodeGenerator->GetFlags() & fscrEval))
- {
- // In the case of a nested function expression, assumes that it escapes.
- // We could try to analyze what it touches to be more precise.
- byteCodeGenerator->FuncEscapes(funcInfo->GetBodyScope());
- }
- byteCodeGenerator->ProcessCapturedSyms(pnode);
- }
- else if (byteCodeGenerator->IsInLoop())
- {
- Symbol *funcSym = pnode->sxFnc.GetFuncSymbol();
- if (funcSym)
- {
- Symbol *funcVarSym = funcSym->GetFuncScopeVarSym();
- if (funcVarSym)
- {
- // We're going to write to the funcVarSym when we do the function instantiation,
- // so treat the funcVarSym as used. That way, we know it will get undef-initialized at the
- // top of the function, so a jitted loop body won't have any issue with boxing if
- // the function instantiation isn't executed.
- Assert(funcVarSym != funcSym);
- funcVarSym->SetIsUsed(true);
- }
- }
- }
- break;
- case knopThis:
- case knopSuper:
- {
- FuncInfo *top = byteCodeGenerator->TopFuncInfo();
- if (top->IsGlobalFunction() && !(byteCodeGenerator->GetFlags() & fscrEval))
- {
- top->SetHasGlobalRef(true);
- }
- else if (top->IsLambda())
- {
- byteCodeGenerator->MarkThisUsedInLambda();
- }
- // "this" should be loaded for both global and non-global functions
- byteCodeGenerator->TopFuncInfo()->GetParsedFunctionBody()->SetHasThis(true);
- break;
- }
- case knopName:
- {
- if (pnode->sxPid.sym == nullptr)
- {
- if (pnode->grfpn & fpnMemberReference)
- {
- // This is a member name. No binding.
- break;
- }
- Symbol *sym = byteCodeGenerator->FindSymbol(pnode->sxPid.symRef, pnode->sxPid.pid);
- if (sym)
- {
- // This is a named load, not just a reference, so if it's a nested function note that all
- // the nested scopes escape.
- Assert(!sym->GetDecl() || (pnode->sxPid.symRef && *pnode->sxPid.symRef));
- Assert(!sym->GetDecl() || ((*pnode->sxPid.symRef)->GetDecl() == sym->GetDecl()));
- pnode->sxPid.sym = sym;
- if (sym->GetSymbolType() == STFunction &&
- (!sym->GetIsGlobal() || (byteCodeGenerator->GetFlags() & fscrEval)))
- {
- byteCodeGenerator->FuncEscapes(sym->GetScope());
- }
- }
- }
- FuncInfo *top = byteCodeGenerator->TopFuncInfo();
- if (pnode->sxPid.sym == nullptr || pnode->sxPid.sym->GetIsGlobal())
- {
- top->SetHasGlobalRef(true);
- }
- if (pnode->sxPid.sym)
- {
- pnode->sxPid.sym->SetIsUsed(true);
- }
- break;
- }
- case knopMember:
- case knopMemberShort:
- case knopObjectPatternMember:
- if (pnode->sxBin.pnode1->nop == knopComputedName)
- {
- // Computed property name - cannot bind yet
- break;
- }
- // fall through
- case knopGetMember:
- case knopSetMember:
- {
- // lhs is knopStr, rhs is expr
- ParseNode *id = pnode->sxBin.pnode1;
- if (id->nop == knopStr || id->nop == knopName)
- {
- byteCodeGenerator->AssignPropertyId(id->sxPid.pid);
- id->sxPid.sym = nullptr;
- id->sxPid.symRef = nullptr;
- id->grfpn |= fpnMemberReference;
- }
- break;
- }
- // TODO: convert index over string to Get/Put Value
- case knopIndex:
- BindReference(pnode, byteCodeGenerator);
- break;
- case knopDot:
- BindInstAndMember(pnode, byteCodeGenerator);
- break;
- case knopTryFinally:
- byteCodeGenerator->SetHasFinally(true);
- case knopTryCatch:
- byteCodeGenerator->SetHasTry(true);
- byteCodeGenerator->TopFuncInfo()->byteCodeFunction->SetDontInline(true);
- byteCodeGenerator->AddTargetStmt(pnode);
- break;
- case knopAsg:
- BindReference(pnode, byteCodeGenerator);
- CheckLocalVarDef(pnode, byteCodeGenerator);
- break;
- case knopVarDecl:
- // "arguments" symbol or decl w/o RHS may have been bound already; otherwise, do the binding here.
- if (pnode->sxVar.sym == nullptr)
- {
- pnode->sxVar.sym = byteCodeGenerator->FindSymbol(pnode->sxVar.symRef, pnode->sxVar.pid);
- }
- SetAdditionalBindInfoForVariables(pnode, byteCodeGenerator);
- break;
- case knopConstDecl:
- case knopLetDecl:
- // "arguments" symbol or decl w/o RHS may have been bound already; otherwise, do the binding here.
- if (!pnode->sxVar.sym)
- {
- AssertMsg(pnode->sxVar.symRef && *pnode->sxVar.symRef, "'const' and 'let' should be binded when we bind block");
- pnode->sxVar.sym = *pnode->sxVar.symRef;
- }
- SetAdditionalBindInfoForVariables(pnode, byteCodeGenerator);
- break;
- case knopCall:
- if (pnode->sxCall.isEvalCall && byteCodeGenerator->TopFuncInfo()->IsLambda())
- {
- byteCodeGenerator->MarkThisUsedInLambda();
- }
- // fallthrough
- case knopTypeof:
- case knopDelete:
- BindReference(pnode, byteCodeGenerator);
- break;
- case knopRegExp:
- pnode->sxPid.regexPatternIndex = byteCodeGenerator->TopFuncInfo()->GetParsedFunctionBody()->NewLiteralRegex();
- break;
- case knopComma:
- pnode->sxBin.pnode1->SetNotEscapedUse();
- break;
- case knopBlock:
- {
- for (ParseNode *pnodeScope = pnode->sxBlock.pnodeScopes; pnodeScope; /* no increment */)
- {
- switch (pnodeScope->nop)
- {
- case knopFncDecl:
- if (pnodeScope->sxFnc.IsDeclaration())
- {
- byteCodeGenerator->ProcessCapturedSyms(pnodeScope);
- }
- pnodeScope = pnodeScope->sxFnc.pnodeNext;
- break;
- case knopBlock:
- pnodeScope = pnodeScope->sxBlock.pnodeNext;
- break;
- case knopCatch:
- pnodeScope = pnodeScope->sxCatch.pnodeNext;
- break;
- case knopWith:
- pnodeScope = pnodeScope->sxWith.pnodeNext;
- break;
- }
- }
- break;
- }
- }
- }
- void ByteCodeGenerator::ProcessCapturedSyms(ParseNode *pnode)
- {
- SymbolTable *capturedSyms = pnode->sxFnc.funcInfo->GetCapturedSyms();
- if (capturedSyms)
- {
- FuncInfo *funcInfo = this->TopFuncInfo();
- CapturedSymMap *capturedSymMap = funcInfo->EnsureCapturedSymMap();
- ParseNode *pnodeStmt = this->GetCurrentTopStatement();
- SList<Symbol*> *capturedSymList;
- if (!pnodeStmt->CapturesSyms())
- {
- capturedSymList = Anew(this->alloc, SList<Symbol*>, this->alloc);
- capturedSymMap->Add(pnodeStmt, capturedSymList);
- pnodeStmt->SetCapturesSyms();
- }
- else
- {
- capturedSymList = capturedSymMap->Item(pnodeStmt);
- }
- capturedSyms->Map([&](Symbol *sym)
- {
- if (!sym->GetIsCommittedToSlot() && !sym->HasVisitedCapturingFunc())
- {
- capturedSymList->Prepend(sym);
- sym->SetHasVisitedCapturingFunc();
- }
- });
- }
- }
- void ByteCodeGenerator::MarkThisUsedInLambda()
- {
- // This is a lambda that refers to "this".
- // Find the enclosing "normal" function and indicate that the lambda captures the enclosing function's "this".
- FuncInfo *parent = this->FindEnclosingNonLambda();
- parent->GetParsedFunctionBody()->SetHasThis(true);
- if (!parent->IsGlobalFunction() || this->GetFlags() & fscrEval)
- {
- // If the enclosing function is non-global or eval global, it will put "this" in a closure slot.
- parent->SetIsThisLexicallyCaptured();
- Scope* scope = parent->IsGlobalFunction() ? parent->GetGlobalEvalBlockScope() : parent->GetBodyScope();
- scope->SetHasLocalInClosure(true);
- this->TopFuncInfo()->SetHasClosureReference(true);
- }
- this->TopFuncInfo()->SetHasCapturedThis();
- }
- void ByteCodeGenerator::FuncEscapes(Scope *scope)
- {
- while (scope)
- {
- Assert(scope->GetFunc());
- scope->GetFunc()->SetEscapes(true);
- scope = scope->GetEnclosingScope();
- }
- if (this->flags & fscrEval)
- {
- // If a function declared inside eval escapes, we'll need
- // to invalidate the caller's cached scope.
- this->funcEscapes = true;
- }
- }
- bool ByteCodeGenerator::HasInterleavingDynamicScope(Symbol * sym) const
- {
- Js::PropertyId unused;
- return this->InDynamicScope() &&
- sym->GetScope() != this->FindScopeForSym(sym->GetScope(), nullptr, &unused, this->TopFuncInfo());
- }
- void CheckMaybeEscapedUse(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, bool isCall = false)
- {
- if (pnode == nullptr)
- {
- return;
- }
- FuncInfo * topFunc = byteCodeGenerator->TopFuncInfo();
- if (topFunc->IsGlobalFunction())
- {
- return;
- }
- switch (pnode->nop)
- {
- case knopAsg:
- if (pnode->sxBin.pnode1->nop != knopName)
- {
- break;
- }
- // use of an assignment (e.g. (y = function() {}) + "1"), just make y an escaped use.
- pnode = pnode->sxBin.pnode1;
- isCall = false;
- // fall-through
- case knopName:
- if (!isCall)
- {
- // Mark the name has having escaped use
- if (pnode->sxPid.sym)
- {
- pnode->sxPid.sym->SetHasMaybeEscapedUse(byteCodeGenerator);
- }
- }
- break;
- case knopFncDecl:
- // A function declaration has an unknown use (not assignment nor call),
- // mark the function as having child escaped
- topFunc->SetHasMaybeEscapedNestedFunc(DebugOnly(L"UnknownUse"));
- break;
- }
- }
- void CheckFuncAssignment(Symbol * sym, ParseNode * pnode2, ByteCodeGenerator * byteCodeGenerator)
- {
- if (pnode2 == nullptr)
- {
- return;
- }
- switch (pnode2->nop)
- {
- default:
- CheckMaybeEscapedUse(pnode2, byteCodeGenerator);
- break;
- case knopFncDecl:
- {
- FuncInfo * topFunc = byteCodeGenerator->TopFuncInfo();
- if (topFunc->IsGlobalFunction())
- {
- return;
- }
- // Use not as an assignment or assignment to an outer function's sym, or assigned to a formal
- // or assigned to multiple names.
- if (sym == nullptr
- || sym->GetScope()->GetFunc() != topFunc)
- {
- topFunc->SetHasMaybeEscapedNestedFunc(DebugOnly(
- sym == nullptr ? L"UnknownAssignment" :
- (sym->GetScope()->GetFunc() != topFunc) ? L"CrossFuncAssignment" :
- L"SomethingIsWrong!")
- );
- }
- else
- {
- // TODO-STACK-NESTED-FUNC: Since we only support single def functions, we can still put the
- // nested function on the stack and reuse even if the function goes out of the block scope.
- // However, we cannot allocate frame display or slots on the stack if the function is
- // declared in a loop, because there might be multiple functions referencing different
- // iterations of the scope.
- // For now, just disable everything.
- Scope * funcParentScope = pnode2->sxFnc.funcInfo->GetBodyScope()->GetEnclosingScope();
- while (sym->GetScope() != funcParentScope)
- {
- if (funcParentScope->GetMustInstantiate())
- {
- topFunc->SetHasMaybeEscapedNestedFunc(DebugOnly(L"CrossScopeAssignment"));
- break;
- }
- funcParentScope->SetHasCrossScopeFuncAssignment();
- funcParentScope = funcParentScope->GetEnclosingScope();
- }
- // Need to always detect interleaving dynamic scope ('with') for assignments
- // as those may end up escaping into the 'with' scope.
- // TODO: the with scope is marked as MustInstantiate late during byte code emit
- // We could detect this using the loop above as well, by marking the with
- // scope as must instantiate early, this is just less risky of a fix for RTM.
- if (byteCodeGenerator->HasInterleavingDynamicScope(sym))
- {
- byteCodeGenerator->TopFuncInfo()->SetHasMaybeEscapedNestedFunc(DebugOnly(L"InterleavingDynamicScope"));
- }
- sym->SetHasFuncAssignment(byteCodeGenerator);
- }
- }
- break;
- };
- }
- inline bool ContainsSuperReference(ParseNodePtr pnode)
- {
- return (pnode->sxCall.pnodeTarget->nop == knopDot && pnode->sxCall.pnodeTarget->sxBin.pnode1->nop == knopSuper) // super.prop()
- || (pnode->sxCall.pnodeTarget->nop == knopIndex && pnode->sxCall.pnodeTarget->sxBin.pnode1->nop == knopSuper); // super[prop]()
- }
- inline bool ContainsDirectSuper(ParseNodePtr pnode)
- {
- return pnode->sxCall.pnodeTarget->nop == knopSuper; // super()
- }
- // Assign permanent (non-temp) registers for the function.
- // These include constants (null, 3.7, this) and locals that use registers as their home locations.
- // Assign the location fields of parse nodes whose values are constants/locals with permanent/known registers.
- // Re-usable expression temps are assigned during the final Emit pass.
- void AssignRegisters(ParseNode *pnode, ByteCodeGenerator *byteCodeGenerator)
- {
- if (pnode == nullptr)
- {
- return;
- }
- Symbol *sym;
- OpCode nop = pnode->nop;
- switch (nop)
- {
- default:
- {
- uint flags = ParseNode::Grfnop(nop);
- if (flags & fnopUni)
- {
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- }
- else if (flags & fnopBin)
- {
- CheckMaybeEscapedUse(pnode->sxBin.pnode1, byteCodeGenerator);
- CheckMaybeEscapedUse(pnode->sxBin.pnode2, byteCodeGenerator);
- }
- break;
- }
- case knopParamPattern:
- byteCodeGenerator->AssignUndefinedConstRegister();
- CheckMaybeEscapedUse(pnode->sxParamPattern.pnode1, byteCodeGenerator);
- break;
- case knopArrayPattern:
- byteCodeGenerator->AssignUndefinedConstRegister();
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- case knopDot:
- CheckMaybeEscapedUse(pnode->sxBin.pnode1, byteCodeGenerator);
- break;
- case knopMember:
- case knopMemberShort:
- case knopGetMember:
- case knopSetMember:
- CheckMaybeEscapedUse(pnode->sxBin.pnode2, byteCodeGenerator);
- break;
- case knopAsg:
- {
- Symbol * sym = pnode->sxBin.pnode1->nop == knopName ? pnode->sxBin.pnode1->sxPid.sym : nullptr;
- CheckFuncAssignment(sym, pnode->sxBin.pnode2, byteCodeGenerator);
- if (pnode->IsInList())
- {
- // Assignment in array literal
- CheckMaybeEscapedUse(pnode->sxBin.pnode1, byteCodeGenerator);
- }
- if (byteCodeGenerator->IsES6DestructuringEnabled() && (pnode->sxBin.pnode1->nop == knopArrayPattern || pnode->sxBin.pnode1->nop == knopObjectPattern))
- {
- // Destructured arrays may have default values and need undefined.
- byteCodeGenerator->AssignUndefinedConstRegister();
- // Any rest parameter in a destructured array will need a 0 constant.
- byteCodeGenerator->EnregisterConstant(0);
- }
- break;
- }
- case knopEllipsis:
- if (byteCodeGenerator->InDestructuredPattern())
- {
- // Get a register for the rest array counter.
- pnode->location = byteCodeGenerator->NextVarRegister();
- // Any rest parameter in a destructured array will need a 0 constant.
- byteCodeGenerator->EnregisterConstant(0);
- }
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- case knopQmark:
- CheckMaybeEscapedUse(pnode->sxTri.pnode1, byteCodeGenerator);
- CheckMaybeEscapedUse(pnode->sxTri.pnode2, byteCodeGenerator);
- CheckMaybeEscapedUse(pnode->sxTri.pnode3, byteCodeGenerator);
- break;
- case knopWith:
- pnode->location = byteCodeGenerator->NextVarRegister();
- CheckMaybeEscapedUse(pnode->sxWith.pnodeObj, byteCodeGenerator);
- break;
- case knopComma:
- if (!pnode->IsNotEscapedUse())
- {
- // Only the last expr in comma expr escape. Mark it if it is escapable.
- CheckMaybeEscapedUse(pnode->sxBin.pnode2, byteCodeGenerator);
- }
- break;
- case knopFncDecl:
- if (!byteCodeGenerator->TopFuncInfo()->IsGlobalFunction())
- {
- if (pnode->sxFnc.IsGenerator())
- {
- // Assume generators always escape; otherwise need to analyze if
- // the return value of calls to generator function, the generator
- // objects, escape.
- FuncInfo* funcInfo = byteCodeGenerator->TopFuncInfo();
- funcInfo->SetHasMaybeEscapedNestedFunc(DebugOnly(L"Generator"));
- }
- if (pnode->IsInList() && !pnode->IsNotEscapedUse())
- {
- byteCodeGenerator->TopFuncInfo()->SetHasMaybeEscapedNestedFunc(DebugOnly(L"InList"));
- }
- ParseNodePtr pnodeName = pnode->sxFnc.pnodeName;
- if (pnodeName != nullptr)
- {
- // REVIEW: does this apply now that compat mode is gone?
- // There is a weird case in compat mode where we may not have a sym assigned to a fnc decl's
- // name node if it is a named function declare inside 'with' that also assigned to something else
- // as well. Instead, We generate two knopFncDecl node one for parent function and one for the assignment.
- // Only the top one gets a sym, not the inner one. The assignment in the 'with' will be using the inner
- // one. Also we will detect that the assignment to a variable is an escape inside a 'with'.
- // Since we need the sym in the fnc decl's name, we just detect the escape here as "WithScopeFuncName".
- if (pnodeName->nop == knopVarDecl && pnodeName->sxVar.sym != nullptr)
- {
- // Unlike in CheckFuncAssignment, we don't check for interleaving
- // dynamic scope ('with') here, because we also generate direct assignment for
- // function decl's names
- pnodeName->sxVar.sym->SetHasFuncAssignment(byteCodeGenerator);
- // Function declaration in block scope and non-strict mode has a
- // corresponding var sym that we assign to as well. Need to
- // mark that symbol as has func assignment as well.
- Symbol * functionScopeVarSym = pnodeName->sxVar.sym->GetFuncScopeVarSym();
- if (functionScopeVarSym)
- {
- functionScopeVarSym->SetHasFuncAssignment(byteCodeGenerator);
- }
- }
- else
- {
- // The function has multiple names, or assign to o.x or o::x
- byteCodeGenerator->TopFuncInfo()->SetHasMaybeEscapedNestedFunc(DebugOnly(
- pnodeName->nop == knopList ? L"MultipleFuncName" :
- pnodeName->nop == knopDot ? L"PropFuncName" :
- pnodeName->nop == knopVarDecl && pnodeName->sxVar.sym == nullptr ? L"WithScopeFuncName" :
- L"WeirdFuncName"
- ));
- }
- }
- }
- break;
- case knopNew:
- CheckMaybeEscapedUse(pnode->sxCall.pnodeTarget, byteCodeGenerator);
- CheckMaybeEscapedUse(pnode->sxCall.pnodeArgs, byteCodeGenerator);
- break;
- case knopThrow:
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- // REVIEW: Technically, switch expr or case expr doesn't really escape as strict equal
- // doesn't cause the function to escape.
- case knopSwitch:
- CheckMaybeEscapedUse(pnode->sxSwitch.pnodeVal, byteCodeGenerator);
- break;
- case knopCase:
- CheckMaybeEscapedUse(pnode->sxCase.pnodeExpr, byteCodeGenerator);
- break;
- // REVIEW: Technically, the object for GetForInEnumerator doesn't escape, except when cached,
- // which we can make work.
- case knopForIn:
- CheckMaybeEscapedUse(pnode->sxForInOrForOf.pnodeObj, byteCodeGenerator);
- break;
- case knopForOf:
- byteCodeGenerator->AssignNullConstRegister();
- byteCodeGenerator->AssignUndefinedConstRegister();
- CheckMaybeEscapedUse(pnode->sxForInOrForOf.pnodeObj, byteCodeGenerator);
- break;
- case knopTrue:
- pnode->location = byteCodeGenerator->AssignTrueConstRegister();
- break;
- case knopFalse:
- pnode->location = byteCodeGenerator->AssignFalseConstRegister();
- break;
- case knopDecPost:
- case knopIncPost:
- case knopDecPre:
- case knopIncPre:
- byteCodeGenerator->EnregisterConstant(1);
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- case knopObject:
- byteCodeGenerator->AssignNullConstRegister();
- break;
- case knopClassDecl:
- {
- FuncInfo * topFunc = byteCodeGenerator->TopFuncInfo();
- topFunc->SetHasMaybeEscapedNestedFunc(DebugOnly(L"Class"));
- // We may need undefined for the 'this', e.g. calling a class expression
- byteCodeGenerator->AssignUndefinedConstRegister();
- break;
- }
- case knopNull:
- pnode->location = byteCodeGenerator->AssignNullConstRegister();
- break;
- case knopThis:
- {
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- pnode->location = func->AssignThisRegister();
- if (func->IsLambda())
- {
- func = byteCodeGenerator->FindEnclosingNonLambda();
- func->AssignThisRegister();
- if (func->IsGlobalFunction() && !(byteCodeGenerator->GetFlags() & fscrEval))
- {
- byteCodeGenerator->AssignNullConstRegister();
- }
- }
- // "this" should be loaded for both global and non global functions
- if (func->IsGlobalFunction() && !(byteCodeGenerator->GetFlags() & fscrEval))
- {
- // We'll pass "null" to LdThis, to simulate "null" passed as "this" to the
- // global function.
- func->AssignNullConstRegister();
- }
- break;
- }
- case knopNewTarget:
- {
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- pnode->location = func->AssignNewTargetRegister();
- FuncInfo* nonLambdaFunc = func;
- if (func->IsLambda())
- {
- nonLambdaFunc = byteCodeGenerator->FindEnclosingNonLambda();
- }
- if (nonLambdaFunc != func || (func->IsGlobalFunction() && (byteCodeGenerator->GetFlags() & fscrEval)))
- {
- nonLambdaFunc->root->sxFnc.SetHasNewTargetReference();
- nonLambdaFunc->AssignNewTargetRegister();
- nonLambdaFunc->SetIsNewTargetLexicallyCaptured();
- nonLambdaFunc->GetBodyScope()->SetHasLocalInClosure(true);
- func->SetHasClosureReference(true);
- }
- break;
- }
- case knopSuper:
- {
- FuncInfo* func = byteCodeGenerator->TopFuncInfo();
- pnode->location = func->AssignSuperRegister();
- func->AssignThisRegister();
- FuncInfo* nonLambdaFunc = func;
- if (func->IsLambda())
- {
- // If this is a lambda inside a class member, the class member will need to load super.
- nonLambdaFunc = byteCodeGenerator->FindEnclosingNonLambda();
- nonLambdaFunc->root->sxFnc.SetHasSuperReference();
- nonLambdaFunc->AssignSuperRegister();
- nonLambdaFunc->AssignThisRegister();
- nonLambdaFunc->SetIsSuperLexicallyCaptured();
- if (nonLambdaFunc->IsClassConstructor())
- {
- func->AssignNewTargetRegister();
- nonLambdaFunc->root->sxFnc.SetHasNewTargetReference();
- nonLambdaFunc->AssignNewTargetRegister();
- nonLambdaFunc->SetIsNewTargetLexicallyCaptured();
- nonLambdaFunc->AssignUndefinedConstRegister();
- }
- nonLambdaFunc->GetBodyScope()->SetHasLocalInClosure(true);
- func->SetHasClosureReference(true);
- }
- else
- {
- if (func->IsClassConstructor())
- {
- func->AssignNewTargetRegister();
- }
- }
- if (nonLambdaFunc->IsGlobalFunction())
- {
- if (!(byteCodeGenerator->GetFlags() & fscrEval))
- {
- // Enable LdSuper for global function to support subsequent emission of call, dot, prop, etc., related to super.
- func->AssignNullConstRegister();
- nonLambdaFunc->AssignNullConstRegister();
- }
- }
- else if (!func->IsClassMember())
- {
- func->AssignUndefinedConstRegister();
- }
- break;
- }
- case knopCall:
- {
- if (pnode->sxCall.pnodeTarget->nop != knopIndex &&
- pnode->sxCall.pnodeTarget->nop != knopDot)
- {
- byteCodeGenerator->AssignUndefinedConstRegister();
- }
- bool containsDirectSuper = ContainsDirectSuper(pnode);
- bool containsSuperReference = ContainsSuperReference(pnode);
- if (containsDirectSuper)
- {
- pnode->sxCall.pnodeTarget->location = byteCodeGenerator->TopFuncInfo()->AssignSuperCtorRegister();
- }
- FuncInfo *funcInfo = byteCodeGenerator->TopFuncInfo();
- if (containsDirectSuper || containsSuperReference)
- {
- // A super call requires 'this' to be available.
- byteCodeGenerator->SetNeedEnvRegister();
- byteCodeGenerator->AssignThisRegister();
- FuncInfo* parent = funcInfo;
- if (funcInfo->IsLambda())
- {
- // If this is a lambda inside a class member, the class member will need to load super.
- parent = byteCodeGenerator->FindEnclosingNonLambda();
- if (parent->root->sxFnc.IsClassMember())
- {
- // Set up super reference
- if (containsSuperReference)
- {
- parent->root->sxFnc.SetHasSuperReference();
- parent->AssignSuperRegister();
- parent->SetIsSuperLexicallyCaptured();
- }
- else if (containsDirectSuper)
- {
- parent->root->sxFnc.SetHasDirectSuper();
- parent->AssignSuperCtorRegister();
- parent->SetIsSuperCtorLexicallyCaptured();
- }
- parent->GetBodyScope()->SetHasLocalInClosure(true);
- funcInfo->SetHasClosureReference(true);
- }
- parent->AssignThisRegister();
- byteCodeGenerator->MarkThisUsedInLambda();
- }
- // If this is a super call we need to have new.target
- if (pnode->sxCall.pnodeTarget->nop == knopSuper)
- {
- byteCodeGenerator->AssignNewTargetRegister();
- }
- }
- if (pnode->sxCall.isEvalCall)
- {
- if (!funcInfo->GetParsedFunctionBody()->IsReparsed())
- {
- Assert(funcInfo->IsGlobalFunction() || funcInfo->GetCallsEval());
- funcInfo->SetCallsEval(true);
- funcInfo->GetParsedFunctionBody()->SetCallsEval(true);
- }
- else
- {
- // On reparsing, load the state from function Body, instead of using the state on the parse node,
- // as they might be different.
- pnode->sxCall.isEvalCall = funcInfo->GetParsedFunctionBody()->GetCallsEval();
- }
- if (funcInfo->IsLambda() && pnode->sxCall.isEvalCall)
- {
- FuncInfo* nonLambdaParent = byteCodeGenerator->FindEnclosingNonLambda();
- if (!nonLambdaParent->IsGlobalFunction() || (byteCodeGenerator->GetFlags() & fscrEval))
- {
- nonLambdaParent->AssignThisRegister();
- }
- }
- // An eval call in a class member needs to load super.
- if (funcInfo->root->sxFnc.IsClassMember())
- {
- funcInfo->AssignSuperRegister();
- if (funcInfo->root->sxFnc.IsClassConstructor() && !funcInfo->root->sxFnc.IsBaseClassConstructor())
- {
- funcInfo->AssignSuperCtorRegister();
- }
- }
- else if (funcInfo->IsLambda())
- {
- // If this is a lambda inside a class member, the class member will need to load super.
- FuncInfo *parent = byteCodeGenerator->FindEnclosingNonLambda();
- if (parent->root->sxFnc.IsClassMember())
- {
- parent->root->sxFnc.SetHasSuperReference();
- parent->AssignSuperRegister();
- if (parent->IsClassConstructor() && !parent->IsBaseClassConstructor())
- {
- parent->AssignSuperCtorRegister();
- }
- }
- }
- }
- // Don't need to check pnode->sxCall.pnodeTarget even if it is a knopFncDecl,
- // e.g. (function(){})();
- // It is only used as a call, so don't count as an escape.
- // Although not assigned to a slot, we will still able to box it by boxing
- // all the stack function on the interpreter frame or the stack function link list
- // on a jitted frame
- break;
- }
- case knopInt:
- pnode->location = byteCodeGenerator->EnregisterConstant(pnode->sxInt.lw);
- break;
- case knopFlt:
- {
- pnode->location = byteCodeGenerator->EnregisterDoubleConstant(pnode->sxFlt.dbl);
- break;
- }
- case knopStr:
- pnode->location = byteCodeGenerator->EnregisterStringConstant(pnode->sxPid.pid);
- break;
- case knopVarDecl:
- case knopConstDecl:
- case knopLetDecl:
- {
- sym = pnode->sxVar.sym;
- Assert(sym != nullptr);
- Assert(sym->GetScope()->GetEnclosingFunc() == byteCodeGenerator->TopFuncInfo());
- if (pnode->sxVar.isBlockScopeFncDeclVar && sym->GetIsBlockVar())
- {
- break;
- }
- if (!sym->GetIsGlobal())
- {
- FuncInfo *funcInfo = byteCodeGenerator->TopFuncInfo();
- // Check the function assignment for the sym that we have, even if we remap it to function level sym below
- // as we are going assign to the original sym
- CheckFuncAssignment(sym, pnode->sxVar.pnodeInit, byteCodeGenerator);
- if (sym->GetIsCatch() || (pnode->nop == knopVarDecl && sym->GetIsBlockVar() && !pnode->sxVar.isBlockScopeFncDeclVar))
- {
- // The LHS of the var decl really binds to the local symbol, not the catch or let symbol.
- // But the assignment will go to the catch or let symbol. Just assign a register to the local
- // so that it can get initialized to undefined.
- #if DBG
- if (!sym->GetIsCatch())
- {
- // Catch cannot be at function scope and let and var at function scope is redeclaration error.
- Assert(funcInfo->bodyScope != sym->GetScope() || !byteCodeGenerator->GetScriptContext()->GetConfig()->IsBlockScopeEnabled());
- }
- #endif
- auto symName = sym->GetName();
- sym = funcInfo->bodyScope->FindLocalSymbol(symName);
- if (sym == nullptr)
- {
- sym = funcInfo->paramScope->FindLocalSymbol(symName);
- }
- Assert((sym && !sym->GetIsCatch() && !sym->GetIsBlockVar()));
- }
- // Don't give the declared var a register if it's in a closure, because the closure slot
- // is its true "home". (Need to check IsGlobal again as the sym may have changed above.)
- if (!sym->GetIsGlobal() && !sym->IsInSlot(funcInfo))
- {
- if (PHASE_TRACE(Js::DelayCapturePhase, funcInfo->byteCodeFunction))
- {
- if (sym->NeedsSlotAlloc(byteCodeGenerator->TopFuncInfo()))
- {
- Output::Print(L"--- DelayCapture: Delayed capturing symbol '%s' during initialization.\n", sym->GetName());
- Output::Flush();
- }
- }
- byteCodeGenerator->AssignRegister(sym);
- }
- }
- else
- {
- Assert(byteCodeGenerator->TopFuncInfo()->IsGlobalFunction());
- }
- break;
- }
- case knopFor:
- if ((pnode->sxFor.pnodeBody != nullptr) && (pnode->sxFor.pnodeBody->nop == knopBlock) &&
- (pnode->sxFor.pnodeBody->sxBlock.pnodeStmt != nullptr) &&
- (pnode->sxFor.pnodeBody->sxBlock.pnodeStmt->nop == knopFor) &&
- (!byteCodeGenerator->IsInDebugMode()))
- {
- FuncInfo *funcInfo = byteCodeGenerator->TopFuncInfo();
- pnode->sxFor.pnodeInverted = InvertLoop(pnode, byteCodeGenerator, funcInfo);
- }
- else
- {
- pnode->sxFor.pnodeInverted = nullptr;
- }
- break;
- case knopName:
- sym = pnode->sxPid.sym;
- if (sym == nullptr)
- {
- Assert(pnode->sxPid.pid->GetPropertyId() != Js::Constants::NoProperty);
- }
- else
- {
- // Note: don't give a register to a local if it's in a closure, because then the closure
- // is its true home.
- if (!sym->GetIsGlobal() &&
- !sym->GetIsMember() &&
- byteCodeGenerator->TopFuncInfo() == sym->GetScope()->GetEnclosingFunc() &&
- !sym->IsInSlot(byteCodeGenerator->TopFuncInfo()) &&
- !sym->HasVisitedCapturingFunc())
- {
- if (PHASE_TRACE(Js::DelayCapturePhase, byteCodeGenerator->TopFuncInfo()->byteCodeFunction))
- {
- if (sym->NeedsSlotAlloc(byteCodeGenerator->TopFuncInfo()))
- {
- Output::Print(L"--- DelayCapture: Delayed capturing symbol '%s'.\n", sym->GetName());
- Output::Flush();
- }
- }
- // Local symbol being accessed in its own frame. Even if "with" or event
- // handler semantics make the binding ambiguous, it has a home location,
- // so assign it.
- byteCodeGenerator->AssignRegister(sym);
- // If we're in something like a "with" we'll need a scratch register to hold
- // the multiple possible values of the property.
- if (!byteCodeGenerator->HasInterleavingDynamicScope(sym))
- {
- // We're not in a dynamic scope, or our home scope is nested within the dynamic scope, so we
- // don't have to do dynamic binding. Just use the home location for this reference.
- pnode->location = sym->GetLocation();
- }
- }
- }
- if (pnode->IsInList() && !pnode->IsNotEscapedUse())
- {
- // A node that is in a list is assumed to be escape, unless marked otherwise.
- // This includes array literal list/object literal list
- CheckMaybeEscapedUse(pnode, byteCodeGenerator);
- }
- break;
- case knopProg:
- if (!byteCodeGenerator->HasParentScopeInfo())
- {
- // If we're compiling a nested deferred function, don't pop the scope stack,
- // because we just want to leave it as-is for the emit pass.
- PostVisitFunction(pnode, byteCodeGenerator);
- }
- break;
- case knopReturn:
- {
- ParseNode *pnodeExpr = pnode->sxReturn.pnodeExpr;
- CheckMaybeEscapedUse(pnodeExpr, byteCodeGenerator);
- break;
- }
- case knopStrTemplate:
- {
- ParseNode* pnodeExprs = pnode->sxStrTemplate.pnodeSubstitutionExpressions;
- if (pnodeExprs != nullptr)
- {
- while (pnodeExprs->nop == knopList)
- {
- Assert(pnodeExprs->sxBin.pnode1 != nullptr);
- Assert(pnodeExprs->sxBin.pnode2 != nullptr);
- CheckMaybeEscapedUse(pnodeExprs->sxBin.pnode1, byteCodeGenerator);
- pnodeExprs = pnodeExprs->sxBin.pnode2;
- }
- // Also check the final element in the list
- CheckMaybeEscapedUse(pnodeExprs, byteCodeGenerator);
- }
- if (pnode->sxStrTemplate.isTaggedTemplate)
- {
- pnode->location = byteCodeGenerator->EnregisterStringTemplateCallsiteConstant(pnode);
- }
- break;
- }
- case knopYieldLeaf:
- byteCodeGenerator->AssignUndefinedConstRegister();
- break;
- case knopYield:
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- case knopYieldStar:
- byteCodeGenerator->AssignNullConstRegister();
- byteCodeGenerator->AssignUndefinedConstRegister();
- CheckMaybeEscapedUse(pnode->sxUni.pnode1, byteCodeGenerator);
- break;
- }
- }
- // TODO[ianhall]: ApplyEnclosesArgs should be in ByteCodeEmitter.cpp but that becomes complicated because it depends on VisitIndirect
- void PostCheckApplyEnclosesArgs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, ApplyCheck* applyCheck);
- void CheckApplyEnclosesArgs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, ApplyCheck* applyCheck);
- bool ApplyEnclosesArgs(ParseNode* fncDecl, ByteCodeGenerator* byteCodeGenerator)
- {
- if (byteCodeGenerator->IsInDebugMode())
- {
- // Inspection of the arguments object will be messed up if we do ApplyArgs.
- return false;
- }
- if (!fncDecl->HasVarArguments()
- && fncDecl->sxFnc.pnodeArgs == nullptr
- && fncDecl->sxFnc.pnodeRest == nullptr
- && fncDecl->sxFnc.nestedCount == 0)
- {
- ApplyCheck applyCheck;
- applyCheck.matches = true;
- applyCheck.sawApply = false;
- applyCheck.insideApplyCall = false;
- VisitIndirect<ApplyCheck>(fncDecl->sxFnc.pnodeBody, byteCodeGenerator, &applyCheck, &CheckApplyEnclosesArgs, &PostCheckApplyEnclosesArgs);
- return applyCheck.matches&&applyCheck.sawApply;
- }
- return false;
- }
- // TODO[ianhall]: VisitClearTmpRegs should be in ByteCodeEmitter.cpp but that becomes complicated because it depends on VisitIndirect
- void ClearTmpRegs(ParseNode* pnode, ByteCodeGenerator* byteCodeGenerator, FuncInfo* emitFunc);
- void VisitClearTmpRegs(ParseNode * pnode, ByteCodeGenerator * byteCodeGenerator, FuncInfo * funcInfo)
- {
- VisitIndirect<FuncInfo>(pnode, byteCodeGenerator, funcInfo, &ClearTmpRegs, nullptr);
- }
- Js::FunctionBody * ByteCodeGenerator::MakeGlobalFunctionBody(ParseNode *pnode)
- {
- Js::FunctionBody * func;
- ENTER_PINNED_SCOPE(Js::PropertyRecordList, propertyRecordList);
- propertyRecordList = EnsurePropertyRecordList();
- func =
- Js::FunctionBody::NewFromRecycler(
- scriptContext,
- Js::Constants::GlobalFunction,
- Js::Constants::GlobalFunctionLength,
- 0,
- pnode->sxFnc.nestedCount,
- m_utf8SourceInfo,
- m_utf8SourceInfo->GetSrcInfo()->sourceContextInfo->sourceContextId,
- pnode->sxFnc.functionId,
- propertyRecordList,
- Js::FunctionInfo::Attributes::None
- #ifdef PERF_COUNTERS
- , false /* is function from deferred deserialized proxy */
- #endif
- );
- func->SetIsGlobalFunc(true);
- scriptContext->RegisterDynamicFunctionReference(func);
- LEAVE_PINNED_SCOPE();
- return func;
- }
- /* static */
- bool ByteCodeGenerator::NeedScopeObjectForArguments(FuncInfo *funcInfo, ParseNode *pnodeFnc)
- {
- // We can avoid creating a scope object with arguments present if:
- bool dontNeedScopeObject =
- // We have arguments, and
- funcInfo->GetHasHeapArguments()
- // Either we are in strict mode, or have strict mode formal semantics from a non-simple parameter list, and
- && (funcInfo->GetIsStrictMode()
- || !pnodeFnc->sxFnc.IsSimpleParameterList())
- // Neither of the scopes are objects
- && !funcInfo->paramScope->GetIsObject()
- && !funcInfo->bodyScope->GetIsObject();
- return funcInfo->GetHasHeapArguments()
- // Regardless of the conditions above, we won't need a scope object if there aren't any formals.
- && (pnodeFnc->sxFnc.pnodeArgs != nullptr || pnodeFnc->sxFnc.pnodeRest != nullptr)
- && !dontNeedScopeObject;
- }
- Js::FunctionBody *ByteCodeGenerator::EnsureFakeGlobalFuncForUndefer(ParseNode *pnode)
- {
- Js::FunctionBody *func = scriptContext->GetFakeGlobalFuncForUndefer();
- if (!func)
- {
- func = this->MakeGlobalFunctionBody(pnode);
- scriptContext->SetFakeGlobalFuncForUndefer(func);
- }
- else
- {
- func->SetBoundPropertyRecords(EnsurePropertyRecordList());
- }
- if (pnode->sxFnc.GetStrictMode() != 0)
- {
- func->SetIsStrictMode();
- }
- return func;
- }
|